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