Skip to main content

mq_lang/eval/
runtime_value.rs

1use super::env::Env;
2use crate::{AstParams, Ident, Program, Shared, SharedCell, ast, number::Number};
3use mq_markdown::Node;
4use smol_str::SmolStr;
5use std::{
6    borrow::Cow,
7    cmp::Ordering,
8    collections::BTreeMap,
9    ops::{Index, IndexMut},
10};
11
12/// Runtime selector for indexing into markdown nodes.
13#[derive(Debug, Clone, PartialEq)]
14pub enum Selector {
15    /// Selects a child node at the specified index.
16    Index(usize),
17}
18
19/// Represents a module's runtime environment with its exports.
20#[derive(Clone, Debug)]
21pub struct ModuleEnv {
22    name: SmolStr,
23    exports: Shared<SharedCell<Env>>,
24}
25
26impl ModuleEnv {
27    /// Creates a new module environment with the given name and exports.
28    pub fn new(name: &str, exports: Shared<SharedCell<Env>>) -> Self {
29        Self {
30            name: SmolStr::new(name),
31            exports,
32        }
33    }
34
35    /// Returns the name of the module.
36    pub fn name(&self) -> &str {
37        &self.name
38    }
39
40    /// Returns a reference to the module's exports environment.
41    pub fn exports(&self) -> &Shared<SharedCell<Env>> {
42        &self.exports
43    }
44
45    /// Returns the number of exports in this module.
46    pub fn len(&self) -> usize {
47        #[cfg(not(feature = "sync"))]
48        {
49            self.exports.borrow().len()
50        }
51
52        #[cfg(feature = "sync")]
53        {
54            self.exports.read().unwrap().len()
55        }
56    }
57}
58
59impl PartialEq for ModuleEnv {
60    fn eq(&self, other: &Self) -> bool {
61        #[cfg(not(feature = "sync"))]
62        let exports = self.exports().borrow();
63        #[cfg(feature = "sync")]
64        let exports = self.exports().read().unwrap();
65
66        #[cfg(not(feature = "sync"))]
67        let other_exports = other.exports().borrow();
68        #[cfg(feature = "sync")]
69        let other_exports = other.exports().read().unwrap();
70
71        self.name == other.name && std::ptr::eq(&*exports, &*other_exports)
72    }
73}
74
75/// A value in the mq runtime.
76///
77/// This enum represents all possible value types that can exist during
78/// program execution, including numbers, strings, markdown nodes, functions,
79/// and more complex data structures.
80#[derive(Clone, Default)]
81pub enum RuntimeValue {
82    /// A numeric value.
83    Number(Number),
84    /// A boolean value (`true` or `false`).
85    Boolean(bool),
86    /// A string value.
87    String(String),
88    /// A symbol (interned identifier).
89    Symbol(Ident),
90    /// An array of runtime values.
91    Array(Vec<RuntimeValue>),
92    /// A markdown node with an optional selector for indexing.
93    Markdown(Node, Option<Selector>),
94    /// A user-defined function with parameters, body (program), and captured environment.
95    Function(AstParams, Program, Shared<SharedCell<Env>>),
96    /// A built-in native function identified by name.
97    NativeFunction(Ident),
98    /// A dictionary mapping identifiers to runtime values.
99    Dict(BTreeMap<Ident, RuntimeValue>),
100    /// A module with its exports.
101    Module(ModuleEnv),
102    /// An AST node (quoted expression).
103    Ast(Shared<ast::node::Node>),
104    /// An empty or null value.
105    #[default]
106    None,
107}
108
109// Custom PartialEq implementation to avoid comparing Env pointers
110impl PartialEq for RuntimeValue {
111    fn eq(&self, other: &Self) -> bool {
112        match (self, other) {
113            (RuntimeValue::Number(a), RuntimeValue::Number(b)) => a == b,
114            (RuntimeValue::Boolean(a), RuntimeValue::Boolean(b)) => a == b,
115            (RuntimeValue::String(a), RuntimeValue::String(b)) => a == b,
116            (RuntimeValue::Symbol(a), RuntimeValue::Symbol(b)) => a == b,
117            (RuntimeValue::Array(a), RuntimeValue::Array(b)) => a == b,
118            (RuntimeValue::Markdown(a, sa), RuntimeValue::Markdown(b, sb)) => a == b && sa == sb,
119            (RuntimeValue::Function(a1, b1, _), RuntimeValue::Function(a2, b2, _)) => a1 == a2 && b1 == b2,
120            (RuntimeValue::NativeFunction(a), RuntimeValue::NativeFunction(b)) => a == b,
121            (RuntimeValue::Dict(a), RuntimeValue::Dict(b)) => a == b,
122            (RuntimeValue::Module(a), RuntimeValue::Module(b)) => a == b,
123            (RuntimeValue::Ast(a), RuntimeValue::Ast(b)) => a == b,
124            (RuntimeValue::None, RuntimeValue::None) => true,
125            _ => false,
126        }
127    }
128}
129
130impl From<Node> for RuntimeValue {
131    fn from(node: Node) -> Self {
132        RuntimeValue::Markdown(node, None)
133    }
134}
135
136impl From<bool> for RuntimeValue {
137    fn from(b: bool) -> Self {
138        RuntimeValue::Boolean(b)
139    }
140}
141
142impl From<String> for RuntimeValue {
143    fn from(s: String) -> Self {
144        RuntimeValue::String(s)
145    }
146}
147
148impl From<&str> for RuntimeValue {
149    fn from(s: &str) -> Self {
150        RuntimeValue::String(s.to_string())
151    }
152}
153
154impl From<&mut str> for RuntimeValue {
155    fn from(s: &mut str) -> Self {
156        RuntimeValue::String(s.to_string())
157    }
158}
159
160impl From<Number> for RuntimeValue {
161    fn from(n: Number) -> Self {
162        RuntimeValue::Number(n)
163    }
164}
165
166impl From<Ident> for RuntimeValue {
167    fn from(i: Ident) -> Self {
168        RuntimeValue::Symbol(i)
169    }
170}
171
172impl From<usize> for RuntimeValue {
173    fn from(n: usize) -> Self {
174        RuntimeValue::Number(Number::from(n))
175    }
176}
177
178impl From<Vec<RuntimeValue>> for RuntimeValue {
179    fn from(arr: Vec<RuntimeValue>) -> Self {
180        RuntimeValue::Array(arr)
181    }
182}
183
184impl From<BTreeMap<Ident, RuntimeValue>> for RuntimeValue {
185    fn from(map: BTreeMap<Ident, RuntimeValue>) -> Self {
186        RuntimeValue::Dict(map)
187    }
188}
189
190impl From<Vec<(String, Number)>> for RuntimeValue {
191    fn from(v: Vec<(String, Number)>) -> Self {
192        RuntimeValue::Dict(
193            v.into_iter()
194                .map(|(k, v)| (Ident::new(&k), RuntimeValue::Number(v)))
195                .collect::<BTreeMap<Ident, RuntimeValue>>(),
196        )
197    }
198}
199
200impl From<mq_markdown::AttrValue> for RuntimeValue {
201    fn from(attr_value: mq_markdown::AttrValue) -> Self {
202        match attr_value {
203            mq_markdown::AttrValue::String(s) => RuntimeValue::String(s),
204            mq_markdown::AttrValue::Number(n) => RuntimeValue::Number(n.into()),
205            mq_markdown::AttrValue::Integer(n) => RuntimeValue::Number(n.into()),
206            mq_markdown::AttrValue::Boolean(b) => RuntimeValue::Boolean(b),
207            mq_markdown::AttrValue::Array(arr) => {
208                RuntimeValue::Array(arr.into_iter().map(RuntimeValue::from).collect())
209            }
210            mq_markdown::AttrValue::Null => RuntimeValue::NONE,
211        }
212    }
213}
214
215impl PartialOrd for RuntimeValue {
216    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
217        match (self, other) {
218            (RuntimeValue::Number(a), RuntimeValue::Number(b)) => a.partial_cmp(b),
219            (RuntimeValue::Boolean(a), RuntimeValue::Boolean(b)) => a.partial_cmp(b),
220            (RuntimeValue::String(a), RuntimeValue::String(b)) => a.partial_cmp(b),
221            (RuntimeValue::Symbol(a), RuntimeValue::Symbol(b)) => a.partial_cmp(b),
222            (RuntimeValue::Array(a), RuntimeValue::Array(b)) => a.partial_cmp(b),
223            (RuntimeValue::Markdown(a, _), RuntimeValue::Markdown(b, _)) => {
224                let a = a.to_string();
225                let b = b.to_string();
226                a.to_string().partial_cmp(&b)
227            }
228            (RuntimeValue::Function(a1, b1, _), RuntimeValue::Function(a2, b2, _)) => match a1.partial_cmp(a2) {
229                Some(Ordering::Equal) => b1.partial_cmp(b2),
230                Some(Ordering::Greater) => Some(Ordering::Greater),
231                Some(Ordering::Less) => Some(Ordering::Less),
232                _ => None,
233            },
234            (RuntimeValue::Dict(_), _) => None,
235            (_, RuntimeValue::Dict(_)) => None,
236            (RuntimeValue::Module(a), RuntimeValue::Module(b)) => a.name.partial_cmp(&b.name),
237            (RuntimeValue::Ast(_), _) => None,
238            (_, RuntimeValue::Ast(_)) => None,
239            _ => None,
240        }
241    }
242}
243
244impl std::fmt::Display for RuntimeValue {
245    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
246        let value: Cow<'_, str> = match self {
247            Self::Number(n) => Cow::Owned(n.to_string()),
248            Self::Boolean(b) => Cow::Owned(b.to_string()),
249            Self::String(s) => Cow::Borrowed(s),
250            Self::Symbol(i) => Cow::Owned(format!(":{}", i)),
251            Self::Array(_) => self.string(),
252            Self::Markdown(m, ..) => Cow::Owned(m.to_string()),
253            Self::None => Cow::Borrowed(""),
254            Self::Function(params, ..) => Cow::Owned(format!("function/{}", params.len())),
255            Self::NativeFunction(_) => Cow::Borrowed("native_function"),
256            Self::Dict(_) => self.string(),
257            Self::Module(module_name) => Cow::Owned(format!(r#"module "{}""#, module_name.name)),
258            Self::Ast(node) => Cow::Owned(node.to_code()),
259        };
260        write!(f, "{}", value)
261    }
262}
263
264impl std::fmt::Debug for RuntimeValue {
265    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
266        let v: Cow<'_, str> = match self {
267            Self::None => Cow::Borrowed("None"),
268            Self::String(s) => Cow::Owned(format!("{:?}", s)),
269            Self::Array(arr) => Cow::Owned(format!("{:?}", arr)),
270            a => a.string(),
271        };
272        write!(f, "{}", v)
273    }
274}
275
276impl RuntimeValue {
277    /// An empty array constant.
278    pub const EMPTY_ARRAY: RuntimeValue = Self::Array(Vec::new());
279    /// The boolean `false` value.
280    pub const FALSE: RuntimeValue = Self::Boolean(false);
281    /// The `None` (null) value.
282    pub const NONE: RuntimeValue = Self::None;
283    /// The boolean `true` value.
284    pub const TRUE: RuntimeValue = Self::Boolean(true);
285
286    /// Creates a new empty dictionary.
287    #[inline(always)]
288    pub fn new_dict() -> RuntimeValue {
289        RuntimeValue::Dict(BTreeMap::new())
290    }
291
292    /// Returns the type name of this runtime value as a string.
293    #[inline(always)]
294    pub fn name(&self) -> &str {
295        match self {
296            RuntimeValue::Number(_) => "number",
297            RuntimeValue::Boolean(_) => "bool",
298            RuntimeValue::String(_) => "string",
299            RuntimeValue::Symbol(_) => "symbol",
300            RuntimeValue::Markdown(_, _) => "markdown",
301            RuntimeValue::Array(_) => "array",
302            RuntimeValue::None => "None",
303            RuntimeValue::Function(_, _, _) => "function",
304            RuntimeValue::NativeFunction(_) => "native_function",
305            RuntimeValue::Dict(_) => "dict",
306            RuntimeValue::Module(_) => "module",
307            RuntimeValue::Ast(_) => "ast",
308        }
309    }
310
311    /// Returns `true` if this value is `None`.
312    #[inline(always)]
313    pub fn is_none(&self) -> bool {
314        matches!(self, RuntimeValue::None)
315    }
316
317    /// Returns `true` if this value is a user-defined function.
318    #[inline(always)]
319    pub fn is_function(&self) -> bool {
320        matches!(self, RuntimeValue::Function(_, _, _))
321    }
322
323    /// Returns `true` if this value is a native (built-in) function.
324    #[inline(always)]
325    pub fn is_native_function(&self) -> bool {
326        matches!(self, RuntimeValue::NativeFunction(_))
327    }
328
329    /// Returns `true` if this value is an array.
330    #[inline(always)]
331    pub fn is_array(&self) -> bool {
332        matches!(self, RuntimeValue::Array(_))
333    }
334
335    /// Returns `true` if this value is considered empty.
336    ///
337    /// Empty values include empty arrays, empty strings, empty markdown nodes,
338    /// empty dictionaries, and `None`.
339    #[inline(always)]
340    pub fn is_empty(&self) -> bool {
341        match self {
342            RuntimeValue::Array(a) => a.is_empty(),
343            RuntimeValue::String(s) => s.is_empty(),
344            RuntimeValue::Markdown(m, _) => m.value().is_empty(),
345            RuntimeValue::Dict(m) => m.is_empty(),
346            RuntimeValue::None => true,
347            _ => false,
348        }
349    }
350
351    /// Returns `true` if this value is considered truthy in conditional contexts.
352    ///
353    /// Truthy values include non-zero numbers, non-empty strings and arrays,
354    /// `true`, functions, symbols, and modules. Falsy values include `false`,
355    /// zero, empty collections, and `None`.
356    #[inline(always)]
357    pub fn is_truthy(&self) -> bool {
358        match self {
359            RuntimeValue::Boolean(b) => *b,
360            RuntimeValue::Number(n) => n.value() != 0.0,
361            RuntimeValue::String(s) => !s.is_empty(),
362            RuntimeValue::Array(a) => !a.is_empty(),
363            RuntimeValue::Markdown(node, selector) => match selector {
364                Some(Selector::Index(i)) => node.find_at_index(*i).is_some(),
365                None => true,
366            },
367            RuntimeValue::Symbol(_)
368            | RuntimeValue::Function(_, _, _)
369            | RuntimeValue::NativeFunction(_)
370            | RuntimeValue::Dict(_) => true,
371            RuntimeValue::Module(_) => true,
372            RuntimeValue::Ast(_) => true,
373            RuntimeValue::None => false,
374        }
375    }
376
377    /// Returns the length of this value.
378    ///
379    /// For numbers, returns the value as `usize`. For strings and arrays, returns
380    /// the number of elements. For dictionaries, returns the number of entries.
381    #[inline(always)]
382    pub fn len(&self) -> usize {
383        match self {
384            RuntimeValue::Number(n) => n.value() as usize,
385            RuntimeValue::Boolean(_) => 1,
386            RuntimeValue::String(s) => s.len(),
387            RuntimeValue::Symbol(i) => i.as_str().len(),
388            RuntimeValue::Array(a) => a.len(),
389            RuntimeValue::Markdown(m, _) => m.value().len(),
390            RuntimeValue::Dict(m) => m.len(),
391            RuntimeValue::None => 0,
392            RuntimeValue::Function(..) => 0,
393            RuntimeValue::Module(m) => m.len(),
394            RuntimeValue::NativeFunction(..) => 0,
395            RuntimeValue::Ast(_) => 0,
396        }
397    }
398
399    /// Extracts the markdown node from this value, if it is a markdown value.
400    ///
401    /// If a selector is present, returns the selected child node.
402    #[inline(always)]
403    pub fn markdown_node(&self) -> Option<Node> {
404        match self {
405            RuntimeValue::Markdown(n, Some(Selector::Index(i))) => n.find_at_index(*i),
406            RuntimeValue::Markdown(n, _) => Some(n.clone()),
407            _ => None,
408        }
409    }
410
411    /// Updates the value of a markdown node, returning a new runtime value.
412    ///
413    /// If this is not a markdown value, returns `None`.
414    #[inline(always)]
415    pub fn update_markdown_value(&self, value: &str) -> RuntimeValue {
416        match self {
417            RuntimeValue::Markdown(n, Some(Selector::Index(i))) => {
418                RuntimeValue::Markdown(n.with_children_value(value, *i), Some(Selector::Index(*i)))
419            }
420            RuntimeValue::Markdown(n, selector) => RuntimeValue::Markdown(n.with_value(value), selector.clone()),
421            _ => RuntimeValue::NONE,
422        }
423    }
424
425    /// Returns the position information for a markdown node, if available.
426    #[inline(always)]
427    pub fn position(&self) -> Option<mq_markdown::Position> {
428        match self {
429            RuntimeValue::Markdown(node, _) => node.position(),
430            _ => None,
431        }
432    }
433
434    /// Sets the position information for a markdown node.
435    ///
436    /// Only affects markdown values; other value types are unaffected.
437    #[inline(always)]
438    pub fn set_position(&mut self, position: Option<mq_markdown::Position>) {
439        if let RuntimeValue::Markdown(node, _) = self {
440            node.set_position(position);
441        }
442    }
443
444    #[inline(always)]
445    fn string(&self) -> Cow<'_, str> {
446        match self {
447            Self::Number(n) => Cow::Owned(n.to_string()),
448            Self::Boolean(b) => Cow::Owned(b.to_string()),
449            Self::String(s) => Cow::Owned(format!(r#""{}""#, s)),
450            Self::Symbol(i) => Cow::Owned(format!(":{}", i)),
451            Self::Array(a) => Cow::Owned(format!(
452                "[{}]",
453                a.iter().map(|v| v.string()).collect::<Vec<Cow<str>>>().join(", ")
454            )),
455            Self::Markdown(m, ..) => Cow::Owned(m.to_string()),
456            Self::None => Cow::Borrowed(""),
457            Self::Function(f, _, _) => Cow::Owned(format!("function/{}", f.len())),
458            Self::NativeFunction(_) => Cow::Borrowed("native_function"),
459            Self::Module(m) => Cow::Owned(format!("module/{}", m.name())),
460            Self::Ast(node) => Cow::Owned(node.to_code()),
461            Self::Dict(map) => {
462                let items = map
463                    .iter()
464                    .map(|(k, v)| format!("\"{}\": {}", k, v.string()))
465                    .collect::<Vec<String>>()
466                    .join(", ");
467                Cow::Owned(format!("{{{}}}", items))
468            }
469        }
470    }
471}
472
473/// A collection of runtime values.
474///
475/// Provides utilities for working with multiple values, such as filtering
476/// and updating operations.
477#[derive(Debug, Clone, PartialEq)]
478pub struct RuntimeValues(Vec<RuntimeValue>);
479
480impl From<Vec<RuntimeValue>> for RuntimeValues {
481    fn from(values: Vec<RuntimeValue>) -> Self {
482        Self(values)
483    }
484}
485
486impl Index<usize> for RuntimeValues {
487    type Output = RuntimeValue;
488
489    fn index(&self, index: usize) -> &Self::Output {
490        &self.0[index]
491    }
492}
493
494impl IndexMut<usize> for RuntimeValues {
495    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
496        &mut self.0[index]
497    }
498}
499
500impl IntoIterator for RuntimeValues {
501    type IntoIter = std::vec::IntoIter<RuntimeValue>;
502    type Item = RuntimeValue;
503
504    fn into_iter(self) -> Self::IntoIter {
505        self.0.into_iter()
506    }
507}
508
509impl RuntimeValues {
510    /// Returns a compacted version of this collection, removing `None` and empty values.
511    pub fn compact(&self) -> Vec<RuntimeValue> {
512        self.0
513            .iter()
514            .filter(|v| !v.is_none() && !v.is_empty())
515            .cloned()
516            .collect::<Vec<_>>()
517    }
518
519    /// Returns a reference to the underlying vector of values.
520    pub fn values(&self) -> &Vec<RuntimeValue> {
521        &self.0
522    }
523
524    /// Returns the number of values in this collection.
525    pub fn len(&self) -> usize {
526        self.0.len()
527    }
528
529    /// Returns `true` if this collection contains no values.
530    pub fn is_empty(&self) -> bool {
531        self.0.len() == 0
532    }
533
534    /// Updates this collection with values from another collection.
535    ///
536    /// Pairs corresponding elements from both collections and applies special
537    /// update logic for markdown nodes.
538    pub fn update_with(&self, other: Self) -> Self {
539        self.0
540            .clone()
541            .into_iter()
542            .zip(other)
543            .map(|(current_value, mut updated_value)| {
544                updated_value.set_position(current_value.position());
545
546                if let RuntimeValue::Markdown(node, _) = &current_value {
547                    match &updated_value {
548                        RuntimeValue::None
549                        | RuntimeValue::Function(_, _, _)
550                        | RuntimeValue::Module(_)
551                        | RuntimeValue::Ast(_)
552                        | RuntimeValue::NativeFunction(_) => current_value.clone(),
553                        RuntimeValue::Markdown(node, _) if node.is_empty() => current_value.clone(),
554                        RuntimeValue::Markdown(node, _) => {
555                            if node.is_fragment() {
556                                if let RuntimeValue::Markdown(mut current_node, selector) = current_value {
557                                    current_node.apply_fragment(node.clone());
558                                    RuntimeValue::Markdown(current_node, selector)
559                                } else {
560                                    updated_value
561                                }
562                            } else {
563                                updated_value
564                            }
565                        }
566                        RuntimeValue::String(s) => RuntimeValue::Markdown(node.clone().with_value(s), None),
567                        RuntimeValue::Symbol(i) => RuntimeValue::Markdown(node.clone().with_value(&i.as_str()), None),
568                        RuntimeValue::Boolean(b) => {
569                            RuntimeValue::Markdown(node.clone().with_value(b.to_string().as_str()), None)
570                        }
571                        RuntimeValue::Number(n) => {
572                            RuntimeValue::Markdown(node.clone().with_value(n.to_string().as_str()), None)
573                        }
574                        RuntimeValue::Array(array) => RuntimeValue::Array(
575                            array
576                                .iter()
577                                .filter_map(|o| {
578                                    if !matches!(o, RuntimeValue::None) {
579                                        Some(RuntimeValue::Markdown(
580                                            node.clone().with_value(o.to_string().as_str()),
581                                            None,
582                                        ))
583                                    } else {
584                                        None
585                                    }
586                                })
587                                .collect::<Vec<_>>(),
588                        ),
589                        RuntimeValue::Dict(map) => {
590                            let mut new_dict = BTreeMap::new();
591                            for (k, v) in map {
592                                if !v.is_none() && !v.is_empty() {
593                                    new_dict.insert(
594                                        *k,
595                                        RuntimeValue::Markdown(node.clone().with_value(v.to_string().as_str()), None),
596                                    );
597                                }
598                            }
599                            RuntimeValue::Dict(new_dict)
600                        }
601                    }
602                } else {
603                    updated_value
604                }
605            })
606            .collect::<Vec<_>>()
607            .into()
608    }
609}
610
611#[cfg(test)]
612mod tests {
613    use crate::ast::node::{IdentWithToken, Param};
614    use rstest::rstest;
615    use smallvec::{SmallVec, smallvec};
616
617    use super::*;
618
619    #[test]
620    fn test_runtime_value_from() {
621        assert_eq!(RuntimeValue::from(true), RuntimeValue::Boolean(true));
622        assert_eq!(RuntimeValue::from(false), RuntimeValue::Boolean(false));
623        assert_eq!(
624            RuntimeValue::from(String::from("test")),
625            RuntimeValue::String(String::from("test"))
626        );
627        assert_eq!(
628            RuntimeValue::from(Number::from(42.0)),
629            RuntimeValue::Number(Number::from(42.0))
630        );
631    }
632
633    #[rstest]
634    #[case(RuntimeValue::Number(Number::from(42.0)), "42")]
635    #[case(RuntimeValue::Boolean(true), "true")]
636    #[case(RuntimeValue::Boolean(false), "false")]
637    #[case(RuntimeValue::String("hello".to_string()), r#""hello""#)]
638    #[case(RuntimeValue::None, "")]
639    #[case(RuntimeValue::Array(vec![
640            RuntimeValue::Number(Number::from(1.0)),
641            RuntimeValue::String("test".to_string())
642        ]), r#"[1, "test"]"#)]
643    #[case(RuntimeValue::Dict({
644            let mut map = BTreeMap::new();
645            map.insert(Ident::new("key1"), RuntimeValue::String("value1".to_string()));
646            map.insert(Ident::new("key2"), RuntimeValue::Number(Number::from(42.0)));
647            map
648        }), r#"{"key1": "value1", "key2": 42}"#)]
649    fn test_string_method(#[case] value: RuntimeValue, #[case] expected: &str) {
650        assert_eq!(value.string(), expected);
651    }
652
653    #[test]
654    fn test_runtime_value_display() {
655        assert_eq!(format!("{}", RuntimeValue::Boolean(true)), "true");
656        assert_eq!(format!("{}", RuntimeValue::Number(Number::from(42.0))), "42");
657        assert_eq!(format!("{}", RuntimeValue::String(String::from("test"))), "test");
658        assert_eq!(format!("{}", RuntimeValue::None), "");
659        let map_val = RuntimeValue::Dict(BTreeMap::default());
660        assert_eq!(format!("{}", map_val), "{}");
661    }
662
663    #[test]
664    fn test_runtime_value_debug() {
665        assert_eq!(format!("{:?}", RuntimeValue::Boolean(true)), "true");
666        assert_eq!(format!("{:?}", RuntimeValue::Number(Number::from(42.0))), "42");
667        assert_eq!(format!("{:?}", RuntimeValue::String(String::from("test"))), "\"test\"");
668        assert_eq!(format!("{:?}", RuntimeValue::None), "None");
669
670        let mut map = BTreeMap::default();
671        map.insert(Ident::new("name"), RuntimeValue::String("MQ".to_string()));
672        map.insert(Ident::new("version"), RuntimeValue::Number(Number::from(1.0)));
673        let map_val = RuntimeValue::Dict(map);
674        let debug_str = format!("{:?}", map_val);
675        assert!(debug_str == r#"{"name": "MQ", "version": 1}"# || debug_str == r#"{"version": 1, "name": "MQ"}"#);
676    }
677
678    #[test]
679    fn test_runtime_value_name() {
680        assert_eq!(RuntimeValue::Boolean(true).name(), "bool");
681        assert_eq!(RuntimeValue::Number(Number::from(42.0)).name(), "number");
682        assert_eq!(RuntimeValue::String(String::from("test")).name(), "string");
683        assert_eq!(RuntimeValue::None.name(), "None");
684        assert_eq!(
685            RuntimeValue::Function(
686                SmallVec::new(),
687                Vec::new(),
688                Shared::new(SharedCell::new(Env::default()))
689            )
690            .name(),
691            "function"
692        );
693        assert_eq!(
694            RuntimeValue::NativeFunction(Ident::new("name")).name(),
695            "native_function"
696        );
697        assert_eq!(
698            RuntimeValue::Markdown(
699                mq_markdown::Node::Text(mq_markdown::Text {
700                    value: "".to_string(),
701                    position: None
702                }),
703                None
704            )
705            .name(),
706            "markdown"
707        );
708        assert_eq!(RuntimeValue::Dict(BTreeMap::default()).name(), "dict");
709    }
710
711    #[test]
712    fn test_runtime_value_is_true() {
713        assert!(RuntimeValue::Boolean(true).is_truthy());
714        assert!(!RuntimeValue::Boolean(false).is_truthy());
715        assert!(RuntimeValue::Number(Number::from(42.0)).is_truthy());
716        assert!(!RuntimeValue::Number(Number::from(0.0)).is_truthy());
717        assert!(RuntimeValue::String(String::from("test")).is_truthy());
718        assert!(!RuntimeValue::String(String::from("")).is_truthy());
719        assert!(RuntimeValue::Array(vec!["".to_string().into()]).is_truthy());
720        assert!(!RuntimeValue::Array(Vec::new()).is_truthy());
721        assert!(
722            RuntimeValue::Markdown(
723                mq_markdown::Node::Text(mq_markdown::Text {
724                    value: "".to_string(),
725                    position: None
726                }),
727                None
728            )
729            .is_truthy()
730        );
731        assert!(
732            !RuntimeValue::Markdown(
733                mq_markdown::Node::Text(mq_markdown::Text {
734                    value: "".to_string(),
735                    position: None
736                }),
737                Some(Selector::Index(1))
738            )
739            .is_truthy()
740        );
741        assert!(!RuntimeValue::Array(Vec::new()).is_truthy());
742        assert!(!RuntimeValue::None.is_truthy());
743        assert!(RuntimeValue::NativeFunction(Ident::new("name")).is_truthy());
744        assert!(
745            RuntimeValue::Function(
746                SmallVec::new(),
747                Vec::new(),
748                Shared::new(SharedCell::new(Env::default()))
749            )
750            .is_truthy()
751        );
752        assert!(RuntimeValue::Dict(BTreeMap::default()).is_truthy());
753    }
754
755    #[test]
756    fn test_runtime_value_partial_ord() {
757        assert!(RuntimeValue::Number(Number::from(1.0)) < RuntimeValue::Number(Number::from(2.0)));
758        assert!(RuntimeValue::String(String::from("a")) < RuntimeValue::String(String::from("b")));
759        assert!(RuntimeValue::Array(Vec::new()) < RuntimeValue::Array(vec!["a".to_string().into()]));
760        assert!(
761            RuntimeValue::Markdown(
762                mq_markdown::Node::Text(mq_markdown::Text {
763                    value: "test".to_string(),
764                    position: None
765                }),
766                None
767            ) < RuntimeValue::Markdown(
768                mq_markdown::Node::Text(mq_markdown::Text {
769                    value: "test2".to_string(),
770                    position: None
771                }),
772                None
773            )
774        );
775        assert!(RuntimeValue::Boolean(false) < RuntimeValue::Boolean(true));
776        assert!(
777            RuntimeValue::Function(
778                SmallVec::new(),
779                Vec::new(),
780                Shared::new(SharedCell::new(Env::default()))
781            ) < RuntimeValue::Function(
782                smallvec![Param::new(IdentWithToken::new("test"))],
783                Vec::new(),
784                Shared::new(SharedCell::new(Env::default()))
785            )
786        );
787    }
788
789    #[test]
790    fn test_runtime_value_len() {
791        assert_eq!(RuntimeValue::Number(Number::from(42.0)).len(), 42);
792        assert_eq!(RuntimeValue::String(String::from("test")).len(), 4);
793        assert_eq!(RuntimeValue::Boolean(true).len(), 1);
794        assert_eq!(RuntimeValue::Array(vec![RuntimeValue::None]).len(), 1);
795        assert_eq!(
796            RuntimeValue::Markdown(
797                mq_markdown::Node::Text(mq_markdown::Text {
798                    value: "a".to_string(),
799                    position: None
800                }),
801                None
802            )
803            .len(),
804            1
805        );
806        let mut map = BTreeMap::default();
807        map.insert(Ident::new("a"), RuntimeValue::String("alpha".to_string()));
808        map.insert(Ident::new("b"), RuntimeValue::String("beta".to_string()));
809        assert_eq!(RuntimeValue::Dict(map).len(), 2);
810    }
811
812    #[test]
813    fn test_runtime_value_debug_output() {
814        let array = RuntimeValue::Array(vec![
815            RuntimeValue::Number(Number::from(1.0)),
816            RuntimeValue::String("hello".to_string()),
817        ]);
818        assert_eq!(format!("{:?}", array), r#"[1, "hello"]"#);
819
820        let node = mq_markdown::Node::Text(mq_markdown::Text {
821            value: "test markdown".to_string(),
822            position: None,
823        });
824        let markdown = RuntimeValue::Markdown(node, None);
825        assert_eq!(format!("{:?}", markdown), "test markdown");
826
827        let function = RuntimeValue::Function(
828            SmallVec::new(),
829            Vec::new(),
830            Shared::new(SharedCell::new(Env::default())),
831        );
832        assert_eq!(format!("{:?}", function), "function/0");
833
834        let native_fn = RuntimeValue::NativeFunction(Ident::new("debug"));
835        assert_eq!(format!("{:?}", native_fn), "native_function");
836
837        let mut map = BTreeMap::default();
838        map.insert(Ident::new("a"), RuntimeValue::String("alpha".to_string()));
839        let map_val = RuntimeValue::Dict(map);
840        assert_eq!(format!("{:?}", map_val), r#"{"a": "alpha"}"#);
841    }
842
843    #[test]
844    fn test_runtime_value_markdown() {
845        let markdown = RuntimeValue::Markdown("test markdown".to_string().into(), None);
846        assert_eq!(markdown.markdown_node().unwrap().value(), "test markdown");
847
848        let updated = markdown.update_markdown_value("updated markdown");
849        match &updated {
850            RuntimeValue::Markdown(node, selector) => {
851                assert_eq!(node.value(), "updated markdown");
852                assert_eq!(*selector, None);
853            }
854            _ => panic!("Expected Markdown variant"),
855        }
856    }
857
858    #[test]
859    fn test_runtime_value_markdown_with_selector() {
860        let child1 = mq_markdown::Node::Text(mq_markdown::Text {
861            value: "child1".to_string(),
862            position: None,
863        });
864        let child2 = mq_markdown::Node::Text(mq_markdown::Text {
865            value: "child2".to_string(),
866            position: None,
867        });
868
869        let parent = mq_markdown::Node::Strong(mq_markdown::Strong {
870            values: vec![child1, child2],
871            position: None,
872        });
873
874        let markdown_with_selector = RuntimeValue::Markdown(parent.clone(), Some(Selector::Index(1)));
875
876        let selected = markdown_with_selector.markdown_node();
877        assert!(selected.is_some());
878        assert_eq!(selected.unwrap().value(), "child2");
879
880        let updated = markdown_with_selector.update_markdown_value("updated child");
881        match &updated {
882            RuntimeValue::Markdown(node, selector) => {
883                assert_eq!(selector, &Some(Selector::Index(1)));
884                assert_eq!(node.find_at_index(1).unwrap().value(), "updated child");
885            }
886            _ => panic!("Expected Markdown variant"),
887        }
888    }
889
890    #[test]
891    fn test_update_markdown_value_non_markdown() {
892        assert_eq!(
893            RuntimeValue::Number(Number::from(42.0)).update_markdown_value("test"),
894            RuntimeValue::NONE
895        );
896        assert_eq!(
897            RuntimeValue::String("hello".to_string()).update_markdown_value("test"),
898            RuntimeValue::NONE
899        );
900        assert_eq!(
901            RuntimeValue::Boolean(true).update_markdown_value("test"),
902            RuntimeValue::NONE
903        );
904        assert_eq!(RuntimeValue::None.update_markdown_value("test"), RuntimeValue::NONE);
905    }
906
907    #[test]
908    fn test_runtime_value_map_creation_and_equality() {
909        let mut map1_data = BTreeMap::default();
910        map1_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
911        map1_data.insert(Ident::new("b"), RuntimeValue::String("hello".to_string()));
912        let map1 = RuntimeValue::Dict(map1_data);
913
914        let mut map2_data = BTreeMap::default();
915        map2_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
916        map2_data.insert(Ident::new("b"), RuntimeValue::String("hello".to_string()));
917        let map2 = RuntimeValue::Dict(map2_data);
918
919        let mut map3_data = BTreeMap::default();
920        map3_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
921        map3_data.insert(Ident::new("c"), RuntimeValue::String("world".to_string()));
922        let map3 = RuntimeValue::Dict(map3_data);
923
924        assert_eq!(map1, map2);
925        assert_ne!(map1, map3);
926    }
927
928    #[test]
929    fn test_runtime_value_map_is_empty() {
930        let empty_map = RuntimeValue::Dict(BTreeMap::default());
931        assert!(empty_map.is_empty());
932
933        let mut map_data = BTreeMap::default();
934        map_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
935        let non_empty_map = RuntimeValue::Dict(map_data);
936        assert!(!non_empty_map.is_empty());
937    }
938
939    #[test]
940    fn test_runtime_value_map_partial_ord() {
941        let mut map1_data = BTreeMap::default();
942        map1_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
943        let map1 = RuntimeValue::Dict(map1_data);
944
945        let mut map2_data = BTreeMap::default();
946        map2_data.insert(Ident::new("b"), RuntimeValue::Number(Number::from(2.0)));
947        let map2 = RuntimeValue::Dict(map2_data);
948
949        assert_eq!(map1.partial_cmp(&map2), None);
950        assert_eq!(map2.partial_cmp(&map1), None);
951        assert_eq!(map1.partial_cmp(&map1), None);
952
953        let num_val = RuntimeValue::Number(Number::from(5.0));
954        assert_eq!(map1.partial_cmp(&num_val), None);
955        assert_eq!(num_val.partial_cmp(&map1), None);
956    }
957}