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