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