reifydb_routine/function/duration/
format.rs1use reifydb_core::value::column::{Column, columns::Columns, data::ColumnData};
5use reifydb_type::value::{constraint::bytes::MaxBytes, container::utf8::Utf8Container, r#type::Type};
6
7use crate::function::{Function, FunctionCapability, FunctionContext, FunctionInfo, error::FunctionError};
8
9pub struct DurationFormat {
10 info: FunctionInfo,
11}
12
13impl Default for DurationFormat {
14 fn default() -> Self {
15 Self::new()
16 }
17}
18
19impl DurationFormat {
20 pub fn new() -> Self {
21 Self {
22 info: FunctionInfo::new("duration::format"),
23 }
24 }
25}
26
27fn format_duration(months: i32, days: i32, nanos: i64, fmt: &str) -> Result<String, String> {
28 let years = months / 12;
29 let remaining_months = months % 12;
30
31 let total_seconds = nanos / 1_000_000_000;
32 let remaining_nanos = (nanos % 1_000_000_000).unsigned_abs();
33
34 let hours = (total_seconds / 3600) % 24;
35 let minutes = (total_seconds % 3600) / 60;
36 let seconds = total_seconds % 60;
37
38 let mut result = String::new();
39 let mut chars = fmt.chars().peekable();
40
41 while let Some(ch) = chars.next() {
42 if ch == '%' {
43 match chars.next() {
44 Some('Y') => result.push_str(&format!("{}", years)),
45 Some('M') => result.push_str(&format!("{}", remaining_months)),
46 Some('D') => result.push_str(&format!("{}", days)),
47 Some('h') => result.push_str(&format!("{}", hours)),
48 Some('m') => result.push_str(&format!("{}", minutes)),
49 Some('s') => result.push_str(&format!("{}", seconds)),
50 Some('f') => result.push_str(&format!("{:09}", remaining_nanos)),
51 Some('%') => result.push('%'),
52 Some(c) => return Err(format!("invalid format specifier: '%{}'", c)),
53 None => return Err("unexpected end of format string after '%'".to_string()),
54 }
55 } else {
56 result.push(ch);
57 }
58 }
59
60 Ok(result)
61}
62
63impl Function for DurationFormat {
64 fn info(&self) -> &FunctionInfo {
65 &self.info
66 }
67
68 fn capabilities(&self) -> &[FunctionCapability] {
69 &[FunctionCapability::Scalar]
70 }
71
72 fn return_type(&self, _input_types: &[Type]) -> Type {
73 Type::Utf8
74 }
75
76 fn execute(&self, ctx: &FunctionContext, args: &Columns) -> Result<Columns, FunctionError> {
77 if args.len() != 2 {
78 return Err(FunctionError::ArityMismatch {
79 function: ctx.fragment.clone(),
80 expected: 2,
81 actual: args.len(),
82 });
83 }
84
85 let dur_col = &args[0];
86 let fmt_col = &args[1];
87
88 let (dur_data, dur_bv) = dur_col.data().unwrap_option();
89 let (fmt_data, _) = fmt_col.data().unwrap_option();
90
91 match (dur_data, fmt_data) {
92 (
93 ColumnData::Duration(dur_container),
94 ColumnData::Utf8 {
95 container: fmt_container,
96 ..
97 },
98 ) => {
99 let row_count = dur_data.len();
100 let mut result_data = Vec::with_capacity(row_count);
101
102 for i in 0..row_count {
103 match (dur_container.get(i), fmt_container.is_defined(i)) {
104 (Some(d), true) => {
105 let fmt_str = &fmt_container[i];
106 match format_duration(
107 d.get_months(),
108 d.get_days(),
109 d.get_nanos(),
110 fmt_str,
111 ) {
112 Ok(formatted) => {
113 result_data.push(formatted);
114 }
115 Err(reason) => {
116 return Err(FunctionError::ExecutionFailed {
117 function: ctx.fragment.clone(),
118 reason,
119 });
120 }
121 }
122 }
123 _ => {
124 result_data.push(String::new());
125 }
126 }
127 }
128
129 let mut final_data = ColumnData::Utf8 {
130 container: Utf8Container::new(result_data),
131 max_bytes: MaxBytes::MAX,
132 };
133 if let Some(bv) = dur_bv {
134 final_data = ColumnData::Option {
135 inner: Box::new(final_data),
136 bitvec: bv.clone(),
137 };
138 }
139 Ok(Columns::new(vec![Column::new(ctx.fragment.clone(), final_data)]))
140 }
141 (ColumnData::Duration(_), other) => Err(FunctionError::InvalidArgumentType {
142 function: ctx.fragment.clone(),
143 argument_index: 1,
144 expected: vec![Type::Utf8],
145 actual: other.get_type(),
146 }),
147 (other, _) => Err(FunctionError::InvalidArgumentType {
148 function: ctx.fragment.clone(),
149 argument_index: 0,
150 expected: vec![Type::Duration],
151 actual: other.get_type(),
152 }),
153 }
154 }
155}