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(Box<Node>, Option<Selector>),
94 Function(Box<AstParams>, Program, Shared<SharedCell<Env>>),
96 NativeFunction(Ident),
98 Dict(BTreeMap<Ident, RuntimeValue>),
100 Module(ModuleEnv),
102 Ast(Shared<ast::node::Node>),
104 Bytes(Vec<u8>),
106 #[default]
108 None,
109}
110
111impl PartialEq for RuntimeValue {
113 fn eq(&self, other: &Self) -> bool {
114 match (self, other) {
115 (RuntimeValue::Number(a), RuntimeValue::Number(b)) => a == b,
116 (RuntimeValue::Boolean(a), RuntimeValue::Boolean(b)) => a == b,
117 (RuntimeValue::String(a), RuntimeValue::String(b)) => a == b,
118 (RuntimeValue::Symbol(a), RuntimeValue::Symbol(b)) => a == b,
119 (RuntimeValue::Array(a), RuntimeValue::Array(b)) => a == b,
120 (RuntimeValue::Markdown(a, sa), RuntimeValue::Markdown(b, sb)) => a == b && sa == sb,
121 (RuntimeValue::Function(a1, b1, _), RuntimeValue::Function(a2, b2, _)) => a1 == a2 && b1 == b2,
122 (RuntimeValue::NativeFunction(a), RuntimeValue::NativeFunction(b)) => a == b,
123 (RuntimeValue::Dict(a), RuntimeValue::Dict(b)) => a == b,
124 (RuntimeValue::Module(a), RuntimeValue::Module(b)) => a == b,
125 (RuntimeValue::Ast(a), RuntimeValue::Ast(b)) => a == b,
126 (RuntimeValue::Bytes(a), RuntimeValue::Bytes(b)) => a == b,
127 (RuntimeValue::None, RuntimeValue::None) => true,
128 _ => false,
129 }
130 }
131}
132
133impl From<Node> for RuntimeValue {
134 fn from(node: Node) -> Self {
135 RuntimeValue::new_markdown(node)
136 }
137}
138
139impl From<bool> for RuntimeValue {
140 fn from(b: bool) -> Self {
141 RuntimeValue::Boolean(b)
142 }
143}
144
145impl From<String> for RuntimeValue {
146 fn from(s: String) -> Self {
147 RuntimeValue::String(s)
148 }
149}
150
151impl From<&str> for RuntimeValue {
152 fn from(s: &str) -> Self {
153 RuntimeValue::String(s.to_string())
154 }
155}
156
157impl From<&mut str> for RuntimeValue {
158 fn from(s: &mut str) -> Self {
159 RuntimeValue::String(s.to_string())
160 }
161}
162
163impl From<Number> for RuntimeValue {
164 fn from(n: Number) -> Self {
165 RuntimeValue::Number(n)
166 }
167}
168
169impl From<Ident> for RuntimeValue {
170 fn from(i: Ident) -> Self {
171 RuntimeValue::Symbol(i)
172 }
173}
174
175impl From<usize> for RuntimeValue {
176 fn from(n: usize) -> Self {
177 RuntimeValue::Number(Number::from(n))
178 }
179}
180
181impl From<Vec<RuntimeValue>> for RuntimeValue {
182 fn from(arr: Vec<RuntimeValue>) -> Self {
183 RuntimeValue::Array(arr)
184 }
185}
186
187impl From<BTreeMap<Ident, RuntimeValue>> for RuntimeValue {
188 fn from(map: BTreeMap<Ident, RuntimeValue>) -> Self {
189 RuntimeValue::Dict(map)
190 }
191}
192
193impl From<Vec<(String, Number)>> for RuntimeValue {
194 fn from(v: Vec<(String, Number)>) -> Self {
195 RuntimeValue::Dict(
196 v.into_iter()
197 .map(|(k, v)| (Ident::new(&k), RuntimeValue::Number(v)))
198 .collect::<BTreeMap<Ident, RuntimeValue>>(),
199 )
200 }
201}
202
203impl From<mq_markdown::AttrValue> for RuntimeValue {
204 fn from(attr_value: mq_markdown::AttrValue) -> Self {
205 match attr_value {
206 mq_markdown::AttrValue::String(s) => RuntimeValue::String(s),
207 mq_markdown::AttrValue::Number(n) => RuntimeValue::Number(n.into()),
208 mq_markdown::AttrValue::Integer(n) => RuntimeValue::Number(n.into()),
209 mq_markdown::AttrValue::Boolean(b) => RuntimeValue::Boolean(b),
210 mq_markdown::AttrValue::Array(arr) => {
211 RuntimeValue::Array(arr.into_iter().map(RuntimeValue::from).collect())
212 }
213 mq_markdown::AttrValue::Null => RuntimeValue::NONE,
214 }
215 }
216}
217
218impl From<yaml_rust2::Yaml> for RuntimeValue {
219 fn from(value: yaml_rust2::Yaml) -> Self {
220 match value {
221 yaml_rust2::Yaml::Null | yaml_rust2::Yaml::BadValue => RuntimeValue::NONE,
222 yaml_rust2::Yaml::Boolean(b) => RuntimeValue::Boolean(b),
223 yaml_rust2::Yaml::Integer(i) => RuntimeValue::Number((i as f64).into()),
224 yaml_rust2::Yaml::Real(s) => s
225 .parse::<f64>()
226 .map(|f| RuntimeValue::Number(f.into()))
227 .unwrap_or(RuntimeValue::NONE),
228 yaml_rust2::Yaml::String(s) => RuntimeValue::String(s),
229 yaml_rust2::Yaml::Array(arr) => RuntimeValue::Array(arr.into_iter().map(RuntimeValue::from).collect()),
230 yaml_rust2::Yaml::Hash(map) => {
231 let mut btree = BTreeMap::new();
232 for (k, v) in map {
233 let key = match k {
234 yaml_rust2::Yaml::String(s) => s,
235 other => format!("{other:?}"),
236 };
237 btree.insert(Ident::new(&key), RuntimeValue::from(v));
238 }
239 RuntimeValue::Dict(btree)
240 }
241 yaml_rust2::Yaml::Alias(_) => RuntimeValue::NONE,
242 }
243 }
244}
245
246impl From<serde_json::Value> for RuntimeValue {
247 fn from(value: serde_json::Value) -> Self {
248 match value {
249 serde_json::Value::Null => RuntimeValue::NONE,
250 serde_json::Value::Bool(b) => RuntimeValue::Boolean(b),
251 serde_json::Value::Number(n) => {
252 if let Some(f) = n.as_f64() {
253 RuntimeValue::Number(f.into())
254 } else {
255 RuntimeValue::Number(0.into())
256 }
257 }
258 serde_json::Value::String(s) => RuntimeValue::String(s),
259 serde_json::Value::Array(arr) => RuntimeValue::Array(arr.into_iter().map(RuntimeValue::from).collect()),
260 serde_json::Value::Object(obj) => {
261 let mut map = BTreeMap::new();
262 for (k, v) in obj {
263 map.insert(Ident::new(&k), RuntimeValue::from(v));
264 }
265 RuntimeValue::Dict(map)
266 }
267 }
268 }
269}
270
271impl From<ciborium::Value> for RuntimeValue {
272 fn from(value: ciborium::Value) -> Self {
273 match value {
274 ciborium::Value::Null => RuntimeValue::NONE,
275 ciborium::Value::Bool(b) => RuntimeValue::Boolean(b),
276 ciborium::Value::Integer(i) => {
277 let n: i128 = i.into();
278 RuntimeValue::Number(crate::number::Number::from(n as f64))
279 }
280 ciborium::Value::Float(f) => RuntimeValue::Number(crate::number::Number::from(f)),
281 ciborium::Value::Text(s) => RuntimeValue::String(s),
282 ciborium::Value::Bytes(b) => RuntimeValue::Bytes(b),
283 ciborium::Value::Array(arr) => {
284 let items = arr.into_iter().map(Into::into).collect();
285 RuntimeValue::Array(items)
286 }
287 ciborium::Value::Map(pairs) => {
288 let mut map = BTreeMap::new();
289 for (k, v) in pairs {
290 let key = match k {
291 ciborium::Value::Text(s) => Ident::new(&s),
292 other => Ident::new(&format!("{:?}", other)),
293 };
294 map.insert(key, v.into());
295 }
296 RuntimeValue::Dict(map)
297 }
298 ciborium::Value::Tag(_, inner) => (*inner).into(),
299 _ => RuntimeValue::NONE,
300 }
301 }
302}
303
304impl PartialOrd for RuntimeValue {
305 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
306 match (self, other) {
307 (RuntimeValue::Number(a), RuntimeValue::Number(b)) => a.partial_cmp(b),
308 (RuntimeValue::Boolean(a), RuntimeValue::Boolean(b)) => a.partial_cmp(b),
309 (RuntimeValue::String(a), RuntimeValue::String(b)) => a.partial_cmp(b),
310 (RuntimeValue::Symbol(a), RuntimeValue::Symbol(b)) => a.partial_cmp(b),
311 (RuntimeValue::Array(a), RuntimeValue::Array(b)) => a.partial_cmp(b),
312 (RuntimeValue::Markdown(a, _), RuntimeValue::Markdown(b, _)) => {
313 let a = a.to_string();
314 let b = b.to_string();
315 a.to_string().partial_cmp(&b)
316 }
317 (RuntimeValue::Function(a1, b1, _), RuntimeValue::Function(a2, b2, _)) => match a1.partial_cmp(a2) {
318 Some(Ordering::Equal) => b1.partial_cmp(b2),
319 Some(Ordering::Greater) => Some(Ordering::Greater),
320 Some(Ordering::Less) => Some(Ordering::Less),
321 _ => None,
322 },
323 (RuntimeValue::Bytes(a), RuntimeValue::Bytes(b)) => a.partial_cmp(b),
324 (RuntimeValue::Dict(_), _) => None,
325 (_, RuntimeValue::Dict(_)) => None,
326 (RuntimeValue::Module(a), RuntimeValue::Module(b)) => a.name.partial_cmp(&b.name),
327 (RuntimeValue::Ast(_), _) => None,
328 (_, RuntimeValue::Ast(_)) => None,
329 _ => None,
330 }
331 }
332}
333
334impl std::fmt::Display for RuntimeValue {
335 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
336 let value: Cow<'_, str> = match self {
337 Self::Number(n) => Cow::Owned(n.to_string()),
338 Self::Boolean(b) => Cow::Owned(b.to_string()),
339 Self::String(s) => Cow::Borrowed(s),
340 Self::Symbol(i) => Cow::Owned(format!(":{}", i)),
341 Self::Array(_) => self.string(),
342 Self::Markdown(m, ..) => Cow::Owned(m.to_string()),
343 Self::None => Cow::Borrowed(""),
344 Self::Function(params, ..) => Cow::Owned(format!("function/{}", params.len())),
345 Self::NativeFunction(_) => Cow::Borrowed("native_function"),
346 Self::Dict(_) => self.string(),
347 Self::Module(module_name) => Cow::Owned(format!(r#"module "{}""#, module_name.name)),
348 Self::Ast(node) => Cow::Owned(node.to_code()),
349 Self::Bytes(b) => Cow::Owned(bytes_to_hex(b)),
350 };
351 write!(f, "{}", value)
352 }
353}
354
355impl std::fmt::Debug for RuntimeValue {
356 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
357 let v: Cow<'_, str> = match self {
358 Self::None => Cow::Borrowed("None"),
359 Self::String(s) => Cow::Owned(format!("{:?}", s)),
360 Self::Array(arr) => Cow::Owned(format!("{:?}", arr)),
361 Self::Bytes(b) => Cow::Owned(format!("bytes({})", bytes_to_hex(b))),
362 a => a.string(),
363 };
364 write!(f, "{}", v)
365 }
366}
367
368fn bytes_to_hex(bytes: &[u8]) -> String {
369 use std::fmt::Write;
370 bytes.iter().fold(String::with_capacity(bytes.len() * 2), |mut s, b| {
371 write!(s, "{b:02x}").unwrap();
372 s
373 })
374}
375
376impl RuntimeValue {
377 pub const EMPTY_ARRAY: RuntimeValue = Self::Array(Vec::new());
379 pub const FALSE: RuntimeValue = Self::Boolean(false);
381 pub const NONE: RuntimeValue = Self::None;
383 pub const TRUE: RuntimeValue = Self::Boolean(true);
385
386 #[inline(always)]
388 pub fn new_dict() -> RuntimeValue {
389 RuntimeValue::Dict(BTreeMap::new())
390 }
391
392 pub fn new_markdown(node: Node) -> RuntimeValue {
394 RuntimeValue::Markdown(Box::new(node), None)
395 }
396
397 #[inline(always)]
399 pub fn name(&self) -> &str {
400 match self {
401 RuntimeValue::Number(_) => "number",
402 RuntimeValue::Boolean(_) => "bool",
403 RuntimeValue::String(_) => "string",
404 RuntimeValue::Symbol(_) => "symbol",
405 RuntimeValue::Markdown(_, _) => "markdown",
406 RuntimeValue::Array(_) => "array",
407 RuntimeValue::None => "None",
408 RuntimeValue::Function(_, _, _) => "function",
409 RuntimeValue::NativeFunction(_) => "native_function",
410 RuntimeValue::Dict(_) => "dict",
411 RuntimeValue::Module(_) => "module",
412 RuntimeValue::Ast(_) => "ast",
413 RuntimeValue::Bytes(_) => "bytes",
414 }
415 }
416
417 #[inline(always)]
419 pub fn is_none(&self) -> bool {
420 matches!(self, RuntimeValue::None)
421 }
422
423 #[inline(always)]
425 pub fn is_function(&self) -> bool {
426 matches!(self, RuntimeValue::Function(_, _, _))
427 }
428
429 #[inline(always)]
431 pub fn is_native_function(&self) -> bool {
432 matches!(self, RuntimeValue::NativeFunction(_))
433 }
434
435 #[inline(always)]
437 pub fn is_array(&self) -> bool {
438 matches!(self, RuntimeValue::Array(_))
439 }
440
441 #[inline(always)]
443 pub fn is_dict(&self) -> bool {
444 matches!(self, RuntimeValue::Dict(_))
445 }
446
447 #[inline(always)]
452 pub fn is_empty(&self) -> bool {
453 match self {
454 RuntimeValue::Array(a) => a.is_empty(),
455 RuntimeValue::String(s) => s.is_empty(),
456 RuntimeValue::Markdown(m, _) => m.value().is_empty(),
457 RuntimeValue::Dict(m) => m.is_empty(),
458 RuntimeValue::Bytes(b) => b.is_empty(),
459 RuntimeValue::None => true,
460 _ => false,
461 }
462 }
463
464 #[inline(always)]
470 pub fn is_truthy(&self) -> bool {
471 match self {
472 RuntimeValue::Boolean(b) => *b,
473 RuntimeValue::Number(n) => n.value() != 0.0,
474 RuntimeValue::String(s) => !s.is_empty(),
475 RuntimeValue::Array(a) => !a.is_empty(),
476 RuntimeValue::Markdown(node, selector) => match selector {
477 Some(Selector::Index(i)) => node.find_at_index(*i).is_some(),
478 None => true,
479 },
480 RuntimeValue::Symbol(_)
481 | RuntimeValue::Function(_, _, _)
482 | RuntimeValue::NativeFunction(_)
483 | RuntimeValue::Dict(_) => true,
484 RuntimeValue::Module(_) => true,
485 RuntimeValue::Ast(_) => true,
486 RuntimeValue::Bytes(b) => !b.is_empty(),
487 RuntimeValue::None => false,
488 }
489 }
490
491 #[inline(always)]
496 pub fn len(&self) -> usize {
497 match self {
498 RuntimeValue::Number(n) => n.value() as usize,
499 RuntimeValue::Boolean(_) => 1,
500 RuntimeValue::String(s) => s.len(),
501 RuntimeValue::Symbol(i) => i.as_str().len(),
502 RuntimeValue::Array(a) => a.len(),
503 RuntimeValue::Markdown(m, _) => m.value().len(),
504 RuntimeValue::Dict(m) => m.len(),
505 RuntimeValue::Bytes(b) => b.len(),
506 RuntimeValue::None => 0,
507 RuntimeValue::Function(..) => 0,
508 RuntimeValue::Module(m) => m.len(),
509 RuntimeValue::NativeFunction(..) => 0,
510 RuntimeValue::Ast(_) => 0,
511 }
512 }
513
514 #[inline(always)]
518 pub fn markdown_node(&self) -> Option<Node> {
519 match self {
520 RuntimeValue::Markdown(n, Some(Selector::Index(i))) => n.find_at_index(*i),
521 RuntimeValue::Markdown(n, _) => Some((**n).clone()),
522 _ => None,
523 }
524 }
525
526 #[inline(always)]
530 pub fn update_markdown_value(&self, value: &str) -> RuntimeValue {
531 match self {
532 RuntimeValue::Markdown(n, Some(Selector::Index(i))) => {
533 RuntimeValue::Markdown(Box::new(n.with_children_value(value, *i)), Some(Selector::Index(*i)))
534 }
535 RuntimeValue::Markdown(n, selector) => {
536 RuntimeValue::Markdown(Box::new(n.with_value(value)), selector.clone())
537 }
538 _ => RuntimeValue::NONE,
539 }
540 }
541
542 #[inline(always)]
544 pub fn position(&self) -> Option<mq_markdown::Position> {
545 match self {
546 RuntimeValue::Markdown(node, _) => node.position(),
547 _ => None,
548 }
549 }
550
551 #[inline(always)]
555 pub fn set_position(&mut self, position: Option<mq_markdown::Position>) {
556 if let RuntimeValue::Markdown(node, _) = self {
557 node.set_position(position);
558 }
559 }
560
561 #[inline(always)]
562 fn string(&self) -> Cow<'_, str> {
563 match self {
564 Self::Number(n) => Cow::Owned(n.to_string()),
565 Self::Boolean(b) => Cow::Owned(b.to_string()),
566 Self::String(s) => Cow::Owned(format!(r#""{}""#, s)),
567 Self::Symbol(i) => Cow::Owned(format!(":{}", i)),
568 Self::Array(a) => Cow::Owned(format!(
569 "[{}]",
570 a.iter().map(|v| v.string()).collect::<Vec<Cow<str>>>().join(", ")
571 )),
572 Self::Markdown(m, ..) => Cow::Owned(m.to_string()),
573 Self::None => Cow::Borrowed(""),
574 Self::Function(f, _, _) => Cow::Owned(format!("function/{}", f.len())),
575 Self::NativeFunction(_) => Cow::Borrowed("native_function"),
576 Self::Module(m) => Cow::Owned(format!("module/{}", m.name())),
577 Self::Ast(node) => Cow::Owned(node.to_code()),
578 Self::Bytes(b) => Cow::Owned(bytes_to_hex(b)),
579 Self::Dict(map) => {
580 let items = map
581 .iter()
582 .map(|(k, v)| format!("\"{}\": {}", k, v.string()))
583 .collect::<Vec<String>>()
584 .join(", ");
585 Cow::Owned(format!("{{{}}}", items))
586 }
587 }
588 }
589
590 pub fn negated(&self) -> Self {
592 match self {
593 RuntimeValue::Boolean(b) => RuntimeValue::Boolean(!b),
594 RuntimeValue::Number(n) => RuntimeValue::Number((-n.value()).into()),
595 RuntimeValue::String(s) => RuntimeValue::String(s.chars().rev().collect()),
596 _ => self.clone(),
597 }
598 }
599
600 pub fn to_json_value(self) -> serde_json::Value {
601 use base64::Engine;
602 match self {
603 RuntimeValue::None => serde_json::Value::Null,
604 RuntimeValue::Boolean(b) => serde_json::Value::Bool(b),
605 RuntimeValue::Number(n) => serde_json::Number::from_f64(n.value())
606 .map(serde_json::Value::Number)
607 .unwrap_or(serde_json::Value::Null),
608 RuntimeValue::String(s) => serde_json::Value::String(s),
609 RuntimeValue::Symbol(i) => serde_json::Value::String(i.to_string()),
610 RuntimeValue::Array(arr) => serde_json::Value::Array(arr.into_iter().map(Self::to_json_value).collect()),
611 RuntimeValue::Dict(map) => {
612 let obj: serde_json::Map<String, serde_json::Value> = map
613 .into_iter()
614 .map(|(k, v)| (k.to_string(), v.to_json_value()))
615 .collect();
616 serde_json::Value::Object(obj)
617 }
618 RuntimeValue::Bytes(b) => serde_json::Value::String(base64::engine::general_purpose::STANDARD.encode(b)),
619 _ => serde_json::Value::Null,
620 }
621 }
622
623 pub fn to_cbor_value(self) -> ciborium::Value {
624 match self {
625 RuntimeValue::None => ciborium::Value::Null,
626 RuntimeValue::Boolean(b) => ciborium::Value::Bool(b),
627 RuntimeValue::Number(n) => ciborium::Value::Float(n.value()),
628 RuntimeValue::String(s) => ciborium::Value::Text(s),
629 RuntimeValue::Symbol(i) => ciborium::Value::Text(i.to_string()),
630 RuntimeValue::Bytes(b) => ciborium::Value::Bytes(b),
631 RuntimeValue::Array(arr) => ciborium::Value::Array(arr.into_iter().map(Self::to_cbor_value).collect()),
632 RuntimeValue::Dict(map) => ciborium::Value::Map(
633 map.into_iter()
634 .map(|(k, v)| (ciborium::Value::Text(k.to_string()), v.to_cbor_value()))
635 .collect(),
636 ),
637 _ => ciborium::Value::Null,
638 }
639 }
640}
641
642#[derive(Debug, Clone, PartialEq)]
647pub struct RuntimeValues(Vec<RuntimeValue>);
648
649impl From<Vec<RuntimeValue>> for RuntimeValues {
650 fn from(values: Vec<RuntimeValue>) -> Self {
651 Self(values)
652 }
653}
654
655impl Index<usize> for RuntimeValues {
656 type Output = RuntimeValue;
657
658 fn index(&self, index: usize) -> &Self::Output {
659 &self.0[index]
660 }
661}
662
663impl IndexMut<usize> for RuntimeValues {
664 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
665 &mut self.0[index]
666 }
667}
668
669impl IntoIterator for RuntimeValues {
670 type IntoIter = std::vec::IntoIter<RuntimeValue>;
671 type Item = RuntimeValue;
672
673 fn into_iter(self) -> Self::IntoIter {
674 self.0.into_iter()
675 }
676}
677
678impl RuntimeValues {
679 pub fn compact(&self) -> Vec<RuntimeValue> {
681 self.0
682 .iter()
683 .filter(|v| !v.is_none() && !v.is_empty())
684 .cloned()
685 .collect::<Vec<_>>()
686 }
687
688 pub fn values(&self) -> &Vec<RuntimeValue> {
690 &self.0
691 }
692
693 pub fn len(&self) -> usize {
695 self.0.len()
696 }
697
698 pub fn is_empty(&self) -> bool {
700 self.0.len() == 0
701 }
702
703 pub fn update_with(&self, other: Self) -> Self {
708 self.0
709 .clone()
710 .into_iter()
711 .zip(other)
712 .map(|(current_value, mut updated_value)| {
713 updated_value.set_position(current_value.position());
714
715 if let RuntimeValue::Markdown(node, _) = ¤t_value {
716 match &updated_value {
717 RuntimeValue::None
718 | RuntimeValue::Function(_, _, _)
719 | RuntimeValue::Module(_)
720 | RuntimeValue::Ast(_)
721 | RuntimeValue::NativeFunction(_) => current_value.clone(),
722 RuntimeValue::Markdown(node, _) if node.is_empty() => current_value.clone(),
723 RuntimeValue::Markdown(node, _) => {
724 if node.is_fragment() {
725 if let RuntimeValue::Markdown(mut current_node, selector) = current_value {
726 current_node.apply_fragment((**node).clone());
727 RuntimeValue::Markdown(current_node, selector)
728 } else {
729 updated_value
730 }
731 } else {
732 updated_value
733 }
734 }
735 RuntimeValue::String(s) => RuntimeValue::new_markdown(node.with_value(s)),
736 RuntimeValue::Symbol(i) => RuntimeValue::new_markdown(node.with_value(&i.as_str())),
737 RuntimeValue::Boolean(b) => RuntimeValue::new_markdown(node.with_value(b.to_string().as_str())),
738 RuntimeValue::Number(n) => RuntimeValue::new_markdown(node.with_value(n.to_string().as_str())),
739 RuntimeValue::Array(array) => RuntimeValue::Array(
740 array
741 .iter()
742 .filter_map(|o| {
743 if o.is_none() {
744 None
745 } else {
746 Some(RuntimeValue::Markdown(
747 Box::new(node.with_value(o.to_string().as_str())),
748 None,
749 ))
750 }
751 })
752 .collect::<Vec<_>>(),
753 ),
754 RuntimeValue::Bytes(b) => RuntimeValue::new_markdown(node.with_value(bytes_to_hex(b).as_str())),
755 RuntimeValue::Dict(map) => {
756 let mut new_dict = BTreeMap::new();
757 for (k, v) in map {
758 if !v.is_none() && !v.is_empty() {
759 new_dict.insert(
760 *k,
761 RuntimeValue::new_markdown(node.with_value(v.to_string().as_str())),
762 );
763 }
764 }
765 RuntimeValue::Dict(new_dict)
766 }
767 }
768 } else {
769 updated_value
770 }
771 })
772 .collect::<Vec<_>>()
773 .into()
774 }
775}
776
777#[cfg(test)]
778mod tests {
779 use crate::ast::node::{IdentWithToken, Param};
780 use rstest::rstest;
781 use smallvec::{SmallVec, smallvec};
782
783 use super::*;
784
785 #[test]
786 fn test_runtime_value_from() {
787 assert_eq!(RuntimeValue::from(true), RuntimeValue::Boolean(true));
788 assert_eq!(RuntimeValue::from(false), RuntimeValue::Boolean(false));
789 assert_eq!(
790 RuntimeValue::from(String::from("test")),
791 RuntimeValue::String(String::from("test"))
792 );
793 assert_eq!(
794 RuntimeValue::from(Number::from(42.0)),
795 RuntimeValue::Number(Number::from(42.0))
796 );
797 }
798
799 #[rstest]
800 #[case(RuntimeValue::Number(Number::from(42.0)), "42")]
801 #[case(RuntimeValue::Boolean(true), "true")]
802 #[case(RuntimeValue::Boolean(false), "false")]
803 #[case(RuntimeValue::String("hello".to_string()), r#""hello""#)]
804 #[case(RuntimeValue::None, "")]
805 #[case(RuntimeValue::Array(vec![
806 RuntimeValue::Number(Number::from(1.0)),
807 RuntimeValue::String("test".to_string())
808 ]), r#"[1, "test"]"#)]
809 #[case(RuntimeValue::Dict({
810 let mut map = BTreeMap::new();
811 map.insert(Ident::new("key1"), RuntimeValue::String("value1".to_string()));
812 map.insert(Ident::new("key2"), RuntimeValue::Number(Number::from(42.0)));
813 map
814 }), r#"{"key1": "value1", "key2": 42}"#)]
815 fn test_string_method(#[case] value: RuntimeValue, #[case] expected: &str) {
816 assert_eq!(value.string(), expected);
817 }
818
819 #[test]
820 fn test_runtime_value_display() {
821 assert_eq!(format!("{}", RuntimeValue::Boolean(true)), "true");
822 assert_eq!(format!("{}", RuntimeValue::Number(Number::from(42.0))), "42");
823 assert_eq!(format!("{}", RuntimeValue::String(String::from("test"))), "test");
824 assert_eq!(format!("{}", RuntimeValue::None), "");
825 let map_val = RuntimeValue::Dict(BTreeMap::default());
826 assert_eq!(format!("{}", map_val), "{}");
827 }
828
829 #[test]
830 fn test_runtime_value_debug() {
831 assert_eq!(format!("{:?}", RuntimeValue::Boolean(true)), "true");
832 assert_eq!(format!("{:?}", RuntimeValue::Number(Number::from(42.0))), "42");
833 assert_eq!(format!("{:?}", RuntimeValue::String(String::from("test"))), "\"test\"");
834 assert_eq!(format!("{:?}", RuntimeValue::None), "None");
835
836 let mut map = BTreeMap::default();
837 map.insert(Ident::new("name"), RuntimeValue::String("MQ".to_string()));
838 map.insert(Ident::new("version"), RuntimeValue::Number(Number::from(1.0)));
839 let map_val = RuntimeValue::Dict(map);
840 let debug_str = format!("{:?}", map_val);
841 assert!(debug_str == r#"{"name": "MQ", "version": 1}"# || debug_str == r#"{"version": 1, "name": "MQ"}"#);
842 }
843
844 #[test]
845 fn test_runtime_value_name() {
846 assert_eq!(RuntimeValue::Boolean(true).name(), "bool");
847 assert_eq!(RuntimeValue::Number(Number::from(42.0)).name(), "number");
848 assert_eq!(RuntimeValue::String(String::from("test")).name(), "string");
849 assert_eq!(RuntimeValue::None.name(), "None");
850 assert_eq!(
851 RuntimeValue::Function(
852 Box::new(SmallVec::new()),
853 Vec::new(),
854 Shared::new(SharedCell::new(Env::default()))
855 )
856 .name(),
857 "function"
858 );
859 assert_eq!(
860 RuntimeValue::NativeFunction(Ident::new("name")).name(),
861 "native_function"
862 );
863 assert_eq!(
864 RuntimeValue::Markdown(
865 Box::new(mq_markdown::Node::Text(mq_markdown::Text {
866 value: "".to_string(),
867 position: None
868 })),
869 None
870 )
871 .name(),
872 "markdown"
873 );
874 assert_eq!(RuntimeValue::Dict(BTreeMap::default()).name(), "dict");
875 }
876
877 #[test]
878 fn test_runtime_value_is_true() {
879 assert!(RuntimeValue::Boolean(true).is_truthy());
880 assert!(!RuntimeValue::Boolean(false).is_truthy());
881 assert!(RuntimeValue::Number(Number::from(42.0)).is_truthy());
882 assert!(!RuntimeValue::Number(Number::from(0.0)).is_truthy());
883 assert!(RuntimeValue::String(String::from("test")).is_truthy());
884 assert!(!RuntimeValue::String(String::from("")).is_truthy());
885 assert!(RuntimeValue::Array(vec!["".to_string().into()]).is_truthy());
886 assert!(!RuntimeValue::Array(Vec::new()).is_truthy());
887 assert!(
888 RuntimeValue::Markdown(
889 Box::new(mq_markdown::Node::Text(mq_markdown::Text {
890 value: "".to_string(),
891 position: None
892 })),
893 None
894 )
895 .is_truthy()
896 );
897 assert!(
898 !RuntimeValue::Markdown(
899 Box::new(mq_markdown::Node::Text(mq_markdown::Text {
900 value: "".to_string(),
901 position: None
902 })),
903 Some(Selector::Index(1))
904 )
905 .is_truthy()
906 );
907 assert!(!RuntimeValue::Array(Vec::new()).is_truthy());
908 assert!(!RuntimeValue::None.is_truthy());
909 assert!(RuntimeValue::NativeFunction(Ident::new("name")).is_truthy());
910 assert!(
911 RuntimeValue::Function(
912 Box::new(SmallVec::new()),
913 Vec::new(),
914 Shared::new(SharedCell::new(Env::default()))
915 )
916 .is_truthy()
917 );
918 assert!(RuntimeValue::Dict(BTreeMap::default()).is_truthy());
919 }
920
921 #[test]
922 fn test_runtime_value_partial_ord() {
923 assert!(RuntimeValue::Number(Number::from(1.0)) < RuntimeValue::Number(Number::from(2.0)));
924 assert!(RuntimeValue::String(String::from("a")) < RuntimeValue::String(String::from("b")));
925 assert!(RuntimeValue::Array(Vec::new()) < RuntimeValue::Array(vec!["a".to_string().into()]));
926 assert!(
927 RuntimeValue::Markdown(
928 Box::new(mq_markdown::Node::Text(mq_markdown::Text {
929 value: "test".to_string(),
930 position: None
931 })),
932 None
933 ) < RuntimeValue::Markdown(
934 Box::new(mq_markdown::Node::Text(mq_markdown::Text {
935 value: "test2".to_string(),
936 position: None
937 })),
938 None
939 )
940 );
941 assert!(RuntimeValue::Boolean(false) < RuntimeValue::Boolean(true));
942 assert!(
943 RuntimeValue::Function(
944 Box::new(SmallVec::new()),
945 Vec::new(),
946 Shared::new(SharedCell::new(Env::default()))
947 ) < RuntimeValue::Function(
948 Box::new(smallvec![Param::new(IdentWithToken::new("test"))]),
949 Vec::new(),
950 Shared::new(SharedCell::new(Env::default()))
951 )
952 );
953 }
954
955 #[test]
956 fn test_runtime_value_len() {
957 assert_eq!(RuntimeValue::Number(Number::from(42.0)).len(), 42);
958 assert_eq!(RuntimeValue::String(String::from("test")).len(), 4);
959 assert_eq!(RuntimeValue::Boolean(true).len(), 1);
960 assert_eq!(RuntimeValue::Array(vec![RuntimeValue::None]).len(), 1);
961 assert_eq!(
962 RuntimeValue::Markdown(
963 Box::new(mq_markdown::Node::Text(mq_markdown::Text {
964 value: "a".to_string(),
965 position: None
966 })),
967 None
968 )
969 .len(),
970 1
971 );
972 let mut map = BTreeMap::default();
973 map.insert(Ident::new("a"), RuntimeValue::String("alpha".to_string()));
974 map.insert(Ident::new("b"), RuntimeValue::String("beta".to_string()));
975 assert_eq!(RuntimeValue::Dict(map).len(), 2);
976 }
977
978 #[test]
979 fn test_negated() {
980 assert_eq!(
981 RuntimeValue::Number(Number::from(42.0)).negated(),
982 RuntimeValue::Number(Number::from(-42.0))
983 );
984 assert_eq!(RuntimeValue::Boolean(true).negated(), RuntimeValue::Boolean(false));
985 assert_eq!(RuntimeValue::Boolean(false).negated(), RuntimeValue::Boolean(true));
986 }
987
988 #[test]
989 fn test_runtime_value_debug_output() {
990 let array = RuntimeValue::Array(vec![
991 RuntimeValue::Number(Number::from(1.0)),
992 RuntimeValue::String("hello".to_string()),
993 ]);
994 assert_eq!(format!("{:?}", array), r#"[1, "hello"]"#);
995
996 let node = mq_markdown::Node::Text(mq_markdown::Text {
997 value: "test markdown".to_string(),
998 position: None,
999 });
1000 let markdown = RuntimeValue::new_markdown(node);
1001 assert_eq!(format!("{:?}", markdown), "test markdown");
1002
1003 let function = RuntimeValue::Function(
1004 Box::new(SmallVec::new()),
1005 Vec::new(),
1006 Shared::new(SharedCell::new(Env::default())),
1007 );
1008 assert_eq!(format!("{:?}", function), "function/0");
1009
1010 let native_fn = RuntimeValue::NativeFunction(Ident::new("debug"));
1011 assert_eq!(format!("{:?}", native_fn), "native_function");
1012
1013 let mut map = BTreeMap::default();
1014 map.insert(Ident::new("a"), RuntimeValue::String("alpha".to_string()));
1015 let map_val = RuntimeValue::Dict(map);
1016 assert_eq!(format!("{:?}", map_val), r#"{"a": "alpha"}"#);
1017 }
1018
1019 #[test]
1020 fn test_runtime_value_markdown() {
1021 let markdown = RuntimeValue::new_markdown("test markdown".to_string().into());
1022 assert_eq!(markdown.markdown_node().unwrap().value(), "test markdown");
1023
1024 let updated = markdown.update_markdown_value("updated markdown");
1025 match &updated {
1026 RuntimeValue::Markdown(node, selector) => {
1027 assert_eq!(node.value(), "updated markdown");
1028 assert_eq!(*selector, None);
1029 }
1030 _ => panic!("Expected Markdown variant"),
1031 }
1032 }
1033
1034 #[test]
1035 fn test_runtime_value_markdown_with_selector() {
1036 let child1 = mq_markdown::Node::Text(mq_markdown::Text {
1037 value: "child1".to_string(),
1038 position: None,
1039 });
1040 let child2 = mq_markdown::Node::Text(mq_markdown::Text {
1041 value: "child2".to_string(),
1042 position: None,
1043 });
1044
1045 let parent = mq_markdown::Node::Strong(mq_markdown::Strong {
1046 values: vec![child1, child2],
1047 position: None,
1048 });
1049
1050 let markdown_with_selector = RuntimeValue::Markdown(Box::new(parent.clone()), Some(Selector::Index(1)));
1051
1052 let selected = markdown_with_selector.markdown_node();
1053 assert!(selected.is_some());
1054 assert_eq!(selected.unwrap().value(), "child2");
1055
1056 let updated = markdown_with_selector.update_markdown_value("updated child");
1057 match &updated {
1058 RuntimeValue::Markdown(node, selector) => {
1059 assert_eq!(selector, &Some(Selector::Index(1)));
1060 assert_eq!(node.find_at_index(1).unwrap().value(), "updated child");
1061 }
1062 _ => panic!("Expected Markdown variant"),
1063 }
1064 }
1065
1066 #[test]
1067 fn test_update_markdown_value_non_markdown() {
1068 assert_eq!(
1069 RuntimeValue::Number(Number::from(42.0)).update_markdown_value("test"),
1070 RuntimeValue::NONE
1071 );
1072 assert_eq!(
1073 RuntimeValue::String("hello".to_string()).update_markdown_value("test"),
1074 RuntimeValue::NONE
1075 );
1076 assert_eq!(
1077 RuntimeValue::Boolean(true).update_markdown_value("test"),
1078 RuntimeValue::NONE
1079 );
1080 assert_eq!(RuntimeValue::None.update_markdown_value("test"), RuntimeValue::NONE);
1081 }
1082
1083 #[test]
1084 fn test_runtime_value_map_creation_and_equality() {
1085 let mut map1_data = BTreeMap::default();
1086 map1_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1087 map1_data.insert(Ident::new("b"), RuntimeValue::String("hello".to_string()));
1088 let map1 = RuntimeValue::Dict(map1_data);
1089
1090 let mut map2_data = BTreeMap::default();
1091 map2_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1092 map2_data.insert(Ident::new("b"), RuntimeValue::String("hello".to_string()));
1093 let map2 = RuntimeValue::Dict(map2_data);
1094
1095 let mut map3_data = BTreeMap::default();
1096 map3_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1097 map3_data.insert(Ident::new("c"), RuntimeValue::String("world".to_string()));
1098 let map3 = RuntimeValue::Dict(map3_data);
1099
1100 assert_eq!(map1, map2);
1101 assert_ne!(map1, map3);
1102 }
1103
1104 #[test]
1105 fn test_runtime_value_map_is_empty() {
1106 let empty_map = RuntimeValue::Dict(BTreeMap::default());
1107 assert!(empty_map.is_empty());
1108
1109 let mut map_data = BTreeMap::default();
1110 map_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1111 let non_empty_map = RuntimeValue::Dict(map_data);
1112 assert!(!non_empty_map.is_empty());
1113 }
1114
1115 #[test]
1116 fn test_runtime_value_map_partial_ord() {
1117 let mut map1_data = BTreeMap::default();
1118 map1_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1119 let map1 = RuntimeValue::Dict(map1_data);
1120
1121 let mut map2_data = BTreeMap::default();
1122 map2_data.insert(Ident::new("b"), RuntimeValue::Number(Number::from(2.0)));
1123 let map2 = RuntimeValue::Dict(map2_data);
1124
1125 assert_eq!(map1.partial_cmp(&map2), None);
1126 assert_eq!(map2.partial_cmp(&map1), None);
1127 assert_eq!(map1.partial_cmp(&map1), None);
1128
1129 let num_val = RuntimeValue::Number(Number::from(5.0));
1130 assert_eq!(map1.partial_cmp(&num_val), None);
1131 assert_eq!(num_val.partial_cmp(&map1), None);
1132 }
1133
1134 #[test]
1135 fn test_bytes_name() {
1136 assert_eq!(RuntimeValue::Bytes(vec![]).name(), "bytes");
1137 assert_eq!(RuntimeValue::Bytes(vec![1, 2, 3]).name(), "bytes");
1138 }
1139
1140 #[test]
1141 fn test_bytes_is_empty() {
1142 assert!(RuntimeValue::Bytes(vec![]).is_empty());
1143 assert!(!RuntimeValue::Bytes(vec![0]).is_empty());
1144 }
1145
1146 #[test]
1147 fn test_bytes_is_truthy() {
1148 assert!(!RuntimeValue::Bytes(vec![]).is_truthy());
1149 assert!(RuntimeValue::Bytes(vec![0]).is_truthy());
1150 assert!(RuntimeValue::Bytes(vec![1, 2, 3]).is_truthy());
1151 }
1152
1153 #[test]
1154 fn test_bytes_len() {
1155 assert_eq!(RuntimeValue::Bytes(vec![]).len(), 0);
1156 assert_eq!(RuntimeValue::Bytes(vec![1, 2, 3]).len(), 3);
1157 }
1158
1159 #[test]
1160 fn test_bytes_display() {
1161 assert_eq!(
1162 format!("{}", RuntimeValue::Bytes(vec![0xde, 0xad, 0xbe, 0xef])),
1163 "deadbeef"
1164 );
1165 assert_eq!(format!("{}", RuntimeValue::Bytes(vec![])), "");
1166 }
1167
1168 #[test]
1169 fn test_bytes_debug() {
1170 assert_eq!(format!("{:?}", RuntimeValue::Bytes(vec![0xca, 0xfe])), "bytes(cafe)");
1171 }
1172
1173 #[test]
1174 fn test_bytes_partial_eq() {
1175 assert_eq!(RuntimeValue::Bytes(vec![1, 2]), RuntimeValue::Bytes(vec![1, 2]));
1176 assert_ne!(RuntimeValue::Bytes(vec![1, 2]), RuntimeValue::Bytes(vec![1, 3]));
1177 assert_ne!(
1178 RuntimeValue::Bytes(vec![1, 2]),
1179 RuntimeValue::String("0102".to_string())
1180 );
1181 }
1182
1183 #[test]
1184 fn test_bytes_partial_ord() {
1185 assert!(RuntimeValue::Bytes(vec![1]) < RuntimeValue::Bytes(vec![2]));
1186 assert!(RuntimeValue::Bytes(vec![1, 2]) > RuntimeValue::Bytes(vec![1]));
1187 assert_eq!(
1188 RuntimeValue::Bytes(vec![1]).partial_cmp(&RuntimeValue::Bytes(vec![1])),
1189 Some(std::cmp::Ordering::Equal)
1190 );
1191 assert_eq!(RuntimeValue::Bytes(vec![]).partial_cmp(&RuntimeValue::None), None);
1192 }
1193
1194 #[rstest]
1195 #[case(RuntimeValue::None, serde_json::Value::Null)]
1196 #[case(RuntimeValue::Boolean(true), serde_json::Value::Bool(true))]
1197 #[case(RuntimeValue::Boolean(false), serde_json::Value::Bool(false))]
1198 #[case(RuntimeValue::String("hi".to_string()), serde_json::Value::String("hi".to_string()))]
1199 #[case(RuntimeValue::Symbol(Ident::new("sym")), serde_json::Value::String("sym".to_string()))]
1200 #[case(RuntimeValue::NativeFunction(Ident::new("f")), serde_json::Value::Null)]
1201 fn test_to_json_value_scalars(#[case] value: RuntimeValue, #[case] expected: serde_json::Value) {
1202 assert_eq!(value.to_json_value(), expected);
1203 }
1204
1205 #[test]
1206 fn test_to_json_value_array() {
1207 let arr = RuntimeValue::Array(vec![RuntimeValue::Boolean(true), RuntimeValue::String("x".to_string())]);
1208 match arr.to_json_value() {
1209 serde_json::Value::Array(items) => {
1210 assert_eq!(items[0], serde_json::Value::Bool(true));
1211 assert_eq!(items[1], serde_json::Value::String("x".to_string()));
1212 }
1213 other => panic!("expected Array, got {other:?}"),
1214 }
1215 }
1216
1217 #[test]
1218 fn test_to_json_value_dict() {
1219 let mut map = BTreeMap::new();
1220 map.insert(Ident::new("k"), RuntimeValue::Boolean(false));
1221 let obj = RuntimeValue::Dict(map).to_json_value();
1222 match obj {
1223 serde_json::Value::Object(m) => {
1224 assert_eq!(m["k"], serde_json::Value::Bool(false));
1225 }
1226 other => panic!("expected Object, got {other:?}"),
1227 }
1228 }
1229
1230 #[test]
1231 fn test_to_json_value_bytes_base64() {
1232 let b = RuntimeValue::Bytes(vec![0x00, 0xff]);
1233 match b.to_json_value() {
1234 serde_json::Value::String(s) => assert!(!s.is_empty()),
1235 other => panic!("expected String, got {other:?}"),
1236 }
1237 }
1238
1239 #[rstest]
1240 #[case(RuntimeValue::None, true)]
1241 #[case(RuntimeValue::Boolean(true), false)]
1242 #[case(RuntimeValue::String("".to_string()), true)]
1243 #[case(RuntimeValue::Array(vec![]), true)]
1244 #[case(RuntimeValue::Dict(BTreeMap::new()), true)]
1245 #[case(RuntimeValue::Bytes(vec![]), true)]
1246 #[case(RuntimeValue::Bytes(vec![1]), false)]
1247 fn test_is_empty(#[case] value: RuntimeValue, #[case] expected: bool) {
1248 assert_eq!(value.is_empty(), expected);
1249 }
1250
1251 #[rstest]
1252 #[case(RuntimeValue::None, false)]
1253 #[case(RuntimeValue::Boolean(true), true)]
1254 #[case(RuntimeValue::Boolean(false), false)]
1255 #[case(RuntimeValue::String("hi".to_string()), true)]
1256 #[case(RuntimeValue::String("".to_string()), false)]
1257 #[case(RuntimeValue::Array(vec![RuntimeValue::None]), true)]
1258 #[case(RuntimeValue::Array(vec![]), false)]
1259 #[case(RuntimeValue::Symbol(Ident::new("s")), true)]
1260 #[case(RuntimeValue::NativeFunction(Ident::new("f")), true)]
1261 fn test_is_truthy_variants(#[case] value: RuntimeValue, #[case] expected: bool) {
1262 assert_eq!(value.is_truthy(), expected);
1263 }
1264
1265 #[rstest]
1266 #[case(RuntimeValue::Symbol(Ident::new("abc")), 3)]
1267 #[case(RuntimeValue::NativeFunction(Ident::new("f")), 0)]
1268 #[case(RuntimeValue::Ast(crate::Shared::new(crate::AstNode { token_id: crate::arena::ArenaId::new(0), expr: crate::Shared::new(crate::AstExpr::Self_) })), 0)]
1269 fn test_len_less_common(#[case] value: RuntimeValue, #[case] expected: usize) {
1270 assert_eq!(value.len(), expected);
1271 }
1272
1273 #[test]
1274 fn test_is_none_predicate() {
1275 assert!(RuntimeValue::None.is_none());
1276 assert!(!RuntimeValue::Boolean(false).is_none());
1277 }
1278
1279 #[test]
1280 fn test_is_function_native() {
1281 assert!(RuntimeValue::NativeFunction(Ident::new("f")).is_native_function());
1282 assert!(!RuntimeValue::NativeFunction(Ident::new("f")).is_function());
1283 assert!(!RuntimeValue::None.is_native_function());
1284 }
1285
1286 #[test]
1287 fn test_is_array_dict() {
1288 assert!(RuntimeValue::Array(vec![]).is_array());
1289 assert!(!RuntimeValue::None.is_array());
1290 assert!(RuntimeValue::Dict(BTreeMap::new()).is_dict());
1291 assert!(!RuntimeValue::None.is_dict());
1292 }
1293
1294 #[test]
1295 fn test_new_dict_and_new_markdown() {
1296 assert!(RuntimeValue::new_dict().is_dict());
1297 let node = mq_markdown::Node::Empty;
1298 assert!(matches!(
1299 RuntimeValue::new_markdown(node),
1300 RuntimeValue::Markdown(_, None)
1301 ));
1302 }
1303
1304 #[test]
1305 fn test_from_vec_runtime_value() {
1306 let arr: RuntimeValue = vec![RuntimeValue::None, RuntimeValue::Boolean(true)].into();
1307 assert!(arr.is_array());
1308 assert_eq!(arr.len(), 2);
1309 }
1310
1311 #[test]
1312 fn test_from_btree_map() {
1313 let mut map = BTreeMap::new();
1314 map.insert(Ident::new("x"), RuntimeValue::Boolean(true));
1315 let dict: RuntimeValue = map.into();
1316 assert!(dict.is_dict());
1317 }
1318
1319 #[test]
1320 fn test_from_usize() {
1321 let v: RuntimeValue = 42usize.into();
1322 assert!(matches!(v, RuntimeValue::Number(_)));
1323 assert_eq!(v.len(), 42);
1324 }
1325
1326 #[test]
1327 fn test_markdown_node_with_no_selector() {
1328 let node = mq_markdown::Node::Empty;
1329 let v = RuntimeValue::Markdown(Box::new(node), None);
1330 assert!(v.markdown_node().is_some());
1331 }
1332
1333 #[test]
1334 fn test_markdown_node_non_markdown_returns_none() {
1335 assert!(RuntimeValue::None.markdown_node().is_none());
1336 assert!(RuntimeValue::String("x".to_string()).markdown_node().is_none());
1337 }
1338
1339 #[test]
1340 fn test_runtime_values_index() {
1341 let values: RuntimeValues =
1342 vec![RuntimeValue::Boolean(true), RuntimeValue::String("second".to_string())].into();
1343 assert_eq!(values[0], RuntimeValue::Boolean(true));
1344 assert_eq!(values[1], RuntimeValue::String("second".to_string()));
1345 }
1346
1347 #[test]
1348 fn test_runtime_values_index_mut() {
1349 let mut values: RuntimeValues = vec![RuntimeValue::None, RuntimeValue::None].into();
1350 values[0] = RuntimeValue::Boolean(true);
1351 assert_eq!(values[0], RuntimeValue::Boolean(true));
1352 }
1353
1354 #[test]
1355 fn test_runtime_values_is_empty() {
1356 let empty: RuntimeValues = vec![].into();
1357 assert!(empty.is_empty());
1358 let non_empty: RuntimeValues = vec![RuntimeValue::None].into();
1359 assert!(!non_empty.is_empty());
1360 }
1361
1362 fn text_node(s: &str) -> mq_markdown::Node {
1363 mq_markdown::Node::Text(mq_markdown::Text {
1364 value: s.to_string(),
1365 position: None,
1366 })
1367 }
1368
1369 fn md(s: &str) -> RuntimeValue {
1370 RuntimeValue::new_markdown(text_node(s))
1371 }
1372
1373 #[test]
1374 fn test_negated_string_reverses() {
1375 let v = RuntimeValue::String("abc".to_string()).negated();
1376 assert_eq!(v, RuntimeValue::String("cba".to_string()));
1377 }
1378
1379 #[test]
1380 fn test_negated_none_returns_self() {
1381 assert_eq!(RuntimeValue::None.negated(), RuntimeValue::None);
1382 }
1383
1384 #[test]
1385 fn test_negated_array_returns_self() {
1386 let arr = RuntimeValue::Array(vec![RuntimeValue::Number(1.into())]);
1387 assert_eq!(arr.clone().negated(), arr);
1388 }
1389
1390 #[test]
1391 fn test_position_non_markdown_returns_none() {
1392 assert!(RuntimeValue::None.position().is_none());
1393 assert!(RuntimeValue::Number(1.into()).position().is_none());
1394 assert!(RuntimeValue::String("x".to_string()).position().is_none());
1395 }
1396
1397 #[test]
1398 fn test_set_position_non_markdown_is_noop() {
1399 let mut v = RuntimeValue::Number(1.into());
1400 v.set_position(None); assert_eq!(v, RuntimeValue::Number(1.into()));
1402 }
1403
1404 #[test]
1405 fn test_to_cbor_value_scalars() {
1406 assert_eq!(RuntimeValue::None.to_cbor_value(), ciborium::Value::Null);
1407 assert_eq!(RuntimeValue::Boolean(true).to_cbor_value(), ciborium::Value::Bool(true));
1408 assert_eq!(
1409 RuntimeValue::Number(1.5.into()).to_cbor_value(),
1410 ciborium::Value::Float(1.5)
1411 );
1412 assert_eq!(
1413 RuntimeValue::String("hi".to_string()).to_cbor_value(),
1414 ciborium::Value::Text("hi".to_string())
1415 );
1416 assert_eq!(
1417 RuntimeValue::Symbol(Ident::new("s")).to_cbor_value(),
1418 ciborium::Value::Text("s".to_string())
1419 );
1420 assert_eq!(
1421 RuntimeValue::Bytes(vec![0x01, 0x02]).to_cbor_value(),
1422 ciborium::Value::Bytes(vec![0x01, 0x02])
1423 );
1424 }
1425
1426 #[test]
1427 fn test_to_cbor_value_array() {
1428 let arr = RuntimeValue::Array(vec![RuntimeValue::Boolean(false)]);
1429 match arr.to_cbor_value() {
1430 ciborium::Value::Array(items) => {
1431 assert_eq!(items[0], ciborium::Value::Bool(false));
1432 }
1433 other => panic!("expected Array, got {other:?}"),
1434 }
1435 }
1436
1437 #[test]
1438 fn test_to_cbor_value_dict() {
1439 let mut map = BTreeMap::new();
1440 map.insert(Ident::new("k"), RuntimeValue::Boolean(true));
1441 let obj = RuntimeValue::Dict(map).to_cbor_value();
1442 match obj {
1443 ciborium::Value::Map(pairs) => {
1444 assert_eq!(pairs[0].0, ciborium::Value::Text("k".to_string()));
1445 assert_eq!(pairs[0].1, ciborium::Value::Bool(true));
1446 }
1447 other => panic!("expected Map, got {other:?}"),
1448 }
1449 }
1450
1451 #[test]
1452 fn test_to_cbor_value_other_is_null() {
1453 let native = RuntimeValue::NativeFunction(Ident::new("f")).to_cbor_value();
1454 assert_eq!(native, ciborium::Value::Null);
1455 }
1456
1457 #[test]
1458 fn test_from_yaml_scalars() {
1459 assert_eq!(RuntimeValue::from(yaml_rust2::Yaml::Null), RuntimeValue::NONE);
1460 assert_eq!(
1461 RuntimeValue::from(yaml_rust2::Yaml::Boolean(true)),
1462 RuntimeValue::Boolean(true)
1463 );
1464 assert_eq!(
1465 RuntimeValue::from(yaml_rust2::Yaml::Integer(42)),
1466 RuntimeValue::Number((42.0_f64).into())
1467 );
1468 assert_eq!(
1469 RuntimeValue::from(yaml_rust2::Yaml::String("hi".to_string())),
1470 RuntimeValue::String("hi".to_string())
1471 );
1472 assert_eq!(RuntimeValue::from(yaml_rust2::Yaml::BadValue), RuntimeValue::NONE);
1473 }
1474
1475 #[test]
1476 fn test_from_yaml_real() {
1477 let v = RuntimeValue::from(yaml_rust2::Yaml::Real("3.14".to_string()));
1478 assert!(matches!(v, RuntimeValue::Number(_)));
1479 }
1480
1481 #[test]
1482 fn test_from_yaml_array() {
1483 let yaml_arr = yaml_rust2::Yaml::Array(vec![yaml_rust2::Yaml::Integer(1), yaml_rust2::Yaml::Integer(2)]);
1484 let v = RuntimeValue::from(yaml_arr);
1485 assert!(matches!(v, RuntimeValue::Array(_)));
1486 if let RuntimeValue::Array(items) = v {
1487 assert_eq!(items.len(), 2);
1488 }
1489 }
1490
1491 #[test]
1492 fn test_from_yaml_hash() {
1493 let mut hash = yaml_rust2::yaml::Hash::new();
1494 hash.insert(
1495 yaml_rust2::Yaml::String("key".to_string()),
1496 yaml_rust2::Yaml::Integer(99),
1497 );
1498 let v = RuntimeValue::from(yaml_rust2::Yaml::Hash(hash));
1499 assert!(matches!(v, RuntimeValue::Dict(_)));
1500 }
1501
1502 #[test]
1503 fn test_from_yaml_alias() {
1504 let v = RuntimeValue::from(yaml_rust2::Yaml::Alias(0));
1505 assert_eq!(v, RuntimeValue::NONE);
1506 }
1507
1508 #[test]
1509 fn test_from_ciborium_tag_unwraps_inner() {
1510 let inner = Box::new(ciborium::Value::Bool(true));
1511 let tagged = ciborium::Value::Tag(1, inner);
1512 let v = RuntimeValue::from(tagged);
1513 assert_eq!(v, RuntimeValue::Boolean(true));
1514 }
1515
1516 #[test]
1517 fn test_from_ciborium_integer() {
1518 let v = RuntimeValue::from(ciborium::Value::Integer(42.into()));
1519 assert!(matches!(v, RuntimeValue::Number(_)));
1520 }
1521
1522 #[test]
1523 fn test_from_ciborium_null_and_unknowns() {
1524 assert_eq!(RuntimeValue::from(ciborium::Value::Null), RuntimeValue::NONE);
1525 }
1526
1527 #[test]
1528 fn test_from_ciborium_map() {
1529 let pairs = vec![(ciborium::Value::Text("k".to_string()), ciborium::Value::Bool(false))];
1530 let v = RuntimeValue::from(ciborium::Value::Map(pairs));
1531 assert!(matches!(v, RuntimeValue::Dict(_)));
1532 }
1533
1534 #[test]
1535 fn test_from_ciborium_map_non_text_key() {
1536 let pairs = vec![(ciborium::Value::Integer(1.into()), ciborium::Value::Bool(true))];
1537 let v = RuntimeValue::from(ciborium::Value::Map(pairs));
1538 assert!(matches!(v, RuntimeValue::Dict(_)));
1539 }
1540
1541 #[rstest]
1542 #[case(mq_markdown::AttrValue::String("s".to_string()), RuntimeValue::String("s".to_string()))]
1543 #[case(mq_markdown::AttrValue::Number(1.0), RuntimeValue::Number(1.0.into()))]
1544 #[case(mq_markdown::AttrValue::Boolean(true), RuntimeValue::Boolean(true))]
1545 #[case(mq_markdown::AttrValue::Null, RuntimeValue::NONE)]
1546 fn test_from_attr_value(#[case] attr: mq_markdown::AttrValue, #[case] expected: RuntimeValue) {
1547 assert_eq!(RuntimeValue::from(attr), expected);
1548 }
1549
1550 #[test]
1551 fn test_from_attr_value_integer() {
1552 let v = RuntimeValue::from(mq_markdown::AttrValue::Integer(42));
1553 assert!(matches!(v, RuntimeValue::Number(_)));
1554 }
1555
1556 #[test]
1557 fn test_from_attr_value_array() {
1558 let arr = mq_markdown::AttrValue::Array(vec![text_node("item")]);
1559 let v = RuntimeValue::from(arr);
1560 assert!(matches!(v, RuntimeValue::Array(_)));
1561 }
1562
1563 #[test]
1564 fn test_from_serde_json_number_f64() {
1565 let n = serde_json::Number::from_f64(1.5).unwrap();
1566 let v = RuntimeValue::from(serde_json::Value::Number(n));
1567 assert!(matches!(v, RuntimeValue::Number(_)));
1568 }
1569
1570 #[test]
1571 fn test_from_serde_json_object() {
1572 let mut obj = serde_json::Map::new();
1573 obj.insert("x".to_string(), serde_json::Value::Bool(true));
1574 let v = RuntimeValue::from(serde_json::Value::Object(obj));
1575 assert!(matches!(v, RuntimeValue::Dict(_)));
1576 }
1577
1578 #[test]
1579 fn test_from_vec_tuple_number() {
1580 let v = RuntimeValue::from(vec![("count".to_string(), Number::from(7.0))]);
1581 if let RuntimeValue::Dict(map) = v {
1582 assert_eq!(map.get(&Ident::new("count")), Some(&RuntimeValue::Number(7.0.into())));
1583 } else {
1584 panic!("expected dict");
1585 }
1586 }
1587
1588 #[test]
1589 fn test_update_with_non_markdown_returns_updated() {
1590 let orig: RuntimeValues = vec![RuntimeValue::Number(1.into())].into();
1591 let updated: RuntimeValues = vec![RuntimeValue::Number(99.into())].into();
1592 let result = orig.update_with(updated);
1593 assert_eq!(result[0], RuntimeValue::Number(99.into()));
1594 }
1595
1596 #[test]
1597 fn test_update_with_markdown_to_none_returns_original() {
1598 let orig: RuntimeValues = vec![md("original")].into();
1599 let updated: RuntimeValues = vec![RuntimeValue::None].into();
1600 let result = orig.update_with(updated);
1601 assert_eq!(result[0], md("original"));
1602 }
1603
1604 #[test]
1605 fn test_update_with_markdown_to_string() {
1606 let orig: RuntimeValues = vec![md("old")].into();
1607 let updated: RuntimeValues = vec![RuntimeValue::String("new".to_string())].into();
1608 let result = orig.update_with(updated);
1609 assert_eq!(result[0].markdown_node().unwrap().value(), "new");
1610 }
1611
1612 #[test]
1613 fn test_update_with_markdown_to_number() {
1614 let orig: RuntimeValues = vec![md("0")].into();
1615 let updated: RuntimeValues = vec![RuntimeValue::Number(42.into())].into();
1616 let result = orig.update_with(updated);
1617 assert_eq!(result[0].markdown_node().unwrap().value(), "42");
1618 }
1619
1620 #[test]
1621 fn test_update_with_markdown_to_boolean() {
1622 let orig: RuntimeValues = vec![md("false")].into();
1623 let updated: RuntimeValues = vec![RuntimeValue::Boolean(true)].into();
1624 let result = orig.update_with(updated);
1625 assert_eq!(result[0].markdown_node().unwrap().value(), "true");
1626 }
1627
1628 #[test]
1629 fn test_update_with_markdown_to_symbol() {
1630 let orig: RuntimeValues = vec![md("sym")].into();
1631 let updated: RuntimeValues = vec![RuntimeValue::Symbol(Ident::new("hello"))].into();
1632 let result = orig.update_with(updated);
1633 assert_eq!(result[0].markdown_node().unwrap().value(), "hello");
1634 }
1635
1636 #[test]
1637 fn test_update_with_markdown_to_bytes() {
1638 let orig: RuntimeValues = vec![md("bytes")].into();
1639 let updated: RuntimeValues = vec![RuntimeValue::Bytes(vec![0xff])].into();
1640 let result = orig.update_with(updated);
1641 assert_eq!(result[0].markdown_node().unwrap().value(), "ff");
1642 }
1643
1644 #[test]
1645 fn test_update_with_markdown_to_array_with_none_filtered() {
1646 let orig: RuntimeValues = vec![md("item")].into();
1647 let updated: RuntimeValues = vec![RuntimeValue::Array(vec![
1648 RuntimeValue::String("a".to_string()),
1649 RuntimeValue::None,
1650 RuntimeValue::String("b".to_string()),
1651 ])]
1652 .into();
1653 let result = orig.update_with(updated);
1654 if let RuntimeValue::Array(items) = &result[0] {
1655 assert_eq!(items.len(), 2);
1656 } else {
1657 panic!("expected Array");
1658 }
1659 }
1660
1661 #[test]
1662 fn test_update_with_markdown_to_dict() {
1663 let orig: RuntimeValues = vec![md("d")].into();
1664 let mut map = BTreeMap::new();
1665 map.insert(Ident::new("a"), RuntimeValue::String("val".to_string()));
1666 map.insert(Ident::new("b"), RuntimeValue::None);
1667 let updated: RuntimeValues = vec![RuntimeValue::Dict(map)].into();
1668 let result = orig.update_with(updated);
1669 if let RuntimeValue::Dict(m) = &result[0] {
1670 assert!(m.contains_key(&Ident::new("a")));
1671 assert!(!m.contains_key(&Ident::new("b"))); } else {
1673 panic!("expected Dict");
1674 }
1675 }
1676
1677 #[test]
1678 fn test_update_with_markdown_to_native_function_returns_original() {
1679 let orig: RuntimeValues = vec![md("orig")].into();
1680 let updated: RuntimeValues = vec![RuntimeValue::NativeFunction(Ident::new("f"))].into();
1681 let result = orig.update_with(updated);
1682 assert_eq!(result[0], md("orig"));
1683 }
1684
1685 #[test]
1686 fn test_update_with_markdown_to_empty_markdown_returns_original() {
1687 let orig: RuntimeValues = vec![md("orig")].into();
1688 let updated: RuntimeValues = vec![RuntimeValue::Markdown(Box::new(mq_markdown::Node::Empty), None)].into();
1689 let result = orig.update_with(updated);
1690 assert_eq!(result[0], md("orig"));
1691 }
1692
1693 #[test]
1694 fn test_update_with_markdown_to_non_empty_markdown_returns_updated() {
1695 let orig: RuntimeValues = vec![md("old")].into();
1696 let updated: RuntimeValues = vec![md("new")].into();
1697 let result = orig.update_with(updated);
1698 assert_eq!(result[0].markdown_node().unwrap().value(), "new");
1699 }
1700
1701 #[test]
1702 fn test_runtime_values_compact() {
1703 let values: RuntimeValues = vec![
1704 RuntimeValue::Number(1.into()),
1705 RuntimeValue::None,
1706 RuntimeValue::String("".to_string()),
1707 RuntimeValue::String("x".to_string()),
1708 ]
1709 .into();
1710 let compact = values.compact();
1711 assert_eq!(compact.len(), 2); }
1713
1714 #[test]
1715 fn test_runtime_values_values() {
1716 let vals = vec![RuntimeValue::Boolean(true), RuntimeValue::None];
1717 let rv: RuntimeValues = vals.clone().into();
1718 assert_eq!(rv.values(), &vals);
1719 }
1720
1721 #[test]
1722 fn test_runtime_values_into_iter() {
1723 let items: RuntimeValues = vec![RuntimeValue::Number(1.into()), RuntimeValue::Number(2.into())].into();
1724 let sum: Vec<_> = items.into_iter().collect();
1725 assert_eq!(sum.len(), 2);
1726 }
1727
1728 #[test]
1729 fn test_module_env_name_and_len() {
1730 use crate::SharedCell;
1731 let env = Shared::new(SharedCell::new(Env::default()));
1732 let m = ModuleEnv::new("mymod", env);
1733 assert_eq!(m.name(), "mymod");
1734 assert_eq!(m.len(), 0);
1735 }
1736
1737 #[test]
1738 fn test_module_partial_cmp() {
1739 use crate::SharedCell;
1740 let e1 = Shared::new(SharedCell::new(Env::default()));
1741 let e2 = Shared::new(SharedCell::new(Env::default()));
1742 let m1 = RuntimeValue::Module(ModuleEnv::new("alpha", e1));
1743 let m2 = RuntimeValue::Module(ModuleEnv::new("beta", e2));
1744 assert!(m1 < m2);
1745 }
1746
1747 #[test]
1748 fn test_cross_type_partial_cmp_is_none() {
1749 let n = RuntimeValue::Number(1.into());
1750 let s = RuntimeValue::String("a".to_string());
1751 assert_eq!(n.partial_cmp(&s), None);
1752 }
1753
1754 #[test]
1755 fn test_ast_partial_cmp_is_none() {
1756 let ast = RuntimeValue::Ast(crate::Shared::new(crate::AstNode {
1757 token_id: crate::arena::ArenaId::new(0),
1758 expr: crate::Shared::new(crate::AstExpr::Self_),
1759 }));
1760 assert_eq!(ast.partial_cmp(&RuntimeValue::None), None);
1761 assert_eq!(RuntimeValue::None.partial_cmp(&ast), None);
1762 }
1763}