aimx/value.rs
1//! Runtime value representation for AIM Expressions (AIMX).
2//!
3//! This module defines the [`Value`] enum that represents runtime values produced
4//! during expression evaluation. The `Value` type serves as the primary data
5//! structure that flows through the expression evaluation engine, representing
6//! all possible data types that can be manipulated by AIMX expressions.
7//!
8//! Unlike the compile-time [`Literal`] enum which represents static values,
9//! `Value` can represent dynamically evaluated results, arrays of values,
10//! closures, and special workflow-related types. This makes it the central
11//! runtime entity for the entire AIMX evaluation system.
12//!
13//! # Design Philosophy
14//!
15//! The `Value` type is designed with several core principles in mind:
16//! - **Tree-like Structure**: Values form a tree where arrays can contain other values, enabling
17//! complex data representations.
18//! - **Type Safety**: Comprehensive type checking and conversion capabilities following AIMX grammar rules
19//! - **Error Handling**: First-class error values that preserve diagnostic information throughout evaluation
20//! - **Memory Efficiency**: Recursive data structures are boxed to prevent stack overflow
21//! - **Thread Safety**: Values can be safely used across threads in concurrent workflows
22//!
23//! # Key Features
24//!
25//! - **Comprehensive Type System**: Supports all AIMX data types including literals,
26//! arrays, closures, and workflow-specific types
27//! - **Automatic Type Conversion**: Provides intuitive type promotion and conversion
28//! methods following AIMX grammar rules
29//! - **Tree-like Structure**: Arrays can contain other values, allowing nested and
30//! complex data representations
31//! - **Error Handling**: Includes error values that capture evaluation failures
32//! - **Workflow Integration**: Supports workflow-specific types like nodes, format,
33//! and eval for agentic workflow applications
34//!
35//! # Core Concepts
36//!
37//! ## Value Hierarchy
38//!
39//! The `Value` enum represents a unified type hierarchy where all values can be categorized into:
40//! - **Base Values**: Empty, Error, and Literal values represent atomic values
41//! - **Composite Values**: Arrays and closures enable structured and functional programming
42//! - **Special Values**: Workflow-specific types for inference functionality
43//!
44//! # Types and Variants
45//!
46//! The `Value` enum encompasses all possible runtime data types in the AIMX system:
47//!
48//! ## Base Values
49//! - **Array**: Collections of values, allowing for nested data structures
50//! - **Closure**: First-class anonymous functions for functional programming
51//! - **Empty**: Represents an empty or uninitialized value
52//! - **Errata**: Captures expression and evaluation error conditions with diagnostic information
53//! - **Literal**: Encapsulates literal values (Bool, Date, Number, Task, Text)
54//!
55//! ## Special Values
56//! - **Assignment**: Variable references for workflow assignments
57//! - **Format**: Inference output formatting data
58//! - **Eval**: Inference evaluation data
59//! - **Node**: References to workflow files and nodes
60//!
61//! # Examples
62//!
63//! ## Creating Values
64//!
65//! ```rust
66//! use aimx::{Value, Literal};
67//!
68//! // Creating different types of values
69//! let empty_value = Value::Empty;
70//! let literal_value = Value::Literal(Literal::from_number(42.0));
71//! let array_value = Value::Array(vec![
72//! Box::new(Value::Literal(Literal::from_text("hello".to_string()))),
73//! Box::new(Value::Literal(Literal::from_number(123.0)))
74//! ]);
75//!
76//! // Checking value types
77//! assert!(empty_value.is_empty());
78//! assert!(literal_value.is_literal());
79//! assert!(literal_value.is_number());
80//! assert!(array_value.is_array());
81//!
82//! // Converting values
83//! let num_as_text = literal_value.clone().as_text().unwrap();
84//! assert!(num_as_text.is_text());
85//! ```
86//!
87//! ## Using Values in Expression Evaluation
88//!
89//! ```rust
90//! use aimx::{Value, Literal, ExpressionLike, Context, Reference, ContextLike};
91//!
92//! let mut context = aimx::Context::new();
93//!
94//! // For this example, we'll just demonstrate creating and using values
95//! let price_value = Value::Literal(Literal::from_number(100.0));
96//! let tax_rate_value = Value::Literal(Literal::from_number(0.08));
97//!
98//! // Calculate total: price * (1 + tax_rate)
99//! let total = 100.0 * (1.0 + 0.08);
100//! assert_eq!(total, 108.0);
101//! ```
102//!
103//! ## Type Conversion Examples
104//!
105//! ```rust
106//! use aimx::{Value, Literal};
107//!
108//! // Type promotion in expressions
109//! let number_value = Value::Literal(Literal::from_number(42.0));
110//!
111//! // Convert number to text
112//! let text_value = number_value.clone().as_text().unwrap();
113//! assert!(text_value.is_text());
114//!
115//! // Convert number to boolean (truthiness)
116//! let bool_value = number_value.clone().as_bool();
117//! assert!(bool_value.to_literal().to_bool());
118//!
119//! // Convert number to date (Unix timestamp)
120//! let date_value = number_value.as_date().unwrap();
121//! assert!(date_value.is_date());
122//! ```
123//!
124//! ## Working with Arrays
125//!
126//! ```rust
127//! use aimx::{Value, Literal};
128//!
129//! // Create nested arrays
130//! let nested_array = Value::Array(vec![
131//! Box::new(Value::Literal(Literal::from_text("first".to_string()))),
132//! Box::new(Value::Array(vec![
133//! Box::new(Value::Literal(Literal::from_number(1.0))),
134//! Box::new(Value::Literal(Literal::from_number(2.0)))
135//! ]))
136//! ]);
137//!
138//! // Check array properties
139//! assert!(nested_array.is_array());
140//!
141//! // Convert single value to array
142//! let single_value = Value::Literal(Literal::from_number(42.0));
143//! let as_array = single_value.as_array();
144//! assert!(as_array.is_array());
145//! ```
146//!
147//! # Type Conversion Rules
148//!
149//! AIMX follows intuitive type conversion rules when values need to be promoted
150//! to different types. The conversion methods (`as_text`, `as_number`, etc.) follow
151//! these principles:
152//!
153//! - **Type Promotion Strategy**: Left-operand-first promotion where the left operand's type
154//! dictates how the right operand is converted
155//! - **Error Handling**: Conversion operations gracefully handle invalid conversions by returning
156//! appropriate error values or `Result` types
157//! - **Idempotency**: Converting a value to its own type typically returns the value unchanged
158//!
159//! - **Empty values** remain empty in most conversions
160//! - **Booleans**: `true`/`false` values maintain their semantic meaning
161//! - **Numbers**: Numeric values can be converted to text, dates, or tasks
162//! - **Text**: String values can be parsed into numbers, dates, or remain as text
163//! - **Dates**: Date values can be converted to timestamps or formatted text
164//! - **Tasks**: Task values preserve their status and description semantics
165//! - **Arrays**: Conversions are applied element-wise to array contents
166//!
167//! # Implementation Details
168//!
169//! The `Value` enum forms the foundation of the AIMX runtime system. It supports:
170//! - **AIMX Grammar Compliance**: All conversion rules match the AIMX specification
171//! - **Memory Efficiency**: Uses `Box<Value>` for recursive data structures to avoid stack overflow
172//! - **Thread Safety**: Can be safely used across threads in concurrent scenarios
173//! - **Serialization**: Provides formatting methods for different display contexts
174//!
175//! # See Also
176//!
177//! - [`Literal`] - Compile-time fixed value representations
178//! - [`ExpressionLike`] - Trait for expression evaluation
179//! - [`ContextLike`] - Evaluation context interface
180//! - [`Value` parsing](parse_value) - Function to parse value literals from text
181use nom::{
182 IResult,
183 Parser,
184 character::complete::{char, multispace0},
185 multi::many1,
186 sequence::delimited,
187};
188use crate::{
189 ContextLike,
190 expressions::Closure,
191 Reference,
192 ExpressionLike,
193 parse_literal,
194 Literal,
195 Node,
196 values::{Errata, Eval, Format},
197 Typedef,
198 Prefix,
199 PrintMode,
200 Writer,
201};
202use std::fmt;
203use std::cmp::Ordering;
204use anyhow::{anyhow, Result};
205
206/// Runtime value representation during expression evaluation.
207///
208/// This enum encapsulates all possible runtime values that can result from
209/// evaluating expressions in the AIMX language. Values are produced when expressions
210/// are evaluated within a context and represent the actual data that flows through
211/// the expression evaluation engine.
212///
213/// The `Value` type forms a tree-like structure where arrays can contain other values,
214/// allowing for nested and complex data representations. This is the primary data
215/// structure used throughout the evaluation phase of the expression engine.
216///
217/// # Variants
218///
219/// - **Empty** - Represents an empty or uninitialized value, typically used as a placeholder
220/// or result of operations that return no meaningful data
221/// - **Errata(Errata)** - Contains evaluation errors with detailed context information
222/// - **Literal(Literal)** - A single literal value (boolean, number, text, date, or task)
223/// - **Array(`Vec<Box<Value>>`)** - An array of values, allowing for collections of data
224/// - **Closure(Closure)** - A first-class anonymous function for functional programming
225/// - **Assignment(Reference)** - A variable reference used for workflow assignments
226/// - **Format(Format)** - Inference output formatting data for agentic workflows
227/// - **Eval(Eval)** - Inference evaluation data for agentic workflows
228/// - **Node(Node)** - A reference to a workflow file or node
229///
230/// # Type Hierarchy
231///
232/// The `Value` enum represents the runtime counterpart to the compile-time [`Typedef`] enum.
233/// While [`Typedef`] represents type definitions, `Value` represents actual runtime data
234/// that conforms to those types.
235///
236/// # Examples
237///
238/// ## Creating Values
239///
240/// ```rust
241/// use aimx::{Value, Literal};
242///
243/// // Creating different value types
244/// let empty = Value::Empty;
245/// let number = Value::Literal(Literal::from_number(42.0));
246/// let text = Value::Literal(Literal::from_text("Hello".to_string()));
247/// let array = Value::Array(vec![
248/// Box::new(Value::Literal(Literal::from_number(1.0))),
249/// Box::new(Value::Literal(Literal::from_number(2.0))),
250/// Box::new(Value::Literal(Literal::from_number(3.0))),
251/// ]);
252///
253/// // Type checking
254/// assert!(empty.is_empty());
255/// assert!(number.is_number());
256/// assert!(text.is_text());
257/// assert!(array.is_array());
258///
259/// // Converting between types
260/// let text_from_number = number.clone().as_text().unwrap();
261/// assert!(text_from_number.is_text());
262/// ```
263///
264/// ## Using Values in Workflow Context
265///
266/// ```rust
267/// use aimx::{Value, Literal, Context, Reference, ContextLike};
268///
269/// let mut context = aimx::Context::new();
270///
271/// // Use assignment references for inference results
272/// let assignment = Value::Assignment(aimx::Reference::new("_result"));
273///
274/// // Use closure for functional programming (simplified example)
275/// // In practice, closures are parsed from expressions like "x => x * 2"
276/// ```
277///
278/// ## Type Conversions
279///
280/// Values can be converted between different types using the `as_*` methods:
281///
282/// ```rust
283/// use aimx::{Value, Literal};
284///
285/// let value = Value::Literal(Literal::from_number(42.0));
286/// let as_text = value.clone().as_text().unwrap();
287/// assert!(as_text.is_text());
288///
289/// let as_bool = value.as_bool();
290/// assert!(as_bool.is_bool());
291/// ```
292///
293/// # Implementation Notes
294///
295/// - **Memory Management**: Arrays use `Box<Value>` to avoid infinite recursion and stack overflow
296/// - **Error Handling**: The `Errata` variant captures detailed error information for debugging
297/// - **Workflow Integration**: Special variants like `Node`, `Format`, and `Eval` support agentic workflows
298/// - **Type Safety**: All conversion methods return `Result` types to handle conversion failures gracefully
299///
300/// # See Also
301///
302/// - [`Literal`] - Compile-time literal value representation
303/// - [`Typedef`] - Type definition system
304/// - [`ExpressionLike`] - Trait for expression evaluation
305/// - [`ContextLike`] - Evaluation context interface
306#[derive(Debug, Clone, PartialEq)]
307pub enum Value {
308 Empty,
309 Errata(Errata),
310 Literal(Literal),
311 Array(Vec<Box<Value>>),
312 Closure(Closure),
313 Assignment(Reference),
314 Format(Format),
315 Eval(Eval),
316 Node(Node),
317}
318
319impl Value {
320 /// Create a Value from a number.
321 ///
322 /// This is a convenience method for creating number values.
323 ///
324 /// # Arguments
325 ///
326 /// * `number` - The numeric value
327 ///
328 /// # Examples
329 ///
330 /// ```rust
331 /// use aimx::Value;
332 ///
333 /// let number_value = Value::from_number(42.0);
334 /// assert!(number_value.is_number());
335 /// ```
336 pub fn from_number(number: f64) -> Value {
337 Value::Literal(Literal::from_number(number))
338 }
339
340 /// Check if this value of the specified type definition.
341 ///
342 /// This method determines whether this value conforms to the specified type
343 /// definition. For arrays, it checks if the first element (if any) matches
344 /// the type. For literals, it checks if the literal matches the type.
345 ///
346 /// # Arguments
347 ///
348 /// * `typedef` - The type definition to check against
349 ///
350 /// # Returns
351 ///
352 /// Returns `true` if this value matches the specified type, `false` otherwise.
353 ///
354 /// # Examples
355 ///
356 /// ```rust
357 /// use aimx::{Value, Literal, Typedef};
358 ///
359 /// let number_value = Value::Literal(Literal::from_number(42.0));
360 /// assert!(number_value.is_of_type(&Typedef::Number));
361 ///
362 /// let text_value = Value::Literal(Literal::from_text("Hello".to_string()));
363 /// assert!(text_value.is_of_type(&Typedef::Text));
364 ///
365 /// let array_value = Value::Array(vec![
366 /// Box::new(Value::Literal(Literal::from_number(1.0))),
367 /// Box::new(Value::Literal(Literal::from_number(2.0))),
368 /// ]);
369 /// assert!(array_value.is_of_type(&Typedef::Number));
370 /// ```
371 pub fn is_of_type(&self, typedef: &Typedef) -> bool {
372 match self {
373 Value::Empty => true,
374 Value::Errata(_) => false,
375 Value::Literal(literal) => typedef.is_literal() && literal.is_type(typedef),
376 Value::Array(array) => {
377 if typedef.is_any_array() {
378 true
379 }
380 else if array.len() > 0 {
381 let literal_typedef = typedef.as_literal();
382 array.last().unwrap().as_ref().is_of_type(&literal_typedef)
383 } else {
384 false
385 }
386 }
387 Value::Closure(_) => typedef.is_closure(),
388 Value::Assignment(_) => true,
389 Value::Format(_) => typedef.is_format(),
390 Value::Eval(_) => typedef.is_eval(),
391 Value::Node(_) => typedef.is_node(),
392 }
393 }
394
395 /// Check if this value matches the actual type definition, considering nested arrays.
396 ///
397 /// This method provides a more precise type checking than `is_of_type` by properly
398 /// handling nested array structures and ensuring the actual type matches the definition.
399 ///
400 /// # Arguments
401 ///
402 /// * `typedef` - The type definition to check against
403 ///
404 /// # Returns
405 ///
406 /// Returns `true` if this value's actual type matches the specified type definition,
407 /// `false` otherwise.
408 pub fn is_actual_type(&self, typedef: &Typedef) -> bool {
409 match self {
410 Value::Empty => true,
411 Value::Errata(_) => false,
412 Value::Literal(literal) => typedef.is_literal() && literal.is_type(typedef),
413 Value::Array(array) => {
414 if typedef.is_any_array() {
415 true
416 } else if typedef.is_literal() {
417 false
418 } else if array.len() > 0 {
419 let last = array.last().unwrap().as_ref();
420 if last.is_array() {
421 last.is_actual_type(typedef)
422 } else {
423 let literal_typedef = typedef.as_literal();
424 last.is_actual_type(&literal_typedef)
425 }
426
427 } else {
428 false
429 }
430 }
431 Value::Closure(_) => typedef.is_closure(),
432 Value::Assignment(_) => false,
433 Value::Format(_) => typedef.is_format(),
434 Value::Eval(_) => typedef.is_eval(),
435 Value::Node(_) => typedef.is_node(),
436 }
437 }
438
439 /// Get the type of this value.
440 ///
441 /// # Returns
442 ///
443 /// Returns a `Result<Typedef>` containing the type of this value,
444 /// or an error if this is an Empty value.
445 pub fn get_type(&self) -> Result<Typedef> {
446 match self {
447 Value::Empty => Err(anyhow!("Expecting type, found Empty")),
448 Value::Errata(_) => Err(anyhow!("Expecting type, found Error")),
449 Value::Literal(literal) => literal.get_type(),
450 Value::Array(array) => {
451 if array.len() > 0 {
452 let typedef = array.last().unwrap().as_ref().get_type()?;
453 Ok(typedef.as_array())
454 } else {
455 Ok(Typedef::Array)
456 }
457 }
458 Value::Closure(_) => Ok(Typedef::Closure),
459 Value::Assignment(_) => Ok(Typedef::Any),
460 Value::Format(_) => Ok(Typedef::Format),
461 Value::Eval(_) => Ok(Typedef::Eval),
462 Value::Node(_) => Ok(Typedef::Node),
463 }
464 }
465
466 /// Convert this value to match the type of another value.
467 ///
468 /// This method performs type conversion according to the AIMX grammar's
469 /// type promotion rules, converting this value to match the type
470 /// of the provided reference value.
471 ///
472 /// Type conversion follows these rules:
473 /// - If the reference value is a `Bool`, converts this value to boolean
474 /// - If the reference value is a `Date`, converts this value to date
475 /// - If the reference value is a `Number`, converts this value to number
476 /// - If the reference value is a `Task`, converts this value to task
477 /// - If the reference value is `Text`, converts this value to text
478 ///
479 /// # Arguments
480 ///
481 /// * `value` - The reference value whose type determines the conversion
482 ///
483 /// # Returns
484 ///
485 /// Returns a `Result<Value>` containing the converted value or an error
486 /// if conversion is not possible (e.g., trying to convert Empty to a specific type).
487 ///
488 /// # Examples
489 ///
490 /// ```rust
491 /// use aimx::{Value, Literal};
492 ///
493 /// let number_value = Value::Literal(Literal::from_number(42.0));
494 /// let text_reference = Value::Literal(Literal::from_text("example".to_string()));
495 ///
496 /// // Convert number to text
497 /// let converted = number_value.clone().as_type(&text_reference).unwrap();
498 /// assert!(converted.is_text());
499 ///
500 /// let bool_reference = Value::Literal(Literal::from_bool(true));
501 /// // Convert number to boolean
502 /// let converted = number_value.as_type(&bool_reference).unwrap();
503 /// assert!(converted.is_bool());
504 /// ```
505 pub fn as_type(self, value: &Value) -> Result<Value> {
506 let literal = value.to_literal();
507 match literal {
508 Literal::Empty => Err(anyhow!("Expecting type as {}, found Empty", literal.type_as_string())),
509 Literal::Bool(_) => Ok(self.as_bool()),
510 Literal::Date(_) => self.as_date(),
511 Literal::Number(_) => self.as_number(),
512 Literal::Task(..) => self.as_task(),
513 Literal::Text(_) => self.as_text(),
514 }
515 }
516
517 /// Convert this value to a specific type definition.
518 ///
519 /// This method performs type conversion according to the AIMX grammar's
520 /// type promotion rules, converting this value to match the specified
521 /// type definition.
522 ///
523 /// # Arguments
524 ///
525 /// * `typedef` - The type definition to convert to
526 ///
527 /// # Returns
528 ///
529 /// Returns a `Result<Value>` containing the converted value or an error
530 /// if conversion is not possible.
531 ///
532 /// # Type Conversion Rules
533 ///
534 /// The conversion follows these rules:
535 /// - **Any**: Returns the value unchanged
536 /// - **Array**: Converts non-arrays to single-element arrays
537 /// - **Literal types**: Converts values to the specified literal type
538 /// - **Array types**: Converts arrays to the specified element type
539 /// - **Closure**: Only allows conversion if already a closure
540 ///
541 /// # Examples
542 ///
543 /// ```rust
544 /// use aimx::{Value, Literal, Typedef};
545 ///
546 /// let number_value = Value::Literal(Literal::from_number(42.0));
547 ///
548 /// // Convert to text
549 /// let text_value = number_value.clone().to_type(&Typedef::Text).unwrap();
550 /// assert!(text_value.is_text());
551 ///
552 /// // Convert to array
553 /// let array_value = number_value.to_type(&Typedef::NumberArray).unwrap();
554 /// assert!(array_value.is_array());
555 /// ```
556 pub fn to_type(self, typedef: &Typedef) -> Result<Value> {
557 // Downgrade to array
558 if self.is_array() && typedef.is_literal() {
559 let literal = self.to_literal().clone().to_type(typedef)?;
560 Ok(Value::Literal(literal))
561 } else {
562 match typedef {
563 // Literally any value
564 Typedef::Any => Ok(self),
565 // Upgrade array
566 Typedef::Array => {
567 if self.is_array() {
568 Ok(self)
569 } else {
570 Ok(self.as_array())
571 }
572 }
573 Typedef::Bool => {
574 if self.is_literal() {
575 Ok(self.as_bool())
576 }
577 else {
578 Ok(self.as_literal().as_bool())
579 }
580 }
581 Typedef::BoolArray => {
582 if self.is_array() {
583 Ok(self.as_bool())
584 } else {
585 Ok(self.as_bool().as_array())
586 }
587 }
588 Typedef::Date => self.as_date(),
589 Typedef::DateArray => {
590 if self.is_array() {
591 self.as_date()
592 } else {
593 let value = self.as_date()?;
594 Ok(value.as_array())
595 }
596 }
597 Typedef::Number => self.as_number(),
598 Typedef::NumberArray => {
599 if self.is_array() {
600 self.as_number()
601 } else {
602 let value = self.as_number()?;
603 Ok(value.as_array())
604 }
605 }
606 Typedef::Task => self.as_task(),
607 Typedef::TaskArray => {
608 if self.is_array() {
609 self.as_task()
610 } else {
611 let value = self.as_task()?;
612 Ok(value.as_array())
613 }
614 }
615 Typedef::Text => self.as_text(),
616 Typedef::TextArray => {
617 if self.is_array() {
618 self.as_text()
619 } else {
620 let value = self.as_text()?;
621 Ok(value.as_array())
622 }
623 }
624 Typedef::Closure => if self.is_closure() {
625 Ok(self)
626 } else {
627 Err(anyhow!("Expecting Closure, found {}", self.type_as_string()))
628 }
629 Typedef::Eval | Typedef::Format | Typedef::Node =>
630 Err(anyhow!("Invalid type conversion from {} to {}", self.type_as_string(), typedef.as_string()))
631 }
632 }
633 }
634
635 /// Check if this value is empty.
636 ///
637 /// Returns `true` if this value is the `Empty` variant, `false` otherwise.
638 ///
639 /// # Examples
640 ///
641 /// ```rust
642 /// use aimx::Value;
643 ///
644 /// let empty_value = Value::Empty;
645 /// let non_empty_value = Value::Literal(aimx::Literal::from_number(42.0));
646 ///
647 /// assert!(empty_value.is_empty());
648 /// assert!(!non_empty_value.is_empty());
649 /// ```
650 pub fn is_empty(&self) -> bool {
651 match self {
652 Value::Empty | Value::Literal(Literal::Empty) => true,
653 _ => false,
654 }
655 }
656
657 /// Check if this value contains an error.
658 ///
659 /// Returns `true` if this value represents an error condition,
660 /// `false` otherwise.
661 pub fn has_error(&self) -> bool {
662 match self {
663 Value::Errata(_) => true,
664 _ => false,
665 }
666 }
667
668 /// Check if this value contains an error (alias for has_error).
669 ///
670 /// Returns `true` if this value represents an error condition,
671 /// `false` otherwise.
672 pub fn has_errata(&self) -> bool {
673 self.has_error()
674 }
675
676 /// Check if this value is a literal.
677 ///
678 /// Returns `true` if this value is the `Literal` variant, `false` otherwise.
679 /// This includes all scalar types: booleans, numbers, text, dates, and tasks.
680 ///
681 /// # Examples
682 ///
683 /// ```rust
684 /// use aimx::{Value, Literal};
685 ///
686 /// let literal_value = Value::Literal(Literal::from_number(42.0));
687 /// let array_value = Value::Array(vec![]);
688 /// let empty_value = Value::Empty;
689 ///
690 /// assert!(literal_value.is_literal());
691 /// assert!(!array_value.is_literal());
692 /// assert!(!empty_value.is_literal());
693 /// ```
694 pub fn is_literal(&self) -> bool {
695 match self {
696 Value::Literal(_) => true,
697 _ => false,
698 }
699 }
700
701
702 /// Check if this value is constant (immutable).
703 ///
704 /// Returns `true` if this value represents a constant value that
705 /// cannot be modified during evaluation, `false` otherwise.
706 pub fn is_constant(&self) -> bool {
707 match self {
708 Value::Literal(_)
709 | Value::Array(_) => true,
710 _ => false,
711 }
712 }
713
714 /// Get a reference to the literal representation of this value.
715 ///
716 /// Returns a reference to a [`Literal`] representing this value's type and content:
717 /// - For literal values, returns a reference to the embedded literal
718 /// - For arrays, returns the literal representation of the last element or `Literal::Empty` for empty arrays
719 /// - For all other value types, returns `Literal::Empty`
720 ///
721 /// This method is useful for type checking and introspection, as it provides
722 /// access to the underlying literal type without consuming the value.
723 ///
724 /// # Examples
725 ///
726 /// ```rust
727 /// use aimx::{Value, Literal};
728 ///
729 /// let literal_value = Value::Literal(Literal::from_number(42.0));
730 /// assert!(literal_value.to_literal().is_number());
731 ///
732 /// let array_value = Value::Array(vec![
733 /// Box::new(Value::Literal(Literal::from_text("first".to_string()))),
734 /// Box::new(Value::Literal(Literal::from_text("last".to_string()))),
735 /// ]);
736 /// assert!(array_value.to_literal().is_text());
737 ///
738 /// let empty_value = Value::Empty;
739 /// assert!(empty_value.to_literal().is_empty());
740 /// ```
741 pub fn to_literal(&self) -> &Literal {
742 match self {
743 Value::Literal(literal) => literal,
744 Value::Array(array) => {
745 if array.len() > 0 {
746 array.last().unwrap().as_ref().to_literal()
747 } else {
748 &Literal::Empty
749 }
750 }
751 _ => &Literal::Empty,
752 }
753 }
754
755 /// Convert this value to its literal representation.
756 ///
757 /// This method consumes the value and returns the underlying literal representation:
758 /// - For literal values, returns the literal itself
759 /// - For arrays, returns the last element or `Value::Empty` for empty arrays
760 /// - For all other value types, returns `Value::Empty`
761 ///
762 /// # Examples
763 ///
764 /// ```rust
765 /// use aimx::{Value, Literal};
766 ///
767 /// let literal_value = Value::Literal(Literal::from_number(42.0));
768 /// let literal = literal_value.as_literal();
769 /// assert!(literal.is_literal());
770 ///
771 /// let array_value = Value::Array(vec![
772 /// Box::new(Value::Literal(Literal::from_text("first".to_string()))),
773 /// Box::new(Value::Literal(Literal::from_text("last".to_string()))),
774 /// ]);
775 /// let literal = array_value.as_literal();
776 /// assert!(literal.is_literal());
777 ///
778 /// let empty_value = Value::Empty;
779 /// let literal = empty_value.as_literal();
780 /// assert!(literal.is_empty());
781 /// ```
782 pub fn as_literal(self) -> Value {
783 match self {
784 Value::Literal(_) => self,
785 Value::Array(mut array) => {
786 if &array.len() > &0 {
787 match array.pop() {
788 Some(value) => *value,
789 None => Value::Empty,
790 }
791 } else {
792 Value::Empty
793 }
794 }
795 _ => Value::Empty,
796 }
797 }
798
799 /// Check if this value is an array.
800 ///
801 /// Returns `true` if this value is the `Array` variant, `false` otherwise.
802 ///
803 /// # Examples
804 ///
805 /// ```
806 /// use aimx::{Value, Literal};
807 ///
808 /// let array_value = Value::Array(vec![]);
809 /// let literal_value = Value::Literal(Literal::from_number(42.0));
810 /// let empty_value = Value::Empty;
811 ///
812 /// assert!(array_value.is_array());
813 /// assert!(!literal_value.is_array());
814 /// assert!(!empty_value.is_array());
815 /// ```
816 pub fn is_array(&self) -> bool {
817 match self {
818 Value::Array(_) => true,
819 _ => false,
820 }
821 }
822
823
824 /// Convert this value to an array representation.
825 ///
826 /// If the value is already an array or empty, returns it unchanged.
827 /// If the value is a literal, wraps it in a single-element array.
828 /// Otherwise, returns an empty value.
829 pub fn as_array(self) -> Value {
830 // Early return if no conversion is needed
831 if self.is_empty() || self.is_array() {
832 self
833 } else if self.is_literal() {
834 let mut array: Vec<Box<Value>> = Vec::new();
835 array.push(Box::new(self));
836 Value::Array(array)
837 } else {
838 Value::Empty
839 }
840 }
841
842 /// Check if this value represents a boolean.
843 ///
844 /// Returns `true` if this value is a literal boolean, `false` otherwise.
845 ///
846 /// # Examples
847 ///
848 /// See [`Value::as_bool`] for examples of boolean conversion behavior.
849 pub fn is_bool(&self) -> bool {
850 match self {
851 Value::Literal(Literal::Bool(_)) => true,
852 _ => false,
853 }
854 }
855
856 /// Convert this value to a boolean representation.
857 ///
858 /// Performs type conversion to boolean according to the AIMX grammar's
859 /// truthiness rules:
860 /// - Empty values: Always false
861 /// - Numbers: 0 is false, non-zero is true
862 /// - Text: Empty string is false, non-empty is true
863 /// - Dates: Always true (they exist)
864 /// - Arrays: Empty array is false, non-empty is true
865 /// - Tasks: Status determines value, no status is false
866 ///
867 /// # Examples
868 ///
869 /// ```
870 /// use aimx::{Value, Literal};
871 ///
872 /// let number_zero = Value::Literal(Literal::from_number(0.0));
873 /// let as_bool = number_zero.as_bool();
874 /// // Zero converts to false
875 /// assert!(!as_bool.to_literal().to_bool());
876 ///
877 /// let number_non_zero = Value::Literal(Literal::from_number(42.0));
878 /// let as_bool = number_non_zero.as_bool();
879 /// // Non-zero converts to true
880 /// assert!(as_bool.to_literal().to_bool());
881 ///
882 /// let empty_text = Value::Literal(Literal::from_text("".to_string()));
883 /// let as_bool = empty_text.as_bool();
884 /// // Empty string converts to false
885 /// assert!(!as_bool.to_literal().to_bool());
886 /// ```
887 pub fn as_bool(self) -> Value {
888 // Early return if no conversion is needed
889 match &self {
890 Value::Empty => return self,
891 Value::Literal(literal) if literal.is_bool() => return self,
892 _ => {}
893 }
894 // Now do the conversion for cases that need it
895 match self {
896 Value::Literal(literal) => Value::Literal(literal.as_bool()),
897 Value::Array(array) => {
898 let bool_array: Vec<Box<Value>> = array
899 .into_iter()
900 .map(|box_value| {
901 let value: Value = *box_value;
902 if value.is_bool() {
903 Box::new(value)
904 } else {
905 let value = value.as_bool();
906 Box::new(value)
907 }
908 })
909 .collect();
910 Value::Array(bool_array)
911 }
912 _ => Value::Empty,
913 }
914 }
915
916 /// Check if this value represents a date.
917 ///
918 /// Returns `true` if this value is a literal date, `false` otherwise.
919 ///
920 /// # Examples
921 ///
922 /// ```
923 /// use aimx::{Value, Literal};
924 ///
925 /// let date_value = Value::Literal(Literal::Date(jiff::civil::DateTime::new(2023, 1, 1, 0, 0, 0, 0).unwrap()));
926 /// let number_value = Value::Literal(Literal::from_number(42.0));
927 ///
928 /// assert!(date_value.is_date());
929 /// assert!(!number_value.is_date());
930 /// ```
931 pub fn is_date(&self) -> bool {
932 match self {
933 Value::Literal(Literal::Date(_)) => true,
934 _ => false,
935 }
936 }
937
938 /// Convert this value to a date representation.
939 ///
940 /// Attempts to convert the value to a date according to the AIMX grammar's
941 /// conversion rules:
942 /// - Empty values: Remain empty (no conversion needed)
943 /// - Dates: No conversion needed
944 /// - Text: Parsed as date if possible (e.g., "2023-01-01")
945 /// - Number: Interpreted as Unix timestamp
946 /// - Boolean: true becomes Unix epoch + 1 second, false becomes Unix epoch
947 /// - Task: Text component parsed as date if possible
948 ///
949 /// # Returns
950 ///
951 /// Returns a `Result<Value>` containing the converted date value or an error
952 /// if conversion is not possible.
953 ///
954 /// # Examples
955 ///
956 /// ```
957 /// use aimx::{Value, Literal};
958 ///
959 /// let text_date = Value::Literal(Literal::from_text("2023-01-01".to_string()));
960 /// let as_date = text_date.as_date().unwrap();
961 /// assert!(as_date.is_date());
962 ///
963 /// let number_timestamp = Value::Literal(Literal::from_number(1672531200.0)); // 2023-01-01 UTC
964 /// let as_date = number_timestamp.as_date().unwrap();
965 /// assert!(as_date.is_date());
966 /// ```
967 pub fn as_date(self) -> Result<Value> {
968 // Early return if no conversion is needed
969 match &self {
970 Value::Empty => return Ok(self),
971 Value::Literal(literal) if literal.is_date() => return Ok(self),
972 _ => {}
973 }
974 // Now do the conversion for cases that need it
975 Ok(match self {
976 Value::Literal(literal) => Value::Literal(literal.as_date()?),
977 Value::Array(array) => {
978 let date_array: Result<Vec<Box<Value>>, anyhow::Error> = array
979 .into_iter()
980 .map(|box_value| {
981 let value: Value = *box_value;
982 if value.is_date() {
983 Ok(Box::new(value))
984 } else {
985 let converted_value = value.as_date()?;
986 Ok(Box::new(converted_value))
987 }
988 })
989 .collect();
990 Value::Array(date_array?)
991 }
992 _ => return Err(anyhow!("Cannot convert {} to Date", self.type_as_string())),
993 })
994 }
995
996 /// Check if this value represents a number.
997 ///
998 /// Returns `true` if this value is a literal number, `false` otherwise.
999 ///
1000 /// # Examples
1001 ///
1002 /// ```rust
1003 /// use aimx::{Value, Literal};
1004 ///
1005 /// let number_value = Value::Literal(Literal::from_number(42.0));
1006 /// let text_value = Value::Literal(Literal::from_text("hello".to_string()));
1007 ///
1008 /// assert!(number_value.is_number());
1009 /// assert!(!text_value.is_number());
1010 /// ```
1011 pub fn is_number(&self) -> bool {
1012 match self {
1013 Value::Literal(Literal::Number(_)) => true,
1014 _ => false,
1015 }
1016 }
1017
1018 /// Convert this value to a number representation.
1019 ///
1020 /// Attempts to convert the value to a number according to the AIMX grammar's
1021 /// conversion rules:
1022 /// - Empty values: Remain empty (no conversion needed)
1023 /// - Numbers: No conversion needed
1024 /// - Text: Parsed as number if it represents a valid numeric literal (e.g., "42", "3.14")
1025 /// - Boolean: false becomes 0, true becomes 1
1026 /// - Date: Converted to Unix timestamp
1027 /// - Task: Status determines value (true=1, false=-1, none=0)
1028 ///
1029 /// # Returns
1030 ///
1031 /// Returns a `Result<Value>` containing the converted number value or an error
1032 /// if conversion is not possible.
1033 ///
1034 /// # Examples
1035 ///
1036 /// ```
1037 /// use aimx::{Value, Literal};
1038 ///
1039 /// let text_number = Value::Literal(Literal::from_text("42".to_string()));
1040 /// let as_number = text_number.as_number().unwrap();
1041 /// assert!(as_number.is_number());
1042 ///
1043 /// let bool_true = Value::Literal(Literal::from_bool(true));
1044 /// let as_number = bool_true.as_number().unwrap();
1045 /// // true converts to 1
1046 /// assert_eq!(as_number.to_literal().to_number().unwrap(), 1.0);
1047 /// ```
1048 pub fn as_number(self) -> Result<Value> {
1049 // Early return if no conversion is needed
1050 match &self {
1051 Value::Empty => return Ok(self),
1052 Value::Literal(literal) if literal.is_number() => return Ok(self),
1053 _ => {}
1054 }
1055 // Now do the conversion for cases that need it
1056 Ok(match self {
1057 Value::Literal(literal) => Value::Literal(literal.as_number()?),
1058 Value::Array(array) => {
1059 let number_array: Result<Vec<Box<Value>>, anyhow::Error> = array
1060 .into_iter()
1061 .map(|box_value| {
1062 let value: Value = *box_value;
1063 if value.is_number() {
1064 Ok(Box::new(value))
1065 } else {
1066 let converted_value = value.as_number()?;
1067 Ok(Box::new(converted_value))
1068 }
1069 })
1070 .collect();
1071 Value::Array(number_array?)
1072 }
1073 _ => return Err(anyhow!("Cannot convert {} to Number", self.type_as_string())),
1074 })
1075 }
1076
1077 /// Extract a numeric value from this value.
1078 ///
1079 /// This is a convenience method for when you specifically need a `f64` number.
1080 /// It's particularly useful for arithmetic operations and mathematical functions.
1081 ///
1082 /// # Returns
1083 ///
1084 /// Returns a `Result<f64>` containing the numeric value or an error if:
1085 /// - The value is empty
1086 /// - The value is an array with no elements
1087 /// - The underlying literal cannot be converted to a number
1088 ///
1089 /// # Examples
1090 ///
1091 /// ```
1092 /// use aimx::{Value, Literal};
1093 ///
1094 /// let number_value = Value::Literal(Literal::from_number(42.5));
1095 /// let num = number_value.to_number().unwrap();
1096 /// assert_eq!(num, 42.5);
1097 ///
1098 /// let array_value = Value::Array(vec![
1099 /// Box::new(Value::Literal(Literal::from_number(10.0))),
1100 /// Box::new(Value::Literal(Literal::from_number(20.0))),
1101 /// ]);
1102 /// let num = array_value.to_number().unwrap();
1103 /// // For arrays, gets the last element's number
1104 /// assert_eq!(num, 20.0);
1105 /// ```
1106 pub fn to_number(&self) -> Result<f64> {
1107 match self {
1108 Value::Literal(literal) => literal.to_number(),
1109 Value::Array(array) => {
1110 if array.len() > 0 {
1111 array.last().unwrap().as_ref().to_number()
1112 } else {
1113 Err(anyhow!("Cannot convert empty Array to Number"))
1114 }
1115 }
1116 _ => Err(anyhow!("Cannot convert {} to Number", self.type_as_string())),
1117 }
1118 }
1119
1120 /// Check if this value represents a task.
1121 ///
1122 /// Returns `true` if this value is a literal task, `false` otherwise.
1123 ///
1124 /// # Examples
1125 ///
1126 /// ```
1127 /// use aimx::{Value, Literal};
1128 ///
1129 /// let task_value = Value::Literal(Literal::from_task(Some(true), "Completed task".to_string()));
1130 /// let text_value = Value::Literal(Literal::from_text("Just text".to_string()));
1131 ///
1132 /// assert!(task_value.is_task());
1133 /// assert!(!text_value.is_task());
1134 /// ```
1135 pub fn is_task(&self) -> bool {
1136 match self {
1137 Value::Literal(Literal::Task(..)) => true,
1138 _ => false,
1139 }
1140 }
1141
1142 /// Convert this value to a task representation.
1143 ///
1144 /// Attempts to convert the value to a task according to the AIMX grammar's
1145 /// conversion rules:
1146 /// - Empty values: Remain empty (no conversion needed)
1147 /// - Tasks: No conversion needed
1148 /// - Boolean: Status becomes the boolean value, text becomes "true"/"false"
1149 /// - Date: No status, text becomes date string
1150 /// - Number: Status based on sign (positive=true, negative=false, zero=none), text becomes number string
1151 /// - Text: No status, text remains the same
1152 ///
1153 /// # Returns
1154 ///
1155 /// Returns a `Result<Value>` containing the converted task value or an error
1156 /// if conversion is not possible.
1157 ///
1158 /// # Examples
1159 ///
1160 /// ```
1161 /// use aimx::{Value, Literal};
1162 ///
1163 /// let bool_true = Value::Literal(Literal::from_bool(true));
1164 /// let as_task = bool_true.as_task().unwrap();
1165 /// assert!(as_task.is_task());
1166 ///
1167 /// let text_value = Value::Literal(Literal::from_text("Pending task".to_string()));
1168 /// let as_task = text_value.as_task().unwrap();
1169 /// // Text converts to task with no status
1170 /// assert_eq!(as_task.to_literal().to_string(), "[ ] Pending task");
1171 /// ```
1172 pub fn as_task(self) -> Result<Value> {
1173 // Early return if no conversion is needed
1174 match &self {
1175 Value::Empty => return Ok(self),
1176 Value::Literal(literal) if literal.is_task() => return Ok(self),
1177 _ => {}
1178 }
1179 // Now do the conversion for cases that need it
1180 Ok(match self {
1181 Value::Literal(literal) => Value::Literal(literal.as_task()?),
1182 Value::Array(array) => {
1183 let task_array: Result<Vec<Box<Value>>, anyhow::Error> = array
1184 .into_iter()
1185 .map(|box_value| {
1186 let value: Value = *box_value;
1187 if value.is_task() {
1188 Ok(Box::new(value))
1189 } else {
1190 let converted_value = value.as_task()?;
1191 Ok(Box::new(converted_value))
1192 }
1193 })
1194 .collect();
1195 Value::Array(task_array?)
1196 }
1197 _ => return Err(anyhow!("Cannot convert {} to Task", self.type_as_string())),
1198 })
1199 }
1200
1201 /// Check if this value represents text.
1202 ///
1203 /// Returns `true` if this value is a literal text, `false` otherwise.
1204 ///
1205 /// # Examples
1206 ///
1207 /// ```
1208 /// use aimx::{Value, Literal};
1209 ///
1210 /// let text_value = Value::Literal(Literal::from_text("Hello".to_string()));
1211 /// let number_value = Value::Literal(Literal::from_number(42.0));
1212 ///
1213 /// assert!(text_value.is_text());
1214 /// assert!(!number_value.is_text());
1215 /// ```
1216 pub fn is_text(&self) -> bool {
1217 match self {
1218 Value::Literal(Literal::Text(_)) => true,
1219 _ => false,
1220 }
1221 }
1222
1223 /// Convert this value to a text representation.
1224 ///
1225 /// Converts the value to text according to the AIMX grammar's conversion rules:
1226 /// - Empty values: Remain empty (no conversion needed)
1227 /// - Text: No conversion needed
1228 /// - Boolean: "true" or "false"
1229 /// - Date: Formatted as ISO 8601 date string (e.g., "2023-01-01T00:00:00.000")
1230 /// - Number: Formatted as string (e.g., "42", "3.14")
1231 /// - Task: Text component of the task (status is preserved in the Value but not in the text conversion)
1232 ///
1233 /// # Returns
1234 ///
1235 /// Returns a `Result<Value>` containing the converted text value or an error
1236 /// if conversion is not possible.
1237 ///
1238 /// # Examples
1239 ///
1240 /// ```rust
1241 /// use aimx::{Value, Literal};
1242 ///
1243 /// let number_value = Value::Literal(Literal::from_number(42.0));
1244 /// let as_text = number_value.as_text().unwrap();
1245 /// assert!(as_text.is_text());
1246 /// assert_eq!(as_text.to_literal().to_string(), "42");
1247 ///
1248 /// let bool_true = Value::Literal(Literal::from_bool(true));
1249 /// let as_text = bool_true.as_text().unwrap();
1250 /// assert_eq!(as_text.to_literal().to_string(), "true");
1251 /// ```
1252 pub fn as_text(self) -> Result<Value> {
1253 // Early return if no conversion is needed
1254 match &self {
1255 Value::Empty => return Ok(self),
1256 Value::Literal(literal) if literal.is_text() => return Ok(self),
1257 _ => {}
1258 }
1259 // Now do the conversion for cases that need it
1260 Ok(match self {
1261 Value::Literal(literal) => Value::Literal(literal.as_text()?),
1262 Value::Array(array) => {
1263 let text_array: Result<Vec<Box<Value>>, anyhow::Error> = array
1264 .into_iter()
1265 .map(|box_value| {
1266 let value: Value = *box_value;
1267 if value.is_text() {
1268 Ok(Box::new(value))
1269 } else {
1270 let converted_value = value.as_text()?;
1271 Ok(Box::new(converted_value))
1272 }
1273 })
1274 .collect();
1275 Value::Array(text_array?)
1276 }
1277 _ => return Err(anyhow!("Cannot convert {} to Text", self.type_as_string())),
1278 })
1279 }
1280
1281 /// Check if this value represents a closure.
1282 ///
1283 /// Returns `true` if this value is a closure, `false` otherwise.
1284 pub fn is_closure(&self) -> bool {
1285 match self {
1286 Value::Closure(_) => true,
1287 _ => false,
1288 }
1289 }
1290
1291 /// Invoke this value as a closure.
1292 ///
1293 /// If this value is a closure, invokes it with the given context.
1294 /// Otherwise, returns an error.
1295 ///
1296 /// # Arguments
1297 ///
1298 /// * `context` - The evaluation context to use for invocation
1299 ///
1300 /// # Returns
1301 ///
1302 /// Returns a `Result<Value>` containing the result of closure invocation
1303 /// or an error if this value is not a closure.
1304 pub fn invoke(&self, context: &mut dyn ContextLike) -> Result<Value> {
1305 match self {
1306 Value::Closure(closure) => closure.invoke(context),
1307 _ => Err(anyhow!("Expecting Closure, found {}", self.type_as_string()))
1308 }
1309 }
1310
1311 /// Check if this value represents a format.
1312 ///
1313 /// Returns `true` if this value is a format, `false` otherwise.
1314 pub fn is_format(&self) -> bool {
1315 match self {
1316 Value::Format(_) => true,
1317 _ => false,
1318 }
1319 }
1320
1321 /// Check if this value represents an eval.
1322 ///
1323 /// Returns `true` if this value is an eval, `false` otherwise.
1324 pub fn is_eval(&self) -> bool {
1325 match self {
1326 Value::Eval(_) => true,
1327 _ => false,
1328 }
1329 }
1330
1331 /// Check if this value represents a node.
1332 ///
1333 /// Returns `true` if this value is a node, `false` otherwise.
1334 pub fn is_node(&self) -> bool {
1335 match self {
1336 Value::Node(_) => true,
1337 _ => false,
1338 }
1339 }
1340
1341 /// Get a string representation of this value's type. Used to provide type information
1342 /// in error messages.
1343 ///
1344 /// Returns a string indicating the type of this value based on its underlying literal
1345 /// representation. Possible return values are: "Empty", "Error", "Bool", "Date", "Number",
1346 /// "Task", "Text", "Closure", "Node".
1347 ///
1348 /// # Examples
1349 ///
1350 /// ```rust
1351 /// use aimx::{Value, Literal};
1352 ///
1353 /// let number_value = Value::Literal(Literal::from_number(42.0));
1354 /// assert_eq!(number_value.type_as_string(), "Number");
1355 ///
1356 /// let text_value = Value::Literal(Literal::from_text("Hello".to_string()));
1357 /// assert_eq!(text_value.type_as_string(), "Text");
1358 ///
1359 /// let array_value = Value::Array(vec![
1360 /// Box::new(Value::Literal(Literal::from_number(1.0))),
1361 /// ]);
1362 /// // For arrays, gets the type of the last element
1363 /// assert_eq!(array_value.type_as_string(), "Number");
1364 /// ```
1365 pub fn type_as_string(&self) -> &'static str {
1366 match self {
1367 Value::Empty => "Empty",
1368 Value::Errata(_) => "Error",
1369 Value::Literal(literal) => literal.type_as_string(),
1370 Value::Array(array) => {
1371 if array.len() > 0 {
1372 array.last().unwrap().as_ref().type_as_string()
1373 } else {
1374 "Empty"
1375 }
1376 }
1377 Value::Closure(_) => "Closure",
1378 Value::Assignment(_) => "Assignment",
1379 Value::Format(_) => "Format",
1380 Value::Eval(_) => "Eval",
1381 Value::Node(_) => "Node",
1382 }
1383 }
1384
1385 /// Convert value to string with specified prefix, for pretty printing.
1386 ///
1387 /// This method formats the value for display with the specified prefix style.
1388 /// It's primarily used for generating human-readable output with appropriate
1389 /// indentation and formatting.
1390 ///
1391 /// # Arguments
1392 ///
1393 /// * `prefix` - The prefix style to use for formatting (None, Unordered, Ordered)
1394 ///
1395 /// # Returns
1396 ///
1397 /// Returns a formatted string representation of the value.
1398 ///
1399 /// # Examples
1400 ///
1401 /// ```
1402 /// use aimx::{Value, Literal, writer::Prefix};
1403 ///
1404 /// let array_value = Value::Array(vec![
1405 /// Box::new(Value::Literal(Literal::from_text("first".to_string()))),
1406 /// Box::new(Value::Literal(Literal::from_text("second".to_string()))),
1407 /// ]);
1408 ///
1409 /// let formatted = array_value.to_pretty(Prefix::Unordered);
1410 /// // Would produce formatted output with bullet points
1411 /// ```
1412 pub fn to_pretty(&self, prefix: Prefix) -> String {
1413 let mut writer = Writer::new(PrintMode::None, prefix);
1414 writer.print_value(&prefix, self);
1415 writer.finish()
1416 }
1417
1418 // Helper function to get type order for consistent sorting
1419 fn type_order(&self) -> u8 {
1420 match self {
1421 Value::Empty => 0,
1422 Value::Errata(_) => 1,
1423 Value::Node(_) => 2,
1424 Value::Eval(_) => 3,
1425 Value::Format(_) => 4,
1426 Value::Assignment(_) => 5,
1427 Value::Closure(_) => 6,
1428 Value::Literal(_) => 7,
1429 Value::Array(_) => 8,
1430 }
1431 }
1432}
1433
1434impl PartialOrd for Value {
1435 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1436 // Define a consistent ordering by type first, then value
1437 // Type ordering: Empty < Bool < Number < Date < Task < Text
1438 let self_type_order = self.type_order();
1439 let other_type_order = other.type_order();
1440 if self_type_order > other_type_order {
1441 Some(Ordering::Greater)
1442 } else if self_type_order < other_type_order {
1443 Some(Ordering::Less)
1444 } else {
1445 match (self, other) {
1446 // Start by assuming the types match
1447 (Value::Empty, Value::Empty) => Some(Ordering::Equal),
1448 (Value::Literal(a), Value::Literal(b)) => a.partial_cmp(b),
1449 (Value::Array(a), Value::Array(b)) => {
1450 // Compare arrays element by element
1451 let len_a = a.len();
1452 let len_b = b.len();
1453 let min_len = std::cmp::min(len_a, len_b);
1454
1455 for i in 0..min_len {
1456 match a[i].partial_cmp(&b[i]) {
1457 Some(Ordering::Equal) => continue,
1458 other => return other,
1459 }
1460 }
1461
1462 // If all compared elements are equal, compare by length
1463 len_a.partial_cmp(&len_b)
1464 }
1465 _ => Some(Ordering::Equal),
1466 }
1467 }
1468 }
1469}
1470
1471impl Eq for Value {}
1472
1473impl Ord for Value {
1474 fn cmp(&self, other: &Self) -> Ordering {
1475 self.partial_cmp(other).unwrap()
1476 }
1477}
1478
1479/// Parse a comma-separated array element.
1480///
1481/// This helper function parses a single element in a comma-separated list,
1482/// handling both literal values and nested arrays. It expects to find a comma
1483/// followed by optional whitespace, then either a literal value or another array.
1484///
1485/// This function is used internally by the array parsing logic to handle
1486/// the elements after the first one in a comma-separated list.
1487///
1488/// # Arguments
1489///
1490/// * `input` - The input string to parse
1491///
1492/// # Returns
1493///
1494/// Returns an `IResult` containing the remaining input and the parsed value.
1495fn parse_comma(input: &str) -> IResult<&str, Value> {
1496 let (input, _) = multispace0.parse(input)?;
1497 let (input, _) = char(',').parse(input)?;
1498 let (input, _) = multispace0.parse(input)?;
1499 let (input, value) = if input.starts_with('(') {
1500 parse_value_array(input)
1501 } else {
1502 let (remain, literal) = parse_literal(input)?;
1503 Ok((remain, Value::Literal(literal)))
1504 }?;
1505 Ok((input, value))
1506}
1507
1508/// Parse a parenthesis-enclosed value array.
1509///
1510/// This function parses arrays that are enclosed in parentheses, such as `(1, 2, 3)`.
1511/// It handles both empty arrays `()` and arrays with values. For singleton literals
1512/// inside parentheses, it creates a single-element array.
1513///
1514/// # Arguments
1515///
1516/// * `input` - The input string to parse
1517///
1518/// # Returns
1519///
1520/// Returns an `IResult` containing the remaining input and the parsed array value.
1521fn parse_value_array(input: &str) -> IResult<&str, Value> {
1522 let (input, value) = delimited(
1523 char('('),
1524 parse_value,
1525 char(')'),
1526 ).parse(input)?;
1527 let value = match value {
1528 Value::Empty => Value::Array(Vec::new()),
1529 Value::Literal(_) => Value::Array(vec![Box::new(value)]),
1530 _ => value,
1531 };
1532 Ok((input, value))
1533}
1534
1535/// Parse a value from input text.
1536///
1537/// This is the main entry point for parsing values in the AIMX expression language.
1538/// It can parse three types of values:
1539/// 1. Empty values (whitespace-only input)
1540/// 2. Literal values (numbers, booleans, text, dates, tasks)
1541/// 3. Arrays (parenthesis-enclosed comma-separated lists)
1542///
1543/// The parser handles whitespace automatically and can parse nested arrays.
1544///
1545/// # Arguments
1546///
1547/// * `input` - The input string to parse
1548///
1549/// # Returns
1550///
1551/// Returns an `IResult` containing the remaining input and the parsed value.
1552///
1553/// # Examples
1554///
1555/// ```rust
1556/// use aimx::value::parse_value;
1557///
1558/// // Parse a number
1559/// let result = parse_value("42");
1560/// assert!(result.is_ok());
1561///
1562/// // Parse an array
1563/// let result = parse_value("(1, 2, 3)");
1564/// assert!(result.is_ok());
1565///
1566/// // Parse nested arrays
1567/// let result = parse_value("((1, 2), (3, 4))");
1568/// assert!(result.is_ok());
1569/// ```
1570pub fn parse_value(input: &str) -> IResult<&str, Value> {
1571 let input = input.trim();
1572 // Value is empty
1573 if input.is_empty() {
1574 return Ok((input, Value::Empty));
1575 }
1576 // Parenthesis enclosed array
1577 let (input, first) = if input.starts_with('(') {
1578 parse_value_array(input)
1579 } else {
1580 match parse_literal(input) {
1581 Ok((remain, literal)) => Ok((remain, Value::Literal(literal))),
1582 _ => Ok((input, Value::Empty)),
1583 }
1584 }?;
1585 if let Ok((input, value_list)) = many1(parse_comma).parse(input) {
1586 let mut array = vec![Box::new(first)];
1587 for conditional in value_list {
1588 array.push(Box::new(conditional));
1589 }
1590 let (input, _) = multispace0(input)?;
1591 Ok((input, Value::Array(array)))
1592 } else {
1593 let (input, _) = multispace0(input)?;
1594 Ok((input, first))
1595 }
1596}
1597
1598impl ExpressionLike for Value {
1599 fn evaluate(&self, _context: &mut dyn ContextLike) -> Result<Value> {
1600 Ok(self.clone())
1601 }
1602
1603 /// Write this value to the provided writer using the appropriate formatting.
1604 ///
1605 /// This implementation delegates to the writer's `print_value` method,
1606 /// which handles the formatting based on the writer's mode (string, sanitized, formula).
1607 ///
1608 /// # Arguments
1609 ///
1610 /// * `writer` - The writer to write to
1611 fn write(&self, writer: &mut Writer) {
1612 let prefix = writer.prefix();
1613 writer.print_value(&prefix, self);
1614 }
1615
1616 /// Convert this value to a sanitized string representation.
1617 ///
1618 /// This method produces a string with special characters escaped
1619 /// to make it safe for various contexts. Useful for generating
1620 /// JSON-compatible or safely quoted output.
1621 ///
1622 /// # Returns
1623 ///
1624 /// A sanitized string representation of this value.
1625 fn to_sanitized(&self) -> String {
1626 let mut writer = Writer::sanitizer();
1627 let prefix = writer.prefix();
1628 writer.print_value(&prefix, self);
1629 writer.finish()
1630 }
1631
1632 /// Convert this value to a formula string representation.
1633 ///
1634 /// This method produces a string with proper quoting and escaping
1635 /// for use in formulas. Useful for generating round-trippable
1636 /// representations that can be parsed back into values.
1637 ///
1638 /// # Returns
1639 ///
1640 /// A formula string representation of this value.
1641 fn to_formula(&self) -> String {
1642 let mut writer = Writer::formulizer();
1643 let prefix = writer.prefix();
1644 writer.print_value(&prefix, self);
1645 writer.finish()
1646 }
1647}
1648
1649impl fmt::Display for Value {
1650 /// Format this value for display using the default string representation.
1651 ///
1652 /// This implementation uses the stringizer writer mode to produce a
1653 /// human-readable representation of the value. For arrays, this will
1654 /// include proper formatting with newlines and indentation where appropriate.
1655 ///
1656 /// # Arguments
1657 ///
1658 /// * `f` - The formatter to write to
1659 ///
1660 /// # Returns
1661 ///
1662 /// A `fmt::Result` indicating success or failure of the formatting operation.
1663 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1664 let mut writer = Writer::stringizer();
1665 let prefix = writer.prefix();
1666 writer.print_value(&prefix, self);
1667 write!(f, "{}", writer.finish())
1668 }
1669}