Encoding and decoding now in libcargo

Commands can now call a simple function (execute_main) with some type
params, and get flags and stdin converted into their expected structs
automatically. Commands that return a json-serializable struct will also
get that struct automatically serialized into the output.

Additionally, error handling is now handled in a central location. If a
command returns a CargoError, its message will be printed (soon in
color!) and its exit code will be used to exit the process.
This commit is contained in:
Yehuda Katz 2014-03-20 17:55:33 -07:00
parent aec45be721
commit 1b1044b639
3 changed files with 58 additions and 37 deletions

View File

@ -8,9 +8,8 @@ extern crate toml;
use hammer::FlagConfig;
use serialize::Decoder;
use serialize::json::Encoder;
use toml::from_toml;
use cargo::{Manifest,LibTarget,ExecTarget,Project,CargoResult,ToCargoError,execute_main};
use cargo::{Manifest,LibTarget,ExecTarget,Project,CargoResult,ToCargoError,execute_main_without_stdin};
use std::path::Path;
#[deriving(Decodable,Encodable,Eq,Clone,Ord)]
@ -38,10 +37,10 @@ struct ReadManifestFlags {
impl FlagConfig for ReadManifestFlags {}
fn main() {
execute_main::<ReadManifestFlags>(execute);
execute_main_without_stdin::<ReadManifestFlags, Manifest>(execute);
}
fn execute(flags: ReadManifestFlags) -> CargoResult<()> {
fn execute(flags: ReadManifestFlags) -> CargoResult<Option<Manifest>> {
let manifest_path = flags.manifest_path;
let root = try!(toml::parse_from_file(manifest_path.clone()).to_cargo_error(format!("Couldn't parse Toml file: {}", manifest_path), 1));
@ -49,18 +48,12 @@ fn execute(flags: ReadManifestFlags) -> CargoResult<()> {
let (lib, bin) = normalize(&toml_manifest.lib, &toml_manifest.bin);
let manifest = Manifest{
Ok(Some(Manifest {
root: try!(Path::new(manifest_path.clone()).dirname_str().to_cargo_error(format!("Could not get dirname from {}", manifest_path), 1)).to_owned(),
project: toml_manifest.project,
lib: lib,
bin: bin
};
let encoded: ~str = Encoder::str_encode(&manifest);
println!("{}", encoded);
Ok(())
}))
}
fn normalize(lib: &Option<~[SerializedLibTarget]>, bin: &Option<~[SerializedExecTarget]>) -> (~[LibTarget], ~[ExecTarget]) {

View File

@ -6,14 +6,11 @@ extern crate hammer;
extern crate serialize;
extern crate cargo;
use hammer::FlagConfig;
use std::os::args;
use std::io;
use std::io::process::{Process,ProcessConfig,InheritFd};
use serialize::json;
use serialize::Decodable;
use std::path::Path;
use cargo::{Manifest,CargoResult,CargoError,ToCargoError,execute_main};
use cargo::{Manifest,CargoResult,CargoError,ToCargoError,NoFlags,execute_main};
/**
cargo-rustc -- ...args
@ -22,22 +19,10 @@ use cargo::{Manifest,CargoResult,CargoError,ToCargoError,execute_main};
*/
fn main() {
execute_main::<RustcFlags>(execute);
execute_main::<NoFlags, Manifest, Manifest>(execute);
}
#[deriving(Decodable,Eq,Clone,Ord)]
struct RustcFlags;
impl FlagConfig for RustcFlags {}
fn execute(_: RustcFlags) -> CargoResult<()> {
let mut reader = io::stdin();
let input = try!(reader.read_to_str().to_cargo_error(~"Cannot read stdin to a string", 1));
let json = try!(json::from_str(input).to_cargo_error(format!("Cannot parse json: {}", input), 1));
let mut decoder = json::Decoder::new(json);
let manifest: Manifest = Decodable::decode(&mut decoder);
fn execute(_: NoFlags, manifest: Manifest) -> CargoResult<Option<Manifest>> {
let Manifest{ root, lib, bin, .. } = manifest;
let (crate_type, out_dir) = if lib.len() > 0 {
@ -78,7 +63,7 @@ fn execute(_: RustcFlags) -> CargoResult<()> {
fail!("Failed to execute")
}
Ok(())
Ok(None)
}
fn join(path: &Path, part: ~str) -> ~str {

View File

@ -6,7 +6,8 @@
extern crate serialize;
extern crate hammer;
use serialize::{Decoder,Decodable};
use serialize::{Decoder,Encoder,Decodable,Encodable,json};
use std::io;
use std::fmt;
use std::fmt::{Show,Formatter};
use hammer::{FlagDecoder,FlagConfig};
@ -84,22 +85,54 @@ impl<T> ToCargoError<T> for Option<T> {
}
}
pub fn execute_main<T: FlagConfig + Decodable<FlagDecoder>>(exec: fn(T) -> CargoResult<()>) {
fn call<T: FlagConfig + Decodable<FlagDecoder>>(exec: fn(T) -> CargoResult<()>) -> CargoResult<()> {
trait RepresentsFlags : FlagConfig + Decodable<FlagDecoder> {}
impl<T: FlagConfig + Decodable<FlagDecoder>> RepresentsFlags for T {}
trait RepresentsJSON : Decodable<json::Decoder> {}
impl <T: Decodable<json::Decoder>> RepresentsJSON for T {}
#[deriving(Decodable)]
pub struct NoFlags;
impl FlagConfig for NoFlags {}
pub fn execute_main<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable<json::Encoder<'a>>>(exec: fn(T, U) -> CargoResult<Option<V>>) {
fn call<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable<json::Encoder<'a>>>(exec: fn(T, U) -> CargoResult<Option<V>>) -> CargoResult<Option<V>> {
let flags = try!(flags_from_args::<T>());
let json = try!(json_from_stdin::<U>());
exec(flags, json)
}
process_executed(call(exec))
}
pub fn execute_main_without_stdin<'a, T: RepresentsFlags, V: Encodable<json::Encoder<'a>>>(exec: fn(T) -> CargoResult<Option<V>>) {
fn call<'a, T: RepresentsFlags, V: Encodable<json::Encoder<'a>>>(exec: fn(T) -> CargoResult<Option<V>>) -> CargoResult<Option<V>> {
let flags = try!(flags_from_args::<T>());
exec(flags)
}
match call(exec) {
process_executed(call(exec))
}
fn process_executed<'a, T: Encodable<json::Encoder<'a>>>(result: CargoResult<Option<T>>) {
match result {
Err(e) => {
let _ = write!(&mut std::io::stderr(), "{}", e.message);
std::os::set_exit_status(e.exit_code as int);
},
Ok(_) => ()
Ok(encodable) => {
encodable.map(|encodable| {
let encoded: ~str = json::Encoder::str_encode(&encodable);
println!("{}", encoded);
});
}
}
}
fn flags_from_args<T: FlagConfig + Decodable<FlagDecoder>>() -> CargoResult<T> {
fn flags_from_args<T: RepresentsFlags>() -> CargoResult<T> {
let mut decoder = FlagDecoder::new::<T>(std::os::args().tail());
let flags: T = Decodable::decode(&mut decoder);
@ -108,3 +141,13 @@ fn flags_from_args<T: FlagConfig + Decodable<FlagDecoder>>() -> CargoResult<T> {
None => Ok(flags)
}
}
fn json_from_stdin<T: RepresentsJSON>() -> CargoResult<T> {
let mut reader = io::stdin();
let input = try!(reader.read_to_str().to_cargo_error(~"Cannot read stdin to a string", 1));
let json = try!(json::from_str(input).to_cargo_error(format!("Cannot parse json: {}", input), 1));
let mut decoder = json::Decoder::new(json);
Ok(Decodable::decode(&mut decoder))
}