reifydb_engine/expression/cast/
text.rs1use std::fmt::Display;
5
6use reifydb_core::value::column::buffer::ColumnBuffer;
7use reifydb_type::{
8 error::TypeError,
9 fragment::LazyFragment,
10 value::{
11 blob::Blob,
12 container::{
13 blob::BlobContainer, bool::BoolContainer, identity_id::IdentityIdContainer,
14 number::NumberContainer, temporal::TemporalContainer, uuid::UuidContainer,
15 },
16 is::{IsNumber, IsTemporal, IsUuid},
17 r#type::Type,
18 },
19};
20
21use crate::{Result, error::CastError};
22
23pub fn to_text(data: &ColumnBuffer, lazy_fragment: impl LazyFragment) -> Result<ColumnBuffer> {
24 match data {
25 ColumnBuffer::Blob {
26 container,
27 ..
28 } => from_blob(container, lazy_fragment),
29 ColumnBuffer::Bool(container) => from_bool(container),
30 ColumnBuffer::Int1(container) => from_number(container),
31 ColumnBuffer::Int2(container) => from_number(container),
32 ColumnBuffer::Int4(container) => from_number(container),
33 ColumnBuffer::Int8(container) => from_number(container),
34 ColumnBuffer::Int16(container) => from_number(container),
35 ColumnBuffer::Uint1(container) => from_number(container),
36 ColumnBuffer::Uint2(container) => from_number(container),
37 ColumnBuffer::Uint4(container) => from_number(container),
38 ColumnBuffer::Uint8(container) => from_number(container),
39 ColumnBuffer::Uint16(container) => from_number(container),
40 ColumnBuffer::Float4(container) => from_number(container),
41 ColumnBuffer::Float8(container) => from_number(container),
42 ColumnBuffer::Date(container) => from_temporal(container),
43 ColumnBuffer::DateTime(container) => from_temporal(container),
44 ColumnBuffer::Time(container) => from_temporal(container),
45 ColumnBuffer::Duration(container) => from_temporal(container),
46 ColumnBuffer::Uuid4(container) => from_uuid(container),
47 ColumnBuffer::Uuid7(container) => from_uuid(container),
48 ColumnBuffer::IdentityId(container) => from_identity_id(container),
49 _ => {
50 let from = data.get_type();
51 Err(TypeError::UnsupportedCast {
52 from,
53 to: Type::Utf8,
54 fragment: lazy_fragment.fragment(),
55 }
56 .into())
57 }
58 }
59}
60
61#[inline]
62pub fn from_blob(container: &BlobContainer, lazy_fragment: impl LazyFragment) -> Result<ColumnBuffer> {
63 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
64 for idx in 0..container.len() {
65 if container.is_defined(idx) {
66 let blob = Blob::new(container.get(idx).unwrap_or(&[]).to_vec());
67 match blob.to_utf8() {
68 Ok(s) => out.push(s),
69 Err(e) => {
70 return Err(CastError::InvalidBlobToUtf8 {
71 fragment: lazy_fragment.fragment(),
72 cause: e.diagnostic(),
73 }
74 .into());
75 }
76 }
77 } else {
78 out.push_none()
79 }
80 }
81 Ok(out)
82}
83
84#[inline]
85fn from_bool(container: &BoolContainer) -> Result<ColumnBuffer> {
86 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
87 for idx in 0..container.len() {
88 if container.is_defined(idx) {
89 out.push::<String>(container.data().get(idx).to_string());
90 } else {
91 out.push_none();
92 }
93 }
94 Ok(out)
95}
96
97#[inline]
98fn from_number<T>(container: &NumberContainer<T>) -> Result<ColumnBuffer>
99where
100 T: Copy + Display + IsNumber + Default,
101{
102 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
103 for idx in 0..container.len() {
104 if container.is_defined(idx) {
105 out.push::<String>(container[idx].to_string());
106 } else {
107 out.push_none();
108 }
109 }
110 Ok(out)
111}
112
113#[inline]
114fn from_temporal<T>(container: &TemporalContainer<T>) -> Result<ColumnBuffer>
115where
116 T: Copy + Display + IsTemporal + Default,
117{
118 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
119 for idx in 0..container.len() {
120 if container.is_defined(idx) {
121 out.push::<String>(container[idx].to_string());
122 } else {
123 out.push_none();
124 }
125 }
126 Ok(out)
127}
128
129#[inline]
130fn from_uuid<T>(container: &UuidContainer<T>) -> Result<ColumnBuffer>
131where
132 T: Copy + Display + IsUuid + Default,
133{
134 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
135 for idx in 0..container.len() {
136 if container.is_defined(idx) {
137 out.push::<String>(container[idx].to_string());
138 } else {
139 out.push_none();
140 }
141 }
142 Ok(out)
143}
144
145#[inline]
146fn from_identity_id(container: &IdentityIdContainer) -> Result<ColumnBuffer> {
147 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
148 for idx in 0..container.len() {
149 if container.is_defined(idx) {
150 out.push::<String>(container[idx].to_string());
151 } else {
152 out.push_none();
153 }
154 }
155 Ok(out)
156}
157
158#[cfg(test)]
159pub mod tests {
160 use reifydb_core::value::column::buffer::ColumnBuffer;
161 use reifydb_type::{
162 fragment::Fragment,
163 value::{blob::Blob, container::blob::BlobContainer},
164 };
165
166 use crate::expression::cast::text::from_blob;
167
168 #[test]
169 fn test_from_blob() {
170 let blobs = vec![
171 Blob::from_utf8(Fragment::internal("Hello")),
172 Blob::from_utf8(Fragment::internal("World")),
173 ];
174 let container = BlobContainer::new(blobs);
175
176 let result = from_blob(&container, || Fragment::testing_empty()).unwrap();
177
178 match result {
179 ColumnBuffer::Utf8 {
180 container,
181 ..
182 } => {
183 assert_eq!(container.get(0), Some("Hello"));
184 assert_eq!(container.get(1), Some("World"));
185 }
186 _ => panic!("Expected UTF8 column data"),
187 }
188 }
189
190 #[test]
191 fn test_from_blob_invalid() {
192 let blobs = vec![
193 Blob::new(vec![0xFF, 0xFE]), ];
195 let container = BlobContainer::new(blobs);
196
197 let result = from_blob(&container, || Fragment::testing_empty());
198 assert!(result.is_err());
199 }
200}