docker_image_pusher/cli/
runner.rs1use 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 Ok(true)
190 }
191}