Skip to main content

reifydb_engine/expression/cast/
temporal.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_core::value::column::buffer::ColumnBuffer;
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: &ColumnBuffer, target: Type, lazy_fragment: impl LazyFragment) -> Result<ColumnBuffer> {
24	if let ColumnBuffer::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 shape_type = data.get_type();
36				Err(TypeError::UnsupportedCast {
37					from: shape_type,
38					to: target,
39					fragment: lazy_fragment.fragment(),
40				}
41				.into())
42			}
43		}
44	} else {
45		let shape_type = data.get_type();
46		Err(TypeError::UnsupportedCast {
47			from: shape_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(container: &Utf8Container, lazy_fragment: impl LazyFragment) -> Result<ColumnBuffer> {
59			let mut out = ColumnBuffer::with_capacity($target_type, container.len());
60			for idx in 0..container.len() {
61				if container.is_defined(idx) {
62					let val = container.get(idx).unwrap();
63					// Use internal fragment for parsing - positions will be replaced with actual
64					// source positions
65					let temp_fragment = Fragment::internal(val);
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
89									|| source_text.contains(&format!("\"{}\"", val))
90								{
91									// This is a string literal - adjust position
92									// within the string
93									let offset =
94										val.find(&**error_text).unwrap_or(0);
95									e.0.fragment = proper_fragment
96										.sub_fragment(offset, error_text.len());
97								} else {
98									// This is a column reference - use the column
99									// name
100									e.0.fragment = proper_fragment.clone();
101								}
102							} else {
103								// Not a Statement fragment - use as is (for column
104								// references)
105								e.0.fragment = proper_fragment.clone();
106							}
107						}
108
109						Error::from(CastError::InvalidTemporal {
110							fragment: e.0.fragment.clone(),
111							target: $target_type,
112							cause: e.diagnostic(),
113						})
114					})?;
115
116					out.push::<$type>(parsed);
117				} else {
118					out.push_none();
119				}
120			}
121			Ok(out)
122		}
123	};
124}
125
126impl_to_temporal!(to_date, Date, Type::Date, parse_date);
127impl_to_temporal!(to_datetime, DateTime, Type::DateTime, parse_datetime);
128impl_to_temporal!(to_time, Time, Type::Time, parse_time);
129impl_to_temporal!(to_duration, Duration, Type::Duration, parse_duration);