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#[derive(Debug, Copy, Clone, PartialEq, Eq)]
16pub enum FieldType {
17 Character,
19 Date,
20 Float,
21 Numeric,
22 Logical,
23 Currency,
25 DateTime,
26 Integer,
27 Double,
29 Memo,
30 }
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 '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 'Y' => Some(FieldType::Currency),
66 'T' => Some(FieldType::DateTime),
67 'I' => Some(FieldType::Integer),
68 'B' => Some(FieldType::Double),
70 'M' => Some(FieldType::Memo),
71 _ => None,
75 }
76 }
77
78 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#[derive(Debug, Clone, PartialEq)]
116pub enum FieldValue {
117 Character(Option<String>),
124 Numeric(Option<f64>),
126 Logical(Option<bool>),
128 Date(Option<Date>),
130 Float(Option<f32>),
132 Integer(i32),
134 Currency(f64),
135 DateTime(DateTime),
136 Double(f64),
137
138 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 = 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 = 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 = 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 = 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 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 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#[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 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 pub fn year(&self) -> u32 {
304 self.year
305 }
306
307 pub fn month(&self) -> u32 {
309 self.month
310 }
311
312 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 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#[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 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 pub fn hours(&self) -> u32 {
445 self.hours
446 }
447
448 pub fn minutes(&self) -> u32 {
450 self.minutes
451 }
452
453 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#[derive(Debug, Copy, Clone, PartialEq)]
481pub struct DateTime {
482 date: Date,
483 time: Time,
484}
485
486impl DateTime {
487 pub fn new(date: Date, time: Time) -> Self {
489 Self { date, time }
490 }
491
492 pub fn date(&self) -> Date {
494 self.date
495 }
496
497 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 let mut first = usize::MAX;
910 let mut last = 0;
911 let ptr = bytes.as_ptr();
912
913 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 if first == usize::MAX {
933 return &[];
934 }
935
936 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}