mirror of
https://github.com/sergi0g/cup.git
synced 2025-11-14 16:13:48 -05:00
168 lines
5.0 KiB
Rust
168 lines
5.0 KiB
Rust
use rustc_hash::FxHashMap;
|
|
use serde::Deserialize;
|
|
use serde::Deserializer;
|
|
use std::env;
|
|
use std::mem;
|
|
use std::path::PathBuf;
|
|
|
|
use crate::error;
|
|
|
|
// We can't assign `a` to `b` in the loop in `Config::load`, so we'll have to use swap. It looks ugly so now we have a macro for it.
|
|
macro_rules! swap {
|
|
($a:expr, $b:expr) => {
|
|
mem::swap(&mut $a, &mut $b)
|
|
};
|
|
}
|
|
|
|
#[derive(Clone, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum Theme {
|
|
Default,
|
|
Blue,
|
|
}
|
|
|
|
impl Default for Theme {
|
|
fn default() -> Self {
|
|
Self::Default
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum UpdateType {
|
|
None,
|
|
Major,
|
|
Minor,
|
|
Patch,
|
|
}
|
|
|
|
impl Default for UpdateType {
|
|
fn default() -> Self {
|
|
Self::None
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Deserialize, Default)]
|
|
#[serde(deny_unknown_fields)]
|
|
#[serde(default)]
|
|
pub struct RegistryConfig {
|
|
pub authentication: Option<String>,
|
|
pub insecure: bool,
|
|
pub ignore: bool,
|
|
}
|
|
|
|
#[derive(Clone, Deserialize, Default)]
|
|
#[serde(default)]
|
|
pub struct ImageConfig {
|
|
pub extra: Vec<String>,
|
|
pub exclude: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct Config {
|
|
version: u8,
|
|
pub agent: bool,
|
|
pub ignore_update_type: UpdateType,
|
|
pub images: ImageConfig,
|
|
#[serde(deserialize_with = "empty_as_none")]
|
|
pub refresh_interval: Option<String>,
|
|
pub registries: FxHashMap<String, RegistryConfig>,
|
|
pub servers: FxHashMap<String, String>,
|
|
pub socket: Option<String>,
|
|
pub theme: Theme,
|
|
}
|
|
|
|
impl Config {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
version: 3,
|
|
agent: false,
|
|
ignore_update_type: UpdateType::default(),
|
|
images: ImageConfig::default(),
|
|
refresh_interval: None,
|
|
registries: FxHashMap::default(),
|
|
servers: FxHashMap::default(),
|
|
socket: None,
|
|
theme: Theme::Default,
|
|
}
|
|
}
|
|
|
|
/// Loads file and env config and merges them
|
|
pub fn load(&mut self, path: Option<PathBuf>) -> Self {
|
|
let mut config = self.load_file(path);
|
|
|
|
// Get environment variables with CUP_ prefix
|
|
let env_vars: FxHashMap<String, String> =
|
|
env::vars().filter(|(k, _)| k.starts_with("CUP_")).collect();
|
|
|
|
if !env_vars.is_empty() {
|
|
if let Ok(mut cfg) = envy::prefixed("CUP_").from_env::<Config>() {
|
|
// If we have environment variables, override config.json options
|
|
for (key, _) in env_vars {
|
|
match key.as_str() {
|
|
"CUP_AGENT" => config.agent = cfg.agent,
|
|
#[rustfmt::skip]
|
|
"CUP_IGNORE_UPDATE_TYPE" => swap!(config.ignore_update_type, cfg.ignore_update_type),
|
|
#[rustfmt::skip]
|
|
"CUP_REFRESH_INTERVAL" => swap!(config.refresh_interval, cfg.refresh_interval),
|
|
"CUP_SOCKET" => swap!(config.socket, cfg.socket),
|
|
"CUP_THEME" => swap!(config.theme, cfg.theme),
|
|
// The syntax for these is slightly more complicated, not sure if they should be enabled or not. Let's stick to simple types for now.
|
|
// "CUP_IMAGES" => swap!(config.images, cfg.images),
|
|
// "CUP_REGISTRIES" => swap!(config.registries, cfg.registries),
|
|
// "CUP_SERVERS" => swap!(config.servers, cfg.servers),
|
|
_ => (), // Maybe print a warning if other CUP_ variables are detected
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
config
|
|
}
|
|
|
|
/// Reads the config from the file path provided and returns the parsed result.
|
|
fn load_file(&self, path: Option<PathBuf>) -> Self {
|
|
let raw_config = match &path {
|
|
Some(path) => std::fs::read_to_string(path),
|
|
None => return Self::new(), // Empty config
|
|
};
|
|
if raw_config.is_err() {
|
|
error!(
|
|
"Failed to read config file from {}. Are you sure the file exists?",
|
|
&path.unwrap().to_str().unwrap()
|
|
)
|
|
};
|
|
self.parse(&raw_config.unwrap()) // We can safely unwrap here
|
|
}
|
|
/// Parses and validates the config.
|
|
fn parse(&self, raw_config: &str) -> Self {
|
|
let config: Self = match serde_json::from_str(raw_config) {
|
|
Ok(config) => config,
|
|
Err(e) => error!("Unexpected error occured while parsing config: {}", e),
|
|
};
|
|
if config.version != 3 {
|
|
error!("You are trying to run Cup with an incompatible config file! Please migrate your config file to the version 3, or if you have already done so, add a `version` key with the value `3`.")
|
|
}
|
|
config
|
|
}
|
|
}
|
|
|
|
impl Default for Config {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
fn empty_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
let s = String::deserialize(deserializer)?;
|
|
if s.is_empty() {
|
|
Ok(None)
|
|
} else {
|
|
Ok(Some(s))
|
|
}
|
|
}
|