Add support for reading config
authorYehuda Katz <wycats@gmail.com>
Mon, 7 Apr 2014 01:26:36 +0000 (18:26 -0700)
committerYehuda Katz <wycats@gmail.com>
Mon, 7 Apr 2014 01:26:36 +0000 (18:26 -0700)
Makefile
libs/rust-toml
src/bin/cargo.rs [new file with mode: 0644]
src/cargo/mod.rs
src/cargo/util/config.rs [new file with mode: 0644]
src/cargo/util/important_paths.rs [new file with mode: 0644]
src/cargo/util/mod.rs

index 054ee0983e10e970104a2d130509d73e14b5b333..6d50d154d7349e0c0180b57850cb7be57a4e3bfa 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,10 +2,11 @@ RUSTC ?= rustc
 RUSTC_FLAGS ?=
 
 # Link flags to pull in dependencies
-BINS = cargo-compile \
-          cargo-read-manifest \
-          cargo-rustc \
-          cargo-verify-project
+BINS = cargo \
+                  cargo-compile \
+            cargo-read-manifest \
+            cargo-rustc \
+            cargo-verify-project
 
 SRC = $(shell find src -name '*.rs')
 
index 49290aedf236c365c08fbc01eb70127eb5ab4a60..a7c9ead90b7eb51521f3578307fe44ca8f087b9a 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 49290aedf236c365c08fbc01eb70127eb5ab4a60
+Subproject commit a7c9ead90b7eb51521f3578307fe44ca8f087b9a
diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs
new file mode 100644 (file)
index 0000000..bfc3b1e
--- /dev/null
@@ -0,0 +1,76 @@
+extern crate cargo;
+extern crate toml;
+extern crate hammer;
+extern crate serialize;
+extern crate collections;
+
+use hammer::{FlagConfig,FlagConfiguration};
+use std::{os,io};
+use serialize::{Decodable,Encodable,json};
+use cargo::{CargoResult,ToCargoError,NoFlags,execute_main_without_stdin,process_executed,handle_error};
+use cargo::util::important_paths::find_project;
+use cargo::util::config;
+
+fn main() {
+    execute();
+}
+
+#[deriving(Encodable)]
+struct ProjectLocation {
+    root: ~str
+}
+
+fn execute() {
+    let (cmd, args) = match process(os::args()) {
+        Ok((cmd, args)) => (cmd, args),
+        Err(err) => return handle_error(err)
+    };
+
+    if cmd == ~"config" { execute_main_without_stdin(config) }
+    else if cmd == ~"locate-project" { execute_main_without_stdin(locate_project) }
+}
+
+fn process(mut args: ~[~str]) -> CargoResult<(~str, ~[~str])> {
+    args = args.tail().to_owned();
+    let head = try!(args.head().to_cargo_error(~"No subcommand found", 1)).to_owned();
+    let tail = args.tail().to_owned();
+
+    Ok((head, tail))
+}
+
+#[deriving(Decodable)]
+struct ConfigFlags {
+    key: ~str,
+    value: Option<~str>,
+    human: bool
+}
+
+impl FlagConfig for ConfigFlags {
+    fn config(_: Option<ConfigFlags>, c: FlagConfiguration) -> FlagConfiguration {
+        c.short("human", 'h')
+    }
+}
+
+#[deriving(Encodable)]
+struct ConfigOut {
+    values: collections::HashMap<~str, config::ConfigValue>
+}
+
+fn config(args: ConfigFlags) -> CargoResult<Option<ConfigOut>> {
+    let value = try!(config::get_config(os::getcwd(), args.key.as_slice()));
+
+    if args.human {
+        println!("{}", value);
+        Ok(None)
+    } else {
+        let mut map = collections::HashMap::new();
+        map.insert(args.key.clone(), value);
+        Ok(Some(ConfigOut { values: map }))
+    }
+}
+
+fn locate_project(args: NoFlags) -> CargoResult<Option<ProjectLocation>> {
+    let root = try!(find_project(os::getcwd(), ~"Cargo.toml"));
+    let string = try!(root.as_str().to_cargo_error(format!("Your project path contains characters not representable in Unicode: {}", os::getcwd().display()), 1));
+    Ok(Some(ProjectLocation { root: string.to_owned() }))
+}
index ed54f4cb66c3a30fad38bfe374858d0229e55109..4c9811441d6210bcdfa2402a8a14630dec56d72f 100644 (file)
@@ -133,12 +133,9 @@ pub fn execute_main_without_stdin<'a, T: RepresentsFlags, V: Encodable<json::Enc
     process_executed(call(exec))
 }
 
-fn process_executed<'a, T: Encodable<json::Encoder<'a>, io::IoError>>(result: CargoResult<Option<T>>) {
+pub fn process_executed<'a, T: Encodable<json::Encoder<'a>, io::IoError>>(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);
-        },
+        Err(e) => handle_error(e),
         Ok(encodable) => {
             encodable.map(|encodable| {
                 let encoded: ~str = json::Encoder::str_encode(&encodable);
@@ -148,6 +145,11 @@ fn process_executed<'a, T: Encodable<json::Encoder<'a>, io::IoError>>(result: Ca
     }
 }
 
+pub fn handle_error(err: CargoError) {
+    let _ = write!(&mut std::io::stderr(), "{}", err.message);
+    std::os::set_exit_status(err.exit_code as int);
+}
+
 fn flags_from_args<T: RepresentsFlags>() -> CargoResult<T> {
     let mut decoder = FlagDecoder::new::<T>(std::os::args().tail());
     Decodable::decode(&mut decoder).to_cargo_error(|e: HammerError| e.message, 1)
diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs
new file mode 100644 (file)
index 0000000..087a7b3
--- /dev/null
@@ -0,0 +1,58 @@
+extern crate toml;
+
+use super::super::{CargoResult,CargoError,ToCargoError};
+use std::{io,fmt};
+
+#[deriving(Eq,Clone,Encodable,Decodable)]
+pub enum Location {
+    Project,
+    Global
+}
+
+#[deriving(Eq,Clone,Encodable,Decodable)]
+pub struct ConfigValue {
+    value: ~str,
+    path: ~str
+}
+
+impl fmt::Show for ConfigValue {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f.buf, "{} (from {})", self.value, self.path)
+    }
+}
+
+pub fn get_config(pwd: Path, key: &str) -> CargoResult<ConfigValue> {
+    walk_tree(&pwd, |file| extract_config(file, key)).to_cargo_error(format!("Config key not found: {}", key), 1)
+}
+
+pub fn set_config(key: ~str, value: ~str, location: Location) -> CargoResult<()> {
+    Ok(())
+}
+
+fn walk_tree<T>(pwd: &Path, walk: |io::fs::File| -> CargoResult<T>) -> Option<T> {
+    let mut current = pwd.clone();
+
+    loop {
+        let possible = current.join(".cargo").join("config");
+        if possible.exists() {
+            let res = io::fs::File::open(&possible).map(|file| walk(file));
+
+            match res {
+                Ok(Ok(res)) => return Some(res),
+                _ => ()
+            }
+        }
+
+        if !current.pop() { break; }
+    }
+
+    None
+}
+
+fn extract_config(file: io::fs::File, key: &str) -> CargoResult<ConfigValue> {
+    let path = try!(file.path().as_str().to_cargo_error(~"", 1)).to_owned();
+    let mut buf = io::BufferedReader::new(file);
+    let root = try!(toml::parse_from_buffer(&mut buf).to_cargo_error(~"", 1));
+    let val = try!(try!(root.lookup(key).to_cargo_error(~"", 1)).get_str().to_cargo_error(~"", 1));
+    Ok(ConfigValue{ value: val.to_owned(), path: path })
+}
diff --git a/src/cargo/util/important_paths.rs b/src/cargo/util/important_paths.rs
new file mode 100644 (file)
index 0000000..231aca6
--- /dev/null
@@ -0,0 +1,16 @@
+use std::os;
+use super::super::{CargoResult,CargoError};
+
+pub fn find_project(pwd: Path, file: ~str) -> CargoResult<Path> {
+    let mut current = pwd.clone();
+
+    loop {
+        if current.join(file.clone()).exists() {
+            return Ok(current)
+        }
+
+        if !current.pop() { break; }
+    }
+
+    Err(CargoError::new(format!("Could not find a Cargo manifest ({}) in your current directory or any parent directory", file), 1))
+}
index e4fbd2514acee007cdfcc263989053f6dd89f5a4..9bd067829df804cc1ad9ab1af6910da76dcf0dfe 100644 (file)
@@ -1,2 +1,4 @@
 pub use self::process_builder::{process,ProcessBuilder};
 pub mod process_builder;
+pub mod config;
+pub mod important_paths;