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<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 pub const EMPTY_ARRAY: RuntimeValue = Self::Array(Vec::new());
304 pub const FALSE: RuntimeValue = Self::Boolean(false);
306 pub const NONE: RuntimeValue = Self::None;
308 pub const TRUE: RuntimeValue = Self::Boolean(true);
310
311 #[inline(always)]
313 pub fn new_dict() -> RuntimeValue {
314 RuntimeValue::Dict(BTreeMap::new())
315 }
316
317 #[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 #[inline(always)]
338 pub fn is_none(&self) -> bool {
339 matches!(self, RuntimeValue::None)
340 }
341
342 #[inline(always)]
344 pub fn is_function(&self) -> bool {
345 matches!(self, RuntimeValue::Function(_, _, _))
346 }
347
348 #[inline(always)]
350 pub fn is_native_function(&self) -> bool {
351 matches!(self, RuntimeValue::NativeFunction(_))
352 }
353
354 #[inline(always)]
356 pub fn is_array(&self) -> bool {
357 matches!(self, RuntimeValue::Array(_))
358 }
359
360 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[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 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 pub fn values(&self) -> &Vec<RuntimeValue> {
546 &self.0
547 }
548
549 pub fn len(&self) -> usize {
551 self.0.len()
552 }
553
554 pub fn is_empty(&self) -> bool {
556 self.0.len() == 0
557 }
558
559 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, _) = ¤t_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}