dev_kit/command/json/
mod.rs

1use crate::command::http_parser::HttpRequest;
2use anyhow::anyhow;
3use derive_more::Display;
4use std::fs;
5use std::path::PathBuf;
6use std::sync::Arc;
7use strum::EnumIter;
8
9#[derive(clap::Subcommand)]
10pub enum JsonCommand {
11    #[clap(about = "json beautify, alias 'format'", alias = "format")]
12    Beauty {
13        #[arg(help = "json input, support string, file-path, url, cmd", default_value = "-")]
14        json: Json,
15        #[arg(short, long, help = "json path to extract")]
16        query: Option<String>,
17        #[arg(short, long, help = "file to write output")]
18        file: Option<PathBuf>,
19    },
20    #[clap(about = "json query, alias 'search'", alias = "search")]
21    Query {
22        #[arg(help = "json input, support string, file-path, url, cmd", default_value = "-")]
23        json: Json,
24        #[arg(short, long, help = "json path to extract")]
25        query: String,
26        #[arg(long, help = "beauty output", alias = "format", default_value = "false")]
27        beauty: bool,
28        #[arg(short, long, help = "file to write output")]
29        file: Option<PathBuf>,
30    },
31    #[clap(about = "json diff with left and right")]
32    Diff {
33        #[arg(help = "json input, support string, file-path, url, cmd", default_value = "-")]
34        left: Json,
35        #[arg(help = "json input, support string, file-path, url, cmd", default_value = "-")]
36        right: Json,
37        #[arg(short, long, help = "json path to extract")]
38        query: Option<String>,
39        #[arg(long, help = "diff tool to use, alias dt, support idea/zed/vscode, and will auto detect if not set", alias = "dt")]
40        diff_tool: Option<DiffTool>,
41    },
42}
43
44impl super::Command for JsonCommand {
45    fn run(&self) -> crate::Result<()> {
46        match self {
47            JsonCommand::Beauty { json, query, file } => {
48                let result = json.beautify(query.as_deref())?;
49                if let Some(file) = file {
50                    fs::write(&file, result).map_err(|err|
51                        anyhow!("write to {} failed, {}", file.display(), err)
52                    )?;
53                    println!("write to {}", file.display())
54                } else {
55                    println!("{result}");
56                }
57                Ok(())
58            }
59            JsonCommand::Query { json, query, beauty, file } => {
60                let result = json.query(query, *beauty)?;
61                if let Some(file) = file {
62                    let content = result.join("\n");
63                    fs::write(&file, content)?;
64                    println!("write to {}", file.display())
65                } else {
66                    for row in result {
67                        println!("{}", row);
68                    }
69                }
70                Ok(())
71            }
72            JsonCommand::Diff { left, right, query, diff_tool } => {
73                let _ = left.diff(right, query.as_deref(), diff_tool.map(|it| it))?;
74                Ok(())
75            }
76        }
77    }
78}
79
80#[derive(Debug, Clone, Display)]
81pub enum Json {
82    #[display("{_0}")]
83    Cmd(String),
84    #[display("{_0}")]
85    HttpRequest(HttpRequest),
86    #[display("{}", _0.display())]
87    Path(PathBuf),
88    #[display("{_0}")]
89    String(String),
90    #[display("{}", _0.to_string())]
91    JsonValue(Arc<serde_json::Value>),
92}
93
94impl Json {
95    fn name(&self) -> &'static str {
96        match self {
97            Json::Cmd(_) => "Cmd",
98            Json::HttpRequest(_) => "HttpRequest",
99            Json::Path(_) => "Path",
100            Json::String(_) => "String",
101            Json::JsonValue(_) => "JsonValue",
102        }
103    }
104}
105
106mod json;
107
108#[derive(Debug, Copy, Clone, Display, EnumIter)]
109pub enum DiffTool {
110    #[display("{_0}")]
111    JetbrainsIDE(JetbrainsIDE),
112    #[display("zed")]
113    Zed,
114    #[display("vscode")]
115    VSCode,
116}
117
118mod difftool;
119
120#[derive(Debug, Copy, Clone, Display, EnumIter, Default)]
121#[display(rename_all = "lowercase")]
122pub enum JetbrainsIDE {
123    #[default]
124    Idea,
125    CLion,
126    RustRover,
127    GoLand,
128    PyCharm,
129    WebStorm,
130    Rider,
131    DataGrip,
132    AppCode,
133}
134
135#[cfg(test)]
136mod tests {
137    #[test]
138    fn test() -> crate::Result<()> {
139        let json1 = r#"{"a":1,"b":2,"c":3}"#;
140        let json1: serde_json::Value = serde_json::from_str(json1)?;
141        let json1 = serde_json::to_string(&json1)?;
142        println!("{}", json1);
143        let json2 = r#"{"c":3,"b":2,"a":1, "d":{"g":"gg","f":"ff","e":"ee"}}"#;
144        let json2: serde_json::Value = serde_json::from_str(json2)?;
145        let json2 = serde_json::to_string(&json2)?;
146        println!("{}", json2);
147        Ok(())
148    }
149}