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}