dcsv/
value.rs

1//! Value is a basic unit of csv struct
2//!
3//! Value can be either number or text.
4
5use crate::error::{DcsvError, DcsvResult};
6use regex::Regex;
7use std::{fmt::Display, str::FromStr};
8
9/// Length of limiter's attributes
10pub const LIMITER_ATTRIBUTE_LEN: usize = 4;
11
12/// Basic component of virtual data
13///
14/// Value can be either number or text.
15/// - "Number" is a signed interger (isize)
16/// - Text is simply any data
17///
18/// Dcsv doesn't support float type because float can change the "original" source while
19/// overriding. Since dcsv's goal is about safe manipulation of csv value, float is not appropriate.
20#[derive(Clone, Eq, PartialEq, PartialOrd, Debug)]
21pub enum Value {
22    Number(isize),
23    Text(String),
24}
25
26impl Value {
27    /// Get a type of value
28    ///
29    /// This returns a new variable "ValueType"
30    pub fn get_type(&self) -> ValueType {
31        match self {
32            Self::Number(_) => ValueType::Number,
33            Self::Text(_) => ValueType::Text,
34        }
35    }
36    /// Convert string into value with given type
37    ///
38    /// This can fail when a given source cannot bed converted to isize
39    pub fn from_str(src: &str, value_type: ValueType) -> DcsvResult<Self> {
40        Ok(match value_type {
41            ValueType::Number => {
42                // Empty value is evaluated to 0
43                if src.is_empty() {
44                    return Ok(Value::Number(0));
45                }
46
47                let src_number = src.parse::<isize>().map_err(|_| {
48                    DcsvError::InvalidValueType(format!("\"{}\" is not a valid number", src))
49                })?;
50                Value::Number(src_number)
51            }
52            ValueType::Text => Value::Text(src.to_string()),
53        })
54    }
55
56    /// Create empty value
57    ///
58    /// Default values for each types are
59    /// - Number : 0
60    /// - Text : ""
61    pub fn empty(value_type: ValueType) -> Self {
62        match value_type {
63            ValueType::Number => Self::Number(0),
64            ValueType::Text => Self::Text(String::new()),
65        }
66    }
67}
68
69impl Default for Value {
70    fn default() -> Self {
71        Self::Text(String::new())
72    }
73}
74
75impl std::fmt::Display for Value {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        let out = match self {
78            Self::Number(num) => num.to_string(),
79            Self::Text(txt) => txt.to_string(),
80        };
81        write!(f, "{}", out)
82    }
83}
84
85// This struct should not expose value directly
86// because some limiters are mutually exclusive.
87/// Limiter that costraints which data that Value can hold
88///
89/// VaulueLimiter has four properties
90/// - type ( Eitehr number or text )
91/// - default value
92/// - variants ( Range of values )
93/// - pattern ( Regex pattern )
94#[derive(Default, Clone, Debug)]
95pub struct ValueLimiter {
96    // Allowed variant
97    value_type: ValueType,
98    default: Option<Value>,
99    variant: Option<Vec<Value>>,
100    pattern: Option<Regex>, // -> This better be a regex
101}
102
103impl Display for ValueLimiter {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        writeln!(f, "type : {}", self.value_type)?;
106        if let Some(var) = &self.variant {
107            writeln!(f, "default value : {:?}", &var)?;
108        }
109        if let Some(var) = &self.variant {
110            write!(f, "variants : {:?}", var)
111        } else if let Some(var) = &self.pattern {
112            write!(f, "pattern : {:?}", var)
113        } else {
114            write!(f, "")
115        }
116    }
117}
118
119impl ValueLimiter {
120    /// Check if given value can be converted to the type of valuelimiter
121    pub fn is_convertible(&self, value: &Value) -> Option<ValueType> {
122        // TODO
123        // Only when value type matches limiter's type
124        match self.value_type {
125            ValueType::Number => {
126                if let Value::Text(text) = value {
127                    // Empty value can be converted to 0 without hassle
128                    if text.is_empty() {
129                        return Some(ValueType::Number);
130                    }
131
132                    // String to Number
133                    match text.parse::<isize>() {
134                        Ok(_) => Some(ValueType::Number),
135                        Err(_) => None,
136                    }
137                } else {
138                    // Number to number
139                    Some(ValueType::Number)
140                }
141            }
142            ValueType::Text => Some(ValueType::Text),
143        }
144    }
145
146    /// Check if value qualifies
147    pub fn qualify(&self, value: &Value) -> bool {
148        if value.get_type() != self.get_type() {
149            return false;
150        }
151        match value {
152            Value::Number(num) => {
153                if let Some(variant) = self.variant.as_ref() {
154                    variant.contains(value)
155                } else if let Some(pattern) = self.pattern.as_ref() {
156                    pattern.is_match(&num.to_string())
157                } else {
158                    true
159                }
160            }
161            Value::Text(text) => {
162                if let Some(variant) = self.variant.as_ref() {
163                    variant.contains(value)
164                } else if let Some(pattern) = self.pattern.as_ref() {
165                    pattern.is_match(text)
166                } else {
167                    true
168                }
169            }
170        }
171    }
172
173    /// Create value limiter from attributes
174    ///
175    /// The order is
176    /// - Type
177    /// - Default
178    /// - Variant
179    /// - Pattern
180    pub fn from_line(attributes: &[impl AsRef<str>]) -> DcsvResult<Self> {
181        let attributes: Vec<&str> = attributes.iter().map(|s| s.as_ref()).collect();
182        if attributes.len() != LIMITER_ATTRIBUTE_LEN {
183            return Err(DcsvError::InvalidRowData(format!(
184                "Schema row has insufficient columns \n= {:?}",
185                attributes
186            )));
187        }
188        let mut limiter = Self::default();
189        let vt = ValueType::from_str(attributes[0])?;
190        let default = attributes[1];
191        let variants = attributes[2];
192        let pattern = attributes[3];
193        limiter.set_type(vt);
194
195        // Default value is necessary for complicated limiter
196        if !default.is_empty() {
197            let default = Value::from_str(default, vt)?;
198
199            // DO variants
200            if !variants.is_empty() {
201                let mut values = vec![];
202                for var in variants.split_whitespace() {
203                    values.push(Value::from_str(var, vt)?);
204                }
205                limiter.set_variant(default, &values)?;
206            } else if !pattern.is_empty() {
207                // Do patterns
208                limiter.set_pattern(
209                    default,
210                    Regex::new(pattern).expect("Failed to create pattern"),
211                )?;
212            } else {
213                limiter.default = Some(default);
214            }
215        } else {
216            // Default is empty
217            if !pattern.is_empty() || !variants.is_empty() {
218                return Err(DcsvError::InvalidLimiter(
219                    "Either pattern or variants needs default value to be valid".to_string(),
220                ));
221            }
222        }
223        Ok(limiter)
224    }
225
226    /// Get type
227    pub fn get_type(&self) -> ValueType {
228        self.value_type
229    }
230
231    /// Set type
232    pub fn set_type(&mut self, column_type: ValueType) {
233        self.value_type = column_type;
234    }
235
236    /// Get default value from limiter
237    pub fn get_default(&self) -> Option<&Value> {
238        self.default.as_ref()
239    }
240
241    /// Return variant reference
242    pub fn get_variant(&self) -> Option<&Vec<Value>> {
243        self.variant.as_ref()
244    }
245
246    /// Set variant
247    pub fn set_variant(&mut self, default: Value, variants: &[Value]) -> DcsvResult<()> {
248        if !variants.contains(&default) {
249            return Err(DcsvError::InvalidLimiter(
250                "Default value should be among one of variants".to_string(),
251            ));
252        }
253        self.default.replace(default);
254        self.variant.replace(variants.to_vec());
255        Ok(())
256    }
257
258    /// Get pattern
259    pub fn get_pattern(&self) -> Option<&Regex> {
260        self.pattern.as_ref()
261    }
262
263    /// Set pattern
264    pub fn set_pattern(&mut self, default: Value, pattern: Regex) -> DcsvResult<()> {
265        if !pattern.is_match(&default.to_string()) {
266            return Err(DcsvError::InvalidLimiter(
267                "Default value should match pattern".to_string(),
268            ));
269        }
270        self.default.replace(default);
271        self.pattern.replace(pattern);
272        Ok(())
273    }
274}
275
276/// Type of a value
277#[derive(Clone, Copy, PartialEq, Debug)]
278pub enum ValueType {
279    Number,
280    Text,
281}
282
283impl std::fmt::Display for ValueType {
284    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
285        write!(
286            f,
287            "{}",
288            match self {
289                Self::Number => "Number",
290                Self::Text => "Text",
291            }
292        )
293    }
294}
295
296impl std::str::FromStr for ValueType {
297    type Err = DcsvError;
298
299    /// This actually never fails
300    fn from_str(s: &str) -> Result<Self, Self::Err> {
301        match s.to_lowercase().as_str() {
302            "number" => Ok(Self::Number),
303            "text" => Ok(Self::Text),
304            _ => Err(DcsvError::InvalidValueType(
305                "Value type should be either number or text".to_string(),
306            )),
307        }
308    }
309}
310
311impl Default for ValueType {
312    fn default() -> Self {
313        Self::Text
314    }
315}