From ac563a884f3d0d73817ce206822d3a7a7c61107d Mon Sep 17 00:00:00 2001 From: upupnoah Date: Mon, 10 Jun 2024 08:35:25 +0800 Subject: [PATCH 1/2] feat(csv): support json and yaml --- assets/juventus.json | 162 +++++++++++++++++++++---------------------- assets/juventus.yaml | 135 ++++++++++++++++++++++++++++++++++++ src/main.rs | 2 +- src/opts.rs | 50 ++++++++++++- src/process.rs | 47 ++++++++++--- 5 files changed, 305 insertions(+), 91 deletions(-) create mode 100644 assets/juventus.yaml diff --git a/assets/juventus.json b/assets/juventus.json index bc604ab..92c6ce4 100644 --- a/assets/juventus.json +++ b/assets/juventus.json @@ -1,191 +1,191 @@ [ { - "Name": "Wojciech Szczesny", - "Position": "Goalkeeper", "DOB": "Apr 18, 1990 (29)", + "Kit Number": "1", + "Name": "Wojciech Szczesny", "Nationality": "Poland", - "Kit Number": 1 + "Position": "Goalkeeper" }, { - "Name": "Mattia Perin", - "Position": "Goalkeeper", "DOB": "Nov 10, 1992 (26)", + "Kit Number": "37", + "Name": "Mattia Perin", "Nationality": "Italy", - "Kit Number": 37 + "Position": "Goalkeeper" }, { - "Name": "Gianluigi Buffon", - "Position": "Goalkeeper", "DOB": "Jan 28, 1978 (41)", + "Kit Number": "77", + "Name": "Gianluigi Buffon", "Nationality": "Italy", - "Kit Number": 77 + "Position": "Goalkeeper" }, { - "Name": "Carlo Pinsoglio", - "Position": "Goalkeeper", "DOB": "Mar 16, 1990 (29)", + "Kit Number": "31", + "Name": "Carlo Pinsoglio", "Nationality": "Italy", - "Kit Number": 31 + "Position": "Goalkeeper" }, { - "Name": "Matthijs de Ligt", - "Position": "Centre-Back", "DOB": "Aug 12, 1999 (20)", + "Kit Number": "4", + "Name": "Matthijs de Ligt", "Nationality": "Netherlands", - "Kit Number": 4 + "Position": "Centre-Back" }, { - "Name": "Leonardo Bonucci", - "Position": "Centre-Back", "DOB": "May 1, 1987 (32)", + "Kit Number": "19", + "Name": "Leonardo Bonucci", "Nationality": "Italy", - "Kit Number": 19 + "Position": "Centre-Back" }, { - "Name": "Daniele Rugani", - "Position": "Centre-Back", "DOB": "Jul 29, 1994 (25)", + "Kit Number": "24", + "Name": "Daniele Rugani", "Nationality": "Italy", - "Kit Number": 24 + "Position": "Centre-Back" }, { - "Name": "Merih Demiral", - "Position": "Centre-Back", "DOB": "Mar 5, 1998 (21)", + "Kit Number": "28", + "Name": "Merih Demiral", "Nationality": "Turkey", - "Kit Number": 28 + "Position": "Centre-Back" }, { - "Name": "Giorgio Chiellini", - "Position": "Centre-Back", "DOB": "Aug 14, 1984 (35)", + "Kit Number": "3", + "Name": "Giorgio Chiellini", "Nationality": "Italy", - "Kit Number": 3 + "Position": "Centre-Back" }, { - "Name": "Alex Sandro", - "Position": "Left-Back", "DOB": "Jan 26, 1991 (28)", + "Kit Number": "12", + "Name": "Alex Sandro", "Nationality": "Brazil", - "Kit Number": 12 + "Position": "Left-Back" }, { - "Name": "Danilo", - "Position": "Right-Back", "DOB": "Jul 15, 1991 (28)", + "Kit Number": "13", + "Name": "Danilo", "Nationality": "Brazil", - "Kit Number": 13 + "Position": "Right-Back" }, { - "Name": "Mattia De Sciglio", - "Position": "Right-Back", "DOB": "Oct 20, 1992 (27)", + "Kit Number": "2", + "Name": "Mattia De Sciglio", "Nationality": "Italy", - "Kit Number": 2 + "Position": "Right-Back" }, { - "Name": "Emre Can", - "Position": "Defensive Midfield", "DOB": "Jan 12, 1994 (25)", + "Kit Number": "23", + "Name": "Emre Can", "Nationality": "Germany", - "Kit Number": 23 + "Position": "Defensive Midfield" }, { - "Name": "Miralem Pjanic", - "Position": "Central Midfield", "DOB": "Apr 2, 1990 (29)", + "Kit Number": "5", + "Name": "Miralem Pjanic", "Nationality": "Bosnia-Herzegovina", - "Kit Number": 5 + "Position": "Central Midfield" }, { - "Name": "Aaron Ramsey", - "Position": "Central Midfield", "DOB": "Dec 26, 1990 (28)", + "Kit Number": "8", + "Name": "Aaron Ramsey", "Nationality": "Wales", - "Kit Number": 8 + "Position": "Central Midfield" }, { - "Name": "Adrien Rabiot", - "Position": "Central Midfield", "DOB": "Apr 3, 1995 (24)", + "Kit Number": "25", + "Name": "Adrien Rabiot", "Nationality": "France", - "Kit Number": 25 + "Position": "Central Midfield" }, { - "Name": "Rodrigo Bentancur", - "Position": "Central Midfield", "DOB": "Jun 25, 1997 (22)", + "Kit Number": "30", + "Name": "Rodrigo Bentancur", "Nationality": "Uruguay", - "Kit Number": 30 + "Position": "Central Midfield" }, { - "Name": "Blaise Matuidi", - "Position": "Central Midfield", "DOB": "Apr 9, 1987 (32)", + "Kit Number": "14", + "Name": "Blaise Matuidi", "Nationality": "France", - "Kit Number": 14 + "Position": "Central Midfield" }, { - "Name": "Sami Khedira", - "Position": "Central Midfield", "DOB": "Apr 4, 1987 (32)", + "Kit Number": "6", + "Name": "Sami Khedira", "Nationality": "Germany", - "Kit Number": 6 + "Position": "Central Midfield" }, { - "Name": "Cristiano Ronaldo", - "Position": "Left Winger", "DOB": "Feb 5, 1985 (34)", + "Kit Number": "7", + "Name": "Cristiano Ronaldo", "Nationality": "Portugal", - "Kit Number": 7 + "Position": "Left Winger" }, { - "Name": "Marko Pjaca", - "Position": "Left Winger", "DOB": "May 6, 1995 (24)", + "Kit Number": "15", + "Name": "Marko Pjaca", "Nationality": "Croatia", - "Kit Number": 15 + "Position": "Left Winger" }, { - "Name": "Federico Bernardeschi", - "Position": "Right Winger", "DOB": "Feb 16, 1994 (25)", + "Kit Number": "33", + "Name": "Federico Bernardeschi", "Nationality": "Italy", - "Kit Number": 33 + "Position": "Right Winger" }, { - "Name": "Douglas Costa", - "Position": "Right Winger", "DOB": "Sep 14, 1990 (29)", + "Kit Number": "11", + "Name": "Douglas Costa", "Nationality": "Brazil", - "Kit Number": 11 + "Position": "Right Winger" }, { - "Name": "Juan Cuadrado", - "Position": "Right Winger", "DOB": "May 26, 1988 (31)", + "Kit Number": "16", + "Name": "Juan Cuadrado", "Nationality": "Colombia", - "Kit Number": 16 + "Position": "Right Winger" }, { - "Name": "Paulo Dybala", - "Position": "Second Striker", "DOB": "Nov 15, 1993 (25)", + "Kit Number": "10", + "Name": "Paulo Dybala", "Nationality": "Argentina", - "Kit Number": 10 + "Position": "Second Striker" }, { - "Name": "Gonzalo Higuaín", - "Position": "Centre-Forward", "DOB": "Dec 10, 1987 (31)", + "Kit Number": "21", + "Name": "Gonzalo Higuaín", "Nationality": "Argentina", - "Kit Number": 21 + "Position": "Centre-Forward" }, { - "Name": "Mario Mandzukic", - "Position": "Centre-Forward", "DOB": "May 21, 1986 (33)", + "Kit Number": "17", + "Name": "Mario Mandzukic", "Nationality": "Croatia", - "Kit Number": 17 + "Position": "Centre-Forward" } ] \ No newline at end of file diff --git a/assets/juventus.yaml b/assets/juventus.yaml new file mode 100644 index 0000000..507226d --- /dev/null +++ b/assets/juventus.yaml @@ -0,0 +1,135 @@ +- DOB: Apr 18, 1990 (29) + Kit Number: '1' + Name: Wojciech Szczesny + Nationality: Poland + Position: Goalkeeper +- DOB: Nov 10, 1992 (26) + Kit Number: '37' + Name: Mattia Perin + Nationality: Italy + Position: Goalkeeper +- DOB: Jan 28, 1978 (41) + Kit Number: '77' + Name: Gianluigi Buffon + Nationality: Italy + Position: Goalkeeper +- DOB: Mar 16, 1990 (29) + Kit Number: '31' + Name: Carlo Pinsoglio + Nationality: Italy + Position: Goalkeeper +- DOB: Aug 12, 1999 (20) + Kit Number: '4' + Name: Matthijs de Ligt + Nationality: Netherlands + Position: Centre-Back +- DOB: May 1, 1987 (32) + Kit Number: '19' + Name: Leonardo Bonucci + Nationality: Italy + Position: Centre-Back +- DOB: Jul 29, 1994 (25) + Kit Number: '24' + Name: Daniele Rugani + Nationality: Italy + Position: Centre-Back +- DOB: Mar 5, 1998 (21) + Kit Number: '28' + Name: Merih Demiral + Nationality: Turkey + Position: Centre-Back +- DOB: Aug 14, 1984 (35) + Kit Number: '3' + Name: Giorgio Chiellini + Nationality: Italy + Position: Centre-Back +- DOB: Jan 26, 1991 (28) + Kit Number: '12' + Name: Alex Sandro + Nationality: Brazil + Position: Left-Back +- DOB: Jul 15, 1991 (28) + Kit Number: '13' + Name: Danilo + Nationality: Brazil + Position: Right-Back +- DOB: Oct 20, 1992 (27) + Kit Number: '2' + Name: Mattia De Sciglio + Nationality: Italy + Position: Right-Back +- DOB: Jan 12, 1994 (25) + Kit Number: '23' + Name: Emre Can + Nationality: Germany + Position: Defensive Midfield +- DOB: Apr 2, 1990 (29) + Kit Number: '5' + Name: Miralem Pjanic + Nationality: Bosnia-Herzegovina + Position: Central Midfield +- DOB: Dec 26, 1990 (28) + Kit Number: '8' + Name: Aaron Ramsey + Nationality: Wales + Position: Central Midfield +- DOB: Apr 3, 1995 (24) + Kit Number: '25' + Name: Adrien Rabiot + Nationality: France + Position: Central Midfield +- DOB: Jun 25, 1997 (22) + Kit Number: '30' + Name: Rodrigo Bentancur + Nationality: Uruguay + Position: Central Midfield +- DOB: Apr 9, 1987 (32) + Kit Number: '14' + Name: Blaise Matuidi + Nationality: France + Position: Central Midfield +- DOB: Apr 4, 1987 (32) + Kit Number: '6' + Name: Sami Khedira + Nationality: Germany + Position: Central Midfield +- DOB: Feb 5, 1985 (34) + Kit Number: '7' + Name: Cristiano Ronaldo + Nationality: Portugal + Position: Left Winger +- DOB: May 6, 1995 (24) + Kit Number: '15' + Name: Marko Pjaca + Nationality: Croatia + Position: Left Winger +- DOB: Feb 16, 1994 (25) + Kit Number: '33' + Name: Federico Bernardeschi + Nationality: Italy + Position: Right Winger +- DOB: Sep 14, 1990 (29) + Kit Number: '11' + Name: Douglas Costa + Nationality: Brazil + Position: Right Winger +- DOB: May 26, 1988 (31) + Kit Number: '16' + Name: Juan Cuadrado + Nationality: Colombia + Position: Right Winger +- DOB: Nov 15, 1993 (25) + Kit Number: '10' + Name: Paulo Dybala + Nationality: Argentina + Position: Second Striker +- DOB: Dec 10, 1987 (31) + Kit Number: '21' + Name: Gonzalo Higuaín + Nationality: Argentina + Position: Centre-Forward +- DOB: May 21, 1986 (33) + Kit Number: '17' + Name: Mario Mandzukic + Nationality: Croatia + Position: Centre-Forward diff --git a/src/main.rs b/src/main.rs index 5f2784c..e454395 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ fn main() -> anyhow::Result<()> { let opts = Opts::parse(); match opts.cmd { SubCommand::Csv(opts) => { - process_csv(&opts.input, &opts.output)?; + process_csv(&opts.input, &opts.output, &opts.format)?; } } Ok(()) diff --git a/src/opts.rs b/src/opts.rs index 774c4ee..5648af9 100644 --- a/src/opts.rs +++ b/src/opts.rs @@ -1,7 +1,12 @@ use clap::Parser; -use std::path::Path; +use std::{path::Path, str::FromStr}; // rcli csv -i input.csv -o output.json --header -d ',' +// -> 支持多格式: json, yaml +// -> 取消 -o 参数, 默认输出到 output.xxx +// rcli csv -i input.csv --format json +// rcli csv -i input.csv --format yaml + #[derive(Debug, Parser)] #[command(name = "rcli", version, author, about, long_about = None)] pub struct Opts { @@ -15,6 +20,13 @@ pub enum SubCommand { Csv(CsvOpts), } +#[derive(Debug, Copy, Clone)] +pub enum OutputFormat { + Json, + Yaml, + // Toml, +} + #[derive(Debug, Parser)] pub struct CsvOpts { /// Input file @@ -25,6 +37,10 @@ pub struct CsvOpts { #[arg(short, long, default_value = "output.json")] // "output.json".into() pub output: String, + /// Output format + #[arg(long, value_parser = parse_output_format, default_value = "json")] + pub format: OutputFormat, + /// Delimiter #[arg(short, long, default_value_t = ',')] // ',' as char pub delimiter: char, @@ -46,6 +62,9 @@ pub struct CsvOpts { // 生命周期与内存一样的类型, 统称为 'static (静态生命周期) // 1. const // 2. Box::leak + +// 会传入文件名 +// csv --input filename(xxx.csv) pub fn verify_input_file(filename: &str) -> Result { if Path::new(filename).exists() { // Ok(filename.to_string()) @@ -54,3 +73,32 @@ pub fn verify_input_file(filename: &str) -> Result { Err("File not found") } } + +// 会传入 json or yaml +// -> csv --format json or csv --format yaml +fn parse_output_format(format: &str) -> Result { + format.parse() +} + +// impl From for &'static str { +// fn from(format: OutputFormat) -> Self { +// match format { +// OutputFormat::Json => "json", +// OutputFormat::Yaml => "yaml", +// // OutputFormat::Toml => "toml", +// } +// } +// } + +impl FromStr for OutputFormat { + type Err = anyhow::Error; + + fn from_str(format: &str) -> Result { + match format { + "json" => Ok(OutputFormat::Json), + "yaml" => Ok(OutputFormat::Yaml), + // "toml" => Ok(OutputFormat::Toml), // unsupported format + _ => Err(anyhow::anyhow!("Invalid output format: {}", format)), + } + } +} diff --git a/src/process.rs b/src/process.rs index 168c3cf..f00d1e8 100644 --- a/src/process.rs +++ b/src/process.rs @@ -2,6 +2,9 @@ use std::fs; use csv::Reader; use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use crate::opts::OutputFormat; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] @@ -18,19 +21,47 @@ struct Player { kit_number: u8, } -pub fn process_csv(input: &str, output: &str) -> anyhow::Result<()> { +pub fn process_csv(input: &str, output: &str, format: &OutputFormat) -> anyhow::Result<()> { let mut rdr = Reader::from_path(input)?; + // ***** from for loop to map ***** // let mut records = Vec::new(); // for result in rdr.deserialize() { // let record: Player = result.unwrap(); // records.push(record); // } - let records = rdr - .deserialize() - .map(|record| record.unwrap()) - .collect::>(); - // println!("{:?}", records); - let json = serde_json::to_string_pretty(&records)?; - fs::write(output, json)?; + // let records = rdr + // .deserialize() + // .map(|record| record.unwrap()) + // .collect::>(); + + // ***** more universal way with map ***** + // let headers = rdr.headers()?.clone(); + // let ret = rdr + // .records() + // .map(|record| { + // let record = record.unwrap(); + // headers.iter().zip(record.iter()).collect::() // return Value type + // }) + // .collect::(); + + // ***** more universal way with for loop ***** + let headers = rdr.headers()?.clone(); + let mut ret = Vec::with_capacity(128); + for record in rdr.records() { + let record = record?; + // headers.iter() -> use the iterator of headers + // record.iter() -> use the iterator of record + // zip() -> combine the two iterators into one iterator of tuples [(header, record), ...] + // collect::() -> convert the iterator of tuples into a Value type + let json_value = headers.iter().zip(record.iter()).collect::(); + ret.push(json_value); + } + // let json = serde_json::to_string_pretty(&ret)?; + let content = match format { + OutputFormat::Json => serde_json::to_string_pretty(&ret)?, + OutputFormat::Yaml => serde_yaml::to_string(&ret)?, + // OutputFormat::Toml => toml::to_string(&ret)?, + }; + fs::write(output, content)?; Ok(()) } From 33ef063a4ef6c55efc9688911f92623a5ae77155 Mon Sep 17 00:00:00 2001 From: upupnoah Date: Mon, 10 Jun 2024 08:35:59 +0800 Subject: [PATCH 2/2] chore: update dependency files --- Cargo.lock | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 ++ README.md | 6 ++++ 3 files changed, 111 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index ff3dddb..4454f87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,12 +124,34 @@ dependencies = [ "memchr", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -175,6 +197,9 @@ dependencies = [ "csv", "serde", "serde_json", + "serde_toml", + "serde_yaml", + "toml", ] [[package]] @@ -214,6 +239,34 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_toml" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e68184169792863bfb6d8b5de8e15456ebf4302f28236f0ff1136591eb66541" + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "strsim" version = "0.11.1" @@ -231,12 +284,52 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "toml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "utf8parse" version = "0.2.1" @@ -315,3 +408,12 @@ name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index 46480af..24d400e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ clap = { version = "4.5.4", features = ["derive"] } csv = "^1.3.0" serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" +serde_toml = "^0.0.1" +serde_yaml = "^0.9" +toml = "0.8.14" diff --git a/README.md b/README.md index 53fe0d5..f92ace9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,13 @@ # rcli +## Functions +- csv to json +- random password generator + ## How to run - `make run ARGS="csv --input ./assets/juventus.csv --output ./assets/juventus.json"` +- `make run ARGS="csv --input ./assets/juventus.csv --format json --output ./assets/juventus.json"` +- `make run ARGS="csv --input ./assets/juventus.csv --format yaml --output ./assets/juventus.yaml"` ## Setup - fork [repo](https://github.com/upupnoah/rcli.git)