1#![allow(unexpected_cfgs)]
3
4use crate::code_handler::parse_context::ParseContext;
5use crate::preprocessor::config::{MapEvalConfig, MapNamespaceScope};
6use anyhow::{Context, Result, anyhow};
7use fasteval::{Compiler, EvalNamespace, Evaler, Parser, Slab};
8use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned};
9use serde_json::{Map as JsonMap, Value, value::Index};
10use std::{collections::BTreeMap, fmt::Debug, fmt::Display};
11
12#[cfg(feature = "map-parser-extensions")]
13use chrono::{DateTime, NaiveDate, NaiveDateTime, TimeDelta, Utc};
14#[cfg(feature = "map-parser-extensions")]
15use rand::{Rng, RngExt, SeedableRng, rngs::StdRng};
16
17pub type Map = JsonMap<String, Value>;
18
19type Vars = BTreeMap<String, f64>;
20
21#[derive(Clone, Debug)]
22pub enum DataPack<T> {
23 Data(T),
24 Index(String),
25}
26
27#[allow(dead_code)]
28#[inline]
29pub fn must_translate_from_context<T, N>(
30 obj: &mut Value,
31 context: &ParseContext<'_>,
32 name: N,
33) -> Result<T>
34where
35 T: DeserializeOwned + Serialize + Debug + Clone,
36 N: Index + Display,
37{
38 must_translate_with_config(obj, context.map(), context.map_eval(), name)
39}
40
41#[inline]
42pub fn must_translate_with_config<T, N>(
43 obj: &mut Value,
44 map: &Map,
45 map_eval: &MapEvalConfig,
46 name: N,
47) -> Result<T>
48where
49 T: DeserializeOwned + Serialize + Debug + Clone,
50 N: Index + Display,
51{
52 take_optional(obj, map, map_eval, &name)?.ok_or_else(|| anyhow!("missing `{}` field", name))
53}
54
55#[inline]
56fn take_optional<T, N>(
57 obj: &mut Value,
58 map: &Map,
59 map_eval: &MapEvalConfig,
60 name: &N,
61) -> Result<Option<T>>
62where
63 T: DeserializeOwned + Serialize + Debug + Clone,
64 N: Index + Display,
65{
66 let Some(value) = obj.get_mut(name) else {
67 return Ok(None);
68 };
69
70 serde_json::from_value::<DataPack<T>>(value.take())
71 .with_context(|| format!("failed to deserialize field '{}'", name))?
72 .unwrap(map, map_eval)
73 .with_context(|| format!("failed to unwrap DataPack for field '{}'", name))
74 .map(Some)
75}
76
77#[inline]
78fn try_deser<T: DeserializeOwned>(value: Value, context: &'static str) -> Result<T> {
79 serde_json::from_value(value).context(context)
80}
81
82#[inline]
83fn json_number(value: f64) -> Result<Value> {
84 serde_json::Number::from_f64(value)
85 .map(Value::Number)
86 .ok_or_else(|| anyhow!("failed to create JSON number from {}", value))
87}
88
89#[inline]
90fn usize_count(count: u64, field: &str) -> Result<usize> {
91 usize::try_from(count).with_context(|| format!("{} is too large for this platform", field))
92}
93
94fn value_to_f64(value: &Value) -> Option<f64> {
95 match value {
96 Value::Number(n) => n.as_f64(),
97 Value::Bool(v) => Some(if *v { 1.0 } else { 0.0 }),
98 Value::String(s) => s.parse::<f64>().ok(),
99 _ => None,
100 }
101}
102
103fn lookup_path<'a>(map: &'a Map, name: &str) -> Option<&'a Value> {
104 let path = name.strip_prefix("map.").unwrap_or(name);
105 let mut parts = path.split('.');
106 let first = parts.next()?;
107 let mut value = map.get(first)?;
108
109 for part in parts {
110 match value {
111 Value::Object(obj) => value = obj.get(part)?,
112 Value::Array(arr) => {
113 let idx = part.parse::<usize>().ok()?;
114 value = arr.get(idx)?;
115 }
116 _ => return None,
117 }
118 }
119
120 Some(value)
121}
122
123fn map_value<'a>(map: &'a Map, index: &str) -> Result<&'a Value> {
124 lookup_path(map, index).ok_or_else(|| anyhow!("missing map value `{}`", index))
125}
126
127struct MapNamespace<'a> {
128 map: &'a Map,
129 vars: &'a Vars,
130 scope: &'a MapNamespaceScope,
131}
132
133impl<'a> MapNamespace<'a> {
134 fn new(map: &'a Map, vars: &'a Vars, scope: &'a MapNamespaceScope) -> Self {
135 Self { map, vars, scope }
136 }
137
138 fn lookup_map_value(&self, name: &str) -> Option<f64> {
139 match self.scope {
140 MapNamespaceScope::FullMap => lookup_path(self.map, name).and_then(value_to_f64),
141 MapNamespaceScope::ExportsOnly => {
142 let path = if name.starts_with("map.exports.") {
143 name.to_owned()
144 } else {
145 format!("exports.{name}")
146 };
147 lookup_path(self.map, &path).and_then(value_to_f64)
148 }
149 }
150 }
151}
152
153impl EvalNamespace for MapNamespace<'_> {
154 fn lookup(&mut self, name: &str, _args: Vec<f64>, _keybuf: &mut String) -> Option<f64> {
155 self.vars
156 .get(name)
157 .copied()
158 .or_else(|| self.lookup_map_value(name))
159 }
160}
161
162struct EvalContext {
163 parser: Parser,
164 slab: Slab,
165 config: MapEvalConfig,
166}
167
168impl EvalContext {
169 fn new(config: &MapEvalConfig) -> Self {
170 Self {
171 parser: Parser::new(),
172 slab: Slab::new(),
173 config: config.clone(),
174 }
175 }
176
177 fn eval(&mut self, expr: &str, map: &Map, vars: &Vars) -> Result<f64> {
178 let mut namespace = MapNamespace::new(map, vars, &self.config.namespace_scope);
179
180 if !self.config.enabled || !self.config.compile_expressions {
181 let expr_ref = self
182 .parser
183 .parse(expr, &mut self.slab.ps)
184 .with_context(|| format!("failed to parse expression `{}`", expr))?
185 .from(&self.slab.ps);
186
187 return expr_ref
188 .eval(&self.slab, &mut namespace)
189 .with_context(|| format!("failed to evaluate expression `{}`", expr));
190 }
191
192 let expr_ref = self
193 .parser
194 .parse(expr, &mut self.slab.ps)
195 .with_context(|| format!("failed to parse expression `{}`", expr))?
196 .from(&self.slab.ps);
197
198 let compiled = expr_ref.compile(&self.slab.ps, &mut self.slab.cs);
199 Ok(fasteval::eval_compiled!(
200 compiled,
201 &self.slab,
202 &mut namespace
203 ))
204 }
205}
206
207impl<T> DataPack<T>
208where
209 T: DeserializeOwned + Serialize + Debug + Clone,
210{
211 pub fn unwrap(self, map: &Map, map_eval: &MapEvalConfig) -> Result<T> {
212 match self {
213 Self::Data(data) => Ok(data),
214 Self::Index(index) => {
215 let value = map_value(map, &index)?.clone();
216 Self::parse_value(map, value, map_eval)
217 .with_context(|| format!("failed to resolve map value `{}`", index))
218 }
219 }
220 }
221
222 pub fn unwrap_from_context(self, context: &ParseContext<'_>) -> Result<T> {
223 self.unwrap(context.map(), context.map_eval())
224 }
225
226 fn parse_value(map: &Map, value: Value, map_eval: &MapEvalConfig) -> Result<T> {
227 if value.is_object() && value.get("type").is_some() {
228 Self::parse_map(map, value, map_eval)
229 } else {
230 serde_json::from_value(value).context("failed to deserialize value")
231 }
232 }
233
234 fn parse_map(map: &Map, mut value: Value, map_eval: &MapEvalConfig) -> Result<T> {
235 let value_type = value
236 .get("type")
237 .and_then(Value::as_str)
238 .ok_or_else(|| anyhow!("`type` must be a string"))?
239 .to_owned();
240
241 let mut eval = EvalContext::new(map_eval);
242 let vars = Vars::new();
243 let context = ParseContext::new(map, map_eval);
244
245 match value_type.as_str() {
246 "raw" => must_translate_with_config(&mut value, map, map_eval, "data"),
247 "g-number" => parse_g_number(&context, &mut value, &mut eval, &vars),
248 "g-number-list" => parse_g_number_list(&context, &mut value, &mut eval),
249 "g-range" => parse_g_range(&context, &mut value),
250 "g-repeat" => parse_g_repeat(&context, &mut value),
251 "g-linear" => parse_g_linear(&context, &mut value),
252 "if" => parse_if(&context, &mut value, &mut eval, &vars),
253 #[cfg(feature = "map-parser-extensions")]
254 "time" => parse_time(&context, &mut value),
255 #[cfg(feature = "map-parser-extensions")]
256 "g-random" => parse_g_random(&context, &mut value),
257 #[cfg(feature = "map-parser-extensions")]
258 "g-choose" => parse_g_choose(&context, &mut value),
259 "g-env" => parse_g_env(&context, &mut value),
260 "g-join" => parse_g_join(&context, &mut value),
261 _ => Err(anyhow!("unknown type `{}`", value_type)),
262 }
263 }
264}
265
266fn parse_g_number<T>(
267 context: &ParseContext<'_>,
268 value: &mut Value,
269 eval: &mut EvalContext,
270 vars: &Vars,
271) -> Result<T>
272where
273 T: DeserializeOwned,
274{
275 let expr: String = must_translate_from_context(value, context, "expr")?;
276 try_deser(
277 json_number(eval.eval(&expr, context.map(), vars)?)?,
278 "failed to deserialize generated number",
279 )
280}
281
282fn parse_g_number_list<T>(
283 context: &ParseContext<'_>,
284 value: &mut Value,
285 eval: &mut EvalContext,
286) -> Result<T>
287where
288 T: DeserializeOwned,
289{
290 let index_begin: u64 = must_translate_from_context(value, context, "begin")?;
291 let index_end: u64 = must_translate_from_context(value, context, "end")?;
292 let expr: String = must_translate_from_context(value, context, "expr")?;
293
294 let len = index_end.saturating_sub(index_begin);
295 let mut result = Vec::with_capacity(usize_count(len, "g-number-list length")?);
296 let mut vars = Vars::new();
297
298 let compiled = eval
299 .parser
300 .parse(&expr, &mut eval.slab.ps)
301 .with_context(|| format!("failed to parse expression `{}`", expr))?
302 .from(&eval.slab.ps)
303 .compile(&eval.slab.ps, &mut eval.slab.cs);
304
305 for i in index_begin..index_end {
306 vars.insert("i".to_owned(), i as f64);
307 let mut namespace = MapNamespace::new(context.map(), &vars, &eval.config.namespace_scope);
308 let value = if eval.config.enabled && eval.config.compile_expressions {
309 fasteval::eval_compiled!(compiled, &eval.slab, &mut namespace)
310 } else {
311 eval.eval(&expr, context.map(), &vars)?
312 };
313 result.push(json_number(value)?);
314 }
315
316 try_deser(
317 Value::Array(result),
318 "failed to deserialize generated number list",
319 )
320}
321
322fn parse_g_range<T>(context: &ParseContext<'_>, value: &mut Value) -> Result<T>
323where
324 T: DeserializeOwned,
325{
326 let begin: f64 = must_translate_from_context(value, context, "begin")?;
327 let end: f64 = must_translate_from_context(value, context, "end")?;
328 let step: f64 =
329 take_optional(value, context.map(), context.map_eval(), &"step")?.unwrap_or(1.0);
330
331 if step <= 0.0 {
332 return Err(anyhow!("step must be positive"));
333 }
334
335 let capacity = if end > begin {
336 ((end - begin) / step).ceil() as usize
337 } else {
338 0
339 };
340 let mut result = Vec::with_capacity(capacity);
341 let mut current = begin;
342
343 while current < end {
344 result.push(json_number(current)?);
345 current += step;
346 }
347
348 try_deser(
349 Value::Array(result),
350 "failed to deserialize generated range",
351 )
352}
353
354fn parse_g_repeat<T>(context: &ParseContext<'_>, value: &mut Value) -> Result<T>
355where
356 T: DeserializeOwned + Serialize + Debug + Clone,
357{
358 let val: Value = must_translate_from_context(value, context, "value")?;
359 let count: u64 = must_translate_from_context(value, context, "count")?;
360 let count = usize_count(count, "count")?;
361 let result = vec![val; count];
362 try_deser(
363 Value::Array(result),
364 "failed to deserialize repeated values",
365 )
366}
367
368fn parse_g_linear<T>(context: &ParseContext<'_>, value: &mut Value) -> Result<T>
369where
370 T: DeserializeOwned,
371{
372 let begin: f64 = must_translate_from_context(value, context, "begin")?;
373 let end: f64 = must_translate_from_context(value, context, "end")?;
374 let count: u64 = must_translate_from_context(value, context, "count")?;
375
376 if count == 0 {
377 return Err(anyhow!("count must be positive"));
378 }
379
380 let count_usize = usize_count(count, "count")?;
381 let mut result = Vec::with_capacity(count_usize);
382
383 if count == 1 {
384 result.push(json_number(begin)?);
385 } else {
386 let step = (end - begin) / ((count - 1) as f64);
387 for i in 0..count {
388 result.push(json_number(begin + (i as f64) * step)?);
389 }
390 }
391
392 try_deser(
393 Value::Array(result),
394 "failed to deserialize linear spaced values",
395 )
396}
397
398fn parse_if<T>(
399 context: &ParseContext<'_>,
400 value: &mut Value,
401 eval: &mut EvalContext,
402 vars: &Vars,
403) -> Result<T>
404where
405 T: DeserializeOwned + Serialize + Debug + Clone,
406{
407 let condition: String = must_translate_from_context(value, context, "condition")?;
408 let true_val: Value = must_translate_from_context(value, context, "true")?;
409 let false_val: Value = must_translate_from_context(value, context, "false")?;
410 let selected = if eval.eval(&condition, context.map(), vars)? != 0.0 {
411 true_val
412 } else {
413 false_val
414 };
415
416 DataPack::<T>::parse_value(context.map(), selected, &eval.config)
417}
418
419#[cfg(feature = "map-parser-extensions")]
420fn parse_time<T>(context: &ParseContext<'_>, value: &mut Value) -> Result<T>
421where
422 T: DeserializeOwned,
423{
424 let start: String = must_translate_from_context(value, context, "start")?;
425 let end: String = must_translate_from_context(value, context, "end")?;
426 let interval: String = must_translate_from_context(value, context, "interval")?;
427 let format: Option<String> =
428 take_optional(value, context.map(), context.map_eval(), &"format")?;
429
430 let start_dt = parse_time_str(&start)?;
431 let end_dt = parse_time_str(&end)?;
432 let step = parse_duration_str(&interval)?;
433
434 if step <= TimeDelta::zero() {
435 return Err(anyhow!("interval must be positive"));
436 }
437
438 let mut result = Vec::new();
439 let mut current = start_dt;
440
441 while current <= end_dt {
442 let ts = format.as_ref().map_or_else(
443 || current.to_rfc3339(),
444 |fmt| current.format(fmt).to_string(),
445 );
446 result.push(Value::String(ts));
447
448 let Some(next) = current.checked_add_signed(step) else {
449 break;
450 };
451 current = next;
452 }
453
454 try_deser(Value::Array(result), "failed to deserialize time values")
455}
456
457#[cfg(feature = "map-parser-extensions")]
458fn parse_g_random<T>(context: &ParseContext<'_>, value: &mut Value) -> Result<T>
459where
460 T: DeserializeOwned,
461{
462 let min: f64 = must_translate_from_context(value, context, "min")?;
463 let max: f64 = must_translate_from_context(value, context, "max")?;
464
465 if min >= max {
466 return Err(anyhow!("min ({}) must be less than max ({})", min, max));
467 }
468
469 let integer = value
470 .get("integer")
471 .and_then(Value::as_bool)
472 .unwrap_or(false);
473 let seed: Option<u64> = take_optional(value, context.map(), context.map_eval(), &"seed")?;
474 let count: Option<u64> = take_optional(value, context.map(), context.map_eval(), &"count")?;
475
476 let gen_value = |rng: &mut dyn Rng| -> Result<Value> {
477 if integer {
478 if min.fract() != 0.0 || max.fract() != 0.0 {
479 return Err(anyhow!("integer random bounds must be whole numbers"));
480 }
481 Ok(Value::from(rng.random_range(min as i64..max as i64)))
482 } else {
483 json_number(rng.random_range(min..max))
484 }
485 };
486
487 match count {
488 Some(0) => Err(anyhow!("count must be positive")),
489 Some(count) => {
490 let count = usize_count(count, "count")?;
491 let values = with_rng(seed, |rng| {
492 (0..count)
493 .map(|_| gen_value(rng))
494 .collect::<Result<Vec<_>>>()
495 })?;
496 try_deser(Value::Array(values), "failed to deserialize g-random array")
497 }
498 None => {
499 let value = with_rng(seed, |rng| gen_value(rng))?;
500 try_deser(value, "failed to deserialize g-random single value")
501 }
502 }
503}
504
505#[cfg(feature = "map-parser-extensions")]
506fn parse_g_choose<T>(context: &ParseContext<'_>, value: &mut Value) -> Result<T>
507where
508 T: DeserializeOwned + Serialize + Debug + Clone,
509{
510 let options: Vec<Value> = must_translate_from_context(value, context, "options")?;
511 if options.is_empty() {
512 return Err(anyhow!("options must not be empty for g-choose"));
513 }
514
515 let seed: Option<u64> = take_optional(value, context.map(), context.map_eval(), &"seed")?;
516 let count: Option<u64> = take_optional(value, context.map(), context.map_eval(), &"count")?;
517
518 match count {
519 Some(0) => Err(anyhow!("count must be positive")),
520 Some(count) => {
521 let count = usize_count(count, "count")?;
522 let selected = with_rng(seed, |rng| {
523 (0..count)
524 .map(|_| options[rng.random_range(0..options.len())].clone())
525 .collect::<Vec<_>>()
526 });
527 try_deser(
528 Value::Array(selected),
529 "failed to deserialize g-choose array",
530 )
531 }
532 None => {
533 let picked = with_rng(seed, |rng| {
534 options[rng.random_range(0..options.len())].clone()
535 });
536 try_deser(picked, "failed to deserialize g-choose single value")
537 }
538 }
539}
540
541fn parse_g_env<T>(context: &ParseContext<'_>, value: &mut Value) -> Result<T>
542where
543 T: DeserializeOwned,
544{
545 let name: String = must_translate_from_context(value, context, "name")?;
546 let default: Option<String> =
547 take_optional(value, context.map(), context.map_eval(), &"default")?;
548 let env_val = std::env::var(&name).ok().or(default).ok_or_else(|| {
549 anyhow!(
550 "environment variable '{}' is not set and no default provided",
551 name
552 )
553 })?;
554
555 try_deser(Value::String(env_val), "failed to deserialize env value")
556}
557
558fn parse_g_join<T>(context: &ParseContext<'_>, value: &mut Value) -> Result<T>
559where
560 T: DeserializeOwned,
561{
562 let values: Vec<String> = must_translate_from_context(value, context, "values")?;
563 let separator: String =
564 take_optional(value, context.map(), context.map_eval(), &"separator")?.unwrap_or_default();
565 try_deser(
566 Value::String(values.join(&separator)),
567 "failed to deserialize joined string",
568 )
569}
570
571#[cfg(feature = "map-parser-extensions")]
572fn parse_duration_str(s: &str) -> Result<TimeDelta> {
573 let s = s.trim();
574 if s.is_empty() {
575 return Err(anyhow!("duration string is empty"));
576 }
577
578 let mut total = TimeDelta::zero();
579 let mut num_str = String::new();
580
581 for ch in s.chars() {
582 if ch.is_ascii_digit() || ch == '.' {
583 num_str.push(ch);
584 continue;
585 }
586
587 if ch.is_whitespace() {
588 continue;
589 }
590
591 if !ch.is_alphabetic() {
592 return Err(anyhow!("unexpected character '{}' in duration string", ch));
593 }
594
595 if num_str.is_empty() {
596 return Err(anyhow!("missing number before duration unit '{}'", ch));
597 }
598
599 let num: f64 = num_str
600 .parse()
601 .with_context(|| format!("invalid number in duration: '{}'", num_str))?;
602 if num <= 0.0 {
603 return Err(anyhow!("duration components must be positive"));
604 }
605 num_str.clear();
606
607 let seconds = match ch.to_ascii_lowercase() {
608 's' => num,
609 'm' => num * 60.0,
610 'h' => num * 3_600.0,
611 'd' => num * 86_400.0,
612 'w' => num * 604_800.0,
613 other => return Err(anyhow!("unknown duration unit: '{}'", other)),
614 };
615
616 let delta = TimeDelta::try_seconds(seconds as i64)
617 .ok_or_else(|| anyhow!("duration overflow: {}{}", num, ch))?;
618 total = total
619 .checked_add(&delta)
620 .ok_or_else(|| anyhow!("duration overflow"))?;
621 }
622
623 if !num_str.is_empty() {
624 return Err(anyhow!("trailing number without unit: '{}'", num_str));
625 }
626 if total.is_zero() {
627 return Err(anyhow!("duration must be positive, got: '{}'", s));
628 }
629
630 Ok(total)
631}
632
633#[cfg(feature = "map-parser-extensions")]
634fn parse_time_str(s: &str) -> Result<DateTime<Utc>> {
635 let s = s.trim();
636
637 if let Some(rest) = s.strip_prefix("now") {
638 let base = Utc::now();
639 if rest.is_empty() {
640 return Ok(base);
641 }
642
643 let sign_char = rest
644 .chars()
645 .next()
646 .ok_or_else(|| anyhow!("expected '+' or '-' after 'now'"))?;
647 let duration_str = &rest[sign_char.len_utf8()..];
648 let delta = parse_duration_str(duration_str)?;
649
650 return match sign_char {
651 '+' => base
652 .checked_add_signed(delta)
653 .ok_or_else(|| anyhow!("time overflow for '{}'", s)),
654 '-' => base
655 .checked_sub_signed(delta)
656 .ok_or_else(|| anyhow!("time overflow for '{}'", s)),
657 _ => Err(anyhow!("expected '+' or '-' after 'now', got '{}'", rest)),
658 };
659 }
660
661 if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
662 return Ok(dt.with_timezone(&Utc));
663 }
664
665 for fmt in [
666 "%Y-%m-%dT%H:%M:%S%.f%:z",
667 "%Y-%m-%dT%H:%M:%S%.f",
668 "%Y-%m-%dT%H:%M:%S%:z",
669 "%Y-%m-%dT%H:%M:%S",
670 "%Y-%m-%d %H:%M:%S",
671 ] {
672 if let Ok(dt) = DateTime::parse_from_str(s, fmt) {
673 return Ok(dt.with_timezone(&Utc));
674 }
675 if let Ok(naive) = NaiveDateTime::parse_from_str(s, fmt) {
676 return Ok(naive.and_utc());
677 }
678 }
679
680 if let Ok(date) = NaiveDate::parse_from_str(s, "%Y-%m-%d") {
681 return date
682 .and_hms_opt(0, 0, 0)
683 .map(|s| s.and_utc())
684 .ok_or_else(|| anyhow!("invalid date: '{}'", s));
685 }
686
687 Err(anyhow!(
688 "unable to parse time string: '{}'. Supported formats: RFC 3339, \
689 'YYYY-MM-DDTHH:MM:SS', 'YYYY-MM-DD HH:MM:SS', 'YYYY-MM-DD', \
690 'now', 'now+duration', 'now-duration'",
691 s
692 ))
693}
694
695#[cfg(feature = "map-parser-extensions")]
696fn with_rng<F, R>(seed: Option<u64>, f: F) -> R
697where
698 F: FnOnce(&mut dyn Rng) -> R,
699{
700 if let Some(seed) = seed {
701 let mut rng = StdRng::seed_from_u64(seed);
702 f(&mut rng)
703 } else {
704 let mut rng = rand::rng();
705 f(&mut rng)
706 }
707}
708
709impl<'de, T> Deserialize<'de> for DataPack<T>
710where
711 T: DeserializeOwned + Serialize + Debug + Clone,
712{
713 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
714 where
715 D: Deserializer<'de>,
716 {
717 let value = Value::deserialize(deserializer)?;
718
719 if let Some(index) = value.as_str().and_then(|s| s.strip_prefix("map.")) {
720 return Ok(Self::Index(index.to_owned()));
721 }
722
723 serde_json::from_value::<T>(value)
724 .map(Self::Data)
725 .map_err(serde::de::Error::custom)
726 }
727}
728
729impl<T> Serialize for DataPack<T>
730where
731 T: DeserializeOwned + Serialize + Debug + Clone,
732{
733 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
734 where
735 S: Serializer,
736 {
737 match self {
738 Self::Data(data) => data.serialize(serializer),
739 Self::Index(index) => serializer.serialize_str(&format!("map.{index}")),
740 }
741 }
742}
743
744use plotly::color;
745
746#[allow(clippy::enum_variant_names)]
748#[derive(Clone, Debug, Serialize)]
749#[serde(rename_all = "snake_case")]
750pub enum Color {
751 NamedColor(color::NamedColor),
752 RgbColor(color::Rgb),
753 RgbaColor(color::Rgba),
754}
755
756impl color::Color for Color {}
757
758impl<'de> Deserialize<'de> for Color {
759 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
760 where
761 D: Deserializer<'de>,
762 {
763 let value = Value::deserialize(deserializer)?;
764
765 if let Some(s) = value.as_str()
766 && let Ok(named) = serde_json::from_str::<color::NamedColor>(&format!("\"{s}\""))
767 {
768 return Ok(Self::NamedColor(named));
769 }
770
771 if let Ok(rgb) = serde_json::from_value::<color::Rgb>(value.clone()) {
772 return Ok(Self::RgbColor(rgb));
773 }
774
775 if let Ok(rgba) = serde_json::from_value::<color::Rgba>(value) {
776 return Ok(Self::RgbaColor(rgba));
777 }
778
779 Err(serde::de::Error::custom("invalid color format"))
780 }
781}