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}