// SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2026, Nathan Gill use std::{ io::{self, Read}, str::FromStr, }; use clap::ValueEnum; use serde::{Deserialize, de}; #[derive(Debug, Deserialize, Copy, Clone, PartialEq, ValueEnum)] #[serde(rename_all = "lowercase")] pub enum BackgroundType { Color, Image, } #[derive(Debug, Deserialize, Copy, Clone, PartialEq, ValueEnum)] #[serde(rename_all = "lowercase")] pub enum BackgroundImageScale { Stretch, Fill, Fit, Center, Tile, } #[derive(Debug, Deserialize, Copy, Clone, PartialEq, ValueEnum)] #[serde(rename_all = "lowercase")] pub enum InputVisibility { Never, Content, Always, } #[derive(Debug, Copy, Clone)] pub struct Rgba { pub r: f64, pub g: f64, pub b: f64, pub a: f64, } impl Rgba { pub fn new(r: f64, g: f64, b: f64, a: f64) -> Self { Self { r, g, b, a } } } impl Default for Rgba { fn default() -> Self { Self::new(0.0, 0.0, 0.0, 1.0) } } impl FromStr for Rgba { type Err = String; fn from_str(s: &str) -> std::result::Result { let s = s.strip_prefix('#').unwrap_or(s); let argb = match s.len() { 6 => { let r = u8::from_str_radix(&s[0..2], 16).map_err(|e| e.to_string())? as f64 / 255.0f64; let g = u8::from_str_radix(&s[2..4], 16).map_err(|e| e.to_string())? as f64 / 255.0f64; let b = u8::from_str_radix(&s[4..6], 16).map_err(|e| e.to_string())? as f64 / 255.0f64; Self { r, g, b, a: 1.0 } } 8 => { let r = u8::from_str_radix(&s[0..2], 16).map_err(|e| e.to_string())? as f64 / 255.0f64; let g = u8::from_str_radix(&s[2..4], 16).map_err(|e| e.to_string())? as f64 / 255.0f64; let b = u8::from_str_radix(&s[4..6], 16).map_err(|e| e.to_string())? as f64 / 255.0f64; let a = u8::from_str_radix(&s[6..8], 16).map_err(|e| e.to_string())? as f64 / 255.0f64; Self { r, g, b, a } } _ => return Err("expected RRGGBBAA or RRGGBB format".to_string()), }; Ok(argb) } } impl<'de> Deserialize<'de> for Rgba { fn deserialize(d: D) -> std::result::Result where D: serde::Deserializer<'de>, { let s = String::deserialize(d)?; let argb = Self::from_str(&s).map_err(de::Error::custom)?; Ok(argb) } } #[derive(Debug, Copy, Clone, ValueEnum)] pub enum LogLevel { Trace, Debug, Info, Warn, Error, } impl From for tracing::level_filters::LevelFilter { fn from(value: LogLevel) -> Self { match value { LogLevel::Trace => Self::TRACE, LogLevel::Debug => Self::DEBUG, LogLevel::Info => Self::INFO, LogLevel::Warn => Self::WARN, LogLevel::Error => Self::ERROR, } } } const PNG_SIG: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]; // Detect if a source stream starts with a PNG signature. // Intended use case is for detecting whether a file is PNG or not. pub fn detect_png(r: &mut R) -> io::Result where R: Read, { let mut buf = [0; 8]; let n = r.read(&mut buf)?; if n < 8 || !buf.eq(&PNG_SIG) { return Ok(false); } Ok(true) }