Skip to main content

sogar_core/
config.rs

1use clap::{crate_name, crate_version, App, Arg, ArgMatches};
2use config::{Config as ConfigCache, ConfigError};
3use serde::{Deserialize, Serialize};
4use std::path::Path;
5
6const REGISTRY_URL: &str = "registry-url";
7const ENV_REGISTRY_URL: &str = "SOGAR_REGISTRY_URL";
8const USERNAME: &str = "username";
9const ENV_USERNAME: &str = "SOGAR_REGISTRY_USERNAME";
10const PASSWORD: &str = "password";
11const ENV_PASSWORD: &str = "SOGAR_REGISTRY_PASSWORD";
12const MEDIA_TYPE: &str = "media-type";
13const VALUE_MEDIA_TYPE: &str = "MEDIA_TYPE";
14const REFERENCE: &str = "reference";
15const VALUE_REFERENCE: &str = "REFERENCE";
16const FILE_PATH: &str = "filepath";
17const VALUE_FILE_PATH: &str = "FILE_PATH";
18const EXPORT_ARTIFACT: &str = "export-artifact";
19const VALUE_EXPORT_ARTIFACT: &str = "EXPORT_ARTIFACT";
20const IMPORT_ARTIFACT: &str = "import-artifact";
21const VALUE_IMPORT_ARTIFACT: &str = "IMPORT_ARTIFACT";
22const ENV_REGISTRY_CACHE: &str = "SOGAR_REGISTRY_CACHE";
23const REGISTRY_CACHE: &str = "registry-cache";
24const COMMAND_DATA: &str = "command_data";
25const COMMAND_TYPE: &str = "command_type";
26
27#[derive(Serialize, Deserialize, Clone, Debug)]
28pub enum CommandType {
29    Export,
30    Import,
31}
32
33#[derive(Serialize, Deserialize, Clone, Debug)]
34pub struct CommandData {
35    #[serde(rename = "media-type")]
36    pub media_type: String,
37    pub reference: String,
38    pub filepath: Vec<String>,
39}
40
41#[derive(Serialize, Deserialize, Clone, Debug)]
42pub struct Settings {
43    #[serde(rename = "registry-url")]
44    pub registry_url: String,
45    pub username: String,
46    pub password: String,
47    pub command_type: CommandType,
48    pub command_data: CommandData,
49    #[serde(rename = "registry-cache")]
50    pub registry_cache: Option<String>,
51}
52
53impl From<CommandType> for config::Value {
54    fn from(val: CommandType) -> Self {
55        match val {
56            CommandType::Export => config::Value::from(String::from("Export")),
57            CommandType::Import => config::Value::from(String::from("Import")),
58        }
59    }
60}
61
62pub fn match_arguments(matches: &ArgMatches, config_cache: &mut ConfigCache) -> Result<(), ConfigError> {
63    if let Some(registry_url) = matches.value_of(REGISTRY_URL) {
64        config_cache.set(REGISTRY_URL, registry_url.to_string())?;
65    }
66
67    if let Some(username) = matches.value_of(USERNAME) {
68        config_cache.set(USERNAME, username.to_string())?;
69    }
70
71    if let Some(password) = matches.value_of(PASSWORD) {
72        config_cache.set(PASSWORD, password.to_string())?;
73    }
74
75    if let Some(namespace) = matches.value_of(REFERENCE) {
76        config_cache.set(
77            format!("{}.{}", COMMAND_DATA, REFERENCE).as_str(),
78            namespace.to_string(),
79        )?;
80    }
81
82    let file_paths = matches
83        .values_of(FILE_PATH)
84        .unwrap_or_default()
85        .map(|plugin| plugin.to_string())
86        .collect::<Vec<String>>();
87
88    for filepath in &file_paths {
89        match matches.value_of(MEDIA_TYPE) {
90            Some(media_type) => {
91                config_cache.set(
92                    format!("{}.{}", COMMAND_DATA, MEDIA_TYPE).as_str(),
93                    media_type.to_string(),
94                )?;
95            }
96            None => {
97                config_cache.set(
98                    format!("{}.{}", COMMAND_DATA, MEDIA_TYPE).as_str(),
99                    get_mime_type_from_file_extension(filepath),
100                )?;
101            }
102        }
103    }
104
105    config_cache.set(format!("{}.{}", COMMAND_DATA, FILE_PATH).as_str(), file_paths)?;
106
107    if let Some(sogar_cache) = matches.value_of(REGISTRY_CACHE) {
108        config_cache.set(REGISTRY_CACHE, sogar_cache.to_string())?;
109    }
110
111    if matches.is_present(EXPORT_ARTIFACT) {
112        config_cache.set(COMMAND_TYPE, CommandType::Export)?;
113    }
114
115    if matches.is_present(IMPORT_ARTIFACT) {
116        config_cache.set(COMMAND_TYPE, CommandType::Import)?;
117    }
118
119    Ok(())
120}
121
122pub fn get_mime_type_from_file_extension(file_name: &str) -> String {
123    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
124
125    if let Some(extension) = Path::new(file_name).extension() {
126        return match format!(".{}", extension.to_str().unwrap()).as_str() {
127            ".zip" => "application/zip".to_string(),
128            ".bz" => "application/x-bzip".to_string(),
129            ".bz2" => "application/x-bzip2".to_string(),
130            ".tar" => "application/x-tar".to_string(),
131            ".7z" => "application/x-7z-compressed".to_string(),
132            ".pdf" => "application/pdf".to_string(),
133            ".json" => "application/json".to_string(),
134            ".js" => "text/javascript".to_string(),
135            ".htm" => "text/html".to_string(),
136            ".html" => "text/html".to_string(),
137            ".rtf" => "application/rtf".to_string(),
138            ".txt" => "text/plain".to_string(),
139            ".bmp" => "image/bmp".to_string(),
140            ".gif" => "image/gif".to_string(),
141            ".ico" => "image/x-icon".to_string(),
142            ".jpeg" => "image/jpeg".to_string(),
143            ".jpg" => "image/jpeg".to_string(),
144            ".png" => "image/png".to_string(),
145            ".svg" => "image/svg+xml".to_string(),
146            ".tif" => "image/tiff".to_string(),
147            ".tiff" => "image/tiff".to_string(),
148            ".webp" => "image/webp".to_string(),
149            ".mp4" => "video/mp4".to_string(),
150            ".mkv" => "video/x-matroska".to_string(),
151            ".mov" => "video/quicktime".to_string(),
152            ".avi" => "video/x-msvideo".to_string(),
153            ".wmv" => "video/x-ms-wmv".to_string(),
154            ".3gp" => "video/3gpp".to_string(),
155            ".flv" => "video/x-flv".to_string(),
156            ".webm" => "video/webm".to_string(),
157            ".mp3" => "audio/mpeg".to_string(),
158            ".wav" => "audio/wav".to_string(),
159            ".weba" => "audio/webm".to_string(),
160            _ => "application/octet-stream".to_string(),
161        };
162    };
163
164    String::from("application/octet-stream")
165}
166
167pub fn create_command_line_app<'a, 'b>() -> App<'a, 'b> {
168    App::new(crate_name!())
169        .author("Devolutions Inc.")
170        .version(concat!(crate_version!(), "\n"))
171        .about("Sogar is a generic implementation of [OCI Artifacts](https://github.com/opencontainers/artifacts)")
172        .arg(
173            Arg::with_name(REGISTRY_URL)
174                .long(REGISTRY_URL)
175                .value_name(ENV_REGISTRY_URL)
176                .help("Registry url to where the artifacts will be pushed.")
177                .takes_value(true)
178                .env(ENV_REGISTRY_URL)
179                .required(true)
180                .empty_values(false),
181        )
182        .arg(
183            Arg::with_name(USERNAME)
184                .short("u")
185                .long(USERNAME)
186                .value_name(ENV_USERNAME)
187                .help("Registry username.")
188                .takes_value(true)
189                .env(ENV_USERNAME)
190                .required(true)
191                .empty_values(false),
192        )
193        .arg(
194            Arg::with_name(PASSWORD)
195                .short("p")
196                .long(PASSWORD)
197                .value_name(ENV_PASSWORD)
198                .help("Registry password.")
199                .takes_value(true)
200                .env(ENV_PASSWORD)
201                .required(true)
202                .empty_values(false),
203        )
204        .arg(
205            Arg::with_name(EXPORT_ARTIFACT)
206                .long(EXPORT_ARTIFACT)
207                .value_name(VALUE_EXPORT_ARTIFACT)
208                .help("Command to export the file to the registry.")
209                .takes_value(false)
210                .requires_all(&[REFERENCE, FILE_PATH]),
211        )
212        .arg(
213            Arg::with_name(MEDIA_TYPE)
214                .long(MEDIA_TYPE)
215                .value_name(VALUE_MEDIA_TYPE)
216                .help("Media type of the file. If not set the default one will be used.")
217                .takes_value(true)
218                .empty_values(false),
219        )
220        .arg(
221            Arg::with_name(REFERENCE)
222                .long(REFERENCE)
223                .value_name(VALUE_REFERENCE)
224                .help("Namespace of the registry where to push the file.")
225                .takes_value(true)
226                .empty_values(false),
227        )
228        .arg(
229            Arg::with_name(FILE_PATH)
230                .long(FILE_PATH)
231                .value_name(VALUE_FILE_PATH)
232                .help("Path of the file that will be exported.")
233                .takes_value(true)
234                .empty_values(false)
235                .multiple(true)
236                .use_delimiter(true)
237                .value_delimiter(";")
238                .number_of_values(1),
239        )
240        .arg(
241            Arg::with_name(IMPORT_ARTIFACT)
242                .long(IMPORT_ARTIFACT)
243                .value_name(VALUE_IMPORT_ARTIFACT)
244                .help("Command to import the file to the registry.")
245                .takes_value(false)
246                .requires_all(&[REFERENCE, FILE_PATH]),
247        )
248        .arg(
249            Arg::with_name(REGISTRY_CACHE)
250                .long(REGISTRY_CACHE)
251                .value_name(ENV_REGISTRY_CACHE)
252                .help("Path to the directory where cache will be located.")
253                .takes_value(true)
254                .env(ENV_REGISTRY_CACHE)
255                .empty_values(false),
256        )
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262
263    #[test]
264    fn test_map_contains_value() {
265        let res = get_mime_type_from_file_extension("test.json");
266
267        assert_eq!(String::from("application/json"), res);
268    }
269
270    #[test]
271    fn test_map_not_contains_value() {
272        let res = get_mime_type_from_file_extension("test.pcap");
273
274        assert_eq!(String::from("application/octet-stream"), res);
275    }
276}