Skip to main content

matugen_parser/
value.rs

1use std::{
2    fmt::{self},
3    str::FromStr,
4};
5
6use chumsky::span::SimpleSpan;
7use colorsys::{Hsl, Rgb};
8use indexmap::IndexMap;
9
10use crate::{FilterReturnType, engine::format_color_all};
11
12#[derive(Debug, Clone)]
13pub enum Value {
14    Ident(String),
15    Int(i64),
16    Float(f64),
17    Color(Rgb),
18    HslColor(Hsl),
19    LazyColor {
20        color: Rgb,
21        scheme: Option<String>, // If known, otherwise None
22    },
23    Bool(bool),
24    Map(IndexMap<String, Value>),
25    Array(Vec<Value>),
26    Null,
27}
28
29pub enum ColorValue {
30    Rgb(Rgb),
31    Hsl(Hsl),
32}
33
34#[derive(Debug, Clone)]
35pub struct SpannedValue {
36    pub value: Value,
37    pub span: SimpleSpan,
38}
39
40impl SpannedValue {
41    pub fn new(value: Value, span: SimpleSpan) -> Self {
42        Self { value, span }
43    }
44}
45
46impl From<&str> for Value {
47    fn from(val: &str) -> Self {
48        Value::Ident(val.to_string())
49    }
50}
51
52impl From<String> for Value {
53    fn from(val: String) -> Self {
54        Value::Ident(val)
55    }
56}
57
58impl From<f64> for Value {
59    fn from(val: f64) -> Self {
60        Value::Int(val as i64)
61    }
62}
63
64impl From<i32> for Value {
65    fn from(val: i32) -> Self {
66        Value::Int(val as i64)
67    }
68}
69
70impl From<i64> for Value {
71    fn from(val: i64) -> Self {
72        Value::Int(val)
73    }
74}
75
76impl From<bool> for Value {
77    fn from(val: bool) -> Self {
78        Value::Bool(val)
79    }
80}
81
82impl From<FilterReturnType> for Value {
83    fn from(value: FilterReturnType) -> Self {
84        match value {
85            FilterReturnType::String(s) => Value::Ident(s),
86            FilterReturnType::Rgb(rgb) => Value::Color(rgb),
87            FilterReturnType::Hsl(hsl) => Value::HslColor(hsl),
88            FilterReturnType::Bool(b) => Value::Bool(b),
89        }
90    }
91}
92
93fn format_float(f: f64) -> String {
94    if f.fract() == 0.0 {
95        return format!("{}", f as i64);
96    }
97    let s = format!("{:.2}", f);
98    let s = s.trim_end_matches('0'); // "1.20" → "1."
99    s.trim_end_matches('.').to_string()
100}
101
102impl fmt::Display for Value {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        match self {
105            Value::Ident(v) => write!(f, "{}", v),
106            Value::Int(v) => write!(f, "{}", v),
107            Value::Float(v) => write!(f, "{}", format_float(*v)),
108            Value::Bool(v) => write!(f, "{}", v),
109            Value::Color(color) | Value::LazyColor { color, scheme: _ } => {
110                let formats = format_color_all(color.clone());
111                write!(f, "{:?}", formats)
112            }
113            Value::HslColor(color) => {
114                let formats = format_color_all(color.clone().into());
115                write!(f, "{:?}", formats)
116            }
117            Value::Map(v) => write!(f, "{:?}", v),
118            Value::Array(v) => write!(f, "{:?}", v),
119            Value::Null => write!(f, "Null"),
120        }
121    }
122}
123
124impl Value {
125    pub fn variant_name(&self) -> String {
126        match self {
127            Value::Ident(_) => "String",
128            Value::Int(_) => "Int",
129            Value::Float(_) => "Float",
130            Value::Bool(_) => "Bool",
131            Value::Color(_) => "Color",
132            Value::HslColor(_) => "Hsl Color",
133            Value::LazyColor {
134                color: _,
135                scheme: _,
136            } => "Color",
137            Value::Map(_) => "Map",
138            Value::Null => "Null",
139            Value::Array(_) => "Array",
140        }
141        .to_string()
142    }
143
144    pub fn get_int(&self) -> Option<i64> {
145        match self {
146            Value::Int(v) => Some(*v),
147            _ => None,
148        }
149    }
150
151    pub fn get_float(&self) -> Option<f64> {
152        match self {
153            Value::Int(v) => Some(*v as f64),
154            Value::Float(v) => Some(*v),
155            _ => None,
156        }
157    }
158
159    pub fn is_color(&self) -> bool {
160        match self {
161            Value::Color(_) => true,
162            Value::LazyColor {
163                color: _,
164                scheme: _,
165            } => true,
166            Value::HslColor(_) => true,
167            _ => false,
168        }
169    }
170
171    pub fn get_color(self) -> Option<ColorValue> {
172        match self {
173            Value::Color(color) => Some(ColorValue::Rgb(color)),
174            Value::LazyColor { color, scheme: _ } => Some(ColorValue::Rgb(color)),
175            Value::HslColor(color) => Some(ColorValue::Hsl(color)),
176            _ => None,
177        }
178    }
179}
180
181impl From<serde_json::Value> for Value {
182    fn from(v: serde_json::Value) -> Self {
183        match v {
184            serde_json::Value::Null => Value::Null,
185            serde_json::Value::Bool(b) => Value::Bool(b),
186            serde_json::Value::Number(n) => {
187                if let Some(i) = n.as_i64() {
188                    Value::Int(i)
189                } else if let Some(f) = n.as_f64() {
190                    Value::Float(f)
191                } else {
192                    panic!("Invalid number format");
193                }
194            }
195            serde_json::Value::String(s) => {
196                if let Ok(color) = Rgb::from_str(&s) {
197                    Value::Color(color)
198                } else {
199                    Value::Ident(s)
200                }
201            }
202            serde_json::Value::Array(arr) => {
203                Value::Array(arr.into_iter().map(Value::from).collect())
204            }
205            serde_json::Value::Object(map) => {
206                Value::Map(map.into_iter().map(|(k, v)| (k, Value::from(v))).collect())
207            }
208        }
209    }
210}