1use crate::error::{RegistryError, Result};
4
5#[derive(Debug, Clone)]
7pub enum OperationMode {
8 PullAndCache {
10 repository: String,
11 reference: String,
12 },
13
14 ExtractAndCache {
16 tar_file: String,
17 repository: String,
18 reference: String,
19 },
20
21 PushFromCacheUsingManifest {
23 repository: String,
24 reference: String,
25 },
26
27 PushFromCacheUsingTar {
29 repository: String,
30 reference: String,
31 },
32
33 PushFromTar {
35 tar_file: String,
36 repository: String,
37 reference: String,
38 },
39}
40
41impl OperationMode {
42 pub fn description(&self) -> &'static str {
44 match self {
45 OperationMode::PullAndCache { .. } => "Pull from registry and cache locally",
46 OperationMode::ExtractAndCache { .. } => "Extract from tar file and cache locally",
47 OperationMode::PushFromCacheUsingManifest { .. } => "Push from cache using manifest",
48 OperationMode::PushFromCacheUsingTar { .. } => "Push from cache using tar reference",
49 OperationMode::PushFromTar { .. } => "Push directly from tar file",
50 }
51 }
52
53 pub fn requires_registry_client(&self) -> bool {
55 match self {
56 OperationMode::PullAndCache { .. } => true,
57 OperationMode::ExtractAndCache { .. } => false,
58 OperationMode::PushFromCacheUsingManifest { .. } => true,
59 OperationMode::PushFromCacheUsingTar { .. } => true,
60 OperationMode::PushFromTar { .. } => true,
61 }
62 }
63
64 pub fn get_target(&self) -> (&str, &str) {
66 match self {
67 OperationMode::PullAndCache {
68 repository,
69 reference,
70 } => (repository, reference),
71 OperationMode::ExtractAndCache {
72 repository,
73 reference,
74 ..
75 } => (repository, reference),
76 OperationMode::PushFromCacheUsingManifest {
77 repository,
78 reference,
79 } => (repository, reference),
80 OperationMode::PushFromCacheUsingTar {
81 repository,
82 reference,
83 } => (repository, reference),
84 OperationMode::PushFromTar {
85 repository,
86 reference,
87 ..
88 } => (repository, reference),
89 }
90 }
91
92 pub fn get_source(&self) -> Option<&str> {
94 match self {
95 OperationMode::ExtractAndCache { tar_file, .. } => Some(tar_file),
96 OperationMode::PushFromTar { tar_file, .. } => Some(tar_file),
97 _ => None,
98 }
99 }
100
101 pub fn validate(&self) -> Result<()> {
103 match self {
104 OperationMode::PullAndCache {
105 repository,
106 reference,
107 } => Self::validate_repository_reference(repository, reference),
108 OperationMode::ExtractAndCache {
109 tar_file,
110 repository,
111 reference,
112 } => {
113 Self::validate_tar_file(tar_file)?;
114 Self::validate_repository_reference(repository, reference)
115 }
116 OperationMode::PushFromCacheUsingManifest {
117 repository,
118 reference,
119 }
120 | OperationMode::PushFromCacheUsingTar {
121 repository,
122 reference,
123 } => Self::validate_repository_reference(repository, reference),
124 OperationMode::PushFromTar {
125 tar_file,
126 repository,
127 reference,
128 } => {
129 Self::validate_tar_file(tar_file)?;
130 Self::validate_repository_reference(repository, reference)
131 }
132 }
133 }
134
135 fn validate_repository_reference(repository: &str, reference: &str) -> Result<()> {
136 if repository.is_empty() {
137 return Err(RegistryError::Validation(
138 "Repository cannot be empty".to_string(),
139 ));
140 }
141 if reference.is_empty() {
142 return Err(RegistryError::Validation(
143 "Reference cannot be empty".to_string(),
144 ));
145 }
146
147 if repository.contains("..") || repository.starts_with('/') || repository.ends_with('/') {
149 return Err(RegistryError::Validation(format!(
150 "Invalid repository format: {}",
151 repository
152 )));
153 }
154
155 Ok(())
156 }
157
158 fn validate_tar_file(tar_file: &str) -> Result<()> {
159 if tar_file.is_empty() {
160 return Err(RegistryError::Validation(
161 "Tar file path cannot be empty".to_string(),
162 ));
163 }
164
165 let path = std::path::Path::new(tar_file);
166 if !path.exists() {
167 return Err(RegistryError::Validation(format!(
168 "Tar file does not exist: {}",
169 tar_file
170 )));
171 }
172
173 if !path.is_file() {
174 return Err(RegistryError::Validation(format!(
175 "Path is not a file: {}",
176 tar_file
177 )));
178 }
179
180 Ok(())
181 }
182
183 pub fn is_push_operation(&self) -> bool {
185 matches!(
186 self,
187 OperationMode::PushFromCacheUsingManifest { .. }
188 | OperationMode::PushFromCacheUsingTar { .. }
189 | OperationMode::PushFromTar { .. }
190 )
191 }
192
193 pub fn is_cache_operation(&self) -> bool {
195 matches!(
196 self,
197 OperationMode::PullAndCache { .. } | OperationMode::ExtractAndCache { .. }
198 )
199 }
200
201 pub fn requires_tar_file(&self) -> bool {
203 matches!(
204 self,
205 OperationMode::ExtractAndCache { .. } | OperationMode::PushFromTar { .. }
206 )
207 }
208}
209
210impl std::fmt::Display for OperationMode {
211 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
212 match self {
213 OperationMode::PullAndCache {
214 repository,
215 reference,
216 } => {
217 write!(f, "Pull {}:{} from registry", repository, reference)
218 }
219 OperationMode::ExtractAndCache {
220 tar_file,
221 repository,
222 reference,
223 } => {
224 write!(
225 f,
226 "Extract {} to cache as {}:{}",
227 tar_file, repository, reference
228 )
229 }
230 OperationMode::PushFromCacheUsingManifest {
231 repository,
232 reference,
233 } => {
234 write!(f, "Push {}:{} from cache (manifest)", repository, reference)
235 }
236 OperationMode::PushFromCacheUsingTar {
237 repository,
238 reference,
239 } => {
240 write!(f, "Push {}:{} from cache (tar)", repository, reference)
241 }
242 OperationMode::PushFromTar {
243 tar_file,
244 repository,
245 reference,
246 } => {
247 write!(
248 f,
249 "Push {} directly as {}:{}",
250 tar_file, repository, reference
251 )
252 }
253 }
254 }
255}