Skip to main content

reifydb_engine/expression/cast/
temporal.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_core::value::column::data::ColumnData;
5use reifydb_type::{
6	error::{Error, TypeError},
7	fragment::{Fragment, LazyFragment},
8	value::{
9		container::utf8::Utf8Container,
10		date::Date,
11		datetime::DateTime,
12		duration::Duration,
13		temporal::parse::{
14			date::parse_date, datetime::parse_datetime, duration::parse_duration, time::parse_time,
15		},
16		time::Time,
17		r#type::Type,
18	},
19};
20
21use crate::{Result, error::CastError};
22
23pub fn to_temporal(data: &ColumnData, target: Type, lazy_fragment: impl LazyFragment) -> Result<ColumnData> {
24	if let ColumnData::Utf8 {
25		container,
26		..
27	} = data
28	{
29		match target {
30			Type::Date => to_date(container, lazy_fragment),
31			Type::DateTime => to_datetime(container, lazy_fragment),
32			Type::Time => to_time(container, lazy_fragment),
33			Type::Duration => to_duration(container, lazy_fragment),
34			_ => {
35				let source_type = data.get_type();
36				Err(TypeError::UnsupportedCast {
37					from: source_type,
38					to: target,
39					fragment: lazy_fragment.fragment(),
40				}
41				.into())
42			}
43		}
44	} else {
45		let source_type = data.get_type();
46		Err(TypeError::UnsupportedCast {
47			from: source_type,
48			to: target,
49			fragment: lazy_fragment.fragment(),
50		}
51		.into())
52	}
53}
54
55macro_rules! impl_to_temporal {
56	($fn_name:ident, $type:ty, $target_type:expr, $parse_fn:expr) => {
57		#[inline]
58		fn $fn_name<'a>(container: &Utf8Container, lazy_fragment: impl LazyFragment) -> Result<ColumnData> {
59			let mut out = ColumnData::with_capacity($target_type, container.len());
60			for idx in 0..container.len() {
61				if container.is_defined(idx) {
62					let val = &container[idx];
63					// Use internal fragment for parsing - positions will be replaced with actual
64					// source positions
65					let temp_fragment = Fragment::internal(val.as_str());
66
67					let parsed = $parse_fn(temp_fragment).map_err(|mut e| {
68						// Get the original fragment for error reporting
69						let proper_fragment = lazy_fragment.fragment();
70
71						// Handle fragment replacement based on the context
72						// For Internal fragments (from parsing), we need to adjust position
73						if let Fragment::Internal {
74							text: error_text,
75						} = &e.0.fragment
76						{
77							// Check if we're dealing with a string literal (Statement
78							// fragment) that contains position information we can use
79							// for sub-fragments
80							if let Fragment::Statement {
81								text: source_text,
82								..
83							} = &proper_fragment
84							{
85								// For string literals, if the source text exactly
86								// matches the value being parsed, or contains it
87								// with quotes, it's a string literal
88								if &**source_text == val.as_str()
89									|| source_text.contains(&format!(
90										"\"{}\"",
91										val.as_str()
92									)) {
93									// This is a string literal - adjust position
94									// within the string
95									let offset = val
96										.as_str()
97										.find(&**error_text)
98										.unwrap_or(0);
99									e.0.fragment = proper_fragment
100										.sub_fragment(offset, error_text.len());
101								} else {
102									// This is a column reference - use the column
103									// name
104									e.0.fragment = proper_fragment.clone();
105								}
106							} else {
107								// Not a Statement fragment - use as is (for column
108								// references)
109								e.0.fragment = proper_fragment.clone();
110							}
111						}
112
113						Error::from(CastError::InvalidTemporal {
114							fragment: e.0.fragment.clone(),
115							target: $target_type,
116							cause: e.diagnostic(),
117						})
118					})?;
119
120					out.push::<$type>(parsed);
121				} else {
122					out.push_none();
123				}
124			}
125			Ok(out)
126		}
127	};
128}
129
130impl_to_temporal!(to_date, Date, Type::Date, parse_date);
131impl_to_temporal!(to_datetime, DateTime, Type::DateTime, parse_datetime);
132impl_to_temporal!(to_time, Time, Type::Time, parse_time);
133impl_to_temporal!(to_duration, Duration, Type::Duration, parse_duration);