use crate::error::{ConfigError, ParseResult};
use std::any::Any;
use std::fmt;
use std::rc::Rc;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vec2 {
pub x: f64,
pub y: f64,
}
impl Vec2 {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
}
impl fmt::Display for Vec2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl Color {
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b, a: 255 }
}
pub fn from_hex(hex: &str) -> ParseResult<Self> {
let hex = hex.trim_start_matches("0x");
if hex.len() != 6 && hex.len() != 8 {
return Err(ConfigError::invalid_color(
hex,
"hex color must be 6 or 8 characters",
));
}
let r = u8::from_str_radix(&hex[0..2], 16)
.map_err(|_| ConfigError::invalid_color(hex, "invalid hex digits"))?;
let g = u8::from_str_radix(&hex[2..4], 16)
.map_err(|_| ConfigError::invalid_color(hex, "invalid hex digits"))?;
let b = u8::from_str_radix(&hex[4..6], 16)
.map_err(|_| ConfigError::invalid_color(hex, "invalid hex digits"))?;
let a = if hex.len() == 8 {
u8::from_str_radix(&hex[6..8], 16)
.map_err(|_| ConfigError::invalid_color(hex, "invalid hex digits"))?
} else {
255
};
Ok(Self { r, g, b, a })
}
pub fn from_rgba_float(r: f64, g: f64, b: f64, a: f64) -> Self {
Self {
r: (r * 255.0).round() as u8,
g: (g * 255.0).round() as u8,
b: (b * 255.0).round() as u8,
a: (a * 255.0).round() as u8,
}
}
pub fn to_argb(&self) -> u32 {
((self.a as u32) << 24) | ((self.r as u32) << 16) | ((self.g as u32) << 8) | (self.b as u32)
}
pub fn to_rgba(&self) -> u32 {
((self.r as u32) << 24) | ((self.g as u32) << 16) | ((self.b as u32) << 8) | (self.a as u32)
}
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "rgba({}, {}, {}, {})", self.r, self.g, self.b, self.a)
}
}
pub trait CustomValueType: Any + fmt::Debug {
fn parse(&self, value: &str) -> ParseResult<Box<dyn Any>>;
fn type_name(&self) -> &str;
fn clone_value(&self, value: &dyn Any) -> Box<dyn Any>;
}
#[derive(Clone)]
pub enum ConfigValue {
Int(i64),
Float(f64),
String(String),
Vec2(Vec2),
Color(Color),
Custom {
type_name: String,
value: Rc<dyn Any>,
},
}
impl ConfigValue {
pub fn as_int(&self) -> ParseResult<i64> {
match self {
ConfigValue::Int(v) => Ok(*v),
_ => Err(ConfigError::type_error("value", "Int", self.type_name())),
}
}
pub fn as_float(&self) -> ParseResult<f64> {
match self {
ConfigValue::Float(v) => Ok(*v),
ConfigValue::Int(v) => Ok(*v as f64),
_ => Err(ConfigError::type_error("value", "Float", self.type_name())),
}
}
pub fn as_string(&self) -> ParseResult<&str> {
match self {
ConfigValue::String(v) => Ok(v),
_ => Err(ConfigError::type_error("value", "String", self.type_name())),
}
}
pub fn as_vec2(&self) -> ParseResult<Vec2> {
match self {
ConfigValue::Vec2(v) => Ok(*v),
_ => Err(ConfigError::type_error("value", "Vec2", self.type_name())),
}
}
pub fn as_color(&self) -> ParseResult<Color> {
match self {
ConfigValue::Color(v) => Ok(*v),
_ => Err(ConfigError::type_error("value", "Color", self.type_name())),
}
}
pub fn as_custom<T: 'static>(&self) -> ParseResult<&T> {
match self {
ConfigValue::Custom { value, .. } => value
.downcast_ref::<T>()
.ok_or_else(|| ConfigError::type_error("value", "Custom", self.type_name())),
_ => Err(ConfigError::type_error("value", "Custom", self.type_name())),
}
}
pub fn type_name(&self) -> &str {
match self {
ConfigValue::Int(_) => "Int",
ConfigValue::Float(_) => "Float",
ConfigValue::String(_) => "String",
ConfigValue::Vec2(_) => "Vec2",
ConfigValue::Color(_) => "Color",
ConfigValue::Custom { type_name, .. } => type_name,
}
}
pub fn parse_bool(s: &str) -> ParseResult<bool> {
match s.to_lowercase().as_str() {
"true" | "on" | "yes" | "1" => Ok(true),
"false" | "off" | "no" | "0" => Ok(false),
_ => Err(ConfigError::invalid_number(s, "not a valid boolean")),
}
}
pub fn parse_int(s: &str) -> ParseResult<i64> {
if let Some(hex) = s.strip_prefix("0x") {
i64::from_str_radix(hex, 16)
.map_err(|_| ConfigError::invalid_number(s, "invalid hex integer"))
} else {
s.parse::<i64>()
.map_err(|_| ConfigError::invalid_number(s, "invalid integer"))
}
}
pub fn parse_float(s: &str) -> ParseResult<f64> {
s.parse::<f64>()
.map_err(|_| ConfigError::invalid_number(s, "invalid float"))
}
}
impl fmt::Debug for ConfigValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConfigValue::Int(v) => write!(f, "Int({})", v),
ConfigValue::Float(v) => write!(f, "Float({})", v),
ConfigValue::String(v) => write!(f, "String({:?})", v),
ConfigValue::Vec2(v) => write!(f, "Vec2({:?})", v),
ConfigValue::Color(v) => write!(f, "Color({:?})", v),
ConfigValue::Custom { type_name, .. } => write!(f, "Custom({})", type_name),
}
}
}
impl fmt::Display for ConfigValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConfigValue::Int(v) => write!(f, "{}", v),
ConfigValue::Float(v) => write!(f, "{}", v),
ConfigValue::String(v) => write!(f, "{}", v),
ConfigValue::Vec2(v) => write!(f, "{}", v),
ConfigValue::Color(v) => write!(f, "{}", v),
ConfigValue::Custom { type_name, .. } => write!(f, "<{}>", type_name),
}
}
}
#[derive(Clone)]
pub struct ConfigValueEntry {
pub value: ConfigValue,
pub set_by_user: bool,
pub raw: String,
}
impl ConfigValueEntry {
pub fn new(value: ConfigValue, raw: String) -> Self {
Self {
value,
set_by_user: true,
raw,
}
}
pub fn with_default(value: ConfigValue) -> Self {
Self {
value: value.clone(),
set_by_user: false,
raw: value.to_string(),
}
}
}
impl fmt::Debug for ConfigValueEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ConfigValueEntry")
.field("value", &self.value)
.field("set_by_user", &self.set_by_user)
.field("raw", &self.raw)
.finish()
}
}