aimx/literal.rs
1//! Literal value parsing and representation for the AIM expression grammar.
2//!
3//! This module defines the [`Literal`] enum that represents literal values in expressions,
4//! including booleans, numbers, text, dates, and tasks. It provides parsing functions
5//! for literal values and comprehensive type conversion capabilities.
6//!
7//! Literal values are the fundamental building blocks of AIM expressions. They represent
8//! concrete values that don't require evaluation and serve as the basis for type promotion
9//! and conversion operations throughout the expression evaluation system.
10//!
11//! # Literal Types
12//!
13//! - **`Empty`** - Represents an empty or null value
14//! - **`Bool`** - Boolean values (`true` or `false`)
15//! - **`Date`** - Date and time values using the `jiff` crate's `DateTime`
16//! - **`Number`** - 64-bit floating point numbers
17//! - **`Task`** - Task primitives with optional status and text description
18//! - **`Text`** - UTF-8 string values
19//!
20//! # Type Conversion Rules
21//!
22//! AIMX uses intuitive type conversion rules that follow these principles:
23//!
24//! - **Boolean conversion**: Non-zero numbers, non-empty text, existing dates are `true`
25//! - **Date conversion**: Numbers as Unix timestamps, text as date strings
26//! - **Number conversion**: Boolean true=1/false=0, dates as timestamps, parsable text
27//! - **Task conversion**: Status from boolean/numbers, text from any convertible value
28//! - **Text conversion**: String representation of any value
29//!
30//! # Examples
31//!
32//! ```rust
33//! use aimx::literal::Literal;
34//! use aimx::typedef::Typedef;
35//!
36//! // Creating literals
37//! let bool_literal = Literal::from_bool(true);
38//! let number_literal = Literal::from_number(42.0);
39//! let text_literal = Literal::from_text("hello".to_string());
40//!
41//! // Type checking
42//! assert!(bool_literal.is_bool());
43//! assert!(number_literal.is_number());
44//! assert!(text_literal.is_text());
45//!
46//! // Type conversion
47//! let number_as_text = number_literal.clone().as_text().unwrap();
48//! assert!(number_as_text.is_text());
49//! assert_eq!(number_as_text.to_string(), "42");
50//!
51//! // Type matching
52//! assert!(number_literal.is_type(&Typedef::Number));
53//! assert!(number_literal.is_type(&Typedef::Any));
54//! ```
55//!
56//! # Parsing
57//!
58//! The module provides parsing functions for all literal types:
59//!
60//! - [`parse_literal`] - Main parser for any literal type
61//! - [`parse_bool`] - Boolean parser (`true`/`false`)
62//! - [`parse_task`] - Task parser (`[status] description`)
63//!
64//! See also: [`parse_date`], [`parse_number`], [`parse_text`] in the [`crate::literals`] module
65
66use crate::{
67 ContextLike,
68 ExpressionLike,
69 literals::{parse_date, parse_number, parse_text},
70 Typedef,
71 Writer,
72 Value,
73};
74use jiff::civil::DateTime;
75use nom::{
76 IResult, Parser,
77 branch::alt,
78 bytes::complete::tag,
79 character::complete::{char, multispace0},
80 combinator::{map, opt},
81};
82use std::fmt;
83use anyhow::{anyhow, Result};
84use std::cmp::{Ordering};
85
86/// Represents a literal value in the AIM expression grammar.
87///
88/// The `Literal` enum encapsulates all fundamental value types that can appear
89/// directly in AIM expressions, without requiring evaluation. Literals form
90/// the basis of the type system and provide the primary mechanism for type
91/// conversion and promotion throughout the expression evaluation pipeline.
92///
93/// # Literal Variants
94///
95/// Each variant represents a specific data type with its own semantics:
96///
97/// - **`Empty`** - Represents an empty or null value. Used as a placeholder
98/// when no meaningful value is present or appropriate. This variant has
99/// special handling in comparison operations and type conversion.
100///
101/// - **`Bool(bool)`** - Boolean values, representing logical true/false states.
102/// Booleans have straightforward truthiness semantics and can be converted
103/// to numbers (1/0) or text ("true"/"false") as needed.
104///
105/// - **`Date(DateTime)`** - Date and time values using the `jiff` crate's
106/// `DateTime` type. Dates support conversion from Unix timestamps (numbers)
107/// and ISO 8601-style date strings while maintaining timezone awareness.
108///
109/// - **`Number(f64)`** - 64-bit floating point numbers. The primary numeric
110/// type used throughout AIMX, supporting arithmetic operations, mathematical
111/// functions, and various conversion contexts.
112///
113/// - **`Task(Option<bool>, String)`** - Task primitives with optional status
114/// and description text. Tasks are a unique semantic type in AIMX that
115/// represent work items in agentic workflows. Status can be `Some(true)`
116/// (completed), `Some(false)` (failed), or `None` (pending).
117///
118/// - **`Text(String)`** - UTF-8 string values. Text literals support the full
119/// Unicode character set and provide comprehensive string manipulation
120/// capabilities through built-in functions.
121///
122/// # Type System Integration
123///
124/// Literals implement comprehensive type checking and conversion capabilities:
125///
126/// - **Type Matching**: The [`Literal::is_type`] method checks compatibility with [`Typedef`]
127/// - **Type Resolution**: The [`Literal::get_type`] method returns the literal's type
128/// - **Type Conversion**: Various `as_*` methods handle type promotion
129/// - **Type Casting**: The [`Literal::as_type`] method converts to match another literal's type
130///
131/// # Operator Implementations
132///
133/// The `Literal` type implements several important traits:
134///
135/// - **`PartialEq`** and **`Eq`** - Equality comparison for all literal types
136/// - **`PartialOrd`** and **`Ord`** - Consistent ordering across different literal types
137/// - **`ExpressionLike`** - Integration with the expression evaluation system
138/// - **`Display`** - Human-readable string representation
139///
140/// # Examples
141///
142/// ## Creating Literals
143///
144/// ```rust
145/// use aimx::literal::Literal;
146///
147/// // Using constructor functions
148/// let bool_lit = Literal::from_bool(true);
149/// let number_lit = Literal::from_number(42.5);
150/// let text_lit = Literal::from_text("hello".to_string());
151///
152/// // Direct enum construction
153/// let task_lit = Literal::Task(Some(true), "Completed task".to_string());
154/// let empty_lit = Literal::Empty;
155/// ```
156///
157/// ## Type Conversion
158///
159/// ```rust
160/// use aimx::literal::Literal;
161///
162/// let number = Literal::from_number(42.0);
163///
164/// // Convert number to text
165/// let as_text = number.clone().as_text().unwrap();
166/// assert_eq!(as_text.to_string(), "42");
167///
168/// // Convert number to boolean (non-zero is true)
169/// let as_bool = number.as_bool();
170/// assert!(as_bool.to_bool());
171/// ```
172///
173/// ## Type Checking
174///
175/// ```rust
176/// use aimx::literal::Literal;
177/// use aimx::typedef::Typedef;
178///
179/// let date_lit = Literal::Date(jiff::civil::DateTime::new(2023, 1, 1, 0, 0, 0, 0).unwrap());
180///
181/// assert!(date_lit.is_date());
182/// assert!(date_lit.is_type(&Typedef::Date));
183/// assert!(date_lit.is_type(&Typedef::Any));
184///
185/// let date_type = date_lit.get_type().unwrap();
186/// assert_eq!(date_type, Typedef::Date);
187/// ```
188///
189/// # See Also
190///
191/// - [`Typedef`] - The type definition system used for type checking
192/// - [`Value`] - The runtime value representation that wraps literals
193/// - [`parse_literal`] - Function for parsing literal values from strings
194#[derive(Debug, Clone, PartialEq)]
195pub enum Literal {
196 /// Represents an empty or null value.
197 ///
198 /// Empty literals are used when no meaningful value is available or appropriate.
199 /// They have special behavior in comparisons and type conversions:
200 /// - Empty compared to empty text is considered equal
201 /// - Cannot be converted to most specific types except boolean (false)
202 /// - Used as default values in various contexts
203 Empty,
204
205 /// A boolean value representing logical true or false.
206 ///
207 /// Boolean literals participate in logical operations and can be converted
208 /// to numbers (1 for true, 0 for false) or text ("true"/"false").
209 Bool(bool),
210
211 /// A date and time value using the `jiff` crate's DateTime type.
212 ///
213 /// Date literals support comprehensive date/time operations including
214 /// date arithmetic, formatting, and timezone-aware operations.
215 Date(DateTime),
216
217 /// A 64-bit floating point number.
218 ///
219 /// Number literals are the primary numeric type in AIMX, supporting
220 /// mathematical operations, functions, and various numeric conversions.
221 Number(f64),
222
223 /// A task primitive with optional status and description text.
224 ///
225 /// Tasks are a semantic type unique to AIMX, representing work items
226 /// in agentic workflows. The status can be:
227 /// - `Some(true)` - Completed task
228 /// - `Some(false)` - Failed task
229 /// - `None` - Pending task
230 ///
231 /// Tasks have special numeric semantics: completed=1, failed=-1, pending=0.
232 Task(Option<bool>, String),
233
234 /// A UTF-8 string value.
235 ///
236 /// Text literals support the full Unicode character set and provide
237 /// comprehensive string manipulation through built-in functions.
238 Text(String),
239}
240
241impl Literal {
242 /// Check if this literal matches the specified type.
243 ///
244 /// # Arguments
245 ///
246 /// * `typedef` - The type definition to check against
247 ///
248 /// # Returns
249 ///
250 /// Returns `true` if this literal matches the specified type, `false` otherwise.
251 pub fn is_type(&self, typedef: &Typedef) -> bool {
252 match self {
253 Literal::Empty => false,
254 Literal::Bool(_) => typedef.is_bool() | typedef.is_any(),
255 Literal::Date(_) => typedef.is_date() | typedef.is_any(),
256 Literal::Number(_) => typedef.is_number() | typedef.is_any(),
257 Literal::Task(..) => typedef.is_task() | typedef.is_any(),
258 Literal::Text(_) => typedef.is_text() | typedef.is_any(),
259 }
260 }
261
262 /// Get the type of this literal.
263 ///
264 /// # Returns
265 ///
266 /// Returns a `Result<Typedef>` containing the type of this literal,
267 /// or an error if this is an Empty literal.
268 pub fn get_type(&self) -> Result<Typedef> {
269 match self {
270 Literal::Empty => Err(anyhow!("Expecting type, found Empty")),
271 Literal::Bool(_) => Ok(Typedef::Bool),
272 Literal::Date(_) => Ok(Typedef::Date),
273 Literal::Number(_) => Ok(Typedef::Number),
274 Literal::Task(..) => Ok(Typedef::Task),
275 Literal::Text(_) => Ok(Typedef::Text),
276 }
277 }
278
279 /// Convert this literal to match the type of another literal.
280 ///
281 /// This method performs type conversion according to the grammar's
282 /// type promotion rules, converting this literal to match the type
283 /// of the provided reference literal.
284 ///
285 /// # Arguments
286 ///
287 /// * `literal` - The reference literal whose type determines the conversion
288 ///
289 /// # Returns
290 ///
291 /// Returns a `Result<Literal>` containing the converted literal or an error
292 /// if conversion is not possible.
293 pub fn as_type(self, literal: &Literal) -> Result<Literal> {
294 match literal {
295 Literal::Empty => Err(anyhow!("Expecting type as {}, found Empty", literal.type_as_string())),
296 Literal::Bool(_) => Ok(self.as_bool()),
297 Literal::Date(_) => self.as_date(),
298 Literal::Number(_) => self.as_number(),
299 Literal::Task(..) => self.as_task(),
300 Literal::Text(_) => self.as_text(),
301 }
302 }
303
304 pub fn to_type(self, typedef: &Typedef) -> Result<Literal> {
305 match typedef {
306 Typedef::Any => Ok(self),
307 Typedef::Bool => Ok(self.as_bool()),
308 Typedef::Date => self.as_date(),
309 Typedef::Number => self.as_number(),
310 Typedef::Task => self.as_task(),
311 Typedef::Text => self.as_text(),
312 _ => Err(anyhow!("Expecting literal type, found {}", typedef.to_string())),
313 }
314 }
315
316 /// Check if this literal is empty.
317 pub fn is_empty(&self) -> bool {
318 match self {
319 Literal::Empty => true,
320 _ => false,
321 }
322 }
323
324 /// Check if this literal represents a boolean value.
325 pub fn is_bool(&self) -> bool {
326 match self {
327 Literal::Bool(_) => true,
328 _ => false,
329 }
330 }
331
332 /// Create a boolean literal from a boolean value.
333 pub fn from_bool(b: bool) -> Self {
334 Literal::Bool(b)
335 }
336
337 /// Convert this literal to a boolean representation.
338 ///
339 /// Performs type conversion to boolean according to the grammar's
340 /// truthiness rules:
341 /// - Numbers: 0 is false, non-zero is true
342 /// - Text: Empty string is false, non-empty is true
343 /// - Dates: Always true (they exist)
344 /// - Tasks: Status determines value, no status is false
345 ///
346 /// This function provides an implicit error free conversion from any
347 /// literal to bool specifically for the conditional ternary operator
348 /// making a type safe error free check possible.
349 ///
350 /// e.g. `user.birthday ? user.birthday : _`
351 pub fn as_bool(self) -> Literal {
352 match self {
353 Literal::Empty => Literal::Bool(false),
354 Literal::Bool(_) => self,
355 Literal::Date(_) => {
356 // Check for non-zero days
357 if let Ok(number) = self.as_number() {
358 number.as_bool()
359 }
360 else {
361 Literal::Bool(false)
362 }
363 }
364 Literal::Number(number) => Literal::Bool(number != 0.0),
365 Literal::Task(status, _) => match status {
366 Some(state) => Literal::Bool(state),
367 None => Literal::Bool(false),
368 },
369 Literal::Text(text) => Literal::Bool(!text.is_empty()),
370 }
371 }
372
373 /// Extract a boolean value from this literal.
374 ///
375 /// This is a convenience method for when you specifically need a `bool` value.
376 pub fn to_bool(&self) -> bool {
377 match self {
378 Literal::Empty => false,
379 Literal::Bool(b) => *b,
380 Literal::Date(_) => {
381 // Check for non-zero days
382 if let Ok(number) = self.to_number() {
383 number != 0.0
384 }
385 else {
386 false
387 }
388 }
389 Literal::Number(number) => *number != 0.0,
390 Literal::Task(status, _) => match *status {
391 Some(state) => state,
392 None => false,
393 },
394 Literal::Text(text) => !text.is_empty(),
395 }
396 }
397
398 /// Check if this literal represents a date value.
399 pub fn is_date(&self) -> bool {
400 match self {
401 Literal::Date(_) => true,
402 _ => false,
403 }
404 }
405
406 /// Create a date literal from a DateTime value.
407 pub fn from_date(d: DateTime) -> Self {
408 Literal::Date(d)
409 }
410
411 /// Convert this literal to a date representation.
412 ///
413 /// Attempts to convert the literal to a date according to the grammar's
414 /// conversion rules:
415 /// - Boolean: true becomes Unix epoch + 1 second, false becomes Unix epoch
416 /// - Number: Interpreted as Unix timestamp
417 /// - Text: Parsed as date if possible
418 /// - Task: Text component parsed as date if possible
419 pub fn as_date(self) -> Result<Literal> {
420 match self {
421 Literal::Empty => Err(anyhow!("Expecting type as Date, found Empty")),
422 Literal::Bool(state) => {
423 // Convert boolean to timestamp: true = 1, false = 0
424 let timestamp = if state { 1i64 } else { 0i64 };
425 match DateTime::new(1970, 1, 1, 0, 0, 0, 0) {
426 Ok(epoch) => {
427 // Add the timestamp seconds to the epoch
428 match jiff::Span::new().try_seconds(timestamp) {
429 Ok(duration) => {
430 match epoch.checked_add(duration) {
431 Ok(new_dt) => Ok(Literal::Date(new_dt)),
432 Err(_) => Ok(Literal::Date(epoch)),
433 }
434 },
435 Err(_) => Ok(Literal::Date(epoch)),
436 }
437 },
438 Err(_) => Err(anyhow!("Failed to create Date from Bool")),
439 }
440 },
441 Literal::Date(_) => Ok(self),
442 Literal::Number(number) => {
443 // Convert number to timestamp (assuming it's a Unix timestamp)
444 let timestamp = number as i64;
445 match DateTime::new(1970, 1, 1, 0, 0, 0, 0) {
446 Ok(epoch) => {
447 // Add the timestamp seconds to the epoch
448 match jiff::Span::new().try_seconds(timestamp) {
449 Ok(duration) => {
450 match epoch.checked_add(duration) {
451 Ok(new_dt) => Ok(Literal::Date(new_dt)),
452 Err(_) => Err(anyhow!("Failed to create Date from Number({})", number)),
453 }
454 },
455 Err(_) => Err(anyhow!("Failed to create Date from Number({})", number)),
456 }
457 },
458 Err(_) => Err(anyhow!("Failed to create Date from Number({})", number)),
459 }
460 },
461 Literal::Task(_, text) => {
462 // Try to parse the task text as a date
463 match parse_date(&text) {
464 Ok((_, date)) => Ok(Literal::Date(date)),
465 Err(_) => Err(anyhow!("Failed to parse Task text as Date")),
466 }
467 },
468 Literal::Text(text) => {
469 // Try to parse the text as a date
470 match parse_date(&text) {
471 Ok((_, date)) => Ok(Literal::Date(date)),
472 Err(_) => Err(anyhow!("Failed to parse Text({}) as Date", text)),
473 }
474 },
475 }
476 }
477
478 /// Check if this literal represents a number value.
479 pub fn is_number(&self) -> bool {
480 match self {
481 Literal::Number(_) => true,
482 _ => false,
483 }
484 }
485
486 /// Create a number literal from an f64 value.
487 pub fn from_number(n: f64) -> Self {
488 Literal::Number(n)
489 }
490
491 /// Convert this literal to a number representation.
492 ///
493 /// Attempts to convert the literal to a number according to the grammar's
494 /// conversion rules:
495 /// - Boolean: false becomes 0, true becomes 1
496 /// - Date: Converted to Unix timestamp
497 /// - Text: Parsed as number if it represents a valid numeric literal
498 /// - Task: Status determines value (true=1, false=-1, none=0)
499 pub fn as_number(self) -> Result<Literal> {
500 match self {
501 Literal::Empty => Err(anyhow!("Expecting type as Number, found Empty")),
502 Literal::Bool(state) => Ok(Literal::Number(if state { 1.0 } else { 0.0 })),
503 Literal::Date(dt) => {
504 if let Ok(utc_dt) = dt.to_zoned(jiff::tz::TimeZone::UTC) {
505 let seconds = utc_dt.timestamp().as_second();
506 Ok(Literal::Number(seconds as f64))
507 } else {
508 Ok(Literal::Number(0.0))
509 }
510 }
511 Literal::Number(_) => Ok(self),
512 Literal::Task(status, _) => match status {
513 Some(state) => Ok(Literal::Number(if state { 1.0 } else { -1.0 })),
514 None => Ok(Literal::Number(0.0)),
515 },
516 Literal::Text(text) => match text.parse::<f64>() {
517 Ok(number) => Ok(Literal::Number(number)),
518 Err(_) => Err(anyhow!("Expecting Text as Number, found \"{}\"", text)),
519 },
520 }
521 }
522
523 /// Extract a numeric value from this literal.
524 ///
525 /// This is a convenience method for when you specifically need a `f64` number.
526 pub fn to_number(&self) -> Result<f64> {
527 match self {
528 Literal::Empty => Err(anyhow!("Expecting type as Number, found Empty")),
529 Literal::Bool(state) => Ok(if *state { 1.0 } else { 0.0 }),
530 Literal::Date(dt) => {
531 if let Ok(utc_dt) = dt.to_zoned(jiff::tz::TimeZone::UTC) {
532 let seconds = utc_dt.timestamp().as_second();
533 Ok(seconds as f64)
534 } else {
535 Ok(0.0)
536 }
537 }
538 Literal::Number(number) => Ok(*number),
539 Literal::Task(status, _) => match *status {
540 Some(state) => Ok(if state { 1.0 } else { -1.0 }),
541 None => Ok(0.0),
542 },
543 Literal::Text(text) => match text.parse::<f64>() {
544 Ok(number) => Ok(number),
545 Err(_) => Err(anyhow!("Expecting Text as Number, found \"{}\"", text)),
546 },
547 }
548 }
549
550 /// Check if this literal represents a task value.
551 pub fn is_task(&self) -> bool {
552 match self {
553 Literal::Task(..) => true,
554 _ => false,
555 }
556 }
557
558 /// Create a task literal from status and text.
559 pub fn from_task(status: Option<bool>, task: String) -> Self {
560 Literal::Task(status, task)
561 }
562
563 /// Convert this literal to a task representation.
564 ///
565 /// Converts the literal to a task according to the grammar's conversion rules:
566 /// - Boolean: Status becomes the boolean value, text becomes "true"/"false"
567 /// - Date: No status, text becomes date string
568 /// - Number: Status based on sign (positive=true, negative=false, zero=none), text becomes number string
569 /// - Text: No status, text remains the same
570 pub fn as_task(self) -> Result<Literal> {
571 match self {
572 Literal::Empty => Err(anyhow!("Expecting type as Task, found Empty")),
573 Literal::Bool(state) => Ok(Literal::Task(Some(state), state.to_string())),
574 Literal::Date(dt) => Ok(Literal::Task(None, dt.to_string())),
575 Literal::Number(number) => {
576 let status = if number > 0.0 {
577 Some(true)
578 } else if number < 0.0 {
579 Some(false)
580 } else {
581 None
582 };
583 Ok(Literal::Task(status, number.to_string()))
584 }
585 Literal::Task(..) => Ok(self),
586 Literal::Text(text) => Ok(Literal::Task(None, text)),
587 }
588 }
589
590 /// Check if this literal represents a text value.
591 pub fn is_text(&self) -> bool {
592 match self {
593 Literal::Text(_) => true,
594 _ => false,
595 }
596 }
597
598 /// Create a text literal from a String.
599 pub fn from_text(t: String) -> Self {
600 Literal::Text(t)
601 }
602
603 /// Convert this literal to a text representation.
604 ///
605 /// Converts the literal to text according to the grammar's conversion rules:
606 /// - Boolean: "true" or "false"
607 /// - Date: Formatted as date string
608 /// - Number: Formatted as string
609 /// - Task: Text component of the task
610 pub fn as_text(self) -> Result<Literal> {
611 match self {
612 Literal::Empty => Err(anyhow!("Expecting type as Text, found Empty")),
613 Literal::Bool(state) => Ok(Literal::Text(state.to_string())),
614 Literal::Date(dt) => Ok(Literal::Text(dt.to_string())),
615 Literal::Number(number) => Ok(Literal::Text(number.to_string())),
616 Literal::Task(_, text) => Ok(Literal::Text(text)),
617 Literal::Text(_) => Ok(self),
618 }
619 }
620
621 /// Get a string representation of this literal's type.
622 pub fn type_as_string(&self) -> &'static str {
623 match self {
624 Literal::Empty => "Empty",
625 Literal::Bool(_) => "Bool",
626 Literal::Date(_) => "Date",
627 Literal::Number(_) => "Number",
628 Literal::Task(..) => "Task",
629 Literal::Text(_) => "Text",
630 }
631 }
632
633 // Helper function to get type order for consistent sorting
634 fn type_order(&self) -> u8 {
635 match self {
636 Literal::Empty => 0,
637 Literal::Bool(_) => 1,
638 Literal::Number(_) => 2,
639 Literal::Date(_) => 3,
640 Literal::Text(_) => 4,
641 Literal::Task(..) => 5,
642 }
643 }
644
645 fn type_promote(self, literal: &Literal) -> Result<Literal> {
646 match literal {
647 Literal::Task(..) => {
648 match self {
649 Literal::Text(t) => Ok(Literal::Task(None, t)),
650 Literal::Date(dt) => Ok(Literal::Task(None, dt.to_string())),
651 Literal::Number(n) => Ok(Literal::Task(None, n.to_string())),
652 //Literal::Bool(b) => Ok(Literal::Task(Some(b), "".to_owned())),
653 _ => Err(anyhow!("Type promote to Task failed")),
654 }
655 }
656 Literal::Text(_) => {
657 match self {
658 Literal::Date(dt) => Ok(Literal::Text(dt.to_string())),
659 Literal::Number(n) => Ok(Literal::Text(n.to_string())),
660 //Literal::Bool(b) => Ok(Literal::Text(b.to_string())),
661 _ => Err(anyhow!("Type promote to Text failed")),
662 }
663 }
664 Literal::Date(_) => {
665 match self {
666 Literal::Number(number) => {
667 // Convert number to timestamp (assuming it's a Unix timestamp)
668 let timestamp = number as i64;
669 match DateTime::new(1970, 1, 1, 0, 0, 0, 0) {
670 Ok(epoch) => {
671 // Add the timestamp seconds to the epoch
672 match jiff::Span::new().try_seconds(timestamp) {
673 Ok(duration) => {
674 match epoch.checked_add(duration) {
675 Ok(new_dt) => Ok(Literal::Date(new_dt)),
676 Err(_) => Err(anyhow!("Failed to create Date from Number({})", number)),
677 }
678 },
679 Err(_) => Err(anyhow!("Failed to create Date from Number({})", number)),
680 }
681 },
682 Err(_) => Err(anyhow!("Failed to create Date from Number({})", number)),
683 }
684 },
685 _ => Err(anyhow!("Type promote to Date failed")),
686 }
687 }
688 Literal::Number(_) => {
689 match self {
690 Literal::Bool(b) => Ok(Literal::Number(if b { 1.0 } else { 0.0 })),
691 _ => Err(anyhow!("Type promote to Task failed")),
692 }
693 }
694 _ => Err(anyhow!("Type promote failed")),
695 }
696 }
697
698}
699
700impl PartialOrd for Literal {
701 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
702 // Check for special cases
703 match (self, other) {
704 (Literal::Text(t), Literal::Bool(b)) => {
705 match t.parse::<bool>() {
706 Ok(a) => return a.partial_cmp(b),
707 _ => {},
708 }
709 }
710 (Literal::Bool(a), Literal::Text(t)) => {
711 match t.parse::<bool>() {
712 Ok(b) => return a.partial_cmp(&b),
713 _ => {},
714 }
715 }
716 (Literal::Text(t), Literal::Number(b)) => {
717 match t.parse::<f64>() {
718 Ok(a) => return a.partial_cmp(b),
719 _ => {},
720 }
721 }
722 (Literal::Number(a), Literal::Text(t)) => {
723 match t.parse::<f64>() {
724 Ok(b) => return a.partial_cmp(&b),
725 _ => {},
726 }
727 }
728 (Literal::Text(t), Literal::Empty) | (Literal::Empty, Literal::Text(t)) => {
729 if t.len() == 0 {
730 return Some(Ordering::Equal);
731 }
732 }
733 _ => {}
734 }
735 // Define a consistent ordering by type first, then value
736 // Type ordering: Empty < Bool < Number < Date < Task < Text
737 let self_type_order = self.type_order();
738 let other_type_order = other.type_order();
739
740 if self_type_order > other_type_order {
741 match other.clone().type_promote(self) {
742 Ok(that) => self.partial_cmp(&that),
743 _ => Some(Ordering::Greater)
744 }
745 } else if self_type_order < other_type_order {
746 match self.clone().type_promote(other) {
747 Ok(this) => this.partial_cmp(other),
748 _ => Some(Ordering::Less)
749 }
750 } else {
751 // Same type, compare values
752 match (self, other) {
753 (Literal::Empty, Literal::Empty) => Some(Ordering::Equal),
754 (Literal::Bool(a), Literal::Bool(b)) => a.partial_cmp(b),
755 (Literal::Date(a), Literal::Date(b)) => a.partial_cmp(b),
756 (Literal::Number(a), Literal::Number(b)) => a.partial_cmp(b),
757 (Literal::Task(status1, text1), Literal::Task(status2, text2)) => {
758 // Order by status first: Some(true) > None > Some(false)
759 match (status1, status2) {
760 (Some(true), Some(false)) => Some(Ordering::Greater),
761 (Some(true), None) => Some(Ordering::Greater),
762 (None, Some(true)) => Some(Ordering::Less),
763 (None, Some(false)) => Some(Ordering::Greater),
764 (Some(false), Some(true)) => Some(Ordering::Less),
765 (Some(false), None) => Some(Ordering::Less),
766 // Same status types or both None, compare texts
767 (Some(true), Some(true)) | (Some(false), Some(false)) | (None, None) => text1.partial_cmp(text2),
768 }
769 },
770 (Literal::Text(a), Literal::Text(b)) => a.partial_cmp(b),
771 _ => Some(Ordering::Equal), // This shouldn't happen due to type ordering check above
772 }
773 }
774 }
775}
776
777impl Eq for Literal {}
778
779impl Ord for Literal {
780 fn cmp(&self, other: &Self) -> Ordering {
781 // Use partial_cmp, but provide a fallback for None cases
782 self.partial_cmp(other).unwrap_or(Ordering::Equal)
783 }
784}
785
786/// Parse a literal value from input text according to AIM grammar rules.
787///
788/// This is the main entry point for parsing literal values in AIM expressions.
789/// It attempts to parse the input as one of the supported literal types:
790/// boolean, date, number, task, or text. The parser uses a precedence order
791/// that matches the grammar's operator precedence rules.
792///
793/// # Supported Literal Types
794///
795/// - **Boolean**: `true` or `false` (case-sensitive)
796/// - **Date**: Various date formats supported by [`parse_date`]
797/// - **Number**: Floating-point numbers with optional sign and decimal point
798/// - **Task**: Task literals with optional status: `[+] text`, `[-] text`, `[ ] text`
799/// - **Text**: Quoted strings using [`parse_text`]
800///
801/// # Arguments
802///
803/// * `input` - A string slice containing the literal to parse
804///
805/// # Returns
806///
807/// * `IResult<&str, Literal>` - A nom parser result containing:
808/// - Remaining input after parsing the literal
809/// - Parsed `Literal` value
810///
811/// # Examples
812///
813/// ```rust
814/// use aimx::{parse_literal, Literal};
815///
816/// // Parse boolean
817/// let (remaining, literal) = parse_literal("true").unwrap();
818/// assert_eq!(remaining, "");
819/// assert_eq!(literal, Literal::Bool(true));
820///
821/// // Parse number
822/// let (remaining, literal) = parse_literal("42.5").unwrap();
823/// assert_eq!(remaining, "");
824/// assert_eq!(literal, Literal::Number(42.5));
825///
826/// // Parse task
827/// let (remaining, literal) = parse_literal("[+] 'Complete task'").unwrap();
828/// assert_eq!(remaining, "");
829/// assert_eq!(literal, Literal::Task(Some(true), "Complete task".to_string()));
830///
831/// // Parse text
832/// let (remaining, literal) = parse_literal("'hello world'").unwrap();
833/// assert_eq!(remaining, "");
834/// assert_eq!(literal, Literal::Text("hello world".to_string()));
835/// ```
836///
837/// # Error Handling
838///
839/// Returns a nom error if the input cannot be parsed as any supported literal type.
840/// The parser will try each type in order until one succeeds or all fail.
841///
842/// # See Also
843///
844/// - [`parse_bool`] - Boolean-specific parser
845/// - [`parse_task`] - Task-specific parser
846/// - [`parse_date`], [`parse_number`], [`parse_text`] - Type-specific parsers in [`crate::literals`] module
847pub fn parse_literal(input: &str) -> IResult<&str, Literal> {
848 let (input, _) = multispace0.parse(input)?;
849 let (input, literal) = alt((
850 map(parse_bool, Literal::Bool),
851 map(parse_date, Literal::Date),
852 map(parse_number, Literal::Number),
853 map(parse_task, |(status, text)| Literal::Task(status, text)),
854 map(parse_text, Literal::Text),
855 ))
856 .parse(input)?;
857 let (input, _) = multispace0.parse(input)?;
858 Ok((input, literal))
859}
860
861/// Parse a boolean literal: `true` or `false`.
862///
863/// This parser recognizes the exact string literals `"true"` and `"false"`
864/// (case-sensitive) as boolean values. It does not accept variations like
865/// `TRUE`, `True`, or numeric representations.
866///
867/// # Arguments
868///
869/// * `input` - A string slice containing the boolean literal to parse
870///
871/// # Returns
872///
873/// * `IResult<&str, bool>` - A nom parser result containing:
874/// - Remaining input after parsing the boolean
875/// - Parsed `bool` value (`true` or `false`)
876///
877/// # Examples
878///
879/// ```rust
880/// use aimx::parse_bool;
881///
882/// let (remaining, value) = parse_bool("true").unwrap();
883/// assert_eq!(remaining, "");
884/// assert_eq!(value, true);
885///
886/// let (remaining, value) = parse_bool("false and more").unwrap();
887/// assert_eq!(remaining, " and more");
888/// assert_eq!(value, false);
889/// ```
890///
891/// # Error Handling
892///
893/// Returns a nom error if the input does not start with `"true"` or `"false"`.
894/// The parser is exact and does not perform case conversion or fuzzy matching.
895pub fn parse_bool(input: &str) -> IResult<&str, bool> {
896 alt((map(tag("true"), |_| true), map(tag("false"), |_| false))).parse(input)
897}
898
899/// Parse a task literal: `[status] description`.
900///
901/// Task literals have a specific syntax that includes an optional status
902/// indicator followed by a text description. The status can be:
903/// - `+` or omitted: Represents a completed task (`Some(true)`)
904/// - `-`: Represents a failed task (`Some(false)`)
905/// - Empty space ` `: Represents a pending task (`None`)
906///
907/// # Syntax
908///
909/// ```text
910/// [status] description
911/// ```
912///
913/// Where `status` is optional and can be:
914/// - `+` for completed (positive)
915/// - `-` for failed (negative)
916/// - ` ` (space) or omitted for pending
917///
918/// # Arguments
919///
920/// * `input` - A string slice containing the task literal to parse
921///
922/// # Returns
923///
924/// * `IResult<&str, (Option<bool>, String)>` - A nom parser result containing:
925/// - Remaining input after parsing the task
926/// - Tuple with optional status (`Some(bool)`) and description text
927///
928/// # Examples
929///
930/// ```rust
931/// use aimx::parse_task;
932///
933/// // Completed task
934/// let (remaining, (status, text)) = parse_task("[+] 'Complete assignment'").unwrap();
935/// assert_eq!(remaining, "");
936/// assert_eq!(status, Some(true));
937/// assert_eq!(text, "Complete assignment");
938///
939/// // Failed task
940/// let (remaining, (status, text)) = parse_task("[-] 'Failed attempt'").unwrap();
941/// assert_eq!(remaining, "");
942/// assert_eq!(status, Some(false));
943/// assert_eq!(text, "Failed attempt");
944///
945/// // Pending task
946/// let (remaining, (status, text)) = parse_task("[ ] 'Pending review'").unwrap();
947/// assert_eq!(remaining, "");
948/// assert_eq!(status, None);
949/// assert_eq!(text, "Pending review");
950/// ```
951///
952/// # Error Handling
953///
954/// Returns a nom error if the input does not follow the task literal syntax:
955/// - Missing opening bracket `[`
956/// - Missing closing bracket `]`
957/// - Invalid status character (only `+`, `-`, or space allowed)
958///
959/// # See Also
960///
961/// - [`Literal::Task`] - The task literal variant
962/// - [`parse_text`] - Used to parse the task description text
963pub fn parse_task(input: &str) -> IResult<&str, (Option<bool>, String)> {
964 let (input, _) = char('[').parse(input)?;
965 let (input, _) = multispace0.parse(input)?;
966
967 // Parse optional status
968 let (input, status) =
969 opt(alt((map(char('+'), |_| true), map(char('-'), |_| false)))).parse(input)?;
970
971 let (input, _) = multispace0.parse(input)?;
972 let (input, _) = char(']').parse(input)?;
973 let (input, _) = multispace0.parse(input)?;
974
975 // Parse the task text (take everything until end or significant delimiter)
976 let (input, text) = parse_text(input)?;
977 Ok((input, (status, text)))
978}
979
980impl ExpressionLike for Literal {
981 fn evaluate(&self, _context: &mut dyn ContextLike) -> Result<Value> {
982 Ok(Value::Literal(self.clone()))
983 }
984
985 fn write(&self, writer: &mut Writer) {
986 writer.print_literal(self);
987 }
988 fn to_sanitized(&self) -> String {
989 let mut writer = Writer::sanitizer();
990 writer.print_literal(self);
991 writer.finish()
992 }
993 fn to_formula(&self) -> String {
994 let mut writer = Writer::formulizer();
995 writer.print_literal(self);
996 writer.finish()
997 }
998}
999
1000impl fmt::Display for Literal {
1001 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1002 let mut writer = Writer::stringizer();
1003 writer.print_literal(self);
1004 write!(f, "{}", writer.finish())
1005 }
1006}
1007