1use std::fmt::{Result, Write};
16use std::num::FpCategory;
17
18use super::{DataType, DatumRef, ScalarRefImpl};
19use crate::dispatch_scalar_ref_variants;
20
21pub trait ToText {
40 fn write<W: Write>(&self, f: &mut W) -> Result;
44
45 fn write_with_type<W: Write>(&self, _ty: &DataType, f: &mut W) -> Result;
47
48 fn to_text_with_type(&self, ty: &DataType) -> String {
50 let mut s = String::new();
51 self.write_with_type(ty, &mut s).unwrap();
52 s
53 }
54
55 fn to_text(&self) -> String {
64 let mut s = String::new();
65 self.write(&mut s).unwrap();
66 s
67 }
68
69 fn text_display(&self) -> impl std::fmt::Display + '_ {
71 std::fmt::from_fn(|f| self.write(f))
72 }
73}
74
75macro_rules! implement_using_to_string {
76 ($({ $scalar_type:ty , $data_type:ident} ),*) => {
77 $(
78 impl ToText for $scalar_type {
79 fn write<W: Write>(&self, f: &mut W) -> Result {
80 write!(f, "{self}")
81 }
82 fn write_with_type<W: Write>(&self, ty: &DataType, f: &mut W) -> Result {
83 match ty {
84 DataType::$data_type => self.write(f),
85 _ => unreachable!(),
86 }
87 }
88 }
89 )*
90 };
91}
92
93macro_rules! implement_using_itoa {
94 ($({ $scalar_type:ty , $data_type:ident} ),*) => {
95 $(
96 impl ToText for $scalar_type {
97 fn write<W: Write>(&self, f: &mut W) -> Result {
98 write!(f, "{}", itoa::Buffer::new().format(*self))
99 }
100 fn write_with_type<W: Write>(&self, ty: &DataType, f: &mut W) -> Result {
101 match ty {
102 DataType::$data_type => self.write(f),
103 _ => unreachable!(),
104 }
105 }
106 }
107 )*
108 };
109}
110
111implement_using_to_string! {
112 { String ,Varchar },
113 { &str ,Varchar}
114}
115
116implement_using_itoa! {
117 { i16, Int16 },
118 { i32, Int32 },
119 { i64, Int64 }
120}
121
122macro_rules! implement_using_ryu {
123 ($({ $scalar_type:ty, $data_type:ident } ),*) => {
124 $(
125 impl ToText for $scalar_type {
126 fn write<W: Write>(&self, f: &mut W) -> Result {
127 let inner = self.0;
128 match inner.classify() {
129 FpCategory::Infinite if inner.is_sign_negative() => write!(f, "-Infinity"),
130 FpCategory::Infinite => write!(f, "Infinity"),
131 FpCategory::Zero if inner.is_sign_negative() => write!(f, "-0"),
132 FpCategory::Nan => write!(f, "NaN"),
133 _ => {
134 let mut buf = ryu::Buffer::new();
135 let mut s = buf.format_finite(self.0);
136 if let Some(trimmed) = s.strip_suffix(".0") {
137 s = trimmed;
138 }
139 if let Some(mut idx) = s.as_bytes().iter().position(|x| *x == b'e') {
140 idx += 1;
141 write!(f, "{}", &s[..idx])?;
142 if s.as_bytes()[idx] == b'-' {
143 write!(f, "-")?;
144 idx += 1;
145 } else {
146 write!(f, "+")?;
147 }
148 if idx + 1 == s.len() {
149 write!(f, "0")?;
150 }
151 write!(f, "{}", &s[idx..])?;
152 } else {
153 write!(f, "{}", s)?;
154 }
155 Ok(())
156 }
157 }
158 }
159 fn write_with_type<W: Write>(&self, ty: &DataType, f: &mut W) -> Result {
160 match ty {
161 DataType::$data_type => self.write(f),
162 _ => unreachable!(),
163 }
164 }
165 }
166 )*
167 };
168}
169
170implement_using_ryu! {
171 { crate::types::F32, Float32 },
172 { crate::types::F64, Float64 }
173}
174
175impl ToText for bool {
176 fn write<W: Write>(&self, f: &mut W) -> Result {
177 if *self {
178 write!(f, "t")
179 } else {
180 write!(f, "f")
181 }
182 }
183
184 fn write_with_type<W: Write>(&self, ty: &DataType, f: &mut W) -> Result {
185 match ty {
186 DataType::Boolean => self.write(f),
187 _ => unreachable!(),
188 }
189 }
190}
191
192impl ToText for &[u8] {
193 fn write<W: Write>(&self, f: &mut W) -> Result {
194 write!(f, "\\x{}", hex::encode(self))
195 }
196
197 fn write_with_type<W: Write>(&self, ty: &DataType, f: &mut W) -> Result {
198 match ty {
199 DataType::Bytea => self.write(f),
200 _ => unreachable!(),
201 }
202 }
203}
204
205impl ToText for ScalarRefImpl<'_> {
206 fn write<W: Write>(&self, f: &mut W) -> Result {
207 dispatch_scalar_ref_variants!(self, v, { v.write(f) })
208 }
209
210 fn write_with_type<W: Write>(&self, ty: &DataType, f: &mut W) -> Result {
211 dispatch_scalar_ref_variants!(self, v, { v.write_with_type(ty, f) })
212 }
213}
214
215impl ToText for DatumRef<'_> {
216 fn write<W: Write>(&self, f: &mut W) -> Result {
217 match self {
218 Some(data) => data.write(f),
219 None => write!(f, "NULL"),
220 }
221 }
222
223 fn write_with_type<W: Write>(&self, ty: &DataType, f: &mut W) -> Result {
224 match self {
225 Some(data) => data.write_with_type(ty, f),
226 None => write!(f, "NULL"),
227 }
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use crate::types::ToText;
234 use crate::types::ordered_float::OrderedFloat;
235
236 #[test]
237 fn test_float_to_text() {
238 let ret: OrderedFloat<f64> = OrderedFloat::<f64>::from(1.234567890123456);
240 tracing::info!("ret: {}", ret.to_text());
241 assert_eq!("1.234567890123456".to_owned(), ret.to_text());
242
243 let ret: OrderedFloat<f32> = OrderedFloat::<f32>::from(1.234567);
245 assert_eq!("1.234567".to_owned(), ret.to_text());
246 }
247}