Skip to main content

shape_ast/ast/
literals.rs

1//! Literal types for Shape AST
2
3use rust_decimal::Decimal;
4use serde::{Deserialize, Serialize};
5
6use crate::data::Timeframe;
7use crate::int_width::IntWidth;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10pub enum InterpolationMode {
11    Braces,
12    Dollar,
13    Hash,
14}
15
16impl InterpolationMode {
17    pub fn prefix(self) -> &'static str {
18        match self {
19            InterpolationMode::Braces => "f",
20            InterpolationMode::Dollar => "f$",
21            InterpolationMode::Hash => "f#",
22        }
23    }
24
25    pub fn sigil(self) -> Option<char> {
26        match self {
27            InterpolationMode::Braces => None,
28            InterpolationMode::Dollar => Some('$'),
29            InterpolationMode::Hash => Some('#'),
30        }
31    }
32}
33
34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
35pub enum Literal {
36    Int(i64),
37    /// Unsigned integer literal > i64::MAX (e.g., 18446744073709551615u64)
38    UInt(u64),
39    /// Explicitly width-typed integer literal (e.g., 42i8, 100u16)
40    TypedInt(i64, IntWidth),
41    Number(f64),
42    /// Decimal type for exact arithmetic (finance, currency)
43    Decimal(Decimal),
44    String(String),
45    /// Unicode scalar value char literal (`'a'`, `'\n'`, `'\u{1F600}'`)
46    Char(char),
47    /// Formatted string literal (`f"..."`, `f$"..."`, `f#"..."` + triple variants)
48    FormattedString {
49        value: String,
50        mode: InterpolationMode,
51    },
52    Bool(bool),
53    /// Option::None literal (replaces null)
54    None,
55    /// Unit literal `()`
56    Unit,
57    Timeframe(Timeframe),
58}
59
60impl std::fmt::Display for Literal {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        match self {
63            Literal::Int(i) => write!(f, "{}", i),
64            Literal::UInt(u) => write!(f, "{}u64", u),
65            Literal::TypedInt(v, w) => write!(f, "{}{}", v, w),
66            Literal::Number(n) => {
67                if n.fract() == 0.0 {
68                    write!(f, "{}", *n as i64)
69                } else {
70                    write!(f, "{}", n)
71                }
72            }
73            Literal::Decimal(d) => write!(f, "{}D", d),
74            Literal::String(s) => write!(f, "\"{}\"", s),
75            Literal::Char(c) => write!(f, "'{}'", c.escape_default()),
76            Literal::FormattedString { value, mode } => write!(f, "{}\"{}\"", mode.prefix(), value),
77            Literal::Bool(b) => write!(f, "{}", b),
78            Literal::None => write!(f, "None"),
79            Literal::Unit => write!(f, "()"),
80            Literal::Timeframe(tf) => write!(f, "{}", tf),
81        }
82    }
83}
84
85impl Literal {
86    /// Convert literal to a JSON value
87    pub fn to_json_value(&self) -> serde_json::Value {
88        match self {
89            Literal::Int(i) => serde_json::json!(*i),
90            Literal::UInt(u) => serde_json::json!(*u),
91            Literal::TypedInt(v, _) => serde_json::json!(*v),
92            Literal::Number(n) => serde_json::json!(*n),
93            Literal::Decimal(d) => serde_json::json!(d.to_string()),
94            Literal::String(s) => serde_json::json!(s),
95            Literal::Char(c) => serde_json::json!(c.to_string()),
96            Literal::FormattedString { value, .. } => serde_json::json!(value),
97            Literal::Bool(b) => serde_json::json!(*b),
98            Literal::None => serde_json::Value::Null,
99            Literal::Unit => serde_json::Value::Null,
100            Literal::Timeframe(t) => serde_json::json!(t.to_string()),
101        }
102    }
103}
104
105#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
106pub struct Duration {
107    pub value: f64,
108    pub unit: DurationUnit,
109}
110
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112pub enum DurationUnit {
113    Seconds,
114    Minutes,
115    Hours,
116    Days,
117    Weeks,
118    Months,
119    Years,
120    Samples,
121}