From 76425417554d3d05709747495478f16f506f549d Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 11 Jun 2019 18:26:03 +1200 Subject: [PATCH] Add xml open support --- Cargo.lock | 16 ++++++++++ Cargo.toml | 1 + src/cli.rs | 1 + src/commands.rs | 1 + src/commands/from_xml.rs | 68 ++++++++++++++++++++++++++++++++++++++++ src/commands/get.rs | 12 +++++++ src/commands/open.rs | 5 +++ src/object/base.rs | 12 +++++++ tests/open_xml.out | 1 + tests/open_xml.txt | 3 ++ tests/test.xml | 22 +++++++++++++ 11 files changed, 142 insertions(+) create mode 100644 src/commands/from_xml.rs create mode 100644 tests/open_xml.out create mode 100644 tests/open_xml.txt create mode 100644 tests/test.xml diff --git a/Cargo.lock b/Cargo.lock index 368af3e0e..ff5b84cef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1583,6 +1583,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)", @@ -2257,6 +2258,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" @@ -3137,6 +3146,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" @@ -3384,6 +3398,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" @@ -3490,4 +3505,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 b4073679f..c5d6770b0 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" [dependencies.pancurses] version = "0.16" diff --git a/src/cli.rs b/src/cli.rs index 4bdffeb78..ccda0d2e0 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 3b620f09d..d88ac6223 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 000000000..83e964e09 --- /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 078e9e8dc..b3869d97a 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 7f3c79ab4..40fe1a8e4 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 f0ec46168..28675f0be 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 000000000..3cfcfc2e6 --- /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 000000000..66fd22bc9 --- /dev/null +++ b/tests/open_xml.txt @@ -0,0 +1,3 @@ +cd tests +open tests\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 000000000..0ce0016c1 --- /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 + + + +