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