Skip to main content

reifydb_routine/function/time/
new.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_core::value::column::{ColumnWithName, buffer::ColumnBuffer, columns::Columns};
5use reifydb_type::value::{container::temporal::TemporalContainer, time::Time, r#type::Type};
6
7use crate::routine::{Function, FunctionKind, Routine, RoutineInfo, context::FunctionContext, error::RoutineError};
8
9pub struct TimeNew {
10	info: RoutineInfo,
11}
12
13impl Default for TimeNew {
14	fn default() -> Self {
15		Self::new()
16	}
17}
18
19impl TimeNew {
20	pub fn new() -> Self {
21		Self {
22			info: RoutineInfo::new("time::new"),
23		}
24	}
25}
26
27fn extract_i32(data: &ColumnBuffer, i: usize) -> Option<i32> {
28	match data {
29		ColumnBuffer::Int1(c) => c.get(i).map(|&v| v as i32),
30		ColumnBuffer::Int2(c) => c.get(i).map(|&v| v as i32),
31		ColumnBuffer::Int4(c) => c.get(i).copied(),
32		ColumnBuffer::Int8(c) => c.get(i).map(|&v| v as i32),
33		ColumnBuffer::Int16(c) => c.get(i).map(|&v| v as i32),
34		ColumnBuffer::Uint1(c) => c.get(i).map(|&v| v as i32),
35		ColumnBuffer::Uint2(c) => c.get(i).map(|&v| v as i32),
36		ColumnBuffer::Uint4(c) => c.get(i).map(|&v| v as i32),
37		ColumnBuffer::Uint8(c) => c.get(i).map(|&v| v as i32),
38		ColumnBuffer::Uint16(c) => c.get(i).map(|&v| v as i32),
39		_ => None,
40	}
41}
42
43fn is_integer_type(data: &ColumnBuffer) -> bool {
44	matches!(
45		data,
46		ColumnBuffer::Int1(_)
47			| ColumnBuffer::Int2(_)
48			| ColumnBuffer::Int4(_)
49			| ColumnBuffer::Int8(_)
50			| ColumnBuffer::Int16(_)
51			| ColumnBuffer::Uint1(_)
52			| ColumnBuffer::Uint2(_)
53			| ColumnBuffer::Uint4(_)
54			| ColumnBuffer::Uint8(_)
55			| ColumnBuffer::Uint16(_)
56	)
57}
58
59impl<'a> Routine<FunctionContext<'a>> for TimeNew {
60	fn info(&self) -> &RoutineInfo {
61		&self.info
62	}
63
64	fn return_type(&self, _input_types: &[Type]) -> Type {
65		Type::Time
66	}
67
68	fn execute(&self, ctx: &mut FunctionContext<'a>, args: &Columns) -> Result<Columns, RoutineError> {
69		if args.len() != 3 && args.len() != 4 {
70			return Err(RoutineError::FunctionArityMismatch {
71				function: ctx.fragment.clone(),
72				expected: 3,
73				actual: args.len(),
74			});
75		}
76
77		let hour_col = &args[0];
78		let min_col = &args[1];
79		let sec_col = &args[2];
80		let nano_col = if args.len() == 4 {
81			Some(&args[3])
82		} else {
83			None
84		};
85
86		let (hour_data, _) = hour_col.unwrap_option();
87		let (min_data, _) = min_col.unwrap_option();
88		let (sec_data, _) = sec_col.unwrap_option();
89		let nano_data = nano_col.map(|c| c.unwrap_option());
90
91		if !is_integer_type(hour_data) {
92			return Err(RoutineError::FunctionInvalidArgumentType {
93				function: ctx.fragment.clone(),
94				argument_index: 0,
95				expected: vec![
96					Type::Int1,
97					Type::Int2,
98					Type::Int4,
99					Type::Int8,
100					Type::Int16,
101					Type::Uint1,
102					Type::Uint2,
103					Type::Uint4,
104					Type::Uint8,
105					Type::Uint16,
106				],
107				actual: hour_data.get_type(),
108			});
109		}
110		if !is_integer_type(min_data) {
111			return Err(RoutineError::FunctionInvalidArgumentType {
112				function: ctx.fragment.clone(),
113				argument_index: 1,
114				expected: vec![
115					Type::Int1,
116					Type::Int2,
117					Type::Int4,
118					Type::Int8,
119					Type::Int16,
120					Type::Uint1,
121					Type::Uint2,
122					Type::Uint4,
123					Type::Uint8,
124					Type::Uint16,
125				],
126				actual: min_data.get_type(),
127			});
128		}
129		if !is_integer_type(sec_data) {
130			return Err(RoutineError::FunctionInvalidArgumentType {
131				function: ctx.fragment.clone(),
132				argument_index: 2,
133				expected: vec![
134					Type::Int1,
135					Type::Int2,
136					Type::Int4,
137					Type::Int8,
138					Type::Int16,
139					Type::Uint1,
140					Type::Uint2,
141					Type::Uint4,
142					Type::Uint8,
143					Type::Uint16,
144				],
145				actual: sec_data.get_type(),
146			});
147		}
148		if let Some((nd, _)) = &nano_data
149			&& !is_integer_type(nd)
150		{
151			return Err(RoutineError::FunctionInvalidArgumentType {
152				function: ctx.fragment.clone(),
153				argument_index: 3,
154				expected: vec![
155					Type::Int1,
156					Type::Int2,
157					Type::Int4,
158					Type::Int8,
159					Type::Uint1,
160					Type::Uint2,
161					Type::Uint4,
162				],
163				actual: nd.get_type(),
164			});
165		}
166
167		let row_count = hour_data.len();
168		let mut container = TemporalContainer::with_capacity(row_count);
169
170		for i in 0..row_count {
171			let hour = extract_i32(hour_data, i);
172			let min = extract_i32(min_data, i);
173			let sec = extract_i32(sec_data, i);
174			let nano = if let Some((nd, _)) = &nano_data {
175				extract_i32(nd, i)
176			} else {
177				Some(0)
178			};
179
180			match (hour, min, sec, nano) {
181				(Some(h), Some(m), Some(s), Some(n)) => {
182					if h >= 0 && m >= 0 && s >= 0 && n >= 0 {
183						match Time::new(h as u32, m as u32, s as u32, n as u32) {
184							Some(time) => container.push(time),
185							None => container.push_default(),
186						}
187					} else {
188						container.push_default();
189					}
190				}
191				_ => container.push_default(),
192			}
193		}
194
195		let result_data = ColumnBuffer::Time(container);
196		Ok(Columns::new(vec![ColumnWithName::new(ctx.fragment.clone(), result_data)]))
197	}
198}
199
200impl Function for TimeNew {
201	fn kinds(&self) -> &[FunctionKind] {
202		&[FunctionKind::Scalar]
203	}
204}