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 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 pub const EMPTY_ARRAY: RuntimeValue = Self::Array(Vec::new());
279 pub const FALSE: RuntimeValue = Self::Boolean(false);
281 pub const NONE: RuntimeValue = Self::None;
283 pub const TRUE: RuntimeValue = Self::Boolean(true);
285
286 #[inline(always)]
288 pub fn new_dict() -> RuntimeValue {
289 RuntimeValue::Dict(BTreeMap::new())
290 }
291
292 #[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 #[inline(always)]
313 pub fn is_none(&self) -> bool {
314 matches!(self, RuntimeValue::None)
315 }
316
317 #[inline(always)]
319 pub fn is_function(&self) -> bool {
320 matches!(self, RuntimeValue::Function(_, _, _))
321 }
322
323 #[inline(always)]
325 pub fn is_native_function(&self) -> bool {
326 matches!(self, RuntimeValue::NativeFunction(_))
327 }
328
329 #[inline(always)]
331 pub fn is_array(&self) -> bool {
332 matches!(self, RuntimeValue::Array(_))
333 }
334
335 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[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 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 pub fn values(&self) -> &Vec<RuntimeValue> {
521 &self.0
522 }
523
524 pub fn len(&self) -> usize {
526 self.0.len()
527 }
528
529 pub fn is_empty(&self) -> bool {
531 self.0.len() == 0
532 }
533
534 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, _) = ¤t_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}