From 1000ec21b5bb2c55bcb000142ad25003cecabc78 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 16 Jun 2019 18:43:40 +1200 Subject: [PATCH] Add .ini loading/saving --- Cargo.lock | 18 ++++++++++++++ Cargo.toml | 1 + src/cli.rs | 2 ++ src/commands.rs | 2 ++ src/commands/enter.rs | 15 +++++++++++ src/commands/from_ini.rs | 54 ++++++++++++++++++++++++++++++++++++++++ src/commands/open.rs | 15 +++++++++++ src/commands/save.rs | 8 ++++++ src/commands/to_ini.rs | 17 +++++++++++++ tests/open_ini.out | 1 + tests/open_ini.txt | 3 +++ tests/test.ini | 19 ++++++++++++++ tests/tests.rs | 5 ++++ 13 files changed, 160 insertions(+) create mode 100644 src/commands/from_ini.rs create mode 100644 src/commands/to_ini.rs create mode 100644 tests/open_ini.out create mode 100644 tests/open_ini.txt create mode 100644 tests/test.ini diff --git a/Cargo.lock b/Cargo.lock index 4415d569d..b446747fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1589,6 +1589,7 @@ dependencies = [ "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)", "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", "subprocess 0.1.19 (git+https://github.com/jonathandturner/rust-subprocess.git?branch=is_already_escaped)", @@ -2267,6 +2268,11 @@ dependencies = [ "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "result" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "roxmltree" version = "0.6.0" @@ -2447,6 +2453,16 @@ dependencies = [ "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "serde_ini" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "result 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "serde_json" version = "1.0.39" @@ -3413,6 +3429,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 result 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560" "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" @@ -3436,6 +3453,7 @@ dependencies = [ "checksum serde-hjson 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4640cf3168e40c00c874ff1ad436c0f18c37edec101d5d897a4396f617abce29" "checksum serde-value 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7a663f873dedc4eac1a559d4c6bc0d0b2c34dc5ac4702e105014b8281489e44f" "checksum serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "101b495b109a3e3ca8c4cbe44cf62391527cdfb6ba15821c5ce80bcd5ea23f9f" +"checksum serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb236687e2bb073a7521c021949be944641e671b8505a94069ca37b656c81139" "checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" "checksum serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5" "checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" diff --git a/Cargo.toml b/Cargo.toml index 4fb8287c0..ba77e2586 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ clipboard = "0.5" reqwest = "0.9" roxmltree = "0.6.0" pretty = "0.5.2" +serde_ini = "0.2.0" [dependencies.pancurses] version = "0.16" diff --git a/src/cli.rs b/src/cli.rs index df448d33e..2c3ec712a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -55,6 +55,7 @@ pub async fn cli() -> Result<(), Box> { command("skip", skip::skip), command("first", first::first), command("size", size::size), + command("from-ini", from_ini::from_ini), command("from-json", from_json::from_json), command("from-toml", from_toml::from_toml), command("from-xml", from_xml::from_xml), @@ -69,6 +70,7 @@ pub async fn cli() -> Result<(), Box> { command("reject", reject::reject), command("trim", trim::trim), command("to-array", to_array::to_array), + command("to-ini", to_ini::to_ini), command("to-json", to_json::to_json), command("to-toml", to_toml::to_toml), Arc::new(Where), diff --git a/src/commands.rs b/src/commands.rs index 5c21cfc20..17ab68b6a 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -8,6 +8,7 @@ crate mod config; crate mod enter; crate mod exit; crate mod first; +crate mod from_ini; crate mod from_json; crate mod from_toml; crate mod from_xml; @@ -25,6 +26,7 @@ crate mod sort_by; crate mod split_column; crate mod split_row; crate mod to_array; +crate mod to_ini; crate mod to_json; crate mod to_toml; crate mod tree; diff --git a/src/commands/enter.rs b/src/commands/enter.rs index c36c83807..2ca3c2cca 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -102,6 +102,8 @@ pub fn enter(args: CommandArgs) -> Result { Some("json".to_string()) } else if s == "--xml" { Some("xml".to_string()) + } else if s == "--ini" { + Some("ini".to_string()) } else if s == "--yaml" { Some("yaml".to_string()) } else if s == "--toml" { @@ -157,6 +159,19 @@ pub fn enter(args: CommandArgs) -> Result { )?, ))); } + Some(x) if x == "ini" => { + stream.push_back(ReturnValue::Action(CommandAction::Enter( + crate::commands::from_ini::from_ini_string_to_value(contents).map_err( + move |_| { + ShellError::maybe_labeled_error( + "Could not load as INI", + "could not load as INI", + span, + ) + }, + )?, + ))); + } Some(x) if x == "yml" => { stream.push_back(ReturnValue::Action(CommandAction::Enter( crate::commands::from_yaml::from_yaml_string_to_value(contents).map_err( diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs new file mode 100644 index 000000000..fd1410136 --- /dev/null +++ b/src/commands/from_ini.rs @@ -0,0 +1,54 @@ +use crate::object::{DataDescriptor, Dictionary, Primitive, Value}; +use crate::prelude::*; +use indexmap::IndexMap; +use std::collections::HashMap; + +fn convert_ini_second_to_nu_value(v: &HashMap) -> Value { + let mut second = Dictionary::new(IndexMap::new()); + for (key, value) in v.into_iter() { + second.add( + DataDescriptor::from(key.as_str()), + Value::Primitive(Primitive::String(value.clone())), + ); + } + Value::Object(second) +} +fn convert_ini_top_to_nu_value(v: &HashMap>) -> Value { + let mut top_level = Dictionary::new(IndexMap::new()); + for (key, value) in v.iter() { + top_level.add( + DataDescriptor::from(key.as_str()), + convert_ini_second_to_nu_value(value), + ); + } + Value::Object(top_level) +} + +pub fn from_ini_string_to_value(s: String) -> Result> { + let v: HashMap> = serde_ini::from_str(&s)?; + Ok(convert_ini_top_to_nu_value(&v)) +} + +pub fn from_ini(args: CommandArgs) -> Result { + let out = args.input; + let span = args.name_span; + Ok(out + .map(move |a| match a { + Value::Primitive(Primitive::String(s)) => match from_ini_string_to_value(s) { + Ok(x) => ReturnValue::Value(x), + Err(e) => { + ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( + "Could not parse as INI", + format!("{:#?}", e), + span, + )))) + } + }, + _ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( + "Expected string values from pipeline", + "expects strings from pipeline", + span, + )))), + }) + .boxed()) +} diff --git a/src/commands/open.rs b/src/commands/open.rs index b085b13c3..895999fa0 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -101,6 +101,8 @@ pub fn open(args: CommandArgs) -> Result { Some("json".to_string()) } else if s == "--xml" { Some("xml".to_string()) + } else if s == "--ini" { + Some("ini".to_string()) } else if s == "--yaml" { Some("yaml".to_string()) } else if s == "--toml" { @@ -143,6 +145,19 @@ pub fn open(args: CommandArgs) -> Result { )?, )); } + Some(x) if x == "ini" => { + stream.push_back(ReturnValue::Value( + crate::commands::from_ini::from_ini_string_to_value(contents).map_err( + move |_| { + ShellError::maybe_labeled_error( + "Could not open as INI", + "could not open as INI", + span, + ) + }, + )?, + )); + } Some(x) if x == "xml" => { stream.push_back(ReturnValue::Value( crate::commands::from_xml::from_xml_string_to_value(contents).map_err( diff --git a/src/commands/save.rs b/src/commands/save.rs index b5e61e4a0..3aeb75103 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -45,6 +45,14 @@ pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> { } toml::to_string(&args.input[0]).unwrap() } + Some(x) if x == "ini" && !save_raw => { + if args.input.len() != 1 { + return Err(ShellError::string( + "saving to ini requires a single object (or use --raw)", + )); + } + serde_ini::to_string(&args.input[0]).unwrap() + } Some(x) if x == "json" && !save_raw => { if args.input.len() != 1 { return Err(ShellError::string( diff --git a/src/commands/to_ini.rs b/src/commands/to_ini.rs new file mode 100644 index 000000000..4b23add21 --- /dev/null +++ b/src/commands/to_ini.rs @@ -0,0 +1,17 @@ +use crate::object::{Primitive, Value}; +use crate::prelude::*; + +pub fn to_ini(args: CommandArgs) -> Result { + let out = args.input; + let span = args.name_span; + Ok(out + .map(move |a| match serde_ini::to_string(&a) { + Ok(x) => ReturnValue::Value(Value::Primitive(Primitive::String(x))), + Err(_) => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( + "Can not convert to INI string", + "can not convert piped data to INI string", + span, + )))), + }) + .boxed()) +} diff --git a/tests/open_ini.out b/tests/open_ini.out new file mode 100644 index 000000000..81c545efe --- /dev/null +++ b/tests/open_ini.out @@ -0,0 +1 @@ +1234 diff --git a/tests/open_ini.txt b/tests/open_ini.txt new file mode 100644 index 000000000..ebb8dd5b4 --- /dev/null +++ b/tests/open_ini.txt @@ -0,0 +1,3 @@ +cd tests +open test.ini | get SectionOne.integer | echo $it +exit diff --git a/tests/test.ini b/tests/test.ini new file mode 100644 index 000000000..7abedd96b --- /dev/null +++ b/tests/test.ini @@ -0,0 +1,19 @@ +[SectionOne] + +key = value +integer = 1234 +real = 3.14 +string1 = 'Case 1' +string2 = "Case 2" + +[SectionTwo] + +; comment line +key = new value +integer = 1234 +real = 3.14 +string1 = 'Case 1' +string2 = "Case 2" +string3 = 'Case 3' + + diff --git a/tests/tests.rs b/tests/tests.rs index d0cc6d264..e3e0aaaab 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -67,6 +67,11 @@ mod tests { test_helper("open_xml"); } + #[test] + fn open_ini() { + test_helper("open_ini"); + } + #[test] fn json_roundtrip() { test_helper("json_roundtrip");