diff --git a/Cargo.lock b/Cargo.lock index a5e3032ab2..4415d569d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1584,6 +1584,7 @@ dependencies = [ "ptree 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)", + "roxmltree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 4.1.0 (git+https://github.com/kkawakam/rustyline.git)", "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2266,6 +2267,14 @@ dependencies = [ "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "roxmltree" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "xmlparser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rust-ini" version = "0.13.0" @@ -3151,6 +3160,11 @@ name = "xml-rs" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "xmlparser" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "yaml-rust" version = "0.4.3" @@ -3399,6 +3413,7 @@ dependencies = [ "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum render-tree 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "68ed587df09cfb7ce1bc6fe8f77e24db219f222c049326ccbfb948ec67e31664" "checksum reqwest 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)" = "00eb63f212df0e358b427f0f40aa13aaea010b470be642ad422bcbca2feff2e4" +"checksum roxmltree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "53b0200cbfa8b3f6cfd6076592717d697a1ddc57cb2a8fbfd3d133c06011b579" "checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" "checksum rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ccc78bfd5acd7bf3e89cffcf899e5cb1a52d6fafa8dec2739ad70c9577a57288" "checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" @@ -3506,4 +3521,5 @@ dependencies = [ "checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" "checksum xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12ea8eda4b1eb72f02d148402e23832d56a33f55d8c1b2d5bcdde91d79d47cb1" "checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" +"checksum xmlparser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ecec95f00fb0ff019153e64ea520f87d1409769db3e8f4db3ea588638a3e1cee" "checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" diff --git a/Cargo.toml b/Cargo.toml index f3849706d0..4fb8287c06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ ctrlc = "3.1.3" ptree = "0.2" clipboard = "0.5" reqwest = "0.9" +roxmltree = "0.6.0" pretty = "0.5.2" [dependencies.pancurses] diff --git a/src/cli.rs b/src/cli.rs index 4bdffeb78e..ccda0d2e0a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -56,6 +56,7 @@ pub async fn cli() -> Result<(), Box> { command("size", size::size), command("from-json", from_json::from_json), command("from-toml", from_toml::from_toml), + command("from-xml", from_xml::from_xml), command("from-yaml", from_yaml::from_yaml), command("get", get::get), command("open", open::open), diff --git a/src/commands.rs b/src/commands.rs index 3b620f09d5..d88ac62233 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -8,6 +8,7 @@ crate mod config; crate mod first; crate mod from_json; crate mod from_toml; +crate mod from_xml; crate mod from_yaml; crate mod get; crate mod ls; diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs new file mode 100644 index 0000000000..83e964e09d --- /dev/null +++ b/src/commands/from_xml.rs @@ -0,0 +1,68 @@ +use crate::object::{DataDescriptor, Dictionary, Primitive, Value}; +use crate::prelude::*; + +fn from_node_to_value<'a, 'd>(n: &roxmltree::Node<'a, 'd>) -> Value { + if n.is_element() { + let name = n.tag_name().name().trim().to_string(); + + let mut children_values = vec![]; + for c in n.children() { + children_values.push(from_node_to_value(&c)); + } + + let children_values: Vec = children_values + .into_iter() + .filter(|x| match x { + Value::Primitive(Primitive::String(f)) => { + if f.trim() == "" { + false + } else { + true + } + } + _ => true, + }) + .collect(); + + let mut collected = Dictionary::default(); + collected.add( + DataDescriptor::from(name.clone()), + Value::List(children_values), + ); + + Value::Object(collected) + } else if n.is_comment() { + Value::Primitive(Primitive::String("".to_string())) + } else if n.is_pi() { + Value::Primitive(Primitive::String("".to_string())) + } else if n.is_text() { + Value::Primitive(Primitive::String(n.text().unwrap().to_string())) + } else { + Value::Primitive(Primitive::String("".to_string())) + } +} + +fn from_document_to_value(d: &roxmltree::Document) -> Value { + from_node_to_value(&d.root_element()) +} + +pub fn from_xml_string_to_value(s: String) -> Value { + match roxmltree::Document::parse(&s) { + Ok(doc) => from_document_to_value(&doc), + Err(_) => Value::Error(Box::new(ShellError::string( + "Can't convert string from xml".to_string(), + ))), + } +} + +pub fn from_xml(args: CommandArgs) -> Result { + let out = args.input; + Ok(out + .map(|a| match a { + Value::Primitive(Primitive::String(s)) => ReturnValue::Value(from_xml_string_to_value(s)), + _ => ReturnValue::Value(Value::Error(Box::new(ShellError::string( + "Trying to convert XML from non-string".to_string(), + )))), + }) + .boxed()) +} diff --git a/src/commands/get.rs b/src/commands/get.rs index 078e9e8dc6..b3869d97a3 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -32,6 +32,18 @@ pub fn get(args: CommandArgs) -> Result { } } + let amount = args.positional[0].as_i64(); + + // If it's a number, get the row instead of the column + if let Ok(amount) = amount { + return Ok(args + .input + .skip(amount as u64) + .take(1) + .map(|v| ReturnValue::Value(v)) + .boxed()); + } + let fields: Result, _> = args.positional.iter().map(|a| a.as_string()).collect(); let fields = fields?; diff --git a/src/commands/open.rs b/src/commands/open.rs index 7f3c79ab4f..40fe1a8e4d 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -103,6 +103,11 @@ pub fn open(args: CommandArgs) -> Result { crate::commands::from_json::from_json_string_to_value(contents), )); } + Some(x) if x == "xml" && !open_raw => { + stream.push_back(ReturnValue::Value( + crate::commands::from_xml::from_xml_string_to_value(contents), + )); + } Some(x) if x == "yml" && !open_raw => { stream.push_back(ReturnValue::Value( crate::commands::from_yaml::from_yaml_string_to_value(contents), diff --git a/src/object/base.rs b/src/object/base.rs index f0ec46168e..28675f0be0 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -183,6 +183,18 @@ impl Value { crate fn get_data_by_key(&'a self, name: &str) -> Option<&Value> { match self { Value::Object(o) => o.get_data_by_key(name), + Value::List(l) => { + for item in l { + match item { + Value::Object(o) => match o.get_data_by_key(name) { + Some(v) => return Some(v), + None => {} + } + _ => {} + } + } + None + } _ => None, } } diff --git a/tests/open_xml.out b/tests/open_xml.out new file mode 100644 index 0000000000..3cfcfc2e62 --- /dev/null +++ b/tests/open_xml.out @@ -0,0 +1 @@ +http://www.jonathanturner.org/2015/10/off-to-new-adventures.html diff --git a/tests/open_xml.txt b/tests/open_xml.txt new file mode 100644 index 0000000000..e3d0fa78e9 --- /dev/null +++ b/tests/open_xml.txt @@ -0,0 +1,3 @@ +cd tests +open test.xml | get rss.channel.item.link | echo $it +exit diff --git a/tests/test.xml b/tests/test.xml new file mode 100644 index 0000000000..0ce0016c19 --- /dev/null +++ b/tests/test.xml @@ -0,0 +1,22 @@ + + + + Jonathan Turner + http://www.jonathanturner.org + + + + Creating crossplatform Rust terminal apps + <p><img src="/images/pikachu.jpg" alt="Pikachu animation in Windows" /></p> + +<p><em>Look Mom, Pikachu running in Windows CMD!</em></p> + +<p>Part of the adventure is not seeing the way ahead and going anyway.</p> + + Mon, 05 Oct 2015 00:00:00 +0000 + http://www.jonathanturner.org/2015/10/off-to-new-adventures.html + http://www.jonathanturner.org/2015/10/off-to-new-adventures.html + + + + diff --git a/tests/tests.rs b/tests/tests.rs index 338cc05c2b..3a1c19c006 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -64,6 +64,11 @@ mod tests { test_helper("open_json"); } + #[test] + fn open_xml() { + test_helper("open_xml"); + } + #[test] fn json_roundtrip() { test_helper("json_roundtrip");