docker_wrapper/command/context/
ls.rs1use crate::command::{CommandExecutor, CommandOutput, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6use serde::Deserialize;
7
8#[derive(Debug, Clone, Deserialize)]
10#[serde(rename_all = "PascalCase")]
11pub struct ContextInfo {
12 pub name: String,
14
15 #[serde(default)]
17 pub description: String,
18
19 #[serde(rename = "DockerEndpoint")]
21 pub docker_endpoint: String,
22
23 #[serde(rename = "KubernetesEndpoint", default)]
25 pub kubernetes_endpoint: String,
26
27 #[serde(default)]
29 pub current: bool,
30}
31
32#[derive(Debug, Clone)]
50pub struct ContextLsCommand {
51 format: Option<String>,
53 quiet: bool,
55 pub executor: CommandExecutor,
57}
58
59impl ContextLsCommand {
60 #[must_use]
62 pub fn new() -> Self {
63 Self {
64 format: None,
65 quiet: false,
66 executor: CommandExecutor::new(),
67 }
68 }
69
70 #[must_use]
72 pub fn format(mut self, template: impl Into<String>) -> Self {
73 self.format = Some(template.into());
74 self
75 }
76
77 #[must_use]
79 pub fn format_json(self) -> Self {
80 self.format("json")
81 }
82
83 #[must_use]
85 pub fn quiet(mut self) -> Self {
86 self.quiet = true;
87 self
88 }
89}
90
91impl Default for ContextLsCommand {
92 fn default() -> Self {
93 Self::new()
94 }
95}
96
97#[async_trait]
98impl DockerCommand for ContextLsCommand {
99 type Output = CommandOutput;
100
101 fn get_executor(&self) -> &CommandExecutor {
102 &self.executor
103 }
104
105 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
106 &mut self.executor
107 }
108
109 fn build_command_args(&self) -> Vec<String> {
110 let mut args = vec!["context".to_string(), "ls".to_string()];
111
112 if let Some(format) = &self.format {
113 args.push("--format".to_string());
114 args.push(format.clone());
115 }
116
117 if self.quiet {
118 args.push("--quiet".to_string());
119 }
120
121 args.extend(self.executor.raw_args.clone());
122 args
123 }
124
125 async fn execute(&self) -> Result<Self::Output> {
126 let args = self.build_command_args();
127 self.execute_command(args).await
128 }
129}
130
131impl CommandOutput {
133 pub fn parse_contexts(&self) -> Result<Vec<ContextInfo>> {
139 if self.stdout.trim().is_empty() {
140 return Ok(Vec::new());
141 }
142
143 let contexts: Vec<ContextInfo> = serde_json::from_str(&self.stdout)?;
144 Ok(contexts)
145 }
146
147 #[must_use]
149 pub fn current_context(&self) -> Option<String> {
150 for line in self.stdout.lines() {
152 if line.contains('*') {
153 let parts: Vec<&str> = line.split_whitespace().collect();
155 if let Some(name) = parts.first() {
156 if name == &"*" {
157 if let Some(actual_name) = parts.get(1) {
158 return Some((*actual_name).to_string());
159 }
160 }
161 }
162 }
163 }
164 None
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_context_ls_basic() {
174 let cmd = ContextLsCommand::new();
175 let args = cmd.build_command_args();
176 assert_eq!(args[0], "context");
177 assert_eq!(args[1], "ls");
178 }
179
180 #[test]
181 fn test_context_ls_with_format() {
182 let cmd = ContextLsCommand::new().format("{{.Name}}");
183 let args = cmd.build_command_args();
184 assert!(args.contains(&"--format".to_string()));
185 assert!(args.contains(&"{{.Name}}".to_string()));
186 }
187
188 #[test]
189 fn test_context_ls_quiet() {
190 let cmd = ContextLsCommand::new().quiet();
191 let args = cmd.build_command_args();
192 assert!(args.contains(&"--quiet".to_string()));
193 }
194
195 #[test]
196 fn test_current_context_parsing() {
197 let output = CommandOutput {
198 stdout: "default Default local daemon \n* production * Production environment unix:///var/run/docker.sock".to_string(),
199 stderr: String::new(),
200 exit_code: 0,
201 success: true,
202 };
203
204 assert_eq!(output.current_context(), Some("production".to_string()));
205 }
206}