ferrite_config/
types.rs

1//! Core types used throughout the configuration system.
2//!
3//! This module provides type-safe wrappers around primitive types to ensure
4//! configuration values are valid and consistent.
5
6use crate::error::{ConfigError, Result};
7use serde::{Deserialize, Serialize};
8
9/// Represents an RGBA color with validation
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub struct ColorRGBA {
12    pub r: u8,
13    pub g: u8,
14    pub b: u8,
15    pub a: u8,
16}
17
18impl ColorRGBA {
19    /// Creates a new color from RGBA components
20    pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
21        Self { r, g, b, a }
22    }
23
24    /// Creates a color from a hexadecimal string (e.g., "#FF0000FF")
25    pub fn from_hex(hex: &str) -> Result<Self> {
26        if !hex.starts_with('#') || hex.len() != 9 {
27            return Err(ConfigError::ColorError(
28                "Invalid hex color format. Expected '#RRGGBBAA'".to_string(),
29            ));
30        }
31
32        let r = u8::from_str_radix(&hex[1..3], 16).map_err(|_| {
33            ConfigError::ColorError("Invalid red component".to_string())
34        })?;
35        let g = u8::from_str_radix(&hex[3..5], 16).map_err(|_| {
36            ConfigError::ColorError("Invalid green component".to_string())
37        })?;
38        let b = u8::from_str_radix(&hex[5..7], 16).map_err(|_| {
39            ConfigError::ColorError("Invalid blue component".to_string())
40        })?;
41        let a = u8::from_str_radix(&hex[7..9], 16).map_err(|_| {
42            ConfigError::ColorError("Invalid alpha component".to_string())
43        })?;
44
45        Ok(Self::new(r, g, b, a))
46    }
47
48    /// Converts the color to a hexadecimal string
49    pub fn to_hex(&self) -> String {
50        format!("#{:02X}{:02X}{:02X}{:02X}", self.r, self.g, self.b, self.a)
51    }
52}
53
54/// Represents a 2D vector with validation
55#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
56pub struct Vector2D {
57    x: f64,
58    y: f64,
59}
60
61impl Vector2D {
62    /// Creates a new vector with validation
63    pub fn new(x: f64, y: f64) -> Result<Self> {
64        if x.is_finite() && y.is_finite() {
65            Ok(Self { x, y })
66        } else {
67            Err(ConfigError::ValidationError(
68                "Vector components must be finite numbers".to_string(),
69            ))
70        }
71    }
72
73    /// Gets the x component
74    pub fn x(&self) -> f64 {
75        self.x
76    }
77
78    /// Gets the y component
79    pub fn y(&self) -> f64 {
80        self.y
81    }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
85pub enum Position {
86    Top,
87    Bottom,
88    Left,
89    Right,
90    TopLeft,
91    TopRight,
92    BottomLeft,
93    BottomRight,
94    Center,
95}
96
97impl Default for Position {
98    fn default() -> Self {
99        Position::TopRight
100    }
101}
102/// Represents a mouse button
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
104pub enum MouseButton {
105    Left,
106    Right,
107    Middle,
108}
109
110impl Default for MouseButton {
111    fn default() -> Self {
112        MouseButton::Left
113    }
114}
115
116/// Re-export eframe types for consistency
117pub use eframe::egui::Key;
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn test_color_conversion() {
125        let color = ColorRGBA::new(255, 128, 0, 255);
126        assert_eq!(color.to_hex(), "#FF8000FF");
127
128        let parsed = ColorRGBA::from_hex("#FF8000FF").unwrap();
129        assert_eq!(color, parsed);
130    }
131
132    #[test]
133    fn test_vector_validation() {
134        assert!(Vector2D::new(1.0, 2.0).is_ok());
135        assert!(Vector2D::new(f64::INFINITY, 2.0).is_err());
136    }
137}
138
139#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
140#[serde(from = "&str", into = "String")]
141pub struct SerializableKey(Key);
142
143impl From<&str> for SerializableKey {
144    fn from(s: &str) -> Self {
145        // Implement key string parsing
146        Self(match s {
147            "Equals" => Key::Equals,
148            "Plus" => Key::Plus,
149            "Minus" => Key::Minus,
150            "W" => Key::W,
151            "S" => Key::S,
152            "F" => Key::F,
153            "Q" => Key::Q,
154            "Num0" => Key::Num0,
155            _ => Key::Equals, // Default
156        })
157    }
158}
159
160impl From<SerializableKey> for String {
161    fn from(key: SerializableKey) -> Self {
162        match key.0 {
163            Key::Equals => "Equals",
164            Key::Plus => "Plus",
165            Key::Minus => "Minus",
166            Key::W => "W",
167            Key::S => "S",
168            Key::F => "F",
169            Key::Num0 => "Num0",
170            _ => "Equal",
171        }
172        .to_string()
173    }
174}