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#[derive(Debug, Clone, PartialEq)]
14pub enum Selector {
15 Index(usize),
17}
18
19#[derive(Clone, Debug)]
21pub struct ModuleEnv {
22 name: SmolStr,
23 exports: Shared<SharedCell<Env>>,
24}
25
26impl ModuleEnv {
27 pub fn new(name: &str, exports: Shared<SharedCell<Env>>) -> Self {
29 Self {
30 name: SmolStr::new(name),
31 exports,
32 }
33 }
34
35 pub fn name(&self) -> &str {
37 &self.name
38 }
39
40 pub fn exports(&self) -> &Shared<SharedCell<Env>> {
42 &self.exports
43 }
44
45 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#[derive(Clone, Default)]
81pub enum RuntimeValue {
82 Number(Number),
84 Boolean(bool),
86 String(String),
88 Symbol(Ident),
90 Array(Vec<RuntimeValue>),
92 Markdown(Node, Option<Selector>),
94 Function(AstParams, Program, Shared<SharedCell<Env>>),
96 NativeFunction(Ident),
98 Dict(BTreeMap<Ident, RuntimeValue>),
100 Module(ModuleEnv),
102 Ast(Shared<ast::node::Node>),
104 #[default]
106 None,
107}
108
109impl 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<yaml_rust2::Yaml> for RuntimeValue {
216 fn from(value: yaml_rust2::Yaml) -> Self {
217 match value {
218 yaml_rust2::Yaml::Null | yaml_rust2::Yaml::BadValue => RuntimeValue::NONE,
219 yaml_rust2::Yaml::Boolean(b) => RuntimeValue::Boolean(b),
220 yaml_rust2::Yaml::Integer(i) => RuntimeValue::Number((i as f64).into()),
221 yaml_rust2::Yaml::Real(s) => s
222 .parse::<f64>()
223 .map(|f| RuntimeValue::Number(f.into()))
224 .unwrap_or(RuntimeValue::NONE),
225 yaml_rust2::Yaml::String(s) => RuntimeValue::String(s),
226 yaml_rust2::Yaml::Array(arr) => RuntimeValue::Array(arr.into_iter().map(RuntimeValue::from).collect()),
227 yaml_rust2::Yaml::Hash(map) => {
228 let mut btree = BTreeMap::new();
229 for (k, v) in map {
230 let key = match k {
231 yaml_rust2::Yaml::String(s) => s,
232 other => format!("{other:?}"),
233 };
234 btree.insert(Ident::new(&key), RuntimeValue::from(v));
235 }
236 RuntimeValue::Dict(btree)
237 }
238 yaml_rust2::Yaml::Alias(_) => RuntimeValue::NONE,
239 }
240 }
241}
242
243impl From<serde_json::Value> for RuntimeValue {
244 fn from(value: serde_json::Value) -> Self {
245 match value {
246 serde_json::Value::Null => RuntimeValue::NONE,
247 serde_json::Value::Bool(b) => RuntimeValue::Boolean(b),
248 serde_json::Value::Number(n) => {
249 if let Some(f) = n.as_f64() {
250 RuntimeValue::Number(f.into())
251 } else {
252 RuntimeValue::Number(0.into())
253 }
254 }
255 serde_json::Value::String(s) => RuntimeValue::String(s),
256 serde_json::Value::Array(arr) => RuntimeValue::Array(arr.into_iter().map(RuntimeValue::from).collect()),
257 serde_json::Value::Object(obj) => {
258 let mut map = BTreeMap::new();
259 for (k, v) in obj {
260 map.insert(Ident::new(&k), RuntimeValue::from(v));
261 }
262 RuntimeValue::Dict(map)
263 }
264 }
265 }
266}
267
268impl PartialOrd for RuntimeValue {
269 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
270 match (self, other) {
271 (RuntimeValue::Number(a), RuntimeValue::Number(b)) => a.partial_cmp(b),
272 (RuntimeValue::Boolean(a), RuntimeValue::Boolean(b)) => a.partial_cmp(b),
273 (RuntimeValue::String(a), RuntimeValue::String(b)) => a.partial_cmp(b),
274 (RuntimeValue::Symbol(a), RuntimeValue::Symbol(b)) => a.partial_cmp(b),
275 (RuntimeValue::Array(a), RuntimeValue::Array(b)) => a.partial_cmp(b),
276 (RuntimeValue::Markdown(a, _), RuntimeValue::Markdown(b, _)) => {
277 let a = a.to_string();
278 let b = b.to_string();
279 a.to_string().partial_cmp(&b)
280 }
281 (RuntimeValue::Function(a1, b1, _), RuntimeValue::Function(a2, b2, _)) => match a1.partial_cmp(a2) {
282 Some(Ordering::Equal) => b1.partial_cmp(b2),
283 Some(Ordering::Greater) => Some(Ordering::Greater),
284 Some(Ordering::Less) => Some(Ordering::Less),
285 _ => None,
286 },
287 (RuntimeValue::Dict(_), _) => None,
288 (_, RuntimeValue::Dict(_)) => None,
289 (RuntimeValue::Module(a), RuntimeValue::Module(b)) => a.name.partial_cmp(&b.name),
290 (RuntimeValue::Ast(_), _) => None,
291 (_, RuntimeValue::Ast(_)) => None,
292 _ => None,
293 }
294 }
295}
296
297impl std::fmt::Display for RuntimeValue {
298 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
299 let value: Cow<'_, str> = match self {
300 Self::Number(n) => Cow::Owned(n.to_string()),
301 Self::Boolean(b) => Cow::Owned(b.to_string()),
302 Self::String(s) => Cow::Borrowed(s),
303 Self::Symbol(i) => Cow::Owned(format!(":{}", i)),
304 Self::Array(_) => self.string(),
305 Self::Markdown(m, ..) => Cow::Owned(m.to_string()),
306 Self::None => Cow::Borrowed(""),
307 Self::Function(params, ..) => Cow::Owned(format!("function/{}", params.len())),
308 Self::NativeFunction(_) => Cow::Borrowed("native_function"),
309 Self::Dict(_) => self.string(),
310 Self::Module(module_name) => Cow::Owned(format!(r#"module "{}""#, module_name.name)),
311 Self::Ast(node) => Cow::Owned(node.to_code()),
312 };
313 write!(f, "{}", value)
314 }
315}
316
317impl std::fmt::Debug for RuntimeValue {
318 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
319 let v: Cow<'_, str> = match self {
320 Self::None => Cow::Borrowed("None"),
321 Self::String(s) => Cow::Owned(format!("{:?}", s)),
322 Self::Array(arr) => Cow::Owned(format!("{:?}", arr)),
323 a => a.string(),
324 };
325 write!(f, "{}", v)
326 }
327}
328
329impl RuntimeValue {
330 pub const EMPTY_ARRAY: RuntimeValue = Self::Array(Vec::new());
332 pub const FALSE: RuntimeValue = Self::Boolean(false);
334 pub const NONE: RuntimeValue = Self::None;
336 pub const TRUE: RuntimeValue = Self::Boolean(true);
338
339 #[inline(always)]
341 pub fn new_dict() -> RuntimeValue {
342 RuntimeValue::Dict(BTreeMap::new())
343 }
344
345 #[inline(always)]
347 pub fn name(&self) -> &str {
348 match self {
349 RuntimeValue::Number(_) => "number",
350 RuntimeValue::Boolean(_) => "bool",
351 RuntimeValue::String(_) => "string",
352 RuntimeValue::Symbol(_) => "symbol",
353 RuntimeValue::Markdown(_, _) => "markdown",
354 RuntimeValue::Array(_) => "array",
355 RuntimeValue::None => "None",
356 RuntimeValue::Function(_, _, _) => "function",
357 RuntimeValue::NativeFunction(_) => "native_function",
358 RuntimeValue::Dict(_) => "dict",
359 RuntimeValue::Module(_) => "module",
360 RuntimeValue::Ast(_) => "ast",
361 }
362 }
363
364 #[inline(always)]
366 pub fn is_none(&self) -> bool {
367 matches!(self, RuntimeValue::None)
368 }
369
370 #[inline(always)]
372 pub fn is_function(&self) -> bool {
373 matches!(self, RuntimeValue::Function(_, _, _))
374 }
375
376 #[inline(always)]
378 pub fn is_native_function(&self) -> bool {
379 matches!(self, RuntimeValue::NativeFunction(_))
380 }
381
382 #[inline(always)]
384 pub fn is_array(&self) -> bool {
385 matches!(self, RuntimeValue::Array(_))
386 }
387
388 #[inline(always)]
390 pub fn is_dict(&self) -> bool {
391 matches!(self, RuntimeValue::Dict(_))
392 }
393
394 #[inline(always)]
399 pub fn is_empty(&self) -> bool {
400 match self {
401 RuntimeValue::Array(a) => a.is_empty(),
402 RuntimeValue::String(s) => s.is_empty(),
403 RuntimeValue::Markdown(m, _) => m.value().is_empty(),
404 RuntimeValue::Dict(m) => m.is_empty(),
405 RuntimeValue::None => true,
406 _ => false,
407 }
408 }
409
410 #[inline(always)]
416 pub fn is_truthy(&self) -> bool {
417 match self {
418 RuntimeValue::Boolean(b) => *b,
419 RuntimeValue::Number(n) => n.value() != 0.0,
420 RuntimeValue::String(s) => !s.is_empty(),
421 RuntimeValue::Array(a) => !a.is_empty(),
422 RuntimeValue::Markdown(node, selector) => match selector {
423 Some(Selector::Index(i)) => node.find_at_index(*i).is_some(),
424 None => true,
425 },
426 RuntimeValue::Symbol(_)
427 | RuntimeValue::Function(_, _, _)
428 | RuntimeValue::NativeFunction(_)
429 | RuntimeValue::Dict(_) => true,
430 RuntimeValue::Module(_) => true,
431 RuntimeValue::Ast(_) => true,
432 RuntimeValue::None => false,
433 }
434 }
435
436 #[inline(always)]
441 pub fn len(&self) -> usize {
442 match self {
443 RuntimeValue::Number(n) => n.value() as usize,
444 RuntimeValue::Boolean(_) => 1,
445 RuntimeValue::String(s) => s.len(),
446 RuntimeValue::Symbol(i) => i.as_str().len(),
447 RuntimeValue::Array(a) => a.len(),
448 RuntimeValue::Markdown(m, _) => m.value().len(),
449 RuntimeValue::Dict(m) => m.len(),
450 RuntimeValue::None => 0,
451 RuntimeValue::Function(..) => 0,
452 RuntimeValue::Module(m) => m.len(),
453 RuntimeValue::NativeFunction(..) => 0,
454 RuntimeValue::Ast(_) => 0,
455 }
456 }
457
458 #[inline(always)]
462 pub fn markdown_node(&self) -> Option<Node> {
463 match self {
464 RuntimeValue::Markdown(n, Some(Selector::Index(i))) => n.find_at_index(*i),
465 RuntimeValue::Markdown(n, _) => Some(n.clone()),
466 _ => None,
467 }
468 }
469
470 #[inline(always)]
474 pub fn update_markdown_value(&self, value: &str) -> RuntimeValue {
475 match self {
476 RuntimeValue::Markdown(n, Some(Selector::Index(i))) => {
477 RuntimeValue::Markdown(n.with_children_value(value, *i), Some(Selector::Index(*i)))
478 }
479 RuntimeValue::Markdown(n, selector) => RuntimeValue::Markdown(n.with_value(value), selector.clone()),
480 _ => RuntimeValue::NONE,
481 }
482 }
483
484 #[inline(always)]
486 pub fn position(&self) -> Option<mq_markdown::Position> {
487 match self {
488 RuntimeValue::Markdown(node, _) => node.position(),
489 _ => None,
490 }
491 }
492
493 #[inline(always)]
497 pub fn set_position(&mut self, position: Option<mq_markdown::Position>) {
498 if let RuntimeValue::Markdown(node, _) = self {
499 node.set_position(position);
500 }
501 }
502
503 #[inline(always)]
504 fn string(&self) -> Cow<'_, str> {
505 match self {
506 Self::Number(n) => Cow::Owned(n.to_string()),
507 Self::Boolean(b) => Cow::Owned(b.to_string()),
508 Self::String(s) => Cow::Owned(format!(r#""{}""#, s)),
509 Self::Symbol(i) => Cow::Owned(format!(":{}", i)),
510 Self::Array(a) => Cow::Owned(format!(
511 "[{}]",
512 a.iter().map(|v| v.string()).collect::<Vec<Cow<str>>>().join(", ")
513 )),
514 Self::Markdown(m, ..) => Cow::Owned(m.to_string()),
515 Self::None => Cow::Borrowed(""),
516 Self::Function(f, _, _) => Cow::Owned(format!("function/{}", f.len())),
517 Self::NativeFunction(_) => Cow::Borrowed("native_function"),
518 Self::Module(m) => Cow::Owned(format!("module/{}", m.name())),
519 Self::Ast(node) => Cow::Owned(node.to_code()),
520 Self::Dict(map) => {
521 let items = map
522 .iter()
523 .map(|(k, v)| format!("\"{}\": {}", k, v.string()))
524 .collect::<Vec<String>>()
525 .join(", ");
526 Cow::Owned(format!("{{{}}}", items))
527 }
528 }
529 }
530
531 pub fn negated(&self) -> Self {
533 match self {
534 RuntimeValue::Boolean(b) => RuntimeValue::Boolean(!b),
535 RuntimeValue::Number(n) => RuntimeValue::Number((-n.value()).into()),
536 RuntimeValue::String(s) => RuntimeValue::String(s.chars().rev().collect()),
537 _ => self.clone(),
538 }
539 }
540}
541
542#[derive(Debug, Clone, PartialEq)]
547pub struct RuntimeValues(Vec<RuntimeValue>);
548
549impl From<Vec<RuntimeValue>> for RuntimeValues {
550 fn from(values: Vec<RuntimeValue>) -> Self {
551 Self(values)
552 }
553}
554
555impl Index<usize> for RuntimeValues {
556 type Output = RuntimeValue;
557
558 fn index(&self, index: usize) -> &Self::Output {
559 &self.0[index]
560 }
561}
562
563impl IndexMut<usize> for RuntimeValues {
564 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
565 &mut self.0[index]
566 }
567}
568
569impl IntoIterator for RuntimeValues {
570 type IntoIter = std::vec::IntoIter<RuntimeValue>;
571 type Item = RuntimeValue;
572
573 fn into_iter(self) -> Self::IntoIter {
574 self.0.into_iter()
575 }
576}
577
578impl RuntimeValues {
579 pub fn compact(&self) -> Vec<RuntimeValue> {
581 self.0
582 .iter()
583 .filter(|v| !v.is_none() && !v.is_empty())
584 .cloned()
585 .collect::<Vec<_>>()
586 }
587
588 pub fn values(&self) -> &Vec<RuntimeValue> {
590 &self.0
591 }
592
593 pub fn len(&self) -> usize {
595 self.0.len()
596 }
597
598 pub fn is_empty(&self) -> bool {
600 self.0.len() == 0
601 }
602
603 pub fn update_with(&self, other: Self) -> Self {
608 self.0
609 .clone()
610 .into_iter()
611 .zip(other)
612 .map(|(current_value, mut updated_value)| {
613 updated_value.set_position(current_value.position());
614
615 if let RuntimeValue::Markdown(node, _) = ¤t_value {
616 match &updated_value {
617 RuntimeValue::None
618 | RuntimeValue::Function(_, _, _)
619 | RuntimeValue::Module(_)
620 | RuntimeValue::Ast(_)
621 | RuntimeValue::NativeFunction(_) => current_value.clone(),
622 RuntimeValue::Markdown(node, _) if node.is_empty() => current_value.clone(),
623 RuntimeValue::Markdown(node, _) => {
624 if node.is_fragment() {
625 if let RuntimeValue::Markdown(mut current_node, selector) = current_value {
626 current_node.apply_fragment(node.clone());
627 RuntimeValue::Markdown(current_node, selector)
628 } else {
629 updated_value
630 }
631 } else {
632 updated_value
633 }
634 }
635 RuntimeValue::String(s) => RuntimeValue::Markdown(node.clone().with_value(s), None),
636 RuntimeValue::Symbol(i) => RuntimeValue::Markdown(node.clone().with_value(&i.as_str()), None),
637 RuntimeValue::Boolean(b) => {
638 RuntimeValue::Markdown(node.clone().with_value(b.to_string().as_str()), None)
639 }
640 RuntimeValue::Number(n) => {
641 RuntimeValue::Markdown(node.clone().with_value(n.to_string().as_str()), None)
642 }
643 RuntimeValue::Array(array) => RuntimeValue::Array(
644 array
645 .iter()
646 .filter_map(|o| {
647 if o.is_none() {
648 None
649 } else {
650 Some(RuntimeValue::Markdown(
651 node.clone().with_value(o.to_string().as_str()),
652 None,
653 ))
654 }
655 })
656 .collect::<Vec<_>>(),
657 ),
658 RuntimeValue::Dict(map) => {
659 let mut new_dict = BTreeMap::new();
660 for (k, v) in map {
661 if !v.is_none() && !v.is_empty() {
662 new_dict.insert(
663 *k,
664 RuntimeValue::Markdown(node.clone().with_value(v.to_string().as_str()), None),
665 );
666 }
667 }
668 RuntimeValue::Dict(new_dict)
669 }
670 }
671 } else {
672 updated_value
673 }
674 })
675 .collect::<Vec<_>>()
676 .into()
677 }
678}
679
680#[cfg(test)]
681mod tests {
682 use crate::ast::node::{IdentWithToken, Param};
683 use rstest::rstest;
684 use smallvec::{SmallVec, smallvec};
685
686 use super::*;
687
688 #[test]
689 fn test_runtime_value_from() {
690 assert_eq!(RuntimeValue::from(true), RuntimeValue::Boolean(true));
691 assert_eq!(RuntimeValue::from(false), RuntimeValue::Boolean(false));
692 assert_eq!(
693 RuntimeValue::from(String::from("test")),
694 RuntimeValue::String(String::from("test"))
695 );
696 assert_eq!(
697 RuntimeValue::from(Number::from(42.0)),
698 RuntimeValue::Number(Number::from(42.0))
699 );
700 }
701
702 #[rstest]
703 #[case(RuntimeValue::Number(Number::from(42.0)), "42")]
704 #[case(RuntimeValue::Boolean(true), "true")]
705 #[case(RuntimeValue::Boolean(false), "false")]
706 #[case(RuntimeValue::String("hello".to_string()), r#""hello""#)]
707 #[case(RuntimeValue::None, "")]
708 #[case(RuntimeValue::Array(vec![
709 RuntimeValue::Number(Number::from(1.0)),
710 RuntimeValue::String("test".to_string())
711 ]), r#"[1, "test"]"#)]
712 #[case(RuntimeValue::Dict({
713 let mut map = BTreeMap::new();
714 map.insert(Ident::new("key1"), RuntimeValue::String("value1".to_string()));
715 map.insert(Ident::new("key2"), RuntimeValue::Number(Number::from(42.0)));
716 map
717 }), r#"{"key1": "value1", "key2": 42}"#)]
718 fn test_string_method(#[case] value: RuntimeValue, #[case] expected: &str) {
719 assert_eq!(value.string(), expected);
720 }
721
722 #[test]
723 fn test_runtime_value_display() {
724 assert_eq!(format!("{}", RuntimeValue::Boolean(true)), "true");
725 assert_eq!(format!("{}", RuntimeValue::Number(Number::from(42.0))), "42");
726 assert_eq!(format!("{}", RuntimeValue::String(String::from("test"))), "test");
727 assert_eq!(format!("{}", RuntimeValue::None), "");
728 let map_val = RuntimeValue::Dict(BTreeMap::default());
729 assert_eq!(format!("{}", map_val), "{}");
730 }
731
732 #[test]
733 fn test_runtime_value_debug() {
734 assert_eq!(format!("{:?}", RuntimeValue::Boolean(true)), "true");
735 assert_eq!(format!("{:?}", RuntimeValue::Number(Number::from(42.0))), "42");
736 assert_eq!(format!("{:?}", RuntimeValue::String(String::from("test"))), "\"test\"");
737 assert_eq!(format!("{:?}", RuntimeValue::None), "None");
738
739 let mut map = BTreeMap::default();
740 map.insert(Ident::new("name"), RuntimeValue::String("MQ".to_string()));
741 map.insert(Ident::new("version"), RuntimeValue::Number(Number::from(1.0)));
742 let map_val = RuntimeValue::Dict(map);
743 let debug_str = format!("{:?}", map_val);
744 assert!(debug_str == r#"{"name": "MQ", "version": 1}"# || debug_str == r#"{"version": 1, "name": "MQ"}"#);
745 }
746
747 #[test]
748 fn test_runtime_value_name() {
749 assert_eq!(RuntimeValue::Boolean(true).name(), "bool");
750 assert_eq!(RuntimeValue::Number(Number::from(42.0)).name(), "number");
751 assert_eq!(RuntimeValue::String(String::from("test")).name(), "string");
752 assert_eq!(RuntimeValue::None.name(), "None");
753 assert_eq!(
754 RuntimeValue::Function(
755 SmallVec::new(),
756 Vec::new(),
757 Shared::new(SharedCell::new(Env::default()))
758 )
759 .name(),
760 "function"
761 );
762 assert_eq!(
763 RuntimeValue::NativeFunction(Ident::new("name")).name(),
764 "native_function"
765 );
766 assert_eq!(
767 RuntimeValue::Markdown(
768 mq_markdown::Node::Text(mq_markdown::Text {
769 value: "".to_string(),
770 position: None
771 }),
772 None
773 )
774 .name(),
775 "markdown"
776 );
777 assert_eq!(RuntimeValue::Dict(BTreeMap::default()).name(), "dict");
778 }
779
780 #[test]
781 fn test_runtime_value_is_true() {
782 assert!(RuntimeValue::Boolean(true).is_truthy());
783 assert!(!RuntimeValue::Boolean(false).is_truthy());
784 assert!(RuntimeValue::Number(Number::from(42.0)).is_truthy());
785 assert!(!RuntimeValue::Number(Number::from(0.0)).is_truthy());
786 assert!(RuntimeValue::String(String::from("test")).is_truthy());
787 assert!(!RuntimeValue::String(String::from("")).is_truthy());
788 assert!(RuntimeValue::Array(vec!["".to_string().into()]).is_truthy());
789 assert!(!RuntimeValue::Array(Vec::new()).is_truthy());
790 assert!(
791 RuntimeValue::Markdown(
792 mq_markdown::Node::Text(mq_markdown::Text {
793 value: "".to_string(),
794 position: None
795 }),
796 None
797 )
798 .is_truthy()
799 );
800 assert!(
801 !RuntimeValue::Markdown(
802 mq_markdown::Node::Text(mq_markdown::Text {
803 value: "".to_string(),
804 position: None
805 }),
806 Some(Selector::Index(1))
807 )
808 .is_truthy()
809 );
810 assert!(!RuntimeValue::Array(Vec::new()).is_truthy());
811 assert!(!RuntimeValue::None.is_truthy());
812 assert!(RuntimeValue::NativeFunction(Ident::new("name")).is_truthy());
813 assert!(
814 RuntimeValue::Function(
815 SmallVec::new(),
816 Vec::new(),
817 Shared::new(SharedCell::new(Env::default()))
818 )
819 .is_truthy()
820 );
821 assert!(RuntimeValue::Dict(BTreeMap::default()).is_truthy());
822 }
823
824 #[test]
825 fn test_runtime_value_partial_ord() {
826 assert!(RuntimeValue::Number(Number::from(1.0)) < RuntimeValue::Number(Number::from(2.0)));
827 assert!(RuntimeValue::String(String::from("a")) < RuntimeValue::String(String::from("b")));
828 assert!(RuntimeValue::Array(Vec::new()) < RuntimeValue::Array(vec!["a".to_string().into()]));
829 assert!(
830 RuntimeValue::Markdown(
831 mq_markdown::Node::Text(mq_markdown::Text {
832 value: "test".to_string(),
833 position: None
834 }),
835 None
836 ) < RuntimeValue::Markdown(
837 mq_markdown::Node::Text(mq_markdown::Text {
838 value: "test2".to_string(),
839 position: None
840 }),
841 None
842 )
843 );
844 assert!(RuntimeValue::Boolean(false) < RuntimeValue::Boolean(true));
845 assert!(
846 RuntimeValue::Function(
847 SmallVec::new(),
848 Vec::new(),
849 Shared::new(SharedCell::new(Env::default()))
850 ) < RuntimeValue::Function(
851 smallvec![Param::new(IdentWithToken::new("test"))],
852 Vec::new(),
853 Shared::new(SharedCell::new(Env::default()))
854 )
855 );
856 }
857
858 #[test]
859 fn test_runtime_value_len() {
860 assert_eq!(RuntimeValue::Number(Number::from(42.0)).len(), 42);
861 assert_eq!(RuntimeValue::String(String::from("test")).len(), 4);
862 assert_eq!(RuntimeValue::Boolean(true).len(), 1);
863 assert_eq!(RuntimeValue::Array(vec![RuntimeValue::None]).len(), 1);
864 assert_eq!(
865 RuntimeValue::Markdown(
866 mq_markdown::Node::Text(mq_markdown::Text {
867 value: "a".to_string(),
868 position: None
869 }),
870 None
871 )
872 .len(),
873 1
874 );
875 let mut map = BTreeMap::default();
876 map.insert(Ident::new("a"), RuntimeValue::String("alpha".to_string()));
877 map.insert(Ident::new("b"), RuntimeValue::String("beta".to_string()));
878 assert_eq!(RuntimeValue::Dict(map).len(), 2);
879 }
880
881 #[test]
882 fn test_negated() {
883 assert_eq!(
884 RuntimeValue::Number(Number::from(42.0)).negated(),
885 RuntimeValue::Number(Number::from(-42.0))
886 );
887 assert_eq!(RuntimeValue::Boolean(true).negated(), RuntimeValue::Boolean(false));
888 assert_eq!(RuntimeValue::Boolean(false).negated(), RuntimeValue::Boolean(true));
889 }
890
891 #[test]
892 fn test_runtime_value_debug_output() {
893 let array = RuntimeValue::Array(vec![
894 RuntimeValue::Number(Number::from(1.0)),
895 RuntimeValue::String("hello".to_string()),
896 ]);
897 assert_eq!(format!("{:?}", array), r#"[1, "hello"]"#);
898
899 let node = mq_markdown::Node::Text(mq_markdown::Text {
900 value: "test markdown".to_string(),
901 position: None,
902 });
903 let markdown = RuntimeValue::Markdown(node, None);
904 assert_eq!(format!("{:?}", markdown), "test markdown");
905
906 let function = RuntimeValue::Function(
907 SmallVec::new(),
908 Vec::new(),
909 Shared::new(SharedCell::new(Env::default())),
910 );
911 assert_eq!(format!("{:?}", function), "function/0");
912
913 let native_fn = RuntimeValue::NativeFunction(Ident::new("debug"));
914 assert_eq!(format!("{:?}", native_fn), "native_function");
915
916 let mut map = BTreeMap::default();
917 map.insert(Ident::new("a"), RuntimeValue::String("alpha".to_string()));
918 let map_val = RuntimeValue::Dict(map);
919 assert_eq!(format!("{:?}", map_val), r#"{"a": "alpha"}"#);
920 }
921
922 #[test]
923 fn test_runtime_value_markdown() {
924 let markdown = RuntimeValue::Markdown("test markdown".to_string().into(), None);
925 assert_eq!(markdown.markdown_node().unwrap().value(), "test markdown");
926
927 let updated = markdown.update_markdown_value("updated markdown");
928 match &updated {
929 RuntimeValue::Markdown(node, selector) => {
930 assert_eq!(node.value(), "updated markdown");
931 assert_eq!(*selector, None);
932 }
933 _ => panic!("Expected Markdown variant"),
934 }
935 }
936
937 #[test]
938 fn test_runtime_value_markdown_with_selector() {
939 let child1 = mq_markdown::Node::Text(mq_markdown::Text {
940 value: "child1".to_string(),
941 position: None,
942 });
943 let child2 = mq_markdown::Node::Text(mq_markdown::Text {
944 value: "child2".to_string(),
945 position: None,
946 });
947
948 let parent = mq_markdown::Node::Strong(mq_markdown::Strong {
949 values: vec![child1, child2],
950 position: None,
951 });
952
953 let markdown_with_selector = RuntimeValue::Markdown(parent.clone(), Some(Selector::Index(1)));
954
955 let selected = markdown_with_selector.markdown_node();
956 assert!(selected.is_some());
957 assert_eq!(selected.unwrap().value(), "child2");
958
959 let updated = markdown_with_selector.update_markdown_value("updated child");
960 match &updated {
961 RuntimeValue::Markdown(node, selector) => {
962 assert_eq!(selector, &Some(Selector::Index(1)));
963 assert_eq!(node.find_at_index(1).unwrap().value(), "updated child");
964 }
965 _ => panic!("Expected Markdown variant"),
966 }
967 }
968
969 #[test]
970 fn test_update_markdown_value_non_markdown() {
971 assert_eq!(
972 RuntimeValue::Number(Number::from(42.0)).update_markdown_value("test"),
973 RuntimeValue::NONE
974 );
975 assert_eq!(
976 RuntimeValue::String("hello".to_string()).update_markdown_value("test"),
977 RuntimeValue::NONE
978 );
979 assert_eq!(
980 RuntimeValue::Boolean(true).update_markdown_value("test"),
981 RuntimeValue::NONE
982 );
983 assert_eq!(RuntimeValue::None.update_markdown_value("test"), RuntimeValue::NONE);
984 }
985
986 #[test]
987 fn test_runtime_value_map_creation_and_equality() {
988 let mut map1_data = BTreeMap::default();
989 map1_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
990 map1_data.insert(Ident::new("b"), RuntimeValue::String("hello".to_string()));
991 let map1 = RuntimeValue::Dict(map1_data);
992
993 let mut map2_data = BTreeMap::default();
994 map2_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
995 map2_data.insert(Ident::new("b"), RuntimeValue::String("hello".to_string()));
996 let map2 = RuntimeValue::Dict(map2_data);
997
998 let mut map3_data = BTreeMap::default();
999 map3_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1000 map3_data.insert(Ident::new("c"), RuntimeValue::String("world".to_string()));
1001 let map3 = RuntimeValue::Dict(map3_data);
1002
1003 assert_eq!(map1, map2);
1004 assert_ne!(map1, map3);
1005 }
1006
1007 #[test]
1008 fn test_runtime_value_map_is_empty() {
1009 let empty_map = RuntimeValue::Dict(BTreeMap::default());
1010 assert!(empty_map.is_empty());
1011
1012 let mut map_data = BTreeMap::default();
1013 map_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1014 let non_empty_map = RuntimeValue::Dict(map_data);
1015 assert!(!non_empty_map.is_empty());
1016 }
1017
1018 #[test]
1019 fn test_runtime_value_map_partial_ord() {
1020 let mut map1_data = BTreeMap::default();
1021 map1_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1022 let map1 = RuntimeValue::Dict(map1_data);
1023
1024 let mut map2_data = BTreeMap::default();
1025 map2_data.insert(Ident::new("b"), RuntimeValue::Number(Number::from(2.0)));
1026 let map2 = RuntimeValue::Dict(map2_data);
1027
1028 assert_eq!(map1.partial_cmp(&map2), None);
1029 assert_eq!(map2.partial_cmp(&map1), None);
1030 assert_eq!(map1.partial_cmp(&map1), None);
1031
1032 let num_val = RuntimeValue::Number(Number::from(5.0));
1033 assert_eq!(map1.partial_cmp(&num_val), None);
1034 assert_eq!(num_val.partial_cmp(&map1), None);
1035 }
1036}