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