Add .ini loading/saving

This commit is contained in:
Jonathan Turner 2019-06-16 18:43:40 +12:00
parent 34ddb779b2
commit 1000ec21b5
13 changed files with 160 additions and 0 deletions

18
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -55,6 +55,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
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),

View File

@ -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;

View File

@ -102,6 +102,8 @@ pub fn enter(args: CommandArgs) -> Result<OutputStream, ShellError> {
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<OutputStream, ShellError> {
)?,
)));
}
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(

54
src/commands/from_ini.rs Normal file
View File

@ -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<String, String>) -> 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<String, HashMap<String, String>>) -> 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<Value, Box<dyn std::error::Error>> {
let v: HashMap<String, HashMap<String, String>> = serde_ini::from_str(&s)?;
Ok(convert_ini_top_to_nu_value(&v))
}
pub fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
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())
}

View File

@ -101,6 +101,8 @@ pub fn open(args: CommandArgs) -> Result<OutputStream, ShellError> {
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<OutputStream, ShellError> {
)?,
));
}
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(

View File

@ -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(

17
src/commands/to_ini.rs Normal file
View File

@ -0,0 +1,17 @@
use crate::object::{Primitive, Value};
use crate::prelude::*;
pub fn to_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
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())
}

1
tests/open_ini.out Normal file
View File

@ -0,0 +1 @@
1234

3
tests/open_ini.txt Normal file
View File

@ -0,0 +1,3 @@
cd tests
open test.ini | get SectionOne.integer | echo $it
exit

19
tests/test.ini Normal file
View File

@ -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'

View File

@ -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");