Add: Argument parsing

- Set wallpaper from arg
- Update config from arg
This commit is contained in:
Candifloss 2025-12-17 12:44:06 +05:30
parent c02d53464d
commit 8e52d12226
3 changed files with 98 additions and 8 deletions

33
src/args.rs Normal file
View File

@ -0,0 +1,33 @@
use anyhow::{Result, bail};
use pico_args::Arguments;
use std::path::PathBuf;
/// Parsed command-line arguments.
#[derive(Debug)]
pub struct Args {
pub set: Option<PathBuf>,
pub update: Option<PathBuf>,
pub mode: Option<String>, // parsed now, used later
}
impl Args {
pub fn parse() -> Result<Self> {
let mut args = Arguments::from_env();
let set = args.opt_value_from_str("--set")?;
let update = args.opt_value_from_str("--update")?;
let mode = args.opt_value_from_str("--mode")?;
if set.is_some() && update.is_some() {
bail!("--set and --update cannot be used together");
}
// Fail if unknown arguments are present
let remaining = args.finish();
if !remaining.is_empty() {
bail!("Unknown arguments: {remaining:?}");
}
Ok(Self { set, update, mode })
}
}

View File

@ -1,10 +1,10 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use serde::Deserialize; use serde::{Deserialize, Serialize};
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
/// Runtime configuration for `icing`. /// Runtime configuration for `icing`.
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct Config { pub struct Config {
pub background_image: String, pub background_image: String,
pub mode: Option<String>, // Unused now, deal with it later pub mode: Option<String>, // Unused now, deal with it later
@ -23,6 +23,23 @@ impl Config {
Ok(config) Ok(config)
} }
/// Update config with image specifeid by CLI arg
pub fn update_background_image(&self, new_path: &str) -> Result<()> {
let path = config_path()?;
let updated = toml::to_string(&Config {
background_image: new_path.to_string(),
mode: self.mode.clone(),
})?;
fs::create_dir_all(path.parent().expect("config path has parent"))?;
fs::write(&path, updated)
.with_context(|| format!("Failed to write config file: {}", path.display()))?;
Ok(())
}
} }
fn config_path() -> Result<PathBuf> { fn config_path() -> Result<PathBuf> {

View File

@ -10,13 +10,53 @@ use x11rb::{
}, },
rust_connection::RustConnection, rust_connection::RustConnection,
}; };
mod args;
mod config; mod config;
fn main() -> Result<()> { fn main() -> Result<()> {
// Load config // Parse CLI arguments
let config = config::Config::load()?; let args = args::Args::parse()?;
// Path to wallpaper image in config
let image_path = &config.background_image; // Load config file if it exists. Can be missing.
let config = config::Config::load().ok();
// Resolve wallpaper image path by precedence:
// 1. args (`--set`, `--update`): Use path from args.
// 2. config: Use path from config.
let image_path = match (&args.set, &args.update) {
// Case: arg `--set`: One-shot wallpaper change. Not persistent.
(Some(path), None) => path.clone(),
// Case: arg `--update`. Persist wallpaper path to config (create if missing), then apply it.
(None, Some(path)) => {
let cfg = config.unwrap_or_else(|| config::Config {
background_image: String::new(),
mode: None,
});
// Write to config.
cfg.update_background_image(
path.to_str()
// Unsupported path format.
.ok_or_else(|| anyhow::anyhow!("Non-UTF8 path"))?,
)?;
path.clone()
}
// Case: No args. Fallback to config file.
(None, None) => {
let cfg = config.ok_or_else(|| {
// No CLI args and no valid config.
anyhow::anyhow!("No or invalid image path specified in config or args.")
})?;
// Successfully loaded.
cfg.background_image.into()
}
// Case: Both args `--set` & `--update`. This case is already rejected during argument parsing.
_ => unreachable!(),
};
// Connect to the running graphical session, the X11 server. // Connect to the running graphical session, the X11 server.
let (conn, screen_num) = RustConnection::connect(None)?; let (conn, screen_num) = RustConnection::connect(None)?;
@ -61,8 +101,8 @@ fn main() -> Result<()> {
conn.create_pixmap(depth, pixmap, root, width, height)?; conn.create_pixmap(depth, pixmap, root, width, height)?;
// Load the wallpaper image from disk. // Load the wallpaper image from disk.
let img = let img = image::open(&image_path)
image::open(image_path).with_context(|| format!("Failed to open image: {image_path}"))?; .with_context(|| format!("Failed to open image: {}", image_path.display()))?;
// Convert the image into RGBA8 (4 bytes per pixel). // Convert the image into RGBA8 (4 bytes per pixel).
// Predictable, CPU-friendly format to work on. // Predictable, CPU-friendly format to work on.