Skip to main content

mdbook_plotly/code_handler/
until.rs

1// Tmp fix
2#![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}