1use crate::command::{CommandExecutor, CommandOutput, DockerCommand};
6use crate::error::Result;
7use async_trait::async_trait;
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::collections::HashMap;
11
12#[derive(Debug, Clone)]
14pub struct VolumeCreateCommand {
15 name: Option<String>,
16 driver: Option<String>,
17 driver_opts: HashMap<String, String>,
18 labels: HashMap<String, String>,
19 pub executor: CommandExecutor,
21}
22
23impl VolumeCreateCommand {
24 #[must_use]
26 pub fn new() -> Self {
27 Self {
28 name: None,
29 driver: None,
30 driver_opts: HashMap::new(),
31 labels: HashMap::new(),
32 executor: CommandExecutor::new(),
33 }
34 }
35
36 #[must_use]
38 pub fn name(mut self, name: impl Into<String>) -> Self {
39 self.name = Some(name.into());
40 self
41 }
42
43 #[must_use]
45 pub fn driver(mut self, driver: impl Into<String>) -> Self {
46 self.driver = Some(driver.into());
47 self
48 }
49
50 #[must_use]
52 pub fn driver_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
53 self.driver_opts.insert(key.into(), value.into());
54 self
55 }
56
57 #[must_use]
59 pub fn label(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
60 self.labels.insert(key.into(), value.into());
61 self
62 }
63
64 pub async fn run(&self) -> Result<VolumeCreateResult> {
70 self.execute().await.map(VolumeCreateResult::from)
71 }
72}
73
74impl Default for VolumeCreateCommand {
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80#[async_trait]
81impl DockerCommand for VolumeCreateCommand {
82 type Output = CommandOutput;
83
84 fn build_command_args(&self) -> Vec<String> {
85 let mut args = vec!["volume".to_string(), "create".to_string()];
86
87 if let Some(ref driver) = self.driver {
88 args.push("--driver".to_string());
89 args.push(driver.clone());
90 }
91
92 for (key, value) in &self.driver_opts {
93 args.push("--opt".to_string());
94 args.push(format!("{key}={value}"));
95 }
96
97 for (key, value) in &self.labels {
98 args.push("--label".to_string());
99 args.push(format!("{key}={value}"));
100 }
101
102 if let Some(ref name) = self.name {
103 args.push(name.clone());
104 }
105
106 args.extend(self.executor.raw_args.clone());
107 args
108 }
109
110 async fn execute(&self) -> Result<Self::Output> {
111 let args = self.build_command_args();
112 let command_name = args[0].clone();
113 let command_args = args[1..].to_vec();
114 self.executor
115 .execute_command(&command_name, command_args)
116 .await
117 }
118
119 fn get_executor(&self) -> &CommandExecutor {
120 &self.executor
121 }
122
123 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
124 &mut self.executor
125 }
126}
127
128#[derive(Debug, Clone)]
130pub struct VolumeCreateResult {
131 pub volume_name: String,
133 pub raw_output: CommandOutput,
135}
136
137impl From<CommandOutput> for VolumeCreateResult {
138 fn from(output: CommandOutput) -> Self {
139 Self {
140 volume_name: output.stdout.trim().to_string(),
141 raw_output: output,
142 }
143 }
144}
145
146#[derive(Debug, Clone)]
148pub struct VolumeLsCommand {
149 filters: HashMap<String, String>,
150 format: Option<String>,
151 quiet: bool,
152 pub executor: CommandExecutor,
154}
155
156impl VolumeLsCommand {
157 #[must_use]
159 pub fn new() -> Self {
160 Self {
161 filters: HashMap::new(),
162 format: None,
163 quiet: false,
164 executor: CommandExecutor::new(),
165 }
166 }
167
168 #[must_use]
170 pub fn filter(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
171 self.filters.insert(key.into(), value.into());
172 self
173 }
174
175 #[must_use]
177 pub fn format(mut self, format: impl Into<String>) -> Self {
178 self.format = Some(format.into());
179 self
180 }
181
182 #[must_use]
184 pub fn quiet(mut self) -> Self {
185 self.quiet = true;
186 self
187 }
188
189 pub async fn run(&self) -> Result<VolumeLsOutput> {
195 self.execute().await.map(VolumeLsOutput::from)
196 }
197}
198
199impl Default for VolumeLsCommand {
200 fn default() -> Self {
201 Self::new()
202 }
203}
204
205#[async_trait]
206impl DockerCommand for VolumeLsCommand {
207 type Output = CommandOutput;
208
209 fn build_command_args(&self) -> Vec<String> {
210 let mut args = vec!["volume".to_string(), "ls".to_string()];
211
212 for (key, value) in &self.filters {
213 args.push("--filter".to_string());
214 args.push(format!("{key}={value}"));
215 }
216
217 if let Some(ref format) = self.format {
218 args.push("--format".to_string());
219 args.push(format.clone());
220 }
221
222 if self.quiet {
223 args.push("--quiet".to_string());
224 }
225
226 args.extend(self.executor.raw_args.clone());
227 args
228 }
229
230 async fn execute(&self) -> Result<Self::Output> {
231 let args = self.build_command_args();
232 let command_name = args[0].clone();
233 let command_args = args[1..].to_vec();
234 self.executor
235 .execute_command(&command_name, command_args)
236 .await
237 }
238
239 fn get_executor(&self) -> &CommandExecutor {
240 &self.executor
241 }
242
243 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
244 &mut self.executor
245 }
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize)]
250#[serde(rename_all = "PascalCase")]
251pub struct VolumeInfo {
252 #[serde(default)]
254 pub driver: String,
255 #[serde(default)]
257 pub name: String,
258 #[serde(default)]
260 pub mountpoint: String,
261 #[serde(default)]
263 pub scope: String,
264 #[serde(default)]
266 pub labels: HashMap<String, String>,
267}
268
269#[derive(Debug, Clone)]
271pub struct VolumeLsOutput {
272 pub volumes: Vec<VolumeInfo>,
274 pub raw_output: CommandOutput,
276}
277
278impl From<CommandOutput> for VolumeLsOutput {
279 fn from(output: CommandOutput) -> Self {
280 let volumes = if output.stdout.starts_with('[') {
281 serde_json::from_str(&output.stdout).unwrap_or_default()
282 } else {
283 vec![]
284 };
285
286 Self {
287 volumes,
288 raw_output: output,
289 }
290 }
291}
292
293#[derive(Debug, Clone)]
295pub struct VolumeRmCommand {
296 volumes: Vec<String>,
297 force: bool,
298 pub executor: CommandExecutor,
300}
301
302impl VolumeRmCommand {
303 #[must_use]
305 pub fn new(volume: impl Into<String>) -> Self {
306 Self {
307 volumes: vec![volume.into()],
308 force: false,
309 executor: CommandExecutor::new(),
310 }
311 }
312
313 #[must_use]
315 pub fn add_volume(mut self, volume: impl Into<String>) -> Self {
316 self.volumes.push(volume.into());
317 self
318 }
319
320 #[must_use]
322 pub fn force(mut self) -> Self {
323 self.force = true;
324 self
325 }
326
327 pub async fn run(&self) -> Result<VolumeRmResult> {
333 self.execute().await.map(VolumeRmResult::from)
334 }
335}
336
337#[async_trait]
338impl DockerCommand for VolumeRmCommand {
339 type Output = CommandOutput;
340
341 fn build_command_args(&self) -> Vec<String> {
342 let mut args = vec!["volume".to_string(), "rm".to_string()];
343
344 if self.force {
345 args.push("--force".to_string());
346 }
347
348 for volume in &self.volumes {
349 args.push(volume.clone());
350 }
351
352 args.extend(self.executor.raw_args.clone());
353 args
354 }
355
356 async fn execute(&self) -> Result<Self::Output> {
357 let args = self.build_command_args();
358 let command_name = args[0].clone();
359 let command_args = args[1..].to_vec();
360 self.executor
361 .execute_command(&command_name, command_args)
362 .await
363 }
364
365 fn get_executor(&self) -> &CommandExecutor {
366 &self.executor
367 }
368
369 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
370 &mut self.executor
371 }
372}
373
374#[derive(Debug, Clone)]
376pub struct VolumeRmResult {
377 pub removed_volumes: Vec<String>,
379 pub raw_output: CommandOutput,
381}
382
383impl From<CommandOutput> for VolumeRmResult {
384 fn from(output: CommandOutput) -> Self {
385 let removed_volumes = output
386 .stdout
387 .lines()
388 .filter(|line| !line.is_empty())
389 .map(String::from)
390 .collect();
391
392 Self {
393 removed_volumes,
394 raw_output: output,
395 }
396 }
397}
398
399#[derive(Debug, Clone)]
401pub struct VolumeInspectCommand {
402 volumes: Vec<String>,
403 format: Option<String>,
404 pub executor: CommandExecutor,
406}
407
408impl VolumeInspectCommand {
409 #[must_use]
411 pub fn new(volume: impl Into<String>) -> Self {
412 Self {
413 volumes: vec![volume.into()],
414 format: None,
415 executor: CommandExecutor::new(),
416 }
417 }
418
419 #[must_use]
421 pub fn format(mut self, format: impl Into<String>) -> Self {
422 self.format = Some(format.into());
423 self
424 }
425
426 pub async fn run(&self) -> Result<VolumeInspectOutput> {
432 self.execute().await.map(VolumeInspectOutput::from)
433 }
434}
435
436#[async_trait]
437impl DockerCommand for VolumeInspectCommand {
438 type Output = CommandOutput;
439
440 fn build_command_args(&self) -> Vec<String> {
441 let mut args = vec!["volume".to_string(), "inspect".to_string()];
442
443 if let Some(ref format) = self.format {
444 args.push("--format".to_string());
445 args.push(format.clone());
446 }
447
448 for volume in &self.volumes {
449 args.push(volume.clone());
450 }
451
452 args.extend(self.executor.raw_args.clone());
453 args
454 }
455
456 async fn execute(&self) -> Result<Self::Output> {
457 let args = self.build_command_args();
458 let command_name = args[0].clone();
459 let command_args = args[1..].to_vec();
460 self.executor
461 .execute_command(&command_name, command_args)
462 .await
463 }
464
465 fn get_executor(&self) -> &CommandExecutor {
466 &self.executor
467 }
468
469 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
470 &mut self.executor
471 }
472}
473
474#[derive(Debug, Clone)]
476pub struct VolumeInspectOutput {
477 pub json: Option<Value>,
479 pub raw_output: CommandOutput,
481}
482
483impl From<CommandOutput> for VolumeInspectOutput {
484 fn from(output: CommandOutput) -> Self {
485 let json = serde_json::from_str(&output.stdout).ok();
486 Self {
487 json,
488 raw_output: output,
489 }
490 }
491}
492
493#[derive(Debug, Clone)]
495pub struct VolumePruneCommand {
496 all: bool,
497 filters: HashMap<String, String>,
498 force: bool,
499 pub executor: CommandExecutor,
501}
502
503impl VolumePruneCommand {
504 #[must_use]
506 pub fn new() -> Self {
507 Self {
508 all: false,
509 filters: HashMap::new(),
510 force: false,
511 executor: CommandExecutor::new(),
512 }
513 }
514
515 #[must_use]
517 pub fn all(mut self) -> Self {
518 self.all = true;
519 self
520 }
521
522 #[must_use]
524 pub fn filter(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
525 self.filters.insert(key.into(), value.into());
526 self
527 }
528
529 #[must_use]
531 pub fn force(mut self) -> Self {
532 self.force = true;
533 self
534 }
535
536 pub async fn run(&self) -> Result<VolumePruneResult> {
542 self.execute().await.map(VolumePruneResult::from)
543 }
544}
545
546impl Default for VolumePruneCommand {
547 fn default() -> Self {
548 Self::new()
549 }
550}
551
552#[async_trait]
553impl DockerCommand for VolumePruneCommand {
554 type Output = CommandOutput;
555
556 fn build_command_args(&self) -> Vec<String> {
557 let mut args = vec!["volume".to_string(), "prune".to_string()];
558
559 if self.all {
560 args.push("--all".to_string());
561 }
562
563 for (key, value) in &self.filters {
564 args.push("--filter".to_string());
565 args.push(format!("{key}={value}"));
566 }
567
568 if self.force {
569 args.push("--force".to_string());
570 }
571
572 args.extend(self.executor.raw_args.clone());
573 args
574 }
575
576 async fn execute(&self) -> Result<Self::Output> {
577 let args = self.build_command_args();
578 let command_name = args[0].clone();
579 let command_args = args[1..].to_vec();
580 self.executor
581 .execute_command(&command_name, command_args)
582 .await
583 }
584
585 fn get_executor(&self) -> &CommandExecutor {
586 &self.executor
587 }
588
589 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
590 &mut self.executor
591 }
592}
593
594#[derive(Debug, Clone)]
596pub struct VolumePruneResult {
597 pub deleted_volumes: Vec<String>,
599 pub space_reclaimed: Option<u64>,
601 pub raw_output: CommandOutput,
603}
604
605impl From<CommandOutput> for VolumePruneResult {
606 fn from(output: CommandOutput) -> Self {
607 let deleted_volumes = Vec::new(); Self {
609 deleted_volumes,
610 space_reclaimed: None,
611 raw_output: output,
612 }
613 }
614}
615
616#[cfg(test)]
617mod tests {
618 use super::*;
619
620 #[test]
621 fn test_volume_create() {
622 let cmd = VolumeCreateCommand::new().name("my-volume");
623 let args = cmd.build_command_args();
624 assert_eq!(args, vec!["volume", "create", "my-volume"]);
625 }
626
627 #[test]
628 fn test_volume_ls() {
629 let cmd = VolumeLsCommand::new();
630 let args = cmd.build_command_args();
631 assert_eq!(args, vec!["volume", "ls"]);
632 }
633
634 #[test]
635 fn test_volume_rm() {
636 let cmd = VolumeRmCommand::new("my-volume");
637 let args = cmd.build_command_args();
638 assert_eq!(args, vec!["volume", "rm", "my-volume"]);
639 }
640
641 #[test]
642 fn test_volume_inspect() {
643 let cmd = VolumeInspectCommand::new("my-volume");
644 let args = cmd.build_command_args();
645 assert_eq!(args, vec!["volume", "inspect", "my-volume"]);
646 }
647
648 #[test]
649 fn test_volume_prune() {
650 let cmd = VolumePruneCommand::new().force();
651 let args = cmd.build_command_args();
652 assert_eq!(args, vec!["volume", "prune", "--force"]);
653 }
654}