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 From<serde_json::Value> for RuntimeValue {
216    fn from(value: serde_json::Value) -> Self {
217        match value {
218            serde_json::Value::Null => RuntimeValue::NONE,
219            serde_json::Value::Bool(b) => RuntimeValue::Boolean(b),
220            serde_json::Value::Number(n) => {
221                if let Some(f) = n.as_f64() {
222                    RuntimeValue::Number(f.into())
223                } else {
224                    RuntimeValue::Number(0.into())
225                }
226            }
227            serde_json::Value::String(s) => RuntimeValue::String(s),
228            serde_json::Value::Array(arr) => RuntimeValue::Array(arr.into_iter().map(RuntimeValue::from).collect()),
229            serde_json::Value::Object(obj) => {
230                let mut map = BTreeMap::new();
231                for (k, v) in obj {
232                    map.insert(Ident::new(&k), RuntimeValue::from(v));
233                }
234                RuntimeValue::Dict(map)
235            }
236        }
237    }
238}
239
240impl PartialOrd for RuntimeValue {
241    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
242        match (self, other) {
243            (RuntimeValue::Number(a), RuntimeValue::Number(b)) => a.partial_cmp(b),
244            (RuntimeValue::Boolean(a), RuntimeValue::Boolean(b)) => a.partial_cmp(b),
245            (RuntimeValue::String(a), RuntimeValue::String(b)) => a.partial_cmp(b),
246            (RuntimeValue::Symbol(a), RuntimeValue::Symbol(b)) => a.partial_cmp(b),
247            (RuntimeValue::Array(a), RuntimeValue::Array(b)) => a.partial_cmp(b),
248            (RuntimeValue::Markdown(a, _), RuntimeValue::Markdown(b, _)) => {
249                let a = a.to_string();
250                let b = b.to_string();
251                a.to_string().partial_cmp(&b)
252            }
253            (RuntimeValue::Function(a1, b1, _), RuntimeValue::Function(a2, b2, _)) => match a1.partial_cmp(a2) {
254                Some(Ordering::Equal) => b1.partial_cmp(b2),
255                Some(Ordering::Greater) => Some(Ordering::Greater),
256                Some(Ordering::Less) => Some(Ordering::Less),
257                _ => None,
258            },
259            (RuntimeValue::Dict(_), _) => None,
260            (_, RuntimeValue::Dict(_)) => None,
261            (RuntimeValue::Module(a), RuntimeValue::Module(b)) => a.name.partial_cmp(&b.name),
262            (RuntimeValue::Ast(_), _) => None,
263            (_, RuntimeValue::Ast(_)) => None,
264            _ => None,
265        }
266    }
267}
268
269impl std::fmt::Display for RuntimeValue {
270    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
271        let value: Cow<'_, str> = match self {
272            Self::Number(n) => Cow::Owned(n.to_string()),
273            Self::Boolean(b) => Cow::Owned(b.to_string()),
274            Self::String(s) => Cow::Borrowed(s),
275            Self::Symbol(i) => Cow::Owned(format!(":{}", i)),
276            Self::Array(_) => self.string(),
277            Self::Markdown(m, ..) => Cow::Owned(m.to_string()),
278            Self::None => Cow::Borrowed(""),
279            Self::Function(params, ..) => Cow::Owned(format!("function/{}", params.len())),
280            Self::NativeFunction(_) => Cow::Borrowed("native_function"),
281            Self::Dict(_) => self.string(),
282            Self::Module(module_name) => Cow::Owned(format!(r#"module "{}""#, module_name.name)),
283            Self::Ast(node) => Cow::Owned(node.to_code()),
284        };
285        write!(f, "{}", value)
286    }
287}
288
289impl std::fmt::Debug for RuntimeValue {
290    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
291        let v: Cow<'_, str> = match self {
292            Self::None => Cow::Borrowed("None"),
293            Self::String(s) => Cow::Owned(format!("{:?}", s)),
294            Self::Array(arr) => Cow::Owned(format!("{:?}", arr)),
295            a => a.string(),
296        };
297        write!(f, "{}", v)
298    }
299}
300
301impl RuntimeValue {
302    /// An empty array constant.
303    pub const EMPTY_ARRAY: RuntimeValue = Self::Array(Vec::new());
304    /// The boolean `false` value.
305    pub const FALSE: RuntimeValue = Self::Boolean(false);
306    /// The `None` (null) value.
307    pub const NONE: RuntimeValue = Self::None;
308    /// The boolean `true` value.
309    pub const TRUE: RuntimeValue = Self::Boolean(true);
310
311    /// Creates a new empty dictionary.
312    #[inline(always)]
313    pub fn new_dict() -> RuntimeValue {
314        RuntimeValue::Dict(BTreeMap::new())
315    }
316
317    /// Returns the type name of this runtime value as a string.
318    #[inline(always)]
319    pub fn name(&self) -> &str {
320        match self {
321            RuntimeValue::Number(_) => "number",
322            RuntimeValue::Boolean(_) => "bool",
323            RuntimeValue::String(_) => "string",
324            RuntimeValue::Symbol(_) => "symbol",
325            RuntimeValue::Markdown(_, _) => "markdown",
326            RuntimeValue::Array(_) => "array",
327            RuntimeValue::None => "None",
328            RuntimeValue::Function(_, _, _) => "function",
329            RuntimeValue::NativeFunction(_) => "native_function",
330            RuntimeValue::Dict(_) => "dict",
331            RuntimeValue::Module(_) => "module",
332            RuntimeValue::Ast(_) => "ast",
333        }
334    }
335
336    /// Returns `true` if this value is `None`.
337    #[inline(always)]
338    pub fn is_none(&self) -> bool {
339        matches!(self, RuntimeValue::None)
340    }
341
342    /// Returns `true` if this value is a user-defined function.
343    #[inline(always)]
344    pub fn is_function(&self) -> bool {
345        matches!(self, RuntimeValue::Function(_, _, _))
346    }
347
348    /// Returns `true` if this value is a native (built-in) function.
349    #[inline(always)]
350    pub fn is_native_function(&self) -> bool {
351        matches!(self, RuntimeValue::NativeFunction(_))
352    }
353
354    /// Returns `true` if this value is an array.
355    #[inline(always)]
356    pub fn is_array(&self) -> bool {
357        matches!(self, RuntimeValue::Array(_))
358    }
359
360    /// Returns `true` if this value is considered empty.
361    ///
362    /// Empty values include empty arrays, empty strings, empty markdown nodes,
363    /// empty dictionaries, and `None`.
364    #[inline(always)]
365    pub fn is_empty(&self) -> bool {
366        match self {
367            RuntimeValue::Array(a) => a.is_empty(),
368            RuntimeValue::String(s) => s.is_empty(),
369            RuntimeValue::Markdown(m, _) => m.value().is_empty(),
370            RuntimeValue::Dict(m) => m.is_empty(),
371            RuntimeValue::None => true,
372            _ => false,
373        }
374    }
375
376    /// Returns `true` if this value is considered truthy in conditional contexts.
377    ///
378    /// Truthy values include non-zero numbers, non-empty strings and arrays,
379    /// `true`, functions, symbols, and modules. Falsy values include `false`,
380    /// zero, empty collections, and `None`.
381    #[inline(always)]
382    pub fn is_truthy(&self) -> bool {
383        match self {
384            RuntimeValue::Boolean(b) => *b,
385            RuntimeValue::Number(n) => n.value() != 0.0,
386            RuntimeValue::String(s) => !s.is_empty(),
387            RuntimeValue::Array(a) => !a.is_empty(),
388            RuntimeValue::Markdown(node, selector) => match selector {
389                Some(Selector::Index(i)) => node.find_at_index(*i).is_some(),
390                None => true,
391            },
392            RuntimeValue::Symbol(_)
393            | RuntimeValue::Function(_, _, _)
394            | RuntimeValue::NativeFunction(_)
395            | RuntimeValue::Dict(_) => true,
396            RuntimeValue::Module(_) => true,
397            RuntimeValue::Ast(_) => true,
398            RuntimeValue::None => false,
399        }
400    }
401
402    /// Returns the length of this value.
403    ///
404    /// For numbers, returns the value as `usize`. For strings and arrays, returns
405    /// the number of elements. For dictionaries, returns the number of entries.
406    #[inline(always)]
407    pub fn len(&self) -> usize {
408        match self {
409            RuntimeValue::Number(n) => n.value() as usize,
410            RuntimeValue::Boolean(_) => 1,
411            RuntimeValue::String(s) => s.len(),
412            RuntimeValue::Symbol(i) => i.as_str().len(),
413            RuntimeValue::Array(a) => a.len(),
414            RuntimeValue::Markdown(m, _) => m.value().len(),
415            RuntimeValue::Dict(m) => m.len(),
416            RuntimeValue::None => 0,
417            RuntimeValue::Function(..) => 0,
418            RuntimeValue::Module(m) => m.len(),
419            RuntimeValue::NativeFunction(..) => 0,
420            RuntimeValue::Ast(_) => 0,
421        }
422    }
423
424    /// Extracts the markdown node from this value, if it is a markdown value.
425    ///
426    /// If a selector is present, returns the selected child node.
427    #[inline(always)]
428    pub fn markdown_node(&self) -> Option<Node> {
429        match self {
430            RuntimeValue::Markdown(n, Some(Selector::Index(i))) => n.find_at_index(*i),
431            RuntimeValue::Markdown(n, _) => Some(n.clone()),
432            _ => None,
433        }
434    }
435
436    /// Updates the value of a markdown node, returning a new runtime value.
437    ///
438    /// If this is not a markdown value, returns `None`.
439    #[inline(always)]
440    pub fn update_markdown_value(&self, value: &str) -> RuntimeValue {
441        match self {
442            RuntimeValue::Markdown(n, Some(Selector::Index(i))) => {
443                RuntimeValue::Markdown(n.with_children_value(value, *i), Some(Selector::Index(*i)))
444            }
445            RuntimeValue::Markdown(n, selector) => RuntimeValue::Markdown(n.with_value(value), selector.clone()),
446            _ => RuntimeValue::NONE,
447        }
448    }
449
450    /// Returns the position information for a markdown node, if available.
451    #[inline(always)]
452    pub fn position(&self) -> Option<mq_markdown::Position> {
453        match self {
454            RuntimeValue::Markdown(node, _) => node.position(),
455            _ => None,
456        }
457    }
458
459    /// Sets the position information for a markdown node.
460    ///
461    /// Only affects markdown values; other value types are unaffected.
462    #[inline(always)]
463    pub fn set_position(&mut self, position: Option<mq_markdown::Position>) {
464        if let RuntimeValue::Markdown(node, _) = self {
465            node.set_position(position);
466        }
467    }
468
469    #[inline(always)]
470    fn string(&self) -> Cow<'_, str> {
471        match self {
472            Self::Number(n) => Cow::Owned(n.to_string()),
473            Self::Boolean(b) => Cow::Owned(b.to_string()),
474            Self::String(s) => Cow::Owned(format!(r#""{}""#, s)),
475            Self::Symbol(i) => Cow::Owned(format!(":{}", i)),
476            Self::Array(a) => Cow::Owned(format!(
477                "[{}]",
478                a.iter().map(|v| v.string()).collect::<Vec<Cow<str>>>().join(", ")
479            )),
480            Self::Markdown(m, ..) => Cow::Owned(m.to_string()),
481            Self::None => Cow::Borrowed(""),
482            Self::Function(f, _, _) => Cow::Owned(format!("function/{}", f.len())),
483            Self::NativeFunction(_) => Cow::Borrowed("native_function"),
484            Self::Module(m) => Cow::Owned(format!("module/{}", m.name())),
485            Self::Ast(node) => Cow::Owned(node.to_code()),
486            Self::Dict(map) => {
487                let items = map
488                    .iter()
489                    .map(|(k, v)| format!("\"{}\": {}", k, v.string()))
490                    .collect::<Vec<String>>()
491                    .join(", ");
492                Cow::Owned(format!("{{{}}}", items))
493            }
494        }
495    }
496}
497
498/// A collection of runtime values.
499///
500/// Provides utilities for working with multiple values, such as filtering
501/// and updating operations.
502#[derive(Debug, Clone, PartialEq)]
503pub struct RuntimeValues(Vec<RuntimeValue>);
504
505impl From<Vec<RuntimeValue>> for RuntimeValues {
506    fn from(values: Vec<RuntimeValue>) -> Self {
507        Self(values)
508    }
509}
510
511impl Index<usize> for RuntimeValues {
512    type Output = RuntimeValue;
513
514    fn index(&self, index: usize) -> &Self::Output {
515        &self.0[index]
516    }
517}
518
519impl IndexMut<usize> for RuntimeValues {
520    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
521        &mut self.0[index]
522    }
523}
524
525impl IntoIterator for RuntimeValues {
526    type IntoIter = std::vec::IntoIter<RuntimeValue>;
527    type Item = RuntimeValue;
528
529    fn into_iter(self) -> Self::IntoIter {
530        self.0.into_iter()
531    }
532}
533
534impl RuntimeValues {
535    /// Returns a compacted version of this collection, removing `None` and empty values.
536    pub fn compact(&self) -> Vec<RuntimeValue> {
537        self.0
538            .iter()
539            .filter(|v| !v.is_none() && !v.is_empty())
540            .cloned()
541            .collect::<Vec<_>>()
542    }
543
544    /// Returns a reference to the underlying vector of values.
545    pub fn values(&self) -> &Vec<RuntimeValue> {
546        &self.0
547    }
548
549    /// Returns the number of values in this collection.
550    pub fn len(&self) -> usize {
551        self.0.len()
552    }
553
554    /// Returns `true` if this collection contains no values.
555    pub fn is_empty(&self) -> bool {
556        self.0.len() == 0
557    }
558
559    /// Updates this collection with values from another collection.
560    ///
561    /// Pairs corresponding elements from both collections and applies special
562    /// update logic for markdown nodes.
563    pub fn update_with(&self, other: Self) -> Self {
564        self.0
565            .clone()
566            .into_iter()
567            .zip(other)
568            .map(|(current_value, mut updated_value)| {
569                updated_value.set_position(current_value.position());
570
571                if let RuntimeValue::Markdown(node, _) = &current_value {
572                    match &updated_value {
573                        RuntimeValue::None
574                        | RuntimeValue::Function(_, _, _)
575                        | RuntimeValue::Module(_)
576                        | RuntimeValue::Ast(_)
577                        | RuntimeValue::NativeFunction(_) => current_value.clone(),
578                        RuntimeValue::Markdown(node, _) if node.is_empty() => current_value.clone(),
579                        RuntimeValue::Markdown(node, _) => {
580                            if node.is_fragment() {
581                                if let RuntimeValue::Markdown(mut current_node, selector) = current_value {
582                                    current_node.apply_fragment(node.clone());
583                                    RuntimeValue::Markdown(current_node, selector)
584                                } else {
585                                    updated_value
586                                }
587                            } else {
588                                updated_value
589                            }
590                        }
591                        RuntimeValue::String(s) => RuntimeValue::Markdown(node.clone().with_value(s), None),
592                        RuntimeValue::Symbol(i) => RuntimeValue::Markdown(node.clone().with_value(&i.as_str()), None),
593                        RuntimeValue::Boolean(b) => {
594                            RuntimeValue::Markdown(node.clone().with_value(b.to_string().as_str()), None)
595                        }
596                        RuntimeValue::Number(n) => {
597                            RuntimeValue::Markdown(node.clone().with_value(n.to_string().as_str()), None)
598                        }
599                        RuntimeValue::Array(array) => RuntimeValue::Array(
600                            array
601                                .iter()
602                                .filter_map(|o| {
603                                    if o.is_none() {
604                                        None
605                                    } else {
606                                        Some(RuntimeValue::Markdown(
607                                            node.clone().with_value(o.to_string().as_str()),
608                                            None,
609                                        ))
610                                    }
611                                })
612                                .collect::<Vec<_>>(),
613                        ),
614                        RuntimeValue::Dict(map) => {
615                            let mut new_dict = BTreeMap::new();
616                            for (k, v) in map {
617                                if !v.is_none() && !v.is_empty() {
618                                    new_dict.insert(
619                                        *k,
620                                        RuntimeValue::Markdown(node.clone().with_value(v.to_string().as_str()), None),
621                                    );
622                                }
623                            }
624                            RuntimeValue::Dict(new_dict)
625                        }
626                    }
627                } else {
628                    updated_value
629                }
630            })
631            .collect::<Vec<_>>()
632            .into()
633    }
634}
635
636#[cfg(test)]
637mod tests {
638    use crate::ast::node::{IdentWithToken, Param};
639    use rstest::rstest;
640    use smallvec::{SmallVec, smallvec};
641
642    use super::*;
643
644    #[test]
645    fn test_runtime_value_from() {
646        assert_eq!(RuntimeValue::from(true), RuntimeValue::Boolean(true));
647        assert_eq!(RuntimeValue::from(false), RuntimeValue::Boolean(false));
648        assert_eq!(
649            RuntimeValue::from(String::from("test")),
650            RuntimeValue::String(String::from("test"))
651        );
652        assert_eq!(
653            RuntimeValue::from(Number::from(42.0)),
654            RuntimeValue::Number(Number::from(42.0))
655        );
656    }
657
658    #[rstest]
659    #[case(RuntimeValue::Number(Number::from(42.0)), "42")]
660    #[case(RuntimeValue::Boolean(true), "true")]
661    #[case(RuntimeValue::Boolean(false), "false")]
662    #[case(RuntimeValue::String("hello".to_string()), r#""hello""#)]
663    #[case(RuntimeValue::None, "")]
664    #[case(RuntimeValue::Array(vec![
665            RuntimeValue::Number(Number::from(1.0)),
666            RuntimeValue::String("test".to_string())
667        ]), r#"[1, "test"]"#)]
668    #[case(RuntimeValue::Dict({
669            let mut map = BTreeMap::new();
670            map.insert(Ident::new("key1"), RuntimeValue::String("value1".to_string()));
671            map.insert(Ident::new("key2"), RuntimeValue::Number(Number::from(42.0)));
672            map
673        }), r#"{"key1": "value1", "key2": 42}"#)]
674    fn test_string_method(#[case] value: RuntimeValue, #[case] expected: &str) {
675        assert_eq!(value.string(), expected);
676    }
677
678    #[test]
679    fn test_runtime_value_display() {
680        assert_eq!(format!("{}", RuntimeValue::Boolean(true)), "true");
681        assert_eq!(format!("{}", RuntimeValue::Number(Number::from(42.0))), "42");
682        assert_eq!(format!("{}", RuntimeValue::String(String::from("test"))), "test");
683        assert_eq!(format!("{}", RuntimeValue::None), "");
684        let map_val = RuntimeValue::Dict(BTreeMap::default());
685        assert_eq!(format!("{}", map_val), "{}");
686    }
687
688    #[test]
689    fn test_runtime_value_debug() {
690        assert_eq!(format!("{:?}", RuntimeValue::Boolean(true)), "true");
691        assert_eq!(format!("{:?}", RuntimeValue::Number(Number::from(42.0))), "42");
692        assert_eq!(format!("{:?}", RuntimeValue::String(String::from("test"))), "\"test\"");
693        assert_eq!(format!("{:?}", RuntimeValue::None), "None");
694
695        let mut map = BTreeMap::default();
696        map.insert(Ident::new("name"), RuntimeValue::String("MQ".to_string()));
697        map.insert(Ident::new("version"), RuntimeValue::Number(Number::from(1.0)));
698        let map_val = RuntimeValue::Dict(map);
699        let debug_str = format!("{:?}", map_val);
700        assert!(debug_str == r#"{"name": "MQ", "version": 1}"# || debug_str == r#"{"version": 1, "name": "MQ"}"#);
701    }
702
703    #[test]
704    fn test_runtime_value_name() {
705        assert_eq!(RuntimeValue::Boolean(true).name(), "bool");
706        assert_eq!(RuntimeValue::Number(Number::from(42.0)).name(), "number");
707        assert_eq!(RuntimeValue::String(String::from("test")).name(), "string");
708        assert_eq!(RuntimeValue::None.name(), "None");
709        assert_eq!(
710            RuntimeValue::Function(
711                SmallVec::new(),
712                Vec::new(),
713                Shared::new(SharedCell::new(Env::default()))
714            )
715            .name(),
716            "function"
717        );
718        assert_eq!(
719            RuntimeValue::NativeFunction(Ident::new("name")).name(),
720            "native_function"
721        );
722        assert_eq!(
723            RuntimeValue::Markdown(
724                mq_markdown::Node::Text(mq_markdown::Text {
725                    value: "".to_string(),
726                    position: None
727                }),
728                None
729            )
730            .name(),
731            "markdown"
732        );
733        assert_eq!(RuntimeValue::Dict(BTreeMap::default()).name(), "dict");
734    }
735
736    #[test]
737    fn test_runtime_value_is_true() {
738        assert!(RuntimeValue::Boolean(true).is_truthy());
739        assert!(!RuntimeValue::Boolean(false).is_truthy());
740        assert!(RuntimeValue::Number(Number::from(42.0)).is_truthy());
741        assert!(!RuntimeValue::Number(Number::from(0.0)).is_truthy());
742        assert!(RuntimeValue::String(String::from("test")).is_truthy());
743        assert!(!RuntimeValue::String(String::from("")).is_truthy());
744        assert!(RuntimeValue::Array(vec!["".to_string().into()]).is_truthy());
745        assert!(!RuntimeValue::Array(Vec::new()).is_truthy());
746        assert!(
747            RuntimeValue::Markdown(
748                mq_markdown::Node::Text(mq_markdown::Text {
749                    value: "".to_string(),
750                    position: None
751                }),
752                None
753            )
754            .is_truthy()
755        );
756        assert!(
757            !RuntimeValue::Markdown(
758                mq_markdown::Node::Text(mq_markdown::Text {
759                    value: "".to_string(),
760                    position: None
761                }),
762                Some(Selector::Index(1))
763            )
764            .is_truthy()
765        );
766        assert!(!RuntimeValue::Array(Vec::new()).is_truthy());
767        assert!(!RuntimeValue::None.is_truthy());
768        assert!(RuntimeValue::NativeFunction(Ident::new("name")).is_truthy());
769        assert!(
770            RuntimeValue::Function(
771                SmallVec::new(),
772                Vec::new(),
773                Shared::new(SharedCell::new(Env::default()))
774            )
775            .is_truthy()
776        );
777        assert!(RuntimeValue::Dict(BTreeMap::default()).is_truthy());
778    }
779
780    #[test]
781    fn test_runtime_value_partial_ord() {
782        assert!(RuntimeValue::Number(Number::from(1.0)) < RuntimeValue::Number(Number::from(2.0)));
783        assert!(RuntimeValue::String(String::from("a")) < RuntimeValue::String(String::from("b")));
784        assert!(RuntimeValue::Array(Vec::new()) < RuntimeValue::Array(vec!["a".to_string().into()]));
785        assert!(
786            RuntimeValue::Markdown(
787                mq_markdown::Node::Text(mq_markdown::Text {
788                    value: "test".to_string(),
789                    position: None
790                }),
791                None
792            ) < RuntimeValue::Markdown(
793                mq_markdown::Node::Text(mq_markdown::Text {
794                    value: "test2".to_string(),
795                    position: None
796                }),
797                None
798            )
799        );
800        assert!(RuntimeValue::Boolean(false) < RuntimeValue::Boolean(true));
801        assert!(
802            RuntimeValue::Function(
803                SmallVec::new(),
804                Vec::new(),
805                Shared::new(SharedCell::new(Env::default()))
806            ) < RuntimeValue::Function(
807                smallvec![Param::new(IdentWithToken::new("test"))],
808                Vec::new(),
809                Shared::new(SharedCell::new(Env::default()))
810            )
811        );
812    }
813
814    #[test]
815    fn test_runtime_value_len() {
816        assert_eq!(RuntimeValue::Number(Number::from(42.0)).len(), 42);
817        assert_eq!(RuntimeValue::String(String::from("test")).len(), 4);
818        assert_eq!(RuntimeValue::Boolean(true).len(), 1);
819        assert_eq!(RuntimeValue::Array(vec![RuntimeValue::None]).len(), 1);
820        assert_eq!(
821            RuntimeValue::Markdown(
822                mq_markdown::Node::Text(mq_markdown::Text {
823                    value: "a".to_string(),
824                    position: None
825                }),
826                None
827            )
828            .len(),
829            1
830        );
831        let mut map = BTreeMap::default();
832        map.insert(Ident::new("a"), RuntimeValue::String("alpha".to_string()));
833        map.insert(Ident::new("b"), RuntimeValue::String("beta".to_string()));
834        assert_eq!(RuntimeValue::Dict(map).len(), 2);
835    }
836
837    #[test]
838    fn test_runtime_value_debug_output() {
839        let array = RuntimeValue::Array(vec![
840            RuntimeValue::Number(Number::from(1.0)),
841            RuntimeValue::String("hello".to_string()),
842        ]);
843        assert_eq!(format!("{:?}", array), r#"[1, "hello"]"#);
844
845        let node = mq_markdown::Node::Text(mq_markdown::Text {
846            value: "test markdown".to_string(),
847            position: None,
848        });
849        let markdown = RuntimeValue::Markdown(node, None);
850        assert_eq!(format!("{:?}", markdown), "test markdown");
851
852        let function = RuntimeValue::Function(
853            SmallVec::new(),
854            Vec::new(),
855            Shared::new(SharedCell::new(Env::default())),
856        );
857        assert_eq!(format!("{:?}", function), "function/0");
858
859        let native_fn = RuntimeValue::NativeFunction(Ident::new("debug"));
860        assert_eq!(format!("{:?}", native_fn), "native_function");
861
862        let mut map = BTreeMap::default();
863        map.insert(Ident::new("a"), RuntimeValue::String("alpha".to_string()));
864        let map_val = RuntimeValue::Dict(map);
865        assert_eq!(format!("{:?}", map_val), r#"{"a": "alpha"}"#);
866    }
867
868    #[test]
869    fn test_runtime_value_markdown() {
870        let markdown = RuntimeValue::Markdown("test markdown".to_string().into(), None);
871        assert_eq!(markdown.markdown_node().unwrap().value(), "test markdown");
872
873        let updated = markdown.update_markdown_value("updated markdown");
874        match &updated {
875            RuntimeValue::Markdown(node, selector) => {
876                assert_eq!(node.value(), "updated markdown");
877                assert_eq!(*selector, None);
878            }
879            _ => panic!("Expected Markdown variant"),
880        }
881    }
882
883    #[test]
884    fn test_runtime_value_markdown_with_selector() {
885        let child1 = mq_markdown::Node::Text(mq_markdown::Text {
886            value: "child1".to_string(),
887            position: None,
888        });
889        let child2 = mq_markdown::Node::Text(mq_markdown::Text {
890            value: "child2".to_string(),
891            position: None,
892        });
893
894        let parent = mq_markdown::Node::Strong(mq_markdown::Strong {
895            values: vec![child1, child2],
896            position: None,
897        });
898
899        let markdown_with_selector = RuntimeValue::Markdown(parent.clone(), Some(Selector::Index(1)));
900
901        let selected = markdown_with_selector.markdown_node();
902        assert!(selected.is_some());
903        assert_eq!(selected.unwrap().value(), "child2");
904
905        let updated = markdown_with_selector.update_markdown_value("updated child");
906        match &updated {
907            RuntimeValue::Markdown(node, selector) => {
908                assert_eq!(selector, &Some(Selector::Index(1)));
909                assert_eq!(node.find_at_index(1).unwrap().value(), "updated child");
910            }
911            _ => panic!("Expected Markdown variant"),
912        }
913    }
914
915    #[test]
916    fn test_update_markdown_value_non_markdown() {
917        assert_eq!(
918            RuntimeValue::Number(Number::from(42.0)).update_markdown_value("test"),
919            RuntimeValue::NONE
920        );
921        assert_eq!(
922            RuntimeValue::String("hello".to_string()).update_markdown_value("test"),
923            RuntimeValue::NONE
924        );
925        assert_eq!(
926            RuntimeValue::Boolean(true).update_markdown_value("test"),
927            RuntimeValue::NONE
928        );
929        assert_eq!(RuntimeValue::None.update_markdown_value("test"), RuntimeValue::NONE);
930    }
931
932    #[test]
933    fn test_runtime_value_map_creation_and_equality() {
934        let mut map1_data = BTreeMap::default();
935        map1_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
936        map1_data.insert(Ident::new("b"), RuntimeValue::String("hello".to_string()));
937        let map1 = RuntimeValue::Dict(map1_data);
938
939        let mut map2_data = BTreeMap::default();
940        map2_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
941        map2_data.insert(Ident::new("b"), RuntimeValue::String("hello".to_string()));
942        let map2 = RuntimeValue::Dict(map2_data);
943
944        let mut map3_data = BTreeMap::default();
945        map3_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
946        map3_data.insert(Ident::new("c"), RuntimeValue::String("world".to_string()));
947        let map3 = RuntimeValue::Dict(map3_data);
948
949        assert_eq!(map1, map2);
950        assert_ne!(map1, map3);
951    }
952
953    #[test]
954    fn test_runtime_value_map_is_empty() {
955        let empty_map = RuntimeValue::Dict(BTreeMap::default());
956        assert!(empty_map.is_empty());
957
958        let mut map_data = BTreeMap::default();
959        map_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
960        let non_empty_map = RuntimeValue::Dict(map_data);
961        assert!(!non_empty_map.is_empty());
962    }
963
964    #[test]
965    fn test_runtime_value_map_partial_ord() {
966        let mut map1_data = BTreeMap::default();
967        map1_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
968        let map1 = RuntimeValue::Dict(map1_data);
969
970        let mut map2_data = BTreeMap::default();
971        map2_data.insert(Ident::new("b"), RuntimeValue::Number(Number::from(2.0)));
972        let map2 = RuntimeValue::Dict(map2_data);
973
974        assert_eq!(map1.partial_cmp(&map2), None);
975        assert_eq!(map2.partial_cmp(&map1), None);
976        assert_eq!(map1.partial_cmp(&map1), None);
977
978        let num_val = RuntimeValue::Number(Number::from(5.0));
979        assert_eq!(map1.partial_cmp(&num_val), None);
980        assert_eq!(num_val.partial_cmp(&map1), None);
981    }
982}