codex_wrapper/command/
doctor.rs1use crate::Codex;
4use crate::command::CodexCommand;
5use crate::error::Result;
6use crate::exec::{self, CommandOutput};
7
8#[derive(Debug, Clone)]
13pub struct DoctorCommand {
14 json: bool,
15 summary: bool,
16 all: bool,
17 no_color: bool,
18 ascii: bool,
19 config_overrides: Vec<String>,
20 enabled_features: Vec<String>,
21 disabled_features: Vec<String>,
22 retry_policy: Option<crate::retry::RetryPolicy>,
23}
24
25impl DoctorCommand {
26 #[must_use]
28 pub fn new() -> Self {
29 Self {
30 json: false,
31 summary: false,
32 all: false,
33 no_color: false,
34 ascii: false,
35 config_overrides: Vec::new(),
36 enabled_features: Vec::new(),
37 disabled_features: Vec::new(),
38 retry_policy: None,
39 }
40 }
41
42 #[must_use]
44 pub fn json(mut self) -> Self {
45 self.json = true;
46 self
47 }
48
49 #[must_use]
51 pub fn summary(mut self) -> Self {
52 self.summary = true;
53 self
54 }
55
56 #[must_use]
58 pub fn all(mut self) -> Self {
59 self.all = true;
60 self
61 }
62
63 #[must_use]
65 pub fn no_color(mut self) -> Self {
66 self.no_color = true;
67 self
68 }
69
70 #[must_use]
72 pub fn ascii(mut self) -> Self {
73 self.ascii = true;
74 self
75 }
76
77 #[must_use]
79 pub fn config(mut self, key_value: impl Into<String>) -> Self {
80 self.config_overrides.push(key_value.into());
81 self
82 }
83
84 #[must_use]
86 pub fn enable(mut self, feature: impl Into<String>) -> Self {
87 self.enabled_features.push(feature.into());
88 self
89 }
90
91 #[must_use]
93 pub fn disable(mut self, feature: impl Into<String>) -> Self {
94 self.disabled_features.push(feature.into());
95 self
96 }
97
98 #[must_use]
100 pub fn retry(mut self, policy: crate::retry::RetryPolicy) -> Self {
101 self.retry_policy = Some(policy);
102 self
103 }
104}
105
106impl Default for DoctorCommand {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112impl CodexCommand for DoctorCommand {
113 type Output = CommandOutput;
114
115 fn args(&self) -> Vec<String> {
116 let mut args = vec!["doctor".to_string()];
117 for value in &self.config_overrides {
118 args.push("-c".into());
119 args.push(value.clone());
120 }
121 for value in &self.enabled_features {
122 args.push("--enable".into());
123 args.push(value.clone());
124 }
125 for value in &self.disabled_features {
126 args.push("--disable".into());
127 args.push(value.clone());
128 }
129 if self.json {
130 args.push("--json".into());
131 }
132 if self.summary {
133 args.push("--summary".into());
134 }
135 if self.all {
136 args.push("--all".into());
137 }
138 if self.no_color {
139 args.push("--no-color".into());
140 }
141 if self.ascii {
142 args.push("--ascii".into());
143 }
144 args
145 }
146
147 async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
148 exec::run_codex_with_retry(codex, self.args(), self.retry_policy.as_ref()).await
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn doctor_args_default() {
158 let args = DoctorCommand::new().args();
159 assert_eq!(args, vec!["doctor"]);
160 }
161
162 #[test]
163 fn doctor_args_flags() {
164 let args = DoctorCommand::new().json().summary().all().args();
165 assert_eq!(args, vec!["doctor", "--json", "--summary", "--all"]);
166 }
167
168 #[test]
169 fn doctor_args_config_and_output() {
170 let args = DoctorCommand::new()
171 .config("model=o3")
172 .no_color()
173 .ascii()
174 .args();
175 assert_eq!(
176 args,
177 vec!["doctor", "-c", "model=o3", "--no-color", "--ascii"]
178 );
179 }
180}