Clean up config and support array inheritance
authorYehuda Katz <wycats@gmail.com>
Tue, 29 Apr 2014 22:24:01 +0000 (15:24 -0700)
committerYehuda Katz <wycats@gmail.com>
Tue, 29 Apr 2014 22:24:01 +0000 (15:24 -0700)
src/cargo/util/config.rs
vimrc [deleted file]

index f97385e2ba37f08d1b10b270078f7e71b1346612..cf72a49c58874adbfadb82378f721884a7b3ccc7 100644 (file)
@@ -1,7 +1,9 @@
 extern crate collections;
+extern crate serialize;
 extern crate toml;
 
 use super::super::{CargoResult,ToCargoError,CargoError};
+use serialize::{Encodable,Encoder};
 use std::{io,fmt};
 
 #[deriving(Eq,TotalEq,Clone,Encodable,Decodable)]
@@ -10,16 +12,50 @@ pub enum Location {
     Global
 }
 
-#[deriving(Eq,TotalEq,Clone,Encodable,Decodable,Show)]
+#[deriving(Eq,TotalEq,Clone,Decodable)]
 enum ConfigValueValue {
     String(~str),
-    List(~[~str])
+    List(Vec<~str>)
 }
 
-#[deriving(Eq,TotalEq,Clone,Encodable,Decodable)]
+impl fmt::Show for ConfigValueValue {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &String(ref string) => write!(f.buf, "{}", string),
+            &List(ref list) => write!(f.buf, "{}", list)
+        }
+    }
+}
+
+impl<E, S: Encoder<E>> Encodable<S, E> for ConfigValueValue {
+    fn encode(&self, s: &mut S) -> Result<(), E> {
+        match self {
+            &String(ref string) => {
+                try!(string.encode(s));
+            },
+            &List(ref list) => {
+                try!(list.encode(s));
+            }
+        }
+
+        Ok(())
+    }
+}
+
+#[deriving(Eq,TotalEq,Clone,Decodable)]
 pub struct ConfigValue {
     value: ConfigValueValue,
-    path: ~str
+    path: Vec<~str>
+}
+
+impl<E, S: Encoder<E>> Encodable<S, E> for ConfigValue {
+    fn encode(&self, s: &mut S) -> Result<(), E> {
+        s.emit_map(2, |s| {
+            try!(s.emit_map_elt_key(0, |s| "value".encode(s)));
+            try!(s.emit_map_elt_val(0, |s| self.value.encode(s)));
+            Ok(())
+        })
+    }
 }
 
 impl fmt::Show for ConfigValue {
@@ -35,13 +71,9 @@ pub fn get_config(pwd: Path, key: &str) -> CargoResult<ConfigValue> {
 pub fn all_configs(pwd: Path) -> CargoResult<collections::HashMap<~str, ConfigValue>> {
     let mut map = collections::HashMap::new();
 
-    walk_tree(&pwd, |file| {
-        let _ = extract_all_configs(file).map(|configs| {
-            for (key, value) in configs.move_iter() {
-                map.find_or_insert(key, value);
-            }
-        });
-    });
+    try!(walk_tree(&pwd, |file| {
+        extract_all_configs(file, &mut map)
+    }));
 
     Ok(map)
 }
@@ -51,16 +83,15 @@ pub fn set_config(key: ~str, value: ~str, location: Location) -> CargoResult<()>
     Ok(())
 }
 
-fn find_in_tree<T>(pwd: &Path, walk: |io::fs::File| -> CargoResult<T>) -> Option<T> {
+fn find_in_tree<T>(pwd: &Path, walk: |io::fs::File| -> CargoResult<T>) -> CargoResult<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),
+            let file = try!(io::fs::File::open(&possible).to_cargo_error(~"", 1));
+            match walk(file) {
+                Ok(res) => return Ok(res),
                 _ => ()
             }
         }
@@ -68,20 +99,28 @@ fn find_in_tree<T>(pwd: &Path, walk: |io::fs::File| -> CargoResult<T>) -> Option
         if !current.pop() { break; }
     }
 
-    None
+    Err(CargoError::new(~"", 1))
 }
 
-fn walk_tree(pwd: &Path, walk: |io::fs::File| -> ()) {
+fn walk_tree(pwd: &Path, walk: |io::fs::File| -> CargoResult<()>) -> CargoResult<()> {
     let mut current = pwd.clone();
+    let mut err = false;
 
     loop {
         let possible = current.join(".cargo").join("config");
         if possible.exists() {
-            let _ = io::fs::File::open(&possible).map(|file| walk(file));
+            let file = try!(io::fs::File::open(&possible).to_cargo_error(~"", 1));
+            match walk(file) {
+                Err(_) => err = false,
+                _ => ()
+            }
         }
 
+        if err { return Err(CargoError::new(~"", 1)); }
         if !current.pop() { break; }
     }
+
+    Ok(())
 }
 
 fn extract_config(file: io::fs::File, key: &str) -> CargoResult<ConfigValue> {
@@ -96,12 +135,10 @@ fn extract_config(file: io::fs::File, key: &str) -> CargoResult<ConfigValue> {
         _ => return Err(CargoError::new(~"", 1))
     };
 
-    Ok(ConfigValue{ value: v, path: path })
+    Ok(ConfigValue{ value: v, path: vec!(path) })
 }
 
-fn extract_all_configs(file: io::fs::File) -> CargoResult<collections::HashMap<~str, ConfigValue>> {
-    let mut map = collections::HashMap::new();
-
+fn extract_all_configs(file: io::fs::File, map: &mut collections::HashMap<~str, ConfigValue>) -> CargoResult<()> {
     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));
@@ -109,11 +146,41 @@ fn extract_all_configs(file: io::fs::File) -> CargoResult<collections::HashMap<~
 
     for (key, value) in table.iter() {
         match value {
-            &toml::String(ref val) => { map.insert(key.to_owned(), ConfigValue { value: String(val.to_owned()), path: path.clone() }); }
-            &toml::Array(ref val) => { map.insert(key.to_owned(), ConfigValue { value: List(val.iter().map(|s: &toml::Value| s.to_str()).collect()), path: path.clone() }); }
+            &toml::String(ref val) => { map.insert(key.to_owned(), ConfigValue { value: String(val.to_owned()), path: vec!(path.clone()) }); }
+            &toml::Array(ref val) => {
+                let config = map.find_or_insert_with(key.to_owned(), |_| {
+                    ConfigValue { path: vec!(), value: List(vec!()) }
+                });
+
+                try!(merge_array(config, val.as_slice(), path.as_slice()));
+            },
             _ => ()
         }
     }
 
-    Ok(map)
+    Ok(())
+}
+
+fn merge_array(existing: &mut ConfigValue, val: &[toml::Value], path: &str) -> CargoResult<()> {
+    match existing.value {
+        String(_) => return Err(CargoError::new(~"", 1)),
+        List(ref mut list) => {
+            let new_list: Vec<CargoResult<~str>> = val.iter().map(|s: &toml::Value| toml_string(s)).collect();
+            if new_list.iter().any(|v| v.is_err()) {
+                return Err(CargoError::new(~"", 1));
+            } else {
+                let new_list: Vec<~str> = new_list.move_iter().map(|v| v.unwrap()).collect();
+                list.push_all(new_list.as_slice());
+                existing.path.push(path.to_owned());
+                Ok(())
+            }
+        }
+    }
+}
+
+fn toml_string(val: &toml::Value) -> CargoResult<~str> {
+    match val {
+        &toml::String(ref str) => Ok(str.to_owned()),
+        _ => Err(CargoError::new(~"", 1))
+    }
 }
diff --git a/vimrc b/vimrc
deleted file mode 100644 (file)
index db5d514..0000000
--- a/vimrc
+++ /dev/null
@@ -1 +0,0 @@
-au BufRead,BufNewFile *.rs set filetype=rust smartindent tabstop=4 shiftwidth=4 expandtab