docker_image_pusher/cli/
config.rs1use crate::error::{RegistryError, Result};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct AuthConfig {
9 pub username: String,
10 pub password: String,
11}
12
13impl AuthConfig {
14 pub fn new(username: String, password: String) -> Self {
15 Self { username, password }
16 }
17
18 pub fn validate(&self) -> Result<()> {
19 if self.username.is_empty() {
20 return Err(RegistryError::Validation(
21 "Username cannot be empty".to_string(),
22 ));
23 }
24 if self.password.is_empty() {
25 return Err(RegistryError::Validation(
26 "Password cannot be empty".to_string(),
27 ));
28 }
29 Ok(())
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct RegistryConfig {
36 pub address: String,
37 pub skip_tls: bool,
38 pub timeout: u64,
39}
40
41impl RegistryConfig {
42 pub fn new(address: String) -> Self {
43 Self {
44 address,
45 skip_tls: false,
46 timeout: 7200,
47 }
48 }
49
50 pub fn with_skip_tls(mut self, skip_tls: bool) -> Self {
51 self.skip_tls = skip_tls;
52 self
53 }
54
55 pub fn with_timeout(mut self, timeout: u64) -> Self {
56 self.timeout = timeout;
57 self
58 }
59
60 pub fn validate(&self) -> Result<()> {
61 if self.address.is_empty() {
62 return Err(RegistryError::Validation(
63 "Registry address cannot be empty".to_string(),
64 ));
65 }
66
67 if !self.address.starts_with("http://") && !self.address.starts_with("https://") {
68 return Err(RegistryError::Validation(format!(
69 "Invalid registry address: {}. Must start with http:// or https://",
70 self.address
71 )));
72 }
73
74 if self.timeout == 0 {
75 return Err(RegistryError::Validation(
76 "Timeout must be greater than 0".to_string(),
77 ));
78 }
79
80 Ok(())
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct AppConfig {
87 pub cache_dir: String,
88 pub timeout: u64,
89 pub max_concurrent: usize,
90 pub retry_attempts: usize,
91 pub large_layer_threshold: u64,
92 pub skip_tls: bool,
93 pub verbose: bool,
94}
95
96impl Default for AppConfig {
97 fn default() -> Self {
98 Self {
99 cache_dir: ".cache".to_string(),
100 timeout: 7200,
101 max_concurrent: 1,
102 retry_attempts: 3,
103 large_layer_threshold: 1024 * 1024 * 1024, skip_tls: false,
105 verbose: false,
106 }
107 }
108}
109
110impl AppConfig {
111 pub fn validate(&self) -> Result<()> {
112 if self.max_concurrent == 0 {
113 return Err(RegistryError::Validation(
114 "max_concurrent must be greater than 0".to_string(),
115 ));
116 }
117 if self.timeout == 0 {
118 return Err(RegistryError::Validation(
119 "timeout must be greater than 0".to_string(),
120 ));
121 }
122 if self.large_layer_threshold == 0 {
123 return Err(RegistryError::Validation(
124 "large_layer_threshold must be greater than 0".to_string(),
125 ));
126 }
127 if self.retry_attempts == 0 {
128 return Err(RegistryError::Validation(
129 "retry_attempts must be greater than 0".to_string(),
130 ));
131 }
132 Ok(())
133 }
134
135 pub fn from_env() -> Self {
137 let mut config = Self::default();
138
139 if let Ok(val) = std::env::var("DOCKER_PUSHER_CACHE_DIR") {
140 config.cache_dir = val;
141 }
142 if let Ok(val) = std::env::var("DOCKER_PUSHER_TIMEOUT") {
143 if let Ok(timeout) = val.parse() {
144 config.timeout = timeout;
145 }
146 }
147 if let Ok(val) = std::env::var("DOCKER_PUSHER_MAX_CONCURRENT") {
148 if let Ok(max_concurrent) = val.parse() {
149 config.max_concurrent = max_concurrent;
150 }
151 }
152 if let Ok(val) = std::env::var("DOCKER_PUSHER_VERBOSE") {
153 config.verbose = val.to_lowercase() == "true" || val == "1";
154 }
155
156 config
157 }
158
159 pub fn merge(mut self, other: &AppConfig) -> Self {
161 let default = AppConfig::default();
163
164 if other.cache_dir != default.cache_dir {
165 self.cache_dir = other.cache_dir.clone();
166 }
167 if other.timeout != default.timeout {
168 self.timeout = other.timeout;
169 }
170 if other.max_concurrent != default.max_concurrent {
171 self.max_concurrent = other.max_concurrent;
172 }
173 if other.retry_attempts != default.retry_attempts {
174 self.retry_attempts = other.retry_attempts;
175 }
176 if other.large_layer_threshold != default.large_layer_threshold {
177 self.large_layer_threshold = other.large_layer_threshold;
178 }
179 if other.skip_tls != default.skip_tls {
180 self.skip_tls = other.skip_tls;
181 }
182 if other.verbose != default.verbose {
183 self.verbose = other.verbose;
184 }
185
186 self
187 }
188}