Skip to content

Commit

Permalink
FeatureAdd: support generic csv to json and yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
Firstero committed Apr 20, 2024
1 parent d40df0c commit 3f36c0d
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 26 deletions.
42 changes: 42 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ clap = { version = "4.5.4", features = ["derive"] }
csv = "1.3.0"
serde = { version = "1.0.198", features = ["derive"] }
serde_json = "1.0.116"
serde_yaml = "0.9.34"
7 changes: 6 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ use process::process_csv;
fn main() -> anyhow::Result<()> {
let cli = Opts::parse();
match cli.subcmd {
SubCommand::Csv(opts) => process_csv(&opts.input, &opts.output)?,
SubCommand::Csv(opts) => {
let output = opts
.output
.unwrap_or_else(|| format!("output.{}", opts.format));
process_csv(&opts.input, output, opts.format)?;
}
}
Ok(())
}
41 changes: 38 additions & 3 deletions src/opts.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clap::Parser;
use std::fs;
use std::{fmt, fs, str::FromStr};

#[derive(Debug, Parser)]
#[command(name = "rcli", version, about, author, long_about=None)]
Expand All @@ -22,8 +22,16 @@ pub struct CsvOpts {
pub delimiter: char,
#[arg(short, long, required = true, value_parser(verify_file_exists))]
pub input: String,
#[arg(short, long, default_value = "output.json")] // default_value_t = "output.json".into()
pub output: String,
#[arg(short, long)] // default_value_t = "output.json".into()
pub output: Option<String>,
#[arg(long, default_value = "json", value_parser=OutputFormat::from_str)]
pub format: OutputFormat,
}

#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
Json,
Yaml,
}

fn verify_file_exists(path: &str) -> Result<String, String> {
Expand All @@ -33,3 +41,30 @@ fn verify_file_exists(path: &str) -> Result<String, String> {
Err(format!("file not found: {}", path))
}
}

impl FromStr for OutputFormat {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"json" => Ok(OutputFormat::Json),
"yaml" => Ok(OutputFormat::Yaml),
v => Err(anyhow::anyhow!("invalid output format: {}", v)),
}
}
}

impl From<OutputFormat> for &'static str {
fn from(f: OutputFormat) -> Self {
match f {
OutputFormat::Json => "json",
OutputFormat::Yaml => "yaml",
}
}
}

impl fmt::Display for OutputFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", Into::<&str>::into(*self))
}
}
37 changes: 15 additions & 22 deletions src/process.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
use std::fs;

use anyhow::Result;
use csv::Reader;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::opts::OutputFormat;

// Name │ Position │ DOB │ Nationality │ Kit Number │
// varchar │ varchar │ varchar │ varchar │ int64 |
// varchar │ varchar │ varchar │ varchar │ int64 |
// 按照上述结构定义一个 Record 结构体
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Player {
name: String,
position: String,
#[serde(rename = "DOB")]
dob: String,
nationality: String,
#[serde(rename = "Kit Number")]
kit: u8,
}

pub fn process_csv(input: &str, output: &str) -> Result<()> {
pub fn process_csv(input: &str, output: String, output_format: OutputFormat) -> anyhow::Result<()> {
let mut rdr = Reader::from_path(input)?;
let mut ret = Vec::with_capacity(100);

for result in rdr.deserialize() {
let record: Player = result?;
ret.push(record)
let headers = rdr.headers()?.clone();
for result in rdr.records() {
let record = result?;
let json_value = headers.iter().zip(record.iter()).collect::<Value>();
ret.push(json_value)
}
let json = serde_json::to_string_pretty(&ret)?;
fs::write(output, json)?;
let contents = match output_format {
OutputFormat::Json => serde_json::to_string_pretty(&ret)?,
OutputFormat::Yaml => serde_yaml::to_string(&ret)?,
};
fs::write(output, contents)?;
Ok(())
}

0 comments on commit 3f36c0d

Please sign in to comment.