Skip to content

Commit

Permalink
imp: adds better error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
kbknapp committed Aug 9, 2015
1 parent bdf300a commit 9032454
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 103 deletions.
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ authors = ["Kevin K. <kbknapp@gmail.com>"]
clap = "*"
toml = "*"
semver = "*"
tabwriter = "*"
tempdir = "*"

[dependencies.ansi_term]
version = "*"
optional = true

[features]
debug = []
default = ["color"]
color = ["ansi_term"]
debug = []
12 changes: 3 additions & 9 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
use std::env;
use std::fs;
use std::path::{PathBuf, Path};

use clap::ArgMatches;

#[derive(Debug)]
pub struct Config<'tu> {
to_update: Option<Vec<&'tu str>>,
depth: u8,
pub tmp_dir: PathBuf
pub verbose: bool
}

impl<'tu> Config<'tu> {
pub fn from_matches(m: &'tu ArgMatches) -> Self {
let temp_dir = env::temp_dir().join("cargo-outdated");
fs::create_dir(&temp_dir).unwrap();
Config {
to_update: m.values_of("PKG"),
depth: m.value_of("DEPTH").unwrap_or("1").parse().unwrap_or(1),
tmp_dir: temp_dir
verbose: m.is_present("verbose")
}
}
}
}
2 changes: 0 additions & 2 deletions src/deps/dep.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use semver::Version;

pub struct Dep {
pub name: String,
pub raw_ver: Option<String>,
Expand Down
71 changes: 71 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::fmt::Result as FmtResult;

use fmt::Format;

#[derive(Debug)]
#[allow(dead_code)]
pub enum CliError {
Generic(String),
FileOpen(String),
TomlTableRoot,
NoRootDeps,
NoNonRootDeps,
Unknown
}

// Copies clog::error::Error;
impl CliError {
/// Return whether this was a fatal error or not.
#[allow(dead_code)]
pub fn is_fatal(&self) -> bool {
// For now all errors are fatal
true
}

/// Print this error and immediately exit the program.
///
/// If the error is non-fatal then the error is printed to stdout and the
/// exit status will be `0`. Otherwise, when the error is fatal, the error
/// is printed to stderr and the exit status will be `1`.
pub fn exit(&self) -> ! {
if self.is_fatal() {
wlnerr!("{}", self);
::std::process::exit(1)
} else {
println!("{}", self);
::std::process::exit(0)
}
}
}

impl Display for CliError {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "{} {}", Format::Error("error:"), self.description())
}
}

impl Error for CliError {
fn description<'a>(&'a self) -> &'a str {
match *self {
CliError::Generic(ref d) => &*d,
CliError::FileOpen(ref d) => &*d,
CliError::TomlTableRoot => "couldn't find '[root]' table in Cargo.lock",
CliError::NoRootDeps => "No root dependencies",
CliError::NoNonRootDeps => "No non root dependencies",
CliError::Unknown => "An unknown fatal error has occurred, please consider filing a bug-report!"
}
}

fn cause(&self) -> Option<&Error> {
match *self {
CliError::Generic(..) => None,
CliError::FileOpen(..) => None,
CliError::Unknown => None,
CliError::NoRootDeps => None,
CliError::NoNonRootDeps => None,
CliError::TomlTableRoot => None,
}
}
}
50 changes: 50 additions & 0 deletions src/fmt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::fmt;

#[cfg(all(feature = "color", not(target_os = "windows")))]
use ansi_term::Colour::{Red, Green, Yellow};
#[cfg(all(feature = "color", not(target_os = "windows")))]
use ansi_term::ANSIString;

#[allow(dead_code)]
pub enum Format<T> {
Error(T),
Warning(T),
Good(T),
}

#[cfg(all(feature = "color", not(target_os = "windows")))]
impl<T: AsRef<str>> Format<T> {
fn format(&self) -> ANSIString {
match *self {
Format::Error(ref e) => Red.bold().paint(e.as_ref()),
Format::Warning(ref e) => Yellow.paint(e.as_ref()),
Format::Good(ref e) => Green.paint(e.as_ref()),
}
}

}

#[cfg(all(feature = "color", not(target_os = "windows")))]
impl<T: AsRef<str>> fmt::Display for Format<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.format())
}
}

#[cfg(any(not(feature = "color"), target_os = "windows"))]
impl<T: fmt::Display> Format<T> {
fn format(&self) -> &T {
match *self {
Format::Error(ref e) => e,
Format::Warning(ref e) => e,
Format::Good(ref e) => e,
}
}
}

#[cfg(any(not(feature = "color"), target_os = "windows"))]
impl<T: fmt::Display> fmt::Display for Format<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.format())
}
}
100 changes: 90 additions & 10 deletions src/lockfile.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use std::collections::HashMap;
use std::io::{Read, Write};
use std::error::Error;
use std::fs::File;
use std::path::Path;

use toml::{Value, Table};
use toml::{self, Value, Table};

use deps::RawDep;
use deps::Dep;
use error::CliError;

use CliResult;

Expand All @@ -17,12 +22,59 @@ impl Lockfile {
Lockfile { deps: HashMap::new() }
}

pub fn from_file<P: AsRef<Path>>(p: P) -> CliResult<Self> {
debugln!("executing; parse_lockfile");
let mut f = match File::open(p.as_ref()) {
Ok(f) => f,
Err(e) => return Err(CliError::FileOpen(e.description().to_owned()))
};

let mut s = String::new();
if let Err(e) = f.read_to_string(&mut s) {
return Err(CliError::Generic(format!("Couldn't read the contents of Cargo.lock with error: {}", e.description())))
}

let mut parser = toml::Parser::new(&s);
match parser.parse() {
Some(toml) => return Lockfile::parse_table(toml),
None => {}
}


// On err
let mut error_str = format!("could not parse input as TOML\n");
for error in parser.errors.iter() {
let (loline, locol) = parser.to_linecol(error.lo);
let (hiline, hicol) = parser.to_linecol(error.hi);
error_str.push_str(&format!("{:?}:{}:{}{} {}\n",
f,
loline + 1, locol + 1,
if loline != hiline || locol != hicol {
format!("-{}:{}", hiline + 1,
hicol + 1)
} else {
"".to_string()
},
error.desc));
}
Err(CliError::Generic(error_str))
}

fn parse_table(table: Table) -> CliResult<Self> {
debugln!("executing; parse_table");
let mut lockfile = Lockfile::new();

try!(lockfile.get_root_deps(&table));

try!(lockfile.get_non_root_deps(&table));

Ok(lockfile)
}

pub fn get_root_deps(&mut self, table: &Table) -> CliResult<()> {
let root_table = match table.get("root") {
Some(table) => table,
None => {
return Err(String::from("couldn't find '[root]' table in Cargo.lock"));
}
None => return Err(CliError::TomlTableRoot)
};

match root_table.lookup("dependencies") {
Expand All @@ -32,33 +84,61 @@ impl Lockfile {
for v in val {
let val_str = v.as_str().unwrap_or("");
debugln!("adding root dep {}", val_str);
let mut raw_dep: RawDep = try!(val_str.parse());
let mut raw_dep: RawDep = match val_str.parse() {
Ok(val) => val,
Err(e) => return Err(CliError::Generic(e))
};
raw_dep.is_root = true;
self.deps.insert(raw_dep.name.clone(), raw_dep);
}
},
Some(_) => unreachable!(),
None => return Err(String::from("No root dependencies"))
None => return Err(CliError::NoRootDeps)
};

debugln!("Root deps: {:?}", self.deps);
Ok(())
}

fn write_manifest_pretext(w: &mut W) -> CliResult<()> where W: Write {
write!(w, "[package]\n\
name = \"temp\"\n\
version = \"1.0.0\"\n\
[[bin]]\n\
name = \"test\"\n\
[dependencies]\n").unwrap();
}

pub fn write_semver_manifest(w: &mut W) -> CliResult<()> where W: Write {
try!(Lockfile::write_manifest_pretext(w));

for dep in all_deps.deps.values() {
write!(mf, "{} = \"~{}\"\n", dep.name, dep.ver).unwrap();
}
}
pub fn write_allver_manifest(w: &mut W) -> CliResult<()> where W: Write {
try!(Lockfile::write_manifest_pretext(w));

for dep in all_deps.deps.values() {
write!(mf, "{} = \"*\"\n", dep.name).unwrap();
}
}

pub fn get_non_root_deps(&mut self, table: &Table) -> CliResult<()> {
let arr = match table.get("package") {
Some(&Value::Array(ref val)) => {
debugln!("found non root deps");

for v in val {
let name_str = v.lookup("name").unwrap().as_str().unwrap_or("");
let ver_str = v.lookup("version").unwrap().as_str().unwrap_or("");
match v.lookup("dependencies") {
Some(&Value::Array(ref deps)) => {
for d in deps {
let dep_str = d.as_str().unwrap_or("");
debugln!("adding non root dep {}", dep_str);
let raw_dep: RawDep = try!(dep_str.parse());
let raw_dep: RawDep = match dep_str.parse() {
Ok(val) => val,
Err(e) => return Err(CliError::Generic(e))
};
self.deps.insert(raw_dep.name.clone(), raw_dep);
}
},
Expand All @@ -68,7 +148,7 @@ impl Lockfile {
}
},
Some(_) => unreachable!(),
None => return Err(String::from("No non root dependencies"))
None => return Err(CliError::NoNonRootDeps)
};

debugln!("All deps: {:#?}", self.deps);
Expand Down
18 changes: 18 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ macro_rules! werr(
})
);

macro_rules! verbose(
($cfg:ident, $($arg:tt)*) => ({
if $cfg.verbose {
use std::io::{Write, stdout};
write!(&mut stdout(), $($arg)*).ok();
}
})
);

macro_rules! verboseln(
($cfg:ident, $($arg:tt)*) => ({
if $cfg.verbose {
use std::io::{Write, stdout};
writeln!(&mut stdout(), $($arg)*).ok();
}
})
);

#[cfg(feature = "debug")]
macro_rules! debugln {
($fmt:expr) => (println!(concat!("**DEBUG** ", $fmt)));
Expand Down
Loading

0 comments on commit 9032454

Please sign in to comment.