pitchfork_cli/cli/
json_output.rs1use crate::log_store::LogEntry;
2use serde::Serialize;
3
4#[derive(Serialize)]
5pub struct JsonListEntry {
6 pub id: String,
7 pub namespace: String,
8 pub name: String,
9 pub pid: Option<u32>,
10 pub status: String,
11 pub oneshot: bool,
15 pub disabled: bool,
16 pub available: bool,
17 pub proxy_url: Option<String>,
19 pub url: Option<String>,
21 pub error: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub active_port: Option<u16>,
24 pub port: Vec<u16>,
25}
26
27#[derive(Serialize)]
28pub struct JsonStatusEntry {
29 pub id: String,
30 pub namespace: String,
31 pub name: String,
32 pub pid: Option<u32>,
33 pub status: String,
34 pub oneshot: bool,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub active_port: Option<u16>,
38 pub port: Vec<u16>,
39 pub proxy_url: Option<String>,
41 pub url: Option<String>,
43}
44
45#[derive(Serialize)]
46pub struct JsonLogEntry {
47 #[serde(skip_serializing_if = "is_zero_id")]
51 pub id: i64,
52 pub timestamp: String,
53 pub daemon_id: String,
54 pub message: String,
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub level: Option<String>,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub msg: Option<String>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub logger: Option<String>,
61 #[serde(skip_serializing_if = "Option::is_none")]
64 pub fields: Option<serde_json::Value>,
65}
66
67fn is_zero_id(id: &i64) -> bool {
68 *id == 0
69}
70
71impl From<LogEntry> for JsonLogEntry {
72 fn from(e: LogEntry) -> Self {
73 let fields = e
74 .fields_json
75 .as_deref()
76 .and_then(|s| serde_json::from_str(s).ok());
77 JsonLogEntry {
78 id: e.id,
79 timestamp: e.timestamp.format("%Y-%m-%d %H:%M:%S").to_string(),
80 daemon_id: e.daemon_id,
81 message: console::strip_ansi_codes(&e.message).to_string(),
82 level: e.level,
83 msg: e.msg,
84 logger: e.logger,
85 fields,
86 }
87 }
88}
89
90#[derive(Serialize)]
91pub struct JsonDaemonConfigEntry {
92 pub id: String,
93 pub run: String,
94}
95
96#[derive(Serialize)]
97pub struct JsonProxyStatus {
98 pub enabled: bool,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub scheme: Option<String>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub tld: Option<String>,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub port: Option<u16>,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub lan: Option<JsonLanInfo>,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub tls_cert: Option<String>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub trusted: Option<bool>,
111 pub slugs: Vec<JsonSlugEntry>,
112 #[serde(default, skip_serializing_if = "Vec::is_empty")]
114 pub projects: Vec<JsonProxyProject>,
115 #[serde(default, skip_serializing_if = "Vec::is_empty")]
118 pub conflicts: Vec<String>,
119}
120
121#[derive(Serialize)]
122pub struct JsonProxyProject {
123 pub project: String,
124 pub dir: String,
125 pub url: String,
127 pub daemons: Vec<JsonProxyHost>,
128 #[serde(skip_serializing_if = "Vec::is_empty")]
129 pub worktrees: Vec<JsonProxyWorktree>,
130}
131
132#[derive(Serialize)]
133pub struct JsonProxyWorktree {
134 pub worktree: String,
135 pub dir: String,
136 pub url: String,
138 pub daemons: Vec<JsonProxyHost>,
139}
140
141#[derive(Serialize)]
142pub struct JsonProxyHost {
143 pub daemon: String,
144 pub host: String,
146 pub url: String,
147 pub status: String,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub port: Option<u16>,
150}
151
152#[derive(Serialize)]
153pub struct JsonLanInfo {
154 pub enabled: bool,
155 pub ip: String,
156}
157
158#[derive(Serialize)]
159pub struct JsonSlugEntry {
160 pub slug: String,
161 #[serde(skip_serializing_if = "Option::is_none")]
163 pub url: Option<String>,
164 pub dir: String,
165 pub daemon: String,
166 pub status: String,
167 #[serde(skip_serializing_if = "Option::is_none")]
168 pub port: Option<u16>,
169}
170
171#[derive(Serialize)]
172pub struct JsonSupervisorStatus {
173 pub status: &'static str,
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub web_ui: Option<String>,
176 #[serde(skip_serializing_if = "Option::is_none")]
177 pub error: Option<String>,
178}
179
180#[derive(Serialize)]
181pub struct JsonSettingEntry {
182 pub key: String,
183 pub value: String,
184 #[serde(skip_serializing_if = "Option::is_none")]
185 pub default: Option<String>,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub r#type: Option<&'static str>,
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub env_var: Option<&'static str>,
190 #[serde(skip_serializing_if = "Option::is_none")]
195 pub origin: Option<String>,
196}
197
198pub fn print_json<T: Serialize>(value: &T) -> crate::Result<()> {
199 let json = serde_json::to_string_pretty(value)
200 .map_err(|e| miette::miette!("failed to serialize JSON: {e}"))?;
201 println!("{json}");
202 Ok(())
203}