1use crate::db::format::Format;
2use crate::regex;
3use odbc_api::{DataType, Nullability};
4use regex::Match;
5use std::cell::RefCell;
6use std::ops::Deref;
7
8enum Template {
9 Text,
10 Number,
11 Unknown(bool),
12}
13
14impl Template {
15 fn from(dtype: DataType) -> Self {
16 match dtype {
17 DataType::Unknown => Self::Unknown(false),
18 DataType::Char { .. } => Self::Text,
19 DataType::WChar { .. } => Self::Text,
20 DataType::Numeric { .. } => Self::Number,
21 DataType::Decimal { .. } => Self::Number,
22 DataType::Integer => Self::Number,
23 DataType::SmallInt => Self::Number,
24 DataType::Float { .. } => Self::Number,
25 DataType::Real => Self::Number,
26 DataType::Double => Self::Number,
27 DataType::Varchar { .. } => Self::Text,
28 DataType::WVarchar { .. } => Self::Text,
29 DataType::LongVarchar { .. } => Self::Text,
30 DataType::WLongVarchar { .. } => Self::Text,
31 DataType::LongVarbinary { .. } => Self::Text,
32 DataType::Date => Self::Text,
33 DataType::Time { .. } => Self::Text,
34 DataType::Timestamp { .. } => Self::Text,
35 DataType::BigInt => Self::Number,
36 DataType::TinyInt => Self::Number,
37 DataType::Bit => Self::Number,
38 DataType::Varbinary { .. } => Self::Text,
39 DataType::Binary { .. } => Self::Text,
40 DataType::Other { .. } => Self::Text,
41 }
42 }
43}
44
45pub struct Metadata {
46 pub dtype: DataType,
47 pub null: Nullability,
48 template: RefCell<Template>,
49}
50
51impl Metadata {
52 pub fn new(dtype: DataType, null: Nullability) -> Self {
53 let template = RefCell::new(Template::from(dtype));
54 Self { dtype, null, template }
55 }
56
57 pub fn do_reformat(&self, format: Format) -> Format {
58 match (format, self.template.borrow().deref()) {
59 (Format::Number(l, r), Template::Text) => Format::Text(l + r),
60 (Format::Number(l, r), Template::Unknown(false)) => Format::Text(l + r),
61 _ => format,
62 }
63 }
64
65 pub fn measure_value(&self, value: &str, length: usize) -> Format {
66 let mut template = self.template.borrow_mut();
67 match template.deref() {
68 Template::Text => Format::Text(length),
69 Template::Number => measure_number(value, length),
70 Template::Unknown(_) => {
71 let format = measure_unknown(value, length);
72 if !value.is_empty() {
73 match format {
74 Format::Text(_) => *template = Template::Text,
75 Format::Number(_, _) => *template = Template::Unknown(true),
76 }
77 }
78 format
79 }
80 }
81 }
82}
83
84fn measure_number(value: &str, length: usize) -> Format {
85 if let Some(left) = value.find('.') {
86 let right = length.checked_sub(left).unwrap_or_default();
87 Format::Number(left, right)
88 } else {
89 Format::Number(length, 0)
90 }
91}
92
93fn measure_unknown(value: &str, length: usize) -> Format {
94 let regex = regex!(r"^(-?\d+)(\.\d+)?$");
95 if value.is_empty() {
96 Format::Number(0, 0)
97 } else if let Some(captures) = regex.captures(value) {
98 if let Some(right) = captures.get(2) {
99 let left = captures.get(1).as_ref().map(Match::len).unwrap_or_default();
100 let right = right.len();
101 Format::Number(left, right)
102 } else {
103 Format::Number(length, 0)
104 }
105 } else {
106 Format::Text(length)
107 }
108}