Skip to main content

zql_cli/db/
metadata.rs

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}