diff --git a/Cargo.lock b/Cargo.lock
index 2ae70ae..a0a1669 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -382,7 +382,6 @@ dependencies = [
"chrono",
"chrono-tz",
"clap",
- "envy",
"futures",
"http-auth",
"http-link",
@@ -446,15 +445,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
-[[package]]
-name = "envy"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965"
-dependencies = [
- "serde",
-]
-
[[package]]
name = "equivalent"
version = "1.0.2"
diff --git a/Cargo.toml b/Cargo.toml
index b132527..3fd3a6a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,7 +25,6 @@ itertools = "0.14.0"
serde_json = "1.0.133"
serde = "1.0.215"
tokio-cron-scheduler = { version = "0.13.0", default-features = false, optional = true }
-envy = "0.4.2"
chrono-tz = "0.10.3"
[features]
diff --git a/docs/src/content/docs/configuration/index.mdx b/docs/src/content/docs/configuration/index.mdx
index 5bda255..a57afa2 100644
--- a/docs/src/content/docs/configuration/index.mdx
+++ b/docs/src/content/docs/configuration/index.mdx
@@ -121,6 +121,8 @@ Here are the ones currently available:
- `CUP_REFRESH_INTERVAL` - Automatic refresh
- `CUP_SOCKET` - Socket
- `CUP_THEME` - Theme
+- `CUP_IMAGES_EXCLUDE` - Images to exclude from checks, space-separated.
+- `CUP_IMAGES_EXTRA` - Extra images to check, space-separated.
Refer to the configuration page for more information on each of these.
@@ -143,4 +145,4 @@ services:
Heads up!
Any configuration option you set with environment variables **always** overrides anything in your `cup.json`.
-
\ No newline at end of file
+
diff --git a/src/config.rs b/src/config.rs
index a60aa44..9427967 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -2,19 +2,11 @@ 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)]
+#[derive(Clone, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Theme {
Default,
@@ -27,7 +19,18 @@ impl Default for Theme {
}
}
-#[derive(Clone, Deserialize)]
+impl std::str::FromStr for Theme {
+ type Err = String;
+ fn from_str(s: &str) -> Result {
+ match s.to_lowercase().as_str() {
+ "default" => Ok(Self::Default),
+ "blue" => Ok(Self::Blue),
+ _ => Err(format!("Invalid theme: {}", s)),
+ }
+ }
+}
+
+#[derive(Clone, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum UpdateType {
None,
@@ -42,7 +45,20 @@ impl Default for UpdateType {
}
}
-#[derive(Clone, Deserialize, Default)]
+impl std::str::FromStr for UpdateType {
+ type Err = String;
+ fn from_str(s: &str) -> Result {
+ match s.to_lowercase().as_str() {
+ "none" => Ok(Self::None),
+ "major" => Ok(Self::Major),
+ "minor" => Ok(Self::Minor),
+ "patch" => Ok(Self::Patch),
+ _ => Err(format!("Invalid update type: {}", s)),
+ }
+ }
+}
+
+#[derive(Clone, Deserialize, Default, Debug)]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct RegistryConfig {
@@ -51,14 +67,14 @@ pub struct RegistryConfig {
pub ignore: bool,
}
-#[derive(Clone, Deserialize, Default)]
+#[derive(Clone, Deserialize, Default, Debug)]
#[serde(default)]
pub struct ImageConfig {
pub extra: Vec,
pub exclude: Vec,
}
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, Debug)]
#[serde(default)]
pub struct Config {
version: u8,
@@ -89,40 +105,12 @@ impl Config {
}
/// Loads file and env config and merges them
- pub fn load(&mut self, path: Option) -> Self {
- let mut config = self.load_file(path);
-
- // Get environment variables with CUP_ prefix
- let env_vars: FxHashMap =
- env::vars().filter(|(k, _)| k.starts_with("CUP_")).collect();
-
- if !env_vars.is_empty() {
- if let Ok(mut cfg) = envy::prefixed("CUP_").from_env::() {
- // 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
+ pub fn load(path: Option) -> Self {
+ Config::load_file(path).load_env()
}
/// Reads the config from the file path provided and returns the parsed result.
- fn load_file(&self, path: Option) -> Self {
+ fn load_file(path: Option) -> Self {
let raw_config = match &path {
Some(path) => std::fs::read_to_string(path),
None => return Self::new(), // Empty config
@@ -133,10 +121,10 @@ impl Config {
&path.unwrap().to_str().unwrap()
)
};
- self.parse(&raw_config.unwrap()) // We can safely unwrap here
+ Config::parse(&raw_config.unwrap()) // We can safely unwrap here
}
/// Parses and validates the config.
- fn parse(&self, raw_config: &str) -> Self {
+ fn parse(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),
@@ -146,6 +134,37 @@ impl Config {
}
config
}
+
+ /// Read and parse environment variables into the config object and return it.
+ fn load_env(mut self) -> Self {
+ env::vars()
+ .filter(|(k, _)| k.starts_with("CUP_"))
+ .for_each(|(key, value)| match key.as_str() {
+ "CUP_AGENT" => self.agent = value.parse().unwrap(),
+ "CUP_IGNORE_UPDATE_TYPE" => self.ignore_update_type = value.parse().unwrap(),
+ "CUP_REFRESH_INTERVAL" => self.refresh_interval = Some(value),
+ "CUP_SOCKET" => self.socket = Some(value),
+ "CUP_THEME" => self.theme = value.parse().unwrap(),
+ "CUP_IMAGES_EXCLUDE" => {
+ self.images.exclude = value
+ .split(' ')
+ .map(|s| s.trim().to_string())
+ .filter(|s| !s.is_empty())
+ .collect()
+ }
+ "CUP_IMAGES_EXTRA" => {
+ self.images.extra = value
+ .split(' ')
+ .map(|s| s.trim().to_string())
+ .filter(|s| !s.is_empty())
+ .collect()
+ }
+ // "CUP_REGISTRIES" => ...
+ // "CUP_SERVERS" => ...
+ _ => println!("Warning: Skip unknown CUP_ variable '{}'.", key),
+ });
+ self
+ }
}
impl Default for Config {
diff --git a/src/main.rs b/src/main.rs
index 5bc4dc6..a3c3cbc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -79,7 +79,7 @@ async fn main() {
"" => None,
path => Some(PathBuf::from(path)),
};
- let mut config = Config::new().load(cfg_path);
+ let mut config = Config::load(cfg_path);
if let Some(socket) = cli.socket {
config.socket = Some(socket)
}
@@ -87,6 +87,7 @@ async fn main() {
config,
logger: Logger::new(cli.debug, false),
};
+ ctx.logger.debug(format!("Config: {:?}", &ctx.config));
match &cli.command {
#[cfg(feature = "cli")]
Some(Commands::Check {