dbase/field/
types.rs

1use std::convert::{TryFrom, TryInto};
2use std::fmt;
3use std::io::{Read, Seek, Write};
4use std::str::FromStr;
5
6use crate::Encoding;
7use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
8
9use crate::error::ErrorKind;
10use crate::field::FieldInfo;
11use crate::memo::MemoReader;
12use crate::writing::WritableAsDbaseField;
13
14/// Enum listing all the field types we know of
15#[derive(Debug, Copy, Clone, PartialEq, Eq)]
16pub enum FieldType {
17    // dBASE III
18    Character,
19    Date,
20    Float,
21    Numeric,
22    Logical,
23    // Visual FoxPro
24    Currency,
25    DateTime,
26    Integer,
27    // Unknown
28    Double,
29    Memo,
30    //General,
31    //BinaryCharacter,
32    //BinaryMemo,
33}
34
35impl From<FieldType> for u8 {
36    fn from(t: FieldType) -> Self {
37        let v = match t {
38            FieldType::Character => 'C',
39            FieldType::Date => 'D',
40            FieldType::Float => 'F',
41            FieldType::Numeric => 'N',
42            FieldType::Logical => 'L',
43            FieldType::Currency => 'Y',
44            FieldType::DateTime => 'T',
45            FieldType::Integer => 'I',
46            FieldType::Double => 'B',
47            FieldType::Memo => 'M',
48        };
49        v as u8
50    }
51}
52
53impl FieldType {
54    pub fn from(c: char) -> Option<FieldType> {
55        match c {
56            // dBASE III field types
57            // All stored as strings
58            'C' => Some(FieldType::Character),
59            'D' => Some(FieldType::Date),
60            'F' => Some(FieldType::Float),
61            'N' => Some(FieldType::Numeric),
62            'L' => Some(FieldType::Logical),
63            // Visual FoxPro field types
64            // stored in binary formats
65            'Y' => Some(FieldType::Currency),
66            'T' => Some(FieldType::DateTime),
67            'I' => Some(FieldType::Integer),
68            // unknown version
69            'B' => Some(FieldType::Double),
70            'M' => Some(FieldType::Memo),
71            //'G' => Some(FieldType::General),
72            //'C' => Some(FieldType::BinaryCharacter), ??
73            //'M' => Some(FieldType::BinaryMemo),
74            _ => None,
75        }
76    }
77
78    /// Returns the size when stored in a file
79    ///
80    /// None is returned when the size cannot be known statically
81    /// (the in-file size depends on the field data)
82    ///
83    /// This could/should be a const fn but they are not stable yet
84    pub(crate) fn size(self) -> Option<u8> {
85        match self {
86            FieldType::Logical => Some(1),
87            FieldType::Date => Some(8),
88            FieldType::Integer => Some(std::mem::size_of::<i32>() as u8),
89            FieldType::Currency => Some(std::mem::size_of::<f64>() as u8),
90            FieldType::DateTime => Some(2 * std::mem::size_of::<i32>() as u8),
91            FieldType::Double => Some(std::mem::size_of::<f64>() as u8),
92            _ => None,
93        }
94    }
95}
96
97impl TryFrom<char> for FieldType {
98    type Error = ErrorKind;
99
100    fn try_from(c: char) -> Result<Self, Self::Error> {
101        match Self::from(c) {
102            Some(t) => Ok(t),
103            None => Err(ErrorKind::InvalidFieldType(c)),
104        }
105    }
106}
107
108impl std::fmt::Display for FieldType {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
110        write!(f, "dbase::{:?}", self)
111    }
112}
113
114/// Enum where each variant stores the record value
115#[derive(Debug, Clone, PartialEq)]
116pub enum FieldValue {
117    // dBase III fields
118    // Stored as strings, fully padded (ie only space char) strings
119    // are interpreted as None
120    /// dBase String type
121    ///
122    /// A string full of 'pad bytes' is considered `None`
123    Character(Option<String>),
124    /// dBase type to represent numbers, stored as String in the file
125    Numeric(Option<f64>),
126    /// dBase type for boolean values, stored as a character in the file
127    Logical(Option<bool>),
128    /// dBase type for dates, stored as a string in the file
129    Date(Option<Date>),
130    /// Another dBase type to represent numbers, stored as String in the file
131    Float(Option<f32>),
132    //Visual FoxPro fields
133    Integer(i32),
134    Currency(f64),
135    DateTime(DateTime),
136    Double(f64),
137
138    /// Memo is a dBase type that allows to store Strings
139    /// that are longer than 255 bytes.
140    /// These strings are stored in an external file
141    /// called the `Memo file`
142    Memo(String),
143}
144
145impl FieldValue {
146    pub(crate) fn read_from<T: Read + Seek, E: Encoding>(
147        mut field_bytes: &[u8],
148        memo_reader: &mut Option<MemoReader<T>>,
149        field_info: &FieldInfo,
150        encoding: &E,
151        character_option: TrimOption,
152    ) -> Result<Self, ErrorKind> {
153        debug_assert_eq!(field_bytes.len(), field_info.length() as usize);
154        let value = match field_info.field_type {
155            FieldType::Logical => match field_bytes[0] as char {
156                ' ' | '?' => FieldValue::Logical(None),
157                '1' | '0' | 'T' | 't' | 'Y' | 'y' => FieldValue::Logical(Some(true)),
158                'N' | 'n' | 'F' | 'f' => FieldValue::Logical(Some(false)),
159                _ => FieldValue::Logical(None),
160            },
161            FieldType::Character => {
162                // let value = read_string_of_len(&mut source, field_info.field_length)?;
163                let value = trim_field_data(field_bytes, character_option);
164                if value.is_empty() {
165                    FieldValue::Character(None)
166                } else {
167                    FieldValue::Character(Some(encoding.decode(value)?.to_string()))
168                }
169            }
170            FieldType::Numeric => {
171                // let value = read_string_of_len(&mut source, field_info.field_length)?;
172                let value = trim_field_data(field_bytes, TrimOption::BeginEnd);
173                if value.is_empty() || value.iter().all(|c| c == &b'*') {
174                    FieldValue::Numeric(None)
175                } else {
176                    let value_str = encoding.decode(value)?;
177                    FieldValue::Numeric(Some(value_str.parse::<f64>()?))
178                }
179            }
180            FieldType::Float => {
181                // let value = read_string_of_len(&mut source, field_info.field_length)?;
182                let value = trim_field_data(field_bytes, TrimOption::BeginEnd);
183                if value.is_empty() || value.iter().all(|c| c == &b'*') {
184                    FieldValue::Float(None)
185                } else {
186                    let value_str = encoding.decode(value)?;
187                    FieldValue::Float(Some(value_str.parse::<f32>()?))
188                }
189            }
190            FieldType::Date => {
191                // let value = read_string_of_len(&mut source, field_info.field_length)?;
192                let value = trim_field_data(field_bytes, TrimOption::BeginEnd);
193                if value.iter().all(|c| c == &b' ') {
194                    FieldValue::Date(None)
195                } else {
196                    let value_str = encoding.decode(value)?;
197                    FieldValue::Date(Some(value_str.parse::<Date>()?))
198                }
199            }
200            FieldType::Integer => {
201                let mut le_bytes = [0u8; std::mem::size_of::<i32>()];
202                le_bytes.copy_from_slice(&field_bytes[..std::mem::size_of::<i32>()]);
203                FieldValue::Integer(i32::from_le_bytes(le_bytes))
204            }
205            FieldType::Double => {
206                let mut le_bytes = [0u8; std::mem::size_of::<f64>()];
207                le_bytes.copy_from_slice(&field_bytes[..std::mem::size_of::<f64>()]);
208                FieldValue::Double(f64::from_le_bytes(le_bytes))
209            }
210            FieldType::Currency => {
211                let mut le_bytes = [0u8; std::mem::size_of::<f64>()];
212                le_bytes.copy_from_slice(&field_bytes[..std::mem::size_of::<f64>()]);
213                FieldValue::Currency(f64::from_le_bytes(le_bytes))
214            }
215            FieldType::DateTime => {
216                let mut source = std::io::Cursor::new(&mut field_bytes);
217                FieldValue::DateTime(DateTime::read_from(&mut source)?)
218            }
219            FieldType::Memo => {
220                let index_in_memo = if field_info.field_length > 4 {
221                    // let string = read_string_of_len(&mut source, field_info.field_length)?;
222                    let trimmed_value = trim_field_data(field_bytes, TrimOption::BeginEnd);
223                    if trimmed_value.is_empty() {
224                        return Ok(FieldValue::Memo(String::from("")));
225                    } else {
226                        encoding.decode(trimmed_value)?.parse::<u32>()?
227                    }
228                } else {
229                    let mut le_bytes = [0u8; std::mem::size_of::<u32>()];
230                    le_bytes.copy_from_slice(&field_bytes[..std::mem::size_of::<u32>()]);
231                    u32::from_le_bytes(le_bytes)
232                };
233
234                if let Some(memo_reader) = memo_reader {
235                    let data_from_memo = memo_reader.read_data_at(index_in_memo)?;
236                    FieldValue::Memo(encoding.decode(data_from_memo)?.to_string())
237                } else {
238                    return Err(ErrorKind::MissingMemoFile);
239                }
240            }
241        };
242        Ok(value)
243    }
244
245    /// Returns the corresponding field type of the contained value
246    pub fn field_type(&self) -> FieldType {
247        match self {
248            FieldValue::Character(_) => FieldType::Character,
249            FieldValue::Numeric(_) => FieldType::Numeric,
250            FieldValue::Logical(_) => FieldType::Logical,
251            FieldValue::Integer(_) => FieldType::Integer,
252            FieldValue::Float(_) => FieldType::Float,
253            FieldValue::Double(_) => FieldType::Double,
254            FieldValue::Date(_) => FieldType::Date,
255            FieldValue::Memo(_) => FieldType::Memo,
256            FieldValue::Currency(_) => FieldType::Currency,
257            FieldValue::DateTime(_) => FieldType::DateTime,
258        }
259    }
260}
261
262impl fmt::Display for FieldValue {
263    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264        write!(f, "{:?}", self)
265    }
266}
267
268/// dBase representation of date
269///
270/// # Note
271///
272/// This is really really naive date, it just holds the day, moth, year value
273/// with just a very few checks.
274///
275/// Also, dBase files do not have concept of timezones.
276#[derive(Debug, Copy, Clone, PartialEq)]
277pub struct Date {
278    pub(crate) year: u32,
279    pub(crate) month: u32,
280    pub(crate) day: u32,
281}
282
283impl Date {
284    /// Creates a new dbase::Date
285    /// # panic
286    ///
287    /// panics if the year has more than 4 digits or if the day is greater than 31 or
288    /// the month greater than 12
289    pub const fn new(day: u32, month: u32, year: u32) -> Self {
290        if year > 9999 {
291            panic!("Year cannot have more than 4 digits")
292        }
293        if day > 31 {
294            panic!("Day cannot be greater than 31")
295        }
296        if month > 12 {
297            panic!("Month cannot be greater than 12")
298        }
299        Self { year, month, day }
300    }
301
302    /// Returns the year
303    pub fn year(&self) -> u32 {
304        self.year
305    }
306
307    /// Returns the month
308    pub fn month(&self) -> u32 {
309        self.month
310    }
311
312    /// Returns the day
313    pub fn day(&self) -> u32 {
314        self.day
315    }
316
317    pub fn to_unix_days(&self) -> i32 {
318        let julian_day = self.to_julian_day_number();
319        return julian_day - 2440588;
320    }
321
322    // https://en.wikipedia.org/wiki/Julian_day
323    // at "Julian or Gregorian calendar from Julian day number"
324    fn julian_day_number_to_gregorian_date(jdn: i32) -> Date {
325        const Y: i32 = 4716;
326        const J: i32 = 1401;
327        const M: i32 = 2;
328        const N: i32 = 12;
329        const R: i32 = 4;
330        const P: i32 = 1461;
331        const V: i32 = 3;
332        const U: i32 = 5;
333        const S: i32 = 153;
334        const W: i32 = 2;
335        const B: i32 = 274_277;
336        const C: i32 = -38;
337
338        let f = jdn + J + ((4 * jdn + B) / 146_097 * 3) / 4 + C;
339        let e = R * f + V;
340        let g = (e % P) / R;
341        let h = U * g + W;
342
343        let day = (h % S) / U + 1;
344        let month = ((h / S + M) % (N)) + 1;
345        let year = (e / P) - Y + (N + M - month) / N;
346
347        Date {
348            year: year as u32,
349            month: month as u32,
350            day: day as u32,
351        }
352    }
353
354    fn to_julian_day_number(&self) -> i32 {
355        let (month, year) = if self.month > 2 {
356            (self.month - 3, self.year)
357        } else {
358            (self.month + 9, self.year - 1)
359        };
360
361        let century = year / 100;
362        let decade = year - 100 * century;
363
364        ((146_097 * century) / 4
365            + (1461 * decade) / 4
366            + (153 * month + 2) / 5
367            + self.day
368            + 1_721_119) as i32
369    }
370}
371
372impl FromStr for Date {
373    type Err = std::num::ParseIntError;
374
375    fn from_str(s: &str) -> Result<Self, Self::Err> {
376        let year = s[0..4].parse::<u32>()?;
377        let month = s[4..6].parse::<u32>()?;
378        let day = s[6..8].parse::<u32>()?;
379
380        Ok(Self { year, month, day })
381    }
382}
383
384impl std::string::ToString for Date {
385    fn to_string(&self) -> String {
386        format!("{:04}{:02}{:02}", self.year, self.month, self.day)
387    }
388}
389
390impl std::convert::TryFrom<Date> for time::Date {
391    type Error = time::error::ComponentRange;
392
393    fn try_from(d: Date) -> Result<Self, Self::Error> {
394        let month: time::Month = (d.month as u8).try_into()?;
395        Self::from_calendar_date(d.year as i32, month, d.day as u8)
396    }
397}
398
399impl From<time::Date> for Date {
400    fn from(d: time::Date) -> Self {
401        let month: u8 = d.month().into();
402        Self {
403            year: d.year() as u32,
404            month: month as u32,
405            day: d.day() as u32,
406        }
407    }
408}
409
410/// FoxBase representation of a time
411/// # note
412///
413/// This is a very naive Time struct, very minimal verifications are done.
414///
415#[derive(Debug, Copy, Clone, PartialEq)]
416pub struct Time {
417    hours: u32,
418    minutes: u32,
419    seconds: u32,
420}
421
422impl Time {
423    const HOURS_FACTOR: i32 = 3_600_000;
424    const MINUTES_FACTOR: i32 = 60_000;
425    const SECONDS_FACTOR: i32 = 1_000;
426
427    /// Creates a new Time
428    ///
429    /// # panics
430    /// will panic if the  minutes or seconds are greater than 60 or
431    /// if the hours are greater than 24
432    pub fn new(hours: u32, minutes: u32, seconds: u32) -> Self {
433        if hours > 24 || minutes > 60 || seconds > 60 {
434            panic!("Invalid Time")
435        }
436        Self {
437            hours,
438            minutes,
439            seconds,
440        }
441    }
442
443    /// Returns the hours.
444    pub fn hours(&self) -> u32 {
445        self.hours
446    }
447
448    /// Returns the minutes.
449    pub fn minutes(&self) -> u32 {
450        self.minutes
451    }
452
453    /// Returns the seconds.
454    pub fn seconds(&self) -> u32 {
455        self.seconds
456    }
457
458    fn from_word(mut time_word: i32) -> Self {
459        let hours: u32 = (time_word / Self::HOURS_FACTOR) as u32;
460        time_word -= (hours * Self::HOURS_FACTOR as u32) as i32;
461        let minutes: u32 = (time_word / Self::MINUTES_FACTOR) as u32;
462        time_word -= (minutes * Self::MINUTES_FACTOR as u32) as i32;
463        let seconds: u32 = (time_word / Self::SECONDS_FACTOR) as u32;
464        Self {
465            hours,
466            minutes,
467            seconds,
468        }
469    }
470
471    fn to_time_word(&self) -> i32 {
472        let mut time_word = self.hours * Self::HOURS_FACTOR as u32;
473        time_word += self.minutes * Self::MINUTES_FACTOR as u32;
474        time_word += self.seconds * Self::SECONDS_FACTOR as u32;
475        time_word as i32
476    }
477}
478
479/// FoxBase representation of a DateTime
480#[derive(Debug, Copy, Clone, PartialEq)]
481pub struct DateTime {
482    date: Date,
483    time: Time,
484}
485
486impl DateTime {
487    /// Creates a new DateTime from a date and a time
488    pub fn new(date: Date, time: Time) -> Self {
489        Self { date, time }
490    }
491
492    /// Returns the [Date] part.
493    pub fn date(&self) -> Date {
494        self.date
495    }
496
497    /// Returns the [Time] part.
498    pub fn time(&self) -> Time {
499        self.time
500    }
501
502    pub fn to_unix_timestamp(&self) -> i64 {
503        return self.date().to_unix_days() as i64 * 86400
504            + self.time().hours() as i64 * 3600
505            + self.time().minutes() as i64 * 60
506            + self.time().seconds() as i64;
507    }
508
509    fn read_from<T: Read>(src: &mut T) -> Result<Self, ErrorKind> {
510        let julian_day_number = src.read_i32::<LittleEndian>()?;
511        let time_word = src.read_i32::<LittleEndian>()?;
512        let time = Time::from_word(time_word);
513        let date = Date::julian_day_number_to_gregorian_date(julian_day_number);
514        Ok(Self { date, time })
515    }
516
517    fn write_to<W: Write>(&self, dest: &mut W) -> std::io::Result<()> {
518        dest.write_i32::<LittleEndian>(self.date.to_julian_day_number())?;
519        dest.write_i32::<LittleEndian>(self.time.to_time_word())?;
520        Ok(())
521    }
522}
523
524impl WritableAsDbaseField for FieldValue {
525    fn write_as<E: Encoding, W: Write>(
526        &self,
527        field_info: &FieldInfo,
528        encoding: &E,
529        dst: &mut W,
530    ) -> Result<(), ErrorKind> {
531        if self.field_type() != field_info.field_type {
532            Err(ErrorKind::IncompatibleType)
533        } else {
534            match self {
535                FieldValue::Character(value) => value.write_as(field_info, encoding, dst),
536                FieldValue::Numeric(value) => value.write_as(field_info, encoding, dst),
537                FieldValue::Logical(value) => value.write_as(field_info, encoding, dst),
538                FieldValue::Date(value) => value.write_as(field_info, encoding, dst),
539                FieldValue::Float(value) => value.write_as(field_info, encoding, dst),
540                FieldValue::Integer(value) => value.write_as(field_info, encoding, dst),
541                FieldValue::Currency(value) => value.write_as(field_info, encoding, dst),
542                FieldValue::DateTime(value) => value.write_as(field_info, encoding, dst),
543                FieldValue::Double(value) => value.write_as(field_info, encoding, dst),
544                FieldValue::Memo(_) => unimplemented!("Cannot write memo"),
545            }
546        }
547    }
548}
549
550impl WritableAsDbaseField for f64 {
551    fn write_as<E: Encoding, W: Write>(
552        &self,
553        field_info: &FieldInfo,
554        encoding: &E,
555        dst: &mut W,
556    ) -> Result<(), ErrorKind> {
557        match field_info.field_type {
558            FieldType::Numeric => {
559                let string = format!(
560                    "{value:.precision$}",
561                    value = self,
562                    precision = field_info.num_decimal_places as usize
563                );
564                let encoded_string = encoding.encode(&string)?;
565                dst.write_all(&*encoded_string)?;
566                Ok(())
567            }
568            FieldType::Currency | FieldType::Double => {
569                dst.write_f64::<LittleEndian>(*self)?;
570                Ok(())
571            }
572            _ => Err(ErrorKind::IncompatibleType),
573        }
574    }
575}
576
577impl WritableAsDbaseField for Date {
578    fn write_as<E: Encoding, W: Write>(
579        &self,
580        field_info: &FieldInfo,
581        encoding: &E,
582        dst: &mut W,
583    ) -> Result<(), ErrorKind> {
584        if field_info.field_type == FieldType::Date {
585            let string = format!("{:04}{:02}{:02}", self.year, self.month, self.day);
586            let encoded_string = encoding.encode(&string)?;
587            dst.write_all(&*encoded_string)?;
588            Ok(())
589        } else {
590            Err(ErrorKind::IncompatibleType)
591        }
592    }
593}
594
595impl WritableAsDbaseField for Option<Date> {
596    fn write_as<E: Encoding, W: Write>(
597        &self,
598        field_info: &FieldInfo,
599        encoding: &E,
600        dst: &mut W,
601    ) -> Result<(), ErrorKind> {
602        if field_info.field_type == FieldType::Date {
603            if let Some(date) = self {
604                date.write_as(field_info, encoding, dst)?;
605            } else {
606                for _ in 0..8 {
607                    dst.write_u8(b' ')?;
608                }
609            }
610            Ok(())
611        } else {
612            Err(ErrorKind::IncompatibleType)
613        }
614    }
615}
616
617impl WritableAsDbaseField for Option<f64> {
618    fn write_as<E: Encoding, W: Write>(
619        &self,
620        field_info: &FieldInfo,
621        encoding: &E,
622        dst: &mut W,
623    ) -> Result<(), ErrorKind> {
624        if field_info.field_type == FieldType::Numeric {
625            if let Some(value) = self {
626                value.write_as(field_info, encoding, dst)
627            } else {
628                Ok(())
629            }
630        } else {
631            Err(ErrorKind::IncompatibleType)
632        }
633    }
634}
635
636impl WritableAsDbaseField for f32 {
637    fn write_as<E: Encoding, W: Write>(
638        &self,
639        field_info: &FieldInfo,
640        encoding: &E,
641        dst: &mut W,
642    ) -> Result<(), ErrorKind> {
643        if field_info.field_type == FieldType::Float {
644            let string = format!(
645                "{value:.precision$}",
646                value = self,
647                precision = field_info.num_decimal_places as usize
648            );
649            let encoded_string = encoding.encode(&string)?;
650            dst.write_all(&*encoded_string)?;
651            Ok(())
652        } else {
653            Err(ErrorKind::IncompatibleType)
654        }
655    }
656}
657
658impl WritableAsDbaseField for Option<f32> {
659    fn write_as<E: Encoding, W: Write>(
660        &self,
661        field_info: &FieldInfo,
662        encoding: &E,
663        dst: &mut W,
664    ) -> Result<(), ErrorKind> {
665        if field_info.field_type == FieldType::Float {
666            if let Some(value) = self {
667                value.write_as(field_info, encoding, dst)?;
668            }
669            Ok(())
670        } else {
671            Err(ErrorKind::IncompatibleType)
672        }
673    }
674}
675
676impl WritableAsDbaseField for String {
677    fn write_as<E: Encoding, W: Write>(
678        &self,
679        field_info: &FieldInfo,
680        encoding: &E,
681        dst: &mut W,
682    ) -> Result<(), ErrorKind> {
683        if field_info.field_type == FieldType::Character {
684            let encoded_bytes = encoding.encode(self.as_str())?;
685            dst.write_all(&*encoded_bytes)?;
686            Ok(())
687        } else {
688            Err(ErrorKind::IncompatibleType)
689        }
690    }
691}
692
693impl WritableAsDbaseField for Option<String> {
694    fn write_as<E: Encoding, W: Write>(
695        &self,
696        field_info: &FieldInfo,
697        encoding: &E,
698        dst: &mut W,
699    ) -> Result<(), ErrorKind> {
700        if field_info.field_type == FieldType::Character {
701            if let Some(s) = self {
702                s.write_as(field_info, encoding, dst)?;
703            }
704            Ok(())
705        } else {
706            Err(ErrorKind::IncompatibleType)
707        }
708    }
709}
710
711impl WritableAsDbaseField for &str {
712    fn write_as<E: Encoding, W: Write>(
713        &self,
714        field_info: &FieldInfo,
715        encoding: &E,
716        dst: &mut W,
717    ) -> Result<(), ErrorKind> {
718        if field_info.field_type == FieldType::Character {
719            let encoded_bytes = encoding.encode(self)?;
720            dst.write_all(&*encoded_bytes)?;
721            Ok(())
722        } else {
723            Err(ErrorKind::IncompatibleType)
724        }
725    }
726}
727
728impl WritableAsDbaseField for bool {
729    fn write_as<E: Encoding, W: Write>(
730        &self,
731        field_info: &FieldInfo,
732        encoding: &E,
733        dst: &mut W,
734    ) -> Result<(), ErrorKind> {
735        if field_info.field_type == FieldType::Logical {
736            if *self {
737                let encoded_bytes = encoding.encode("t")?;
738                dst.write_all(&*encoded_bytes)?;
739            } else {
740                let encoded_bytes = encoding.encode("f")?;
741                dst.write_all(&*encoded_bytes)?;
742            }
743            Ok(())
744        } else {
745            Err(ErrorKind::IncompatibleType)
746        }
747    }
748}
749
750impl WritableAsDbaseField for Option<bool> {
751    fn write_as<E: Encoding, W: Write>(
752        &self,
753        field_info: &FieldInfo,
754        encoding: &E,
755        dst: &mut W,
756    ) -> Result<(), ErrorKind> {
757        if field_info.field_type == FieldType::Logical {
758            if let Some(v) = self {
759                v.write_as(field_info, encoding, dst)?;
760            }
761            Ok(())
762        } else {
763            Err(ErrorKind::IncompatibleType)
764        }
765    }
766}
767
768impl WritableAsDbaseField for i32 {
769    fn write_as<E: Encoding, W: Write>(
770        &self,
771        field_info: &FieldInfo,
772        _encoding: &E,
773        dst: &mut W,
774    ) -> Result<(), ErrorKind> {
775        if field_info.field_type == FieldType::Integer {
776            dst.write_i32::<LittleEndian>(*self)?;
777            Ok(())
778        } else {
779            Err(ErrorKind::IncompatibleType)
780        }
781    }
782}
783
784impl WritableAsDbaseField for DateTime {
785    fn write_as<E: Encoding, W: Write>(
786        &self,
787        field_info: &FieldInfo,
788        _encoding: &E,
789        dst: &mut W,
790    ) -> Result<(), ErrorKind> {
791        if field_info.field_type == FieldType::DateTime {
792            self.write_to(dst)?;
793            Ok(())
794        } else {
795            Err(ErrorKind::IncompatibleType)
796        }
797    }
798}
799
800#[cfg(feature = "serde")]
801mod de {
802    use super::*;
803    use serde::de::{Deserialize, Visitor};
804    use serde::Deserializer;
805    use std::io::Cursor;
806
807    impl<'de> Deserialize<'de> for Date {
808        fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
809        where
810            D: Deserializer<'de>,
811        {
812            struct DateVisitor;
813            impl<'de> Visitor<'de> for DateVisitor {
814                type Value = Date;
815
816                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
817                    formatter.write_str("struct Date")
818                }
819
820                fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
821                where
822                    E: serde::de::Error,
823                {
824                    let string = String::from_utf8(v).unwrap();
825                    Ok(Date::from_str(&string).unwrap())
826                }
827            }
828            deserializer.deserialize_byte_buf(DateVisitor)
829        }
830    }
831
832    struct DateTimeVisitor;
833
834    impl<'de> Visitor<'de> for DateTimeVisitor {
835        type Value = DateTime;
836
837        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
838            formatter.write_str("struct dbase::DateTime")
839        }
840
841        fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
842        where
843            E: serde::de::Error,
844        {
845            let mut cursor = Cursor::new(v);
846            match DateTime::read_from(&mut cursor) {
847                Ok(d) => Ok(d),
848                Err(e) => Err(E::custom(e)),
849            }
850        }
851    }
852
853    impl<'de> Deserialize<'de> for DateTime {
854        fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
855        where
856            D: Deserializer<'de>,
857        {
858            deserializer.deserialize_byte_buf(DateTimeVisitor)
859        }
860    }
861}
862
863#[cfg(feature = "serde")]
864mod ser {
865    use super::*;
866
867    use serde::ser::Serialize;
868    use serde::Serializer;
869
870    impl Serialize for Date {
871        fn serialize<S>(
872            &self,
873            serializer: S,
874        ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
875        where
876            S: Serializer,
877        {
878            serializer.serialize_bytes(self.to_string().as_bytes())
879        }
880    }
881
882    impl Serialize for DateTime {
883        fn serialize<S>(
884            &self,
885            serializer: S,
886        ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
887        where
888            S: Serializer,
889        {
890            let mut bytes = [0u8; 8];
891            bytes[..4].copy_from_slice(&self.date.to_julian_day_number().to_le_bytes());
892            bytes[4..8].copy_from_slice(&self.time.to_time_word().to_le_bytes());
893            serializer.serialize_bytes(&bytes)
894        }
895    }
896}
897
898#[derive(Copy, Clone, Debug)]
899pub enum TrimOption {
900    Begin,
901    End,
902    BeginEnd,
903}
904
905fn trim_field_data(bytes: &[u8], option: TrimOption) -> &[u8] {
906    // Value in the dbf file is surrounded by space characters (32u8). We discard them before
907    // parsing the bytes into string. Doing so doubles the performance in comparison to
908    // using String::trim() afterwards.
909    let mut first = usize::MAX;
910    let mut last = 0;
911    let ptr = bytes.as_ptr();
912
913    // Using unchecked indexing of the vector provides around 30% increase of reading speed.
914    // SAFETY: index is always between 0 and bytes.len(), so using pointers here is safe.
915    unsafe {
916        for i in 0..bytes.len() {
917            if *ptr.add(i) == 0u8 {
918                break;
919            }
920
921            if *ptr.add(i) != 32 {
922                if first == usize::MAX {
923                    first = i;
924                }
925
926                last = i;
927            }
928        }
929    }
930
931    // Input starts with zero character or consists only of spaces.
932    if first == usize::MAX {
933        return &[];
934    }
935
936    // Discarding spaces in front and at the end. The space character (32u8) is 00110000 in binary
937    // format, which makes it safe to drop without checking, as it cannot be part of the multi-byte
938    // UTF-8 symbol (all such bytes must start with 10).
939    match option {
940        TrimOption::Begin => &bytes[first..],
941        TrimOption::End => &bytes[..last + 1],
942        TrimOption::BeginEnd => &bytes[first..last + 1],
943    }
944}
945
946#[cfg(test)]
947mod test {
948    use super::*;
949
950    use crate::encoding::UnicodeLossy;
951    use crate::field::FieldFlags;
952    use std::io::Cursor;
953
954    fn create_temp_field_info(field_type: FieldType, len: u8) -> FieldInfo {
955        FieldInfo {
956            name: "".to_owned(),
957            field_type,
958            displacement_field: [0u8; 4],
959            field_length: len,
960            num_decimal_places: 0,
961            flags: FieldFlags { 0: 0u8 },
962            autoincrement_next_val: [0u8; 5],
963            autoincrement_step: 0u8,
964        }
965    }
966
967    fn test_we_can_read_back(field_info: &FieldInfo, value: &FieldValue) {
968        let mut out = Cursor::new(Vec::<u8>::with_capacity(field_info.field_length as usize));
969        let encoding = UnicodeLossy;
970
971        value.write_as(field_info, &encoding, &mut out).unwrap();
972        out.set_position(0);
973
974        let read_value = FieldValue::read_from::<std::io::Cursor<Vec<u8>>, _>(
975            out.get_mut(),
976            &mut None,
977            field_info,
978            &encoding,
979            TrimOption::BeginEnd,
980        )
981        .unwrap();
982        assert_eq!(value, &read_value);
983    }
984
985    #[test]
986    fn write_read_date() {
987        let date = FieldValue::from(Date {
988            year: 2019,
989            month: 01,
990            day: 01,
991        });
992
993        let field_info = create_temp_field_info(FieldType::Date, FieldType::Date.size().unwrap());
994        test_we_can_read_back(&field_info, &date);
995    }
996
997    #[test]
998    fn test_write_read_empty_date() {
999        let date = FieldValue::Date(None);
1000
1001        let field_info = create_temp_field_info(FieldType::Date, FieldType::Date.size().unwrap());
1002        test_we_can_read_back(&field_info, &date);
1003    }
1004
1005    #[test]
1006    fn write_read_ascii_char() {
1007        let field = FieldValue::Character(Some(String::from("Only ASCII")));
1008
1009        let record_info = create_temp_field_info(FieldType::Character, 10);
1010        test_we_can_read_back(&record_info, &field);
1011    }
1012
1013    #[test]
1014    fn test_write_read_integer_via_enum() {
1015        use crate::field::FieldName;
1016
1017        let value = FieldValue::Integer(1457);
1018
1019        let field_info = FieldInfo::new(
1020            FieldName::try_from("Integer").unwrap(),
1021            FieldType::Integer,
1022            FieldType::Integer.size().unwrap(),
1023        );
1024
1025        test_we_can_read_back(&field_info, &value);
1026    }
1027
1028    #[test]
1029    fn test_from_julian_day_number() {
1030        let date = Date::julian_day_number_to_gregorian_date(2458685);
1031        assert_eq!(date.year, 2019);
1032        assert_eq!(date.month, 07);
1033        assert_eq!(date.day, 20);
1034    }
1035
1036    #[test]
1037    fn test_to_julian_day_number() {
1038        let date = Date {
1039            year: 2019,
1040            month: 07,
1041            day: 20,
1042        };
1043        assert_eq!(date.to_julian_day_number(), 2458685);
1044    }
1045
1046    #[test]
1047    fn test_to_unix_days() {
1048        let date = Date {
1049            year: 1970,
1050            month: 1,
1051            day: 1,
1052        };
1053        assert_eq!(date.to_unix_days(), 0);
1054    }
1055
1056    #[test]
1057    fn test_to_unix_timestamp() {
1058        let datetime = DateTime::new(Date::new(1, 1, 1970), Time::new(1, 1, 1));
1059        assert_eq!(datetime.to_unix_timestamp(), 3661);
1060    }
1061}