1use crate::error::{ConfigError, Result};
7use serde::{Deserialize, Serialize};
8
9#[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 pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
21 Self { r, g, b, a }
22 }
23
24 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 pub fn to_hex(&self) -> String {
50 format!("#{:02X}{:02X}{:02X}{:02X}", self.r, self.g, self.b, self.a)
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
56pub struct Vector2D {
57 x: f64,
58 y: f64,
59}
60
61impl Vector2D {
62 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 pub fn x(&self) -> f64 {
75 self.x
76 }
77
78 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#[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
116pub 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 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, })
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}