1use crate::error::AvengerVegaError;
2use avenger::marks::value::{
3 ColorOrGradient, Gradient, GradientStop, LinearGradient, RadialGradient,
4};
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use std::borrow::Cow;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9#[serde(untagged)]
10pub enum StrokeDashSpec {
11 String(String),
12 Array(Vec<f32>),
13}
14
15impl StrokeDashSpec {
16 pub fn to_array(&self) -> Result<Cow<Vec<f32>>, AvengerVegaError> {
17 match self {
18 StrokeDashSpec::Array(a) => Ok(Cow::Borrowed(a)),
19 StrokeDashSpec::String(s) => {
20 let clean_dash_str = s.replace(',', " ");
21 let mut dashes: Vec<f32> = Vec::new();
22 for s in clean_dash_str.split_whitespace() {
23 let d = s
24 .parse::<f32>()
25 .map_err(|_| AvengerVegaError::InvalidDashString(s.to_string()))?
26 .abs();
27 dashes.push(d);
28 }
29 Ok(Cow::Owned(dashes))
30 }
31 }
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
36#[serde(untagged)]
37pub enum CssColorOrGradient {
38 Color(String),
39 Gradient(CssGradient),
40}
41
42impl CssColorOrGradient {
43 pub fn to_color_or_grad(
44 &self,
45 opacity: f32,
46 gradients: &mut Vec<Gradient>,
47 ) -> Result<ColorOrGradient, AvengerVegaError> {
48 match self {
49 CssColorOrGradient::Color(c) => {
50 let c = csscolorparser::parse(c)?;
51 Ok(ColorOrGradient::Color([
52 c.r as f32,
53 c.g as f32,
54 c.b as f32,
55 c.a as f32 * opacity,
56 ]))
57 }
58 CssColorOrGradient::Gradient(grad) => {
59 let grad = match grad.gradient {
61 VegaGradientType::Linear => Gradient::LinearGradient(LinearGradient {
62 x0: grad.x1.unwrap_or(0.0),
63 y0: grad.y1.unwrap_or(0.0),
64 x1: grad.x2.unwrap_or(1.0),
65 y1: grad.y2.unwrap_or(0.0),
66 stops: grad
67 .stops
68 .iter()
69 .map(|s| s.to_grad_stop(opacity))
70 .collect::<Result<Vec<_>, AvengerVegaError>>()?,
71 }),
72 VegaGradientType::Radial => Gradient::RadialGradient(RadialGradient {
73 x0: grad.x1.unwrap_or(0.5),
74 y0: grad.y1.unwrap_or(0.5),
75 x1: grad.x2.unwrap_or(0.5),
76 y1: grad.y2.unwrap_or(0.5),
77 r0: grad.r1.unwrap_or(0.0),
78 r1: grad.r2.unwrap_or(0.5),
79 stops: grad
80 .stops
81 .iter()
82 .map(|s| s.to_grad_stop(opacity))
83 .collect::<Result<Vec<_>, AvengerVegaError>>()?,
84 }),
85 };
86
87 let pos = match gradients.iter().position(|g| g == &grad) {
89 Some(pos) => pos,
90 None => {
91 let pos = gradients.len();
92 gradients.push(grad);
93 pos
94 }
95 };
96 Ok(ColorOrGradient::GradientIndex(pos as u32))
97 }
98 }
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103pub struct CssGradient {
104 #[serde(default)]
105 gradient: VegaGradientType,
106 x1: Option<f32>,
107 y1: Option<f32>,
108 x2: Option<f32>,
109 y2: Option<f32>,
110 r1: Option<f32>,
111 r2: Option<f32>,
112 stops: Vec<CssGradientStop>,
113}
114
115#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
116#[serde(rename_all = "lowercase")]
117pub enum VegaGradientType {
118 #[default]
119 Linear,
120 Radial,
121}
122
123#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
124pub struct CssGradientStop {
125 offset: f32,
126 color: String,
127}
128
129impl CssGradientStop {
130 pub fn to_grad_stop(&self, opacity: f32) -> Result<GradientStop, AvengerVegaError> {
131 let c = csscolorparser::parse(&self.color)?;
132 Ok(GradientStop {
133 offset: self.offset,
134 color: [c.r as f32, c.g as f32, c.b as f32, c.a as f32 * opacity],
135 })
136 }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Default)]
141pub enum MissingNullOrValue<V> {
142 #[default]
143 Missing,
144 Null,
145 Value(V),
146}
147
148impl<V> MissingNullOrValue<V> {
149 pub fn is_missing(&self) -> bool {
150 matches!(self, MissingNullOrValue::Missing)
151 }
152
153 pub fn is_null(&self) -> bool {
154 matches!(self, MissingNullOrValue::Null)
155 }
156
157 pub fn as_option(&self) -> Option<&V> {
158 match self {
159 MissingNullOrValue::Missing | MissingNullOrValue::Null => None,
160 MissingNullOrValue::Value(v) => Some(v),
161 }
162 }
163}
164
165impl<V> From<Option<V>> for MissingNullOrValue<V> {
166 fn from(opt: Option<V>) -> MissingNullOrValue<V> {
167 match opt {
168 Some(v) => MissingNullOrValue::Value(v),
169 None => MissingNullOrValue::Null,
170 }
171 }
172}
173
174impl<'de, V: Deserialize<'de>> Deserialize<'de> for MissingNullOrValue<V> {
175 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
176 where
177 D: Deserializer<'de>,
178 {
179 Option::deserialize(deserializer).map(Into::into)
180 }
181}
182
183impl<V: Serialize> Serialize for MissingNullOrValue<V> {
184 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
185 where
186 S: Serializer,
187 {
188 match self {
189 MissingNullOrValue::Missing => None::<Option<String>>.serialize(serializer),
190 MissingNullOrValue::Null => serde_json::Value::Null.serialize(serializer),
191 MissingNullOrValue::Value(v) => v.serialize(serializer),
192 }
193 }
194}