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