use clap::{Parser, ValueEnum};
use figment::{
Figment,
providers::{Env, Format, Serialized, Toml},
};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
#[arg(short, long)]
pub wallpaper_dir: Option<PathBuf>,
#[arg(short, long)]
pub time: Option<String>,
#[arg(short, long, value_enum)]
pub backend: Option<BackendType>,
#[arg(short, long, value_enum)]
pub renderer: Option<RendererType>,
#[arg(short, long)]
pub outputs: Option<Vec<String>>,
#[arg(short, long)]
pub transition_type: Option<String>,
#[arg(short = 's', long)]
pub transition_step: Option<u8>,
#[arg(short = 'f', long)]
pub transition_fps: Option<u8>,
#[arg(long)]
pub config: Option<PathBuf>,
#[arg(long, default_value_t = false)]
pub daemon: bool,
}
#[derive(Serialize)]
struct CliOverrides {
#[serde(skip_serializing_if = "Option::is_none")]
wallpaper_dir: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
backend: Option<BackendType>,
#[serde(skip_serializing_if = "Option::is_none")]
renderer: Option<RendererType>,
#[serde(skip_serializing_if = "Option::is_none")]
outputs: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
transition_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
transition_step: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
transition_fps: Option<u8>,
daemon: bool,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum BackendType {
Hyprland,
Sway,
Mango,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum RendererType {
Swaybg,
#[clap(alias = "swww")]
#[serde(alias = "swww")]
Awww,
Native,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Config {
pub wallpaper_dir: PathBuf,
pub time: Option<String>,
pub backend: BackendType,
pub renderer: RendererType,
pub outputs: Vec<String>,
pub transition_type: String,
pub transition_step: u8,
pub transition_fps: u8,
pub daemon: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
wallpaper_dir: PathBuf::from("."),
time: None,
backend: BackendType::Sway,
renderer: RendererType::Swaybg,
outputs: Vec::new(),
transition_type: "simple".to_string(),
transition_step: 90,
transition_fps: 30,
daemon: false,
}
}
}
impl Config {
pub fn new() -> anyhow::Result<Self> {
let cli = Cli::parse();
let mut builder = Figment::new().merge(Serialized::defaults(Self::default()));
let config_file = cli.config.or_else(|| {
let xdg_dirs = xdg::BaseDirectories::with_prefix("randpaper");
xdg_dirs.find_config_file("config.toml")
});
if let Some(path) = config_file {
builder = builder.merge(Toml::file(path));
}
let overrides = CliOverrides {
wallpaper_dir: cli.wallpaper_dir,
time: cli.time,
backend: cli.backend,
renderer: cli.renderer,
outputs: cli.outputs,
transition_type: cli.transition_type,
transition_step: cli.transition_step,
transition_fps: cli.transition_fps,
daemon: cli.daemon,
};
builder = builder.merge(Serialized::defaults(overrides));
builder = builder.merge(Env::prefixed("RANDPAPER_"));
Ok(builder.extract()?)
}
}