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
94mod json;
95
96#[derive(Debug, Copy, Clone, Display, EnumIter)]
97pub enum DiffTool {
98 #[display("{_0}")]
99 JetbrainsIDE(JetbrainsIDE),
100 #[display("zed")]
101 Zed,
102 #[display("vscode")]
103 VSCode,
104}
105
106mod difftool;
107
108#[derive(Debug, Copy, Clone, Display, EnumIter, Default)]
109#[display(rename_all = "lowercase")]
110pub enum JetbrainsIDE {
111 #[default]
112 Idea,
113 CLion,
114 RustRover,
115 GoLand,
116 PyCharm,
117 WebStorm,
118 Rider,
119 DataGrip,
120 AppCode,
121}
122
123#[cfg(test)]
124mod tests {
125 #[test]
126 fn test() -> crate::Result<()> {
127 let json1 = r#"{"a":1,"b":2,"c":3}"#;
128 let json1: serde_json::Value = serde_json::from_str(json1)?;
129 let json1 = serde_json::to_string(&json1)?;
130 println!("{}", json1);
131 let json2 = r#"{"c":3,"b":2,"a":1, "d":{"g":"gg","f":"ff","e":"ee"}}"#;
132 let json2: serde_json::Value = serde_json::from_str(json2)?;
133 let json2 = serde_json::to_string(&json2)?;
134 println!("{}", json2);
135 Ok(())
136 }
137}