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