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());
69 match blob.to_utf8() {
70 Ok(s) => out.push(s),
71 Err(e) => {
72 return Err(CastError::InvalidBlobToUtf8 {
73 fragment: lazy_fragment.fragment(),
74 cause: e.diagnostic(),
75 }
76 .into());
77 }
78 }
79 } else {
80 out.push_none()
81 }
82 }
83 Ok(out)
84}
85
86#[inline]
87fn from_bool(container: &BoolContainer) -> Result<ColumnBuffer> {
88 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
89 for idx in 0..container.len() {
90 if container.is_defined(idx) {
91 out.push::<String>(container.data().get(idx).to_string());
92 } else {
93 out.push_none();
94 }
95 }
96 Ok(out)
97}
98
99#[inline]
100fn from_number<T>(container: &NumberContainer<T>) -> Result<ColumnBuffer>
101where
102 T: Copy + Display + IsNumber + Default,
103{
104 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
105 for idx in 0..container.len() {
106 if container.is_defined(idx) {
107 out.push::<String>(container[idx].to_string());
108 } else {
109 out.push_none();
110 }
111 }
112 Ok(out)
113}
114
115#[inline]
116fn from_temporal<T>(container: &TemporalContainer<T>) -> Result<ColumnBuffer>
117where
118 T: Copy + Display + IsTemporal + Default,
119{
120 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
121 for idx in 0..container.len() {
122 if container.is_defined(idx) {
123 out.push::<String>(container[idx].to_string());
124 } else {
125 out.push_none();
126 }
127 }
128 Ok(out)
129}
130
131#[inline]
132fn from_uuid<T>(container: &UuidContainer<T>) -> Result<ColumnBuffer>
133where
134 T: Copy + Display + IsUuid + Default,
135{
136 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
137 for idx in 0..container.len() {
138 if container.is_defined(idx) {
139 out.push::<String>(container[idx].to_string());
140 } else {
141 out.push_none();
142 }
143 }
144 Ok(out)
145}
146
147#[inline]
148fn from_identity_id(container: &IdentityIdContainer) -> Result<ColumnBuffer> {
149 let mut out = ColumnBuffer::with_capacity(Type::Utf8, container.len());
150 for idx in 0..container.len() {
151 if container.is_defined(idx) {
152 out.push::<String>(container[idx].to_string());
153 } else {
154 out.push_none();
155 }
156 }
157 Ok(out)
158}
159
160#[cfg(test)]
161pub mod tests {
162 use reifydb_core::value::column::buffer::ColumnBuffer;
163 use reifydb_type::{
164 fragment::Fragment,
165 value::{blob::Blob, container::blob::BlobContainer},
166 };
167
168 use crate::expression::cast::text::from_blob;
169
170 #[test]
171 fn test_from_blob() {
172 let blobs = vec![
173 Blob::from_utf8(Fragment::internal("Hello")),
174 Blob::from_utf8(Fragment::internal("World")),
175 ];
176 let container = BlobContainer::new(blobs);
177
178 let result = from_blob(&container, || Fragment::testing_empty()).unwrap();
179
180 match result {
181 ColumnBuffer::Utf8 {
182 container,
183 ..
184 } => {
185 assert_eq!(container.get(0), Some("Hello"));
186 assert_eq!(container.get(1), Some("World"));
187 }
188 _ => panic!("Expected UTF8 column data"),
189 }
190 }
191
192 #[test]
193 fn test_from_blob_invalid() {
194 let blobs = vec![
195 Blob::new(vec![0xFF, 0xFE]), ];
197 let container = BlobContainer::new(blobs);
198
199 let result = from_blob(&container, || Fragment::testing_empty());
200 assert!(result.is_err());
201 }
202}