docker_image_pusher/cli/
runner.rs

1//! 简化的运行器,使用操作模式
2//!
3//! 这个模块实现了主要的工作流程,根据操作模式执行相应的操作
4
5use crate::cli::args::{Args, Commands};
6use crate::cli::config::AuthConfig;
7use crate::cli::operation_mode::OperationMode;
8use crate::error::{RegistryError, Result};
9use crate::image::image_manager::ImageManager;
10use crate::logging::Logger;
11use crate::registry::RegistryClientBuilder;
12
13pub struct Runner {
14    output: Logger,
15}
16
17impl Runner {
18    pub fn new(verbose: bool) -> Self {
19        Self {
20            output: Logger::new(verbose),
21        }
22    }
23
24    pub async fn run(&self, args: Args) -> Result<()> {
25        self.output.section("Docker Image Pusher");
26        args.validate()?;
27
28        match args.command {
29            Some(Commands::Pull(pull_args)) => {
30                let mut image_manager = ImageManager::new(
31                    Some(pull_args.cache_dir.to_str().unwrap()),
32                    pull_args.verbose,
33                )?;
34
35                let auth_config = if let (Some(username), Some(password)) =
36                    (&pull_args.username, &pull_args.password)
37                {
38                    Some(AuthConfig::new(username.clone(), password.clone()))
39                } else {
40                    None
41                };
42
43                let client = RegistryClientBuilder::new(pull_args.registry.clone())
44                    .with_auth(auth_config.clone())
45                    .with_timeout(pull_args.timeout)
46                    .with_skip_tls(pull_args.skip_tls)
47                    .with_verbose(pull_args.verbose)
48                    .build()?;
49
50                let token = if let Some(auth) = &auth_config {
51                    client
52                        .authenticate_for_repository(auth, &pull_args.repository)
53                        .await?
54                } else {
55                    None
56                };
57
58                let mode = OperationMode::PullAndCache {
59                    repository: pull_args.repository,
60                    reference: pull_args.reference,
61                };
62
63                image_manager
64                    .execute_operation(&mode, Some(&client), token.as_deref())
65                    .await?;
66            }
67            Some(Commands::Extract(extract_args)) => {
68                let mut image_manager = ImageManager::new(
69                    Some(extract_args.cache_dir.to_str().unwrap()),
70                    extract_args.verbose,
71                )?;
72
73                let file_stem = extract_args
74                    .file
75                    .file_stem()
76                    .and_then(|s| s.to_str())
77                    .unwrap_or("extracted-image");
78                let repository = format!("local/{}", file_stem);
79                let reference = "latest".to_string();
80
81                let mode = OperationMode::ExtractAndCache {
82                    tar_file: extract_args.file.to_string_lossy().to_string(),
83                    repository,
84                    reference,
85                };
86
87                image_manager.execute_operation(&mode, None, None).await?;
88            }
89            Some(Commands::Push(push_args)) => {
90                let mut image_manager = ImageManager::new(
91                    Some(push_args.cache_dir.to_str().unwrap()),
92                    push_args.verbose,
93                )?;
94
95                let auth_config = if let (Some(username), Some(password)) =
96                    (&push_args.username, &push_args.password)
97                {
98                    Some(AuthConfig::new(username.clone(), password.clone()))
99                } else {
100                    None
101                };
102
103                let client = RegistryClientBuilder::new(push_args.registry.clone())
104                    .with_auth(auth_config.clone())
105                    .with_timeout(push_args.timeout)
106                    .with_skip_tls(push_args.skip_tls)
107                    .with_verbose(push_args.verbose)
108                    .build()?;
109
110                let token = if let Some(auth) = &auth_config {
111                    client
112                        .authenticate_for_repository(auth, &push_args.repository)
113                        .await?
114                } else {
115                    None
116                };
117
118                let mode = if push_args.is_tar_source() {
119                    OperationMode::PushFromTar {
120                        tar_file: push_args.source.clone(),
121                        repository: push_args.repository,
122                        reference: push_args.reference,
123                    }
124                } else {
125                    if let Some((source_repo, source_ref)) = push_args.parse_source_repository() {
126                        if image_manager.is_image_cached(&source_repo, &source_ref)? {
127                            OperationMode::PushFromCacheUsingManifest {
128                                repository: push_args.repository,
129                                reference: push_args.reference,
130                            }
131                        } else {
132                            return Err(RegistryError::Validation(format!(
133                                "Source image {}:{} not found in cache. Please pull it first.",
134                                source_repo, source_ref
135                            )));
136                        }
137                    } else {
138                        return Err(RegistryError::Validation(format!(
139                            "Invalid source format: {}. Expected repository:tag or tar file path.",
140                            push_args.source
141                        )));
142                    }
143                };
144
145                image_manager
146                    .execute_operation(&mode, Some(&client), token.as_deref())
147                    .await?;
148            }
149            Some(Commands::List(list_args)) => {
150                let image_manager =
151                    ImageManager::new(Some(list_args.cache_dir.to_str().unwrap()), false)?;
152
153                self.output.section("Cached Images");
154                let images = image_manager.list_cached_images();
155
156                if images.is_empty() {
157                    self.output.info("No images found in cache");
158                } else {
159                    for (repository, reference) in images {
160                        self.output.info(&format!("{}:{}", repository, reference));
161                    }
162                }
163            }
164            Some(Commands::Clean(clean_args)) => {
165                self.output.section("Cleaning Cache");
166
167                if clean_args.force || self.confirm_cleanup()? {
168                    if clean_args.cache_dir.exists() {
169                        std::fs::remove_dir_all(&clean_args.cache_dir)?;
170                        self.output.success("Cache cleaned successfully");
171                    } else {
172                        self.output.info("Cache directory does not exist");
173                    }
174                } else {
175                    self.output.info("Cache cleanup cancelled");
176                }
177            }
178            None => {
179                return Err(RegistryError::Validation("No command provided".to_string()));
180            }
181        }
182
183        self.output.success("Operation completed successfully!");
184        Ok(())
185    }
186
187    fn confirm_cleanup(&self) -> Result<bool> {
188        // 简化确认逻辑
189        Ok(true)
190    }
191}