1use crate::registry::*;
2use akar_common::types::{Date, Interval, Timestamp, Value};
3use time::{Date as TimeDate, Month, Time as TimeTime};
4
5pub(crate) fn evaluate_cast(target: CastTarget, args: &[Value]) -> Result<Value, String> {
8 if args.is_empty() {
9 return Err("Cast requires an argument".into());
10 }
11 let v = &args[0];
12
13 match target {
14 CastTarget::String => Ok(Value::String(format!("{:?}", v))),
15 CastTarget::Int64 => match v {
16 Value::Int64(x) => Ok(Value::Int64(*x)),
17 Value::Int32(x) => Ok(Value::Int64(*x as i64)),
18 Value::Int16(x) => Ok(Value::Int64(*x as i64)),
19 Value::Int8(x) => Ok(Value::Int64(*x as i64)),
20 Value::UInt64(x) => Ok(Value::Int64(*x as i64)),
21 Value::UInt32(x) => Ok(Value::Int64(*x as i64)),
22 Value::UInt16(x) => Ok(Value::Int64(*x as i64)),
23 Value::UInt8(x) => Ok(Value::Int64(*x as i64)),
24 Value::Double(x) => Ok(Value::Int64(*x as i64)),
25 Value::Float(x) => Ok(Value::Int64(*x as i64)),
26 Value::Bool(x) => Ok(Value::Int64(if *x { 1 } else { 0 })),
27 Value::String(s) => s
28 .parse::<i64>()
29 .map(Value::Int64)
30 .map_err(|e| format!("Cannot cast string to int: {e}")),
31 _ => Err("Cannot cast to Int64".into()),
32 },
33 CastTarget::Int32 => match v {
34 Value::Int32(x) => Ok(Value::Int32(*x)),
35 Value::Int64(x) => Ok(Value::Int32(*x as i32)),
36 Value::Int16(x) => Ok(Value::Int32(*x as i32)),
37 Value::Int8(x) => Ok(Value::Int32(*x as i32)),
38 Value::Double(x) => Ok(Value::Int32(*x as i32)),
39 Value::Float(x) => Ok(Value::Int32(*x as i32)),
40 Value::String(s) => s
41 .parse::<i32>()
42 .map(Value::Int32)
43 .map_err(|e| format!("Cannot cast string to int32: {e}")),
44 _ => Err("Cannot cast to Int32".into()),
45 },
46 CastTarget::Double => match v {
47 Value::Int64(x) => Ok(Value::Double(*x as f64)),
48 Value::Int32(x) => Ok(Value::Double(*x as f64)),
49 Value::Int16(x) => Ok(Value::Double(*x as f64)),
50 Value::Int8(x) => Ok(Value::Double(*x as f64)),
51 Value::Double(x) => Ok(Value::Double(*x)),
52 Value::Float(x) => Ok(Value::Double(*x as f64)),
53 Value::String(s) => s
54 .parse::<f64>()
55 .map(Value::Double)
56 .map_err(|e| format!("Cannot cast string to double: {e}")),
57 _ => Err("Cannot cast to Double".into()),
58 },
59 CastTarget::Float => match v {
60 Value::Float(x) => Ok(Value::Float(*x)),
61 Value::Int64(x) => Ok(Value::Float(*x as f32)),
62 Value::Int32(x) => Ok(Value::Float(*x as f32)),
63 Value::Double(x) => Ok(Value::Float(*x as f32)),
64 Value::String(s) => s
65 .parse::<f32>()
66 .map(Value::Float)
67 .map_err(|e| format!("Cannot cast string to float: {e}")),
68 _ => Err("Cannot cast to Float".into()),
69 },
70 CastTarget::Bool => match v {
71 Value::Bool(x) => Ok(Value::Bool(*x)),
72 Value::Int64(x) => Ok(Value::Bool(*x != 0)),
73 Value::Int32(x) => Ok(Value::Bool(*x != 0)),
74 Value::String(s) => {
75 let lower = s.to_lowercase();
76 match lower.as_str() {
77 "true" | "yes" | "1" => Ok(Value::Bool(true)),
78 "false" | "no" | "0" => Ok(Value::Bool(false)),
79 _ => Err(format!("Cannot cast string '{}' to Bool", s)),
80 }
81 }
82 _ => Err("Cannot cast to Bool".into()),
83 },
84 CastTarget::Date => match v {
85 Value::Date(x) => Ok(Value::Date(*x)),
86 Value::Timestamp(t) => {
87 let days = (t.0.div_euclid(1_000_000) / 86400) as i32;
89 Ok(Value::Date(Date(days)))
90 }
91 Value::String(s) => parse_date_string(s).map(Value::Date),
92 _ => Err("Cannot cast to Date".into()),
93 },
94 CastTarget::Timestamp => match v {
95 Value::Timestamp(x) => Ok(Value::Timestamp(*x)),
96 Value::Date(d) => Ok(Value::Timestamp(Timestamp(d.0 as i64 * 86400 * 1_000_000))),
97 Value::String(s) => parse_timestamp_string(s).map(Value::Timestamp),
98 _ => Err("Cannot cast to Timestamp".into()),
99 },
100 CastTarget::Interval => match v {
101 Value::Interval(x) => Ok(Value::Interval(*x)),
102 Value::Int64(x) => {
103 Ok(Value::Interval(Interval {
105 months: 0,
106 days: 0,
107 micros: *x,
108 }))
109 }
110 _ => Err("Cannot cast to Interval".into()),
111 },
112 }
113}
114
115fn parse_date_string(s: &str) -> Result<Date, String> {
117 let s = s.trim();
118 let parts: Vec<&str> = s.split('-').collect();
119 if parts.len() != 3 {
120 return Err(format!("Cannot parse date '{}': expected YYYY-MM-DD", s));
121 }
122 let year: i32 = parts[0].parse().map_err(|_| format!("Invalid year in '{}'", s))?;
123 let month: u32 = parts[1].parse().map_err(|_| format!("Invalid month in '{}'", s))?;
124 let day: u8 = parts[2].parse().map_err(|_| format!("Invalid day in '{}'", s))?;
125 let month_enum = Month::try_from(month as u8).map_err(|_| format!("Invalid month in '{}'", s))?;
126 let date = TimeDate::from_calendar_date(year, month_enum, day).map_err(|e| format!("Invalid date '{}': {e}", s))?;
127 let epoch = TimeDate::from_calendar_date(1970, Month::January, 1).map_err(|e| format!("Date error: {e}"))?;
128 let days = (date - epoch).whole_days() as i32;
129 Ok(Date(days))
130}
131
132fn parse_timestamp_string(s: &str) -> Result<Timestamp, String> {
134 let s = s.trim();
135 let (date_part, time_part) = match s.find(' ') {
136 Some(idx) => (&s[..idx], &s[idx + 1..]),
137 None => (s, "00:00:00"),
138 };
139 let date = parse_date_string(date_part)?;
140 let mut time_parts: Vec<&str> = time_part.split(':').collect();
141 if time_parts.len() != 3 {
142 return Err(format!("Cannot parse time '{}': expected HH:MM:SS", time_part));
143 }
144 let sec_str = time_parts.pop().unwrap_or("0");
145 let (sec_str, frac_micros) = match sec_str.find('.') {
146 Some(idx) => {
147 let frac = &sec_str[idx + 1..];
148 let micros = if frac.is_empty() {
149 0
150 } else {
151 let padded = format!("{:<6}", frac);
152 padded.chars().take(6).collect::<String>().parse::<i64>().unwrap_or(0)
153 };
154 (&sec_str[..idx], micros)
155 }
156 None => (sec_str, 0),
157 };
158 let hour: u8 = time_parts[0]
159 .parse()
160 .map_err(|_| format!("Invalid hour in '{}'", time_part))?;
161 let minute: u8 = time_parts[1]
162 .parse()
163 .map_err(|_| format!("Invalid minute in '{}'", time_part))?;
164 let second: u8 = sec_str
165 .parse()
166 .map_err(|_| format!("Invalid second in '{}'", time_part))?;
167 let time = TimeTime::from_hms(hour, minute, second).map_err(|e| format!("Invalid time '{}': {e}", time_part))?;
168 let seconds = time.as_hms().0 as i64 * 3600 + time.as_hms().1 as i64 * 60 + time.as_hms().2 as i64;
169 let micros = (date.0 as i64 * 86400) + seconds * 1_000_000 + frac_micros;
170 Ok(Timestamp(micros))
171}