1use std::{
2 fmt::{Display, LowerHex},
3 string::FromUtf8Error,
4};
5
6use chrono::{DateTime, FixedOffset, NaiveDateTime, Offset, Utc};
7
8use nom::{multi::many_m_n, number::Endianness, AsChar};
9#[cfg(feature = "json_dump")]
10use serde::{Deserialize, Serialize, Serializer};
11use thiserror::Error;
12
13use crate::ExifTag;
14
15#[derive(Debug, Clone, PartialEq)]
17#[non_exhaustive]
18pub enum EntryValue {
19 Text(String),
20 URational(URational),
21 IRational(IRational),
22
23 U8(u8),
24 U16(u16),
25 U32(u32),
26 U64(u64),
27
28 I8(i8),
29 I16(i16),
30 I32(i32),
31 I64(i64),
32
33 F32(f32),
34 F64(f64),
35
36 Time(DateTime<FixedOffset>),
37 NaiveDateTime(NaiveDateTime),
38 Undefined(Vec<u8>),
39
40 URationalArray(Vec<URational>),
41 IRationalArray(Vec<IRational>),
42
43 U8Array(Vec<u8>),
44 U16Array(Vec<u16>),
45 U32Array(Vec<u32>),
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
49pub(crate) struct EntryData<'a> {
50 pub endian: Endianness,
51 pub tag: u16,
52 pub data: &'a [u8],
53 pub data_format: DataFormat,
54 pub components_num: u32,
55}
56
57#[derive(Debug, Clone, Error)]
58pub(crate) enum ParseEntryError {
59 #[error("size is too big")]
60 EntrySizeTooBig,
61
62 #[error("data is invalid: {0}")]
63 InvalidData(String),
64
65 #[error("data format is unsupported (please file a bug): {0}")]
66 Unsupported(String),
67}
68
69impl From<chrono::ParseError> for ParseEntryError {
70 fn from(value: chrono::ParseError) -> Self {
71 ParseEntryError::InvalidData(format!("invalid time format: {value}"))
72 }
73}
74
75use ParseEntryError as Error;
76
77impl EntryData<'_> {
78 fn try_as_rationals<T: TryFromBytes>(&self) -> Result<Vec<Rational<T>>, Error> {
80 if self.components_num == 0 {
81 return Err(Error::InvalidData("components is 0".to_string()));
82 }
83
84 let mut vec = Vec::with_capacity(self.components_num as usize);
85 for i in 0..self.components_num {
86 let rational = decode_rational::<T>(&self.data[i as usize * 8..], self.endian)?;
87 vec.push(rational);
88 }
89 Ok(vec)
90 }
91}
92
93impl EntryValue {
94 pub(crate) fn parse(entry: &EntryData, tz: &Option<String>) -> Result<EntryValue, Error> {
116 if entry.data.is_empty() {
117 return Err(Error::InvalidData(
118 "invalid DirectoryEntry: entry data is empty".into(),
119 ));
120 }
121
122 let endian = entry.endian;
123 let tag = entry.tag;
124 let data_format = entry.data_format;
125 let data = entry.data;
126 let components_num = entry.components_num;
127
128 if data.is_empty() || components_num == 0 {
129 return Ok(EntryValue::variant_default(data_format));
130 }
131
132 let exif_tag: Result<ExifTag, _> = tag.try_into();
133 if let Ok(tag) = exif_tag {
134 if tag == ExifTag::DateTimeOriginal
135 || tag == ExifTag::CreateDate
136 || tag == ExifTag::ModifyDate
137 {
138 let s = get_cstr(data).map_err(|e| Error::InvalidData(e.to_string()))?;
145
146 let t = if let Some(tz) = tz {
147 let tz = repair_tz_str(tz);
148 let ss = format!("{s} {tz}");
149 match DateTime::parse_from_str(&ss, "%Y:%m:%d %H:%M:%S %z") {
150 Ok(t) => t,
151 Err(_) => return Ok(EntryValue::NaiveDateTime(parse_naive_time(s)?)),
152 }
153 } else {
154 return Ok(EntryValue::NaiveDateTime(parse_naive_time(s)?));
155 };
156
157 return Ok(EntryValue::Time(t));
158 }
159 }
160
161 match data_format {
162 DataFormat::U8 => match components_num {
163 1 => Ok(Self::U8(data[0])),
164 _ => Ok(Self::U8Array(data.into())),
165 },
166 DataFormat::Text => Ok(EntryValue::Text(
167 get_cstr(data).map_err(|e| Error::InvalidData(e.to_string()))?,
168 )),
169 DataFormat::U16 => {
170 if components_num == 1 {
171 Ok(Self::U16(u16::try_from_bytes(data, endian)?))
172 } else {
173 let (_, v) = many_m_n::<_, _, nom::error::Error<_>, _>(
174 components_num as usize,
175 components_num as usize,
176 nom::number::complete::u16(endian),
177 )(data)
178 .map_err(|e| {
179 ParseEntryError::InvalidData(format!("parse U16Array error: {e:?}"))
180 })?;
181 Ok(Self::U16Array(v))
182 }
183 }
184 DataFormat::U32 => {
185 if components_num == 1 {
186 Ok(Self::U32(u32::try_from_bytes(data, endian)?))
187 } else {
188 let (_, v) = many_m_n::<_, _, nom::error::Error<_>, _>(
189 components_num as usize,
190 components_num as usize,
191 nom::number::complete::u32(endian),
192 )(data)
193 .map_err(|e| {
194 ParseEntryError::InvalidData(format!("parse U32Array error: {e:?}"))
195 })?;
196 Ok(Self::U32Array(v))
197 }
198 }
199 DataFormat::URational => {
200 let rationals = entry.try_as_rationals::<u32>()?;
201 if rationals.len() == 1 {
202 Ok(Self::URational(rationals[0]))
203 } else {
204 Ok(Self::URationalArray(rationals))
205 }
206 }
207 DataFormat::I8 => match components_num {
208 1 => Ok(Self::I8(data[0] as i8)),
209 x => Err(Error::Unsupported(format!(
210 "signed byte with {x} components"
211 ))),
212 },
213 DataFormat::Undefined => Ok(Self::Undefined(data.to_vec())),
214 DataFormat::I16 => match components_num {
215 1 => Ok(Self::I16(i16::try_from_bytes(data, endian)?)),
216 x => Err(Error::Unsupported(format!(
217 "signed short with {x} components"
218 ))),
219 },
220 DataFormat::I32 => match components_num {
221 1 => Ok(Self::I32(i32::try_from_bytes(data, endian)?)),
222 x => Err(Error::Unsupported(format!(
223 "signed long with {x} components"
224 ))),
225 },
226 DataFormat::IRational => {
227 let rationals = entry.try_as_rationals::<i32>()?;
228 if rationals.len() == 1 {
229 Ok(Self::IRational(rationals[0]))
230 } else {
231 Ok(Self::IRationalArray(rationals))
232 }
233 }
234 DataFormat::F32 => match components_num {
235 1 => Ok(Self::F32(f32::try_from_bytes(data, endian)?)),
236 x => Err(Error::Unsupported(format!("float with {x} components"))),
237 },
238 DataFormat::F64 => match components_num {
239 1 => Ok(Self::F64(f64::try_from_bytes(data, endian)?)),
240 x => Err(Error::Unsupported(format!("double with {x} components"))),
241 },
242 }
243 }
244
245 fn variant_default(data_format: DataFormat) -> EntryValue {
246 match data_format {
247 DataFormat::U8 => Self::U8(0),
248 DataFormat::Text => Self::Text(String::default()),
249 DataFormat::U16 => Self::U16(0),
250 DataFormat::U32 => Self::U32(0),
251 DataFormat::URational => Self::URational(URational::default()),
252 DataFormat::I8 => Self::I8(0),
253 DataFormat::Undefined => Self::Undefined(Vec::default()),
254 DataFormat::I16 => Self::I16(0),
255 DataFormat::I32 => Self::I32(0),
256 DataFormat::IRational => Self::IRational(IRational::default()),
257 DataFormat::F32 => Self::F32(0.0),
258 DataFormat::F64 => Self::F64(0.0),
259 }
260 }
261
262 pub fn as_str(&self) -> Option<&str> {
263 match self {
264 EntryValue::Text(v) => Some(v),
265 _ => None,
266 }
267 }
268
269 #[deprecated(since = "2.7.0")]
271 pub fn as_time(&self) -> Option<DateTime<FixedOffset>> {
272 match self {
273 EntryValue::Time(v) => Some(*v),
274 _ => None,
275 }
276 }
277
278 pub fn as_time_components(&self) -> Option<(NaiveDateTime, Option<FixedOffset>)> {
313 match self {
314 EntryValue::Time(v) => Some((v.naive_local(), Some(v.offset().fix()))),
315 EntryValue::NaiveDateTime(v) => Some((*v, None)),
316 _ => None,
317 }
318 }
319
320 pub fn as_u8(&self) -> Option<u8> {
321 match self {
322 EntryValue::U8(v) => Some(*v),
323 _ => None,
324 }
325 }
326
327 pub fn as_i8(&self) -> Option<i8> {
328 match self {
329 EntryValue::I8(v) => Some(*v),
330 _ => None,
331 }
332 }
333
334 pub fn as_u16(&self) -> Option<u16> {
335 match self {
336 EntryValue::U16(v) => Some(*v),
337 _ => None,
338 }
339 }
340
341 pub fn as_i16(&self) -> Option<i16> {
342 match self {
343 EntryValue::I16(v) => Some(*v),
344 _ => None,
345 }
346 }
347
348 pub fn as_u64(&self) -> Option<u64> {
349 match self {
350 EntryValue::U64(v) => Some(*v),
351 _ => None,
352 }
353 }
354
355 pub fn as_u32(&self) -> Option<u32> {
356 match self {
357 EntryValue::U32(v) => Some(*v),
358 _ => None,
359 }
360 }
361
362 pub fn as_i32(&self) -> Option<i32> {
363 match self {
364 EntryValue::I32(v) => Some(*v),
365 _ => None,
366 }
367 }
368
369 pub fn as_urational(&self) -> Option<URational> {
370 if let EntryValue::URational(v) = self {
371 Some(*v)
372 } else {
373 None
374 }
375 }
376
377 pub fn as_irational(&self) -> Option<IRational> {
378 if let EntryValue::IRational(v) = self {
379 Some(*v)
380 } else {
381 None
382 }
383 }
384
385 pub fn as_urational_array(&self) -> Option<&[URational]> {
386 if let EntryValue::URationalArray(v) = self {
387 Some(v)
388 } else {
389 None
390 }
391 }
392
393 pub fn as_irational_array(&self) -> Option<&[IRational]> {
394 if let EntryValue::IRationalArray(v) = self {
395 Some(v)
396 } else {
397 None
398 }
399 }
400
401 pub fn as_u8array(&self) -> Option<&[u8]> {
402 if let EntryValue::U8Array(v) = self {
403 Some(v)
404 } else {
405 None
406 }
407 }
408 pub fn to_u8array(self) -> Option<Vec<u8>> {
409 if let EntryValue::U8Array(v) = self {
410 Some(v)
411 } else {
412 None
413 }
414 }
415}
416
417impl From<(NaiveDateTime, Option<FixedOffset>)> for EntryValue {
419 fn from(value: (NaiveDateTime, Option<FixedOffset>)) -> Self {
420 if let Some(offset) = value.1 {
421 EntryValue::Time(value.0.and_local_timezone(offset).unwrap())
422 } else {
423 EntryValue::NaiveDateTime(value.0)
424 }
425 }
426}
427
428fn parse_naive_time(s: String) -> Result<NaiveDateTime, ParseEntryError> {
429 let t = NaiveDateTime::parse_from_str(&s, "%Y:%m:%d %H:%M:%S")?;
430 Ok(t)
431}
432fn repair_tz_str(tz: &str) -> String {
444 if let Some(idx) = tz.find(":") {
445 if tz[idx..].len() < 3 {
446 return format!("{tz}0");
448 }
449 }
450 tz.into()
451}
452
453#[repr(u16)]
469#[derive(Clone, Copy, Debug, PartialEq, Eq)]
470#[allow(unused)]
471pub(crate) enum DataFormat {
472 U8 = 1,
473 Text = 2,
474 U16 = 3,
475 U32 = 4,
476 URational = 5,
477 I8 = 6,
478 Undefined = 7,
479 I16 = 8,
480 I32 = 9,
481 IRational = 10,
482 F32 = 11,
483 F64 = 12,
484}
485
486impl DataFormat {
487 pub fn component_size(&self) -> usize {
488 match self {
489 Self::U8 | Self::I8 | Self::Text | Self::Undefined => 1,
490 Self::U16 | Self::I16 => 2,
491 Self::U32 | Self::I32 | Self::F32 => 4,
492 Self::URational | Self::IRational | Self::F64 => 8,
493 }
494 }
495}
496
497impl TryFrom<u16> for DataFormat {
498 type Error = Error;
499 fn try_from(v: u16) -> Result<Self, Self::Error> {
500 if v >= Self::U8 as u16 && v <= Self::F64 as u16 {
501 Ok(unsafe { std::mem::transmute::<u16, Self>(v) })
502 } else {
503 Err(Error::InvalidData(format!("data format 0x{v:02x}")))
504 }
505 }
506}
507
508#[cfg(feature = "json_dump")]
509impl Serialize for EntryValue {
510 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
511 where
512 S: Serializer,
513 {
514 serializer.serialize_str(&self.to_string())
515 }
516}
517
518impl Display for EntryValue {
525 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
526 match self {
527 EntryValue::Text(v) => v.fmt(f),
528 EntryValue::URational(v) => {
529 format!("{}/{} ({:.04})", v.0, v.1, v.0 as f64 / v.1 as f64).fmt(f)
530 }
531 EntryValue::IRational(v) => {
532 format!("{}/{} ({:.04})", v.0, v.1, v.0 as f64 / v.1 as f64).fmt(f)
533 }
534 EntryValue::U32(v) => Display::fmt(&v, f),
535 EntryValue::U16(v) => Display::fmt(&v, f),
536 EntryValue::U64(v) => Display::fmt(&v, f),
537 EntryValue::I16(v) => Display::fmt(&v, f),
538 EntryValue::I32(v) => Display::fmt(&v, f),
539 EntryValue::I64(v) => Display::fmt(&v, f),
540 EntryValue::F32(v) => Display::fmt(&v, f),
541 EntryValue::F64(v) => Display::fmt(&v, f),
542 EntryValue::U8(v) => Display::fmt(&v, f),
543 EntryValue::I8(v) => Display::fmt(&v, f),
544 EntryValue::Time(v) => Display::fmt(&v.to_rfc3339(), f),
545 EntryValue::NaiveDateTime(v) => Display::fmt(&v.format("%Y-%m-%d %H:%M:%S"), f),
546 EntryValue::Undefined(v) => fmt_array_to_string("Undefined", v, f),
547 EntryValue::URationalArray(v) => {
548 format!("URationalArray[{}]", rationals_to_string::<u32>(v)).fmt(f)
549 }
550 EntryValue::IRationalArray(v) => {
551 format!("IRationalArray[{}]", rationals_to_string::<i32>(v)).fmt(f)
552 }
553 EntryValue::U8Array(v) => fmt_array_to_string("U8Array", v, f),
554 EntryValue::U32Array(v) => fmt_array_to_string("U32Array", v, f),
555 EntryValue::U16Array(v) => fmt_array_to_string("U16Array", v, f),
556 }
557 }
558}
559
560pub(crate) fn fmt_array_to_string<T: Display + LowerHex>(
561 name: &str,
562 v: &[T],
563 f: &mut std::fmt::Formatter,
564) -> Result<(), std::fmt::Error> {
565 array_to_string(name, v).fmt(f)
566 }
576
577pub(crate) fn array_to_string<T: Display + LowerHex>(name: &str, v: &[T]) -> String {
578 const MAX_DISPLAY_NUM: usize = 8;
580 let s = v
581 .iter()
582 .map(|x| format!("0x{x:02x}"))
583 .take(MAX_DISPLAY_NUM + 1)
584 .enumerate()
585 .map(|(i, x)| {
586 if i >= MAX_DISPLAY_NUM {
587 "...".to_owned()
588 } else {
589 x
590 }
591 })
592 .collect::<Vec<String>>()
593 .join(", ");
594 format!("{}[{}]", name, s)
595}
596
597fn rationals_to_string<T>(rationals: &[Rational<T>]) -> String
598where
599 T: Display + Into<f64> + Copy,
600{
601 const MAX_DISPLAY_NUM: usize = 3;
603 rationals
604 .iter()
605 .map(|x| format!("{}/{} ({:.04})", x.0, x.1, x.0.into() / x.1.into()))
606 .take(MAX_DISPLAY_NUM + 1)
607 .enumerate()
608 .map(|(i, x)| {
609 if i >= MAX_DISPLAY_NUM {
610 "...".to_owned()
611 } else {
612 x
613 }
614 })
615 .collect::<Vec<String>>()
616 .join(", ")
617}
618
619impl From<DateTime<Utc>> for EntryValue {
620 fn from(value: DateTime<Utc>) -> Self {
621 assert_eq!(value.offset().fix(), FixedOffset::east_opt(0).unwrap());
622 EntryValue::Time(value.fixed_offset())
623 }
624}
625
626impl From<DateTime<FixedOffset>> for EntryValue {
627 fn from(value: DateTime<FixedOffset>) -> Self {
628 EntryValue::Time(value)
629 }
630}
631
632impl From<u8> for EntryValue {
633 fn from(value: u8) -> Self {
634 EntryValue::U8(value)
635 }
636}
637impl From<u16> for EntryValue {
638 fn from(value: u16) -> Self {
639 EntryValue::U16(value)
640 }
641}
642impl From<u32> for EntryValue {
643 fn from(value: u32) -> Self {
644 EntryValue::U32(value)
645 }
646}
647impl From<u64> for EntryValue {
648 fn from(value: u64) -> Self {
649 EntryValue::U64(value)
650 }
651}
652
653impl From<i8> for EntryValue {
654 fn from(value: i8) -> Self {
655 EntryValue::I8(value)
656 }
657}
658impl From<i16> for EntryValue {
659 fn from(value: i16) -> Self {
660 EntryValue::I16(value)
661 }
662}
663impl From<i32> for EntryValue {
664 fn from(value: i32) -> Self {
665 EntryValue::I32(value)
666 }
667}
668impl From<i64> for EntryValue {
669 fn from(value: i64) -> Self {
670 EntryValue::I64(value)
671 }
672}
673
674impl From<f32> for EntryValue {
675 fn from(value: f32) -> Self {
676 EntryValue::F32(value)
677 }
678}
679impl From<f64> for EntryValue {
680 fn from(value: f64) -> Self {
681 EntryValue::F64(value)
682 }
683}
684
685impl From<String> for EntryValue {
686 fn from(value: String) -> Self {
687 EntryValue::Text(value)
688 }
689}
690
691impl From<&String> for EntryValue {
692 fn from(value: &String) -> Self {
693 EntryValue::Text(value.to_owned())
694 }
695}
696
697impl From<&str> for EntryValue {
698 fn from(value: &str) -> Self {
699 value.to_owned().into()
700 }
701}
702
703impl From<(u32, u32)> for EntryValue {
704 fn from(value: (u32, u32)) -> Self {
705 Self::URational(value.into())
706 }
707}
708
709impl From<(i32, i32)> for EntryValue {
710 fn from(value: (i32, i32)) -> Self {
711 Self::IRational((value.0, value.1).into())
712 }
713}
714
715pub type URational = Rational<u32>;
720pub type IRational = Rational<i32>;
721
722#[cfg_attr(feature = "json_dump", derive(Serialize, Deserialize))]
723#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
724pub struct Rational<T>(pub T, pub T);
725
726impl<T> Rational<T>
727where
728 T: Copy + Into<f64>,
729{
730 pub fn as_float(&self) -> f64 {
731 std::convert::Into::<f64>::into(self.0) / std::convert::Into::<f64>::into(self.1)
732 }
733}
734
735impl<T> From<(T, T)> for Rational<T>
736where
737 T: Copy,
738{
739 fn from(value: (T, T)) -> Self {
740 Self(value.0, value.1)
741 }
742}
743
744impl<T> From<Rational<T>> for (T, T)
745where
746 T: Copy,
747{
748 fn from(value: Rational<T>) -> Self {
749 (value.0, value.1)
750 }
751}
752
753impl From<IRational> for URational {
754 fn from(value: IRational) -> Self {
755 Self(value.0 as u32, value.1 as u32)
756 }
757}
758
759pub(crate) fn get_cstr(data: &[u8]) -> std::result::Result<String, FromUtf8Error> {
760 let vec = filter_zero(data);
761 if let Ok(s) = String::from_utf8(vec) {
762 Ok(s)
763 } else {
764 Ok(filter_zero(data)
765 .into_iter()
766 .map(|x| x.as_char())
767 .collect::<String>())
768 }
769}
770
771pub(crate) fn filter_zero(data: &[u8]) -> Vec<u8> {
772 data.iter()
773 .skip_while(|b| **b == 0)
775 .take_while(|b| **b != 0)
777 .cloned()
778 .collect::<Vec<u8>>()
779}
780
781pub(crate) trait TryFromBytes: Sized {
782 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error>;
783}
784
785impl TryFromBytes for u32 {
786 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
787 fn make_err<T>() -> Error {
788 Error::InvalidData(format!(
789 "data is too small to convert to {}",
790 std::any::type_name::<T>(),
791 ))
792 }
793 match endian {
794 Endianness::Big => {
795 let (int_bytes, _) = bs
796 .split_at_checked(std::mem::size_of::<Self>())
797 .ok_or_else(make_err::<Self>)?;
798 Ok(Self::from_be_bytes(
799 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
800 ))
801 }
802 Endianness::Little => {
803 let (int_bytes, _) = bs
804 .split_at_checked(std::mem::size_of::<Self>())
805 .ok_or_else(make_err::<Self>)?;
806 Ok(Self::from_le_bytes(
807 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
808 ))
809 }
810 Endianness::Native => unimplemented!(),
811 }
812 }
813}
814
815impl TryFromBytes for i32 {
816 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
817 fn make_err<T>() -> Error {
818 Error::InvalidData(format!(
819 "data is too small to convert to {}",
820 std::any::type_name::<T>(),
821 ))
822 }
823 match endian {
824 Endianness::Big => {
825 let (int_bytes, _) = bs
826 .split_at_checked(std::mem::size_of::<Self>())
827 .ok_or_else(make_err::<Self>)?;
828 Ok(Self::from_be_bytes(
829 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
830 ))
831 }
832 Endianness::Little => {
833 let (int_bytes, _) = bs
834 .split_at_checked(std::mem::size_of::<Self>())
835 .ok_or_else(make_err::<Self>)?;
836 Ok(Self::from_le_bytes(
837 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
838 ))
839 }
840 Endianness::Native => unimplemented!(),
841 }
842 }
843}
844
845impl TryFromBytes for u16 {
846 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
847 fn make_err<T>() -> Error {
848 Error::InvalidData(format!(
849 "data is too small to convert to {}",
850 std::any::type_name::<T>(),
851 ))
852 }
853 match endian {
854 Endianness::Big => {
855 let (int_bytes, _) = bs
856 .split_at_checked(std::mem::size_of::<Self>())
857 .ok_or_else(make_err::<Self>)?;
858 Ok(Self::from_be_bytes(
859 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
860 ))
861 }
862 Endianness::Little => {
863 let (int_bytes, _) = bs
864 .split_at_checked(std::mem::size_of::<Self>())
865 .ok_or_else(make_err::<Self>)?;
866 Ok(Self::from_le_bytes(
867 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
868 ))
869 }
870 Endianness::Native => unimplemented!(),
871 }
872 }
873}
874
875impl TryFromBytes for i16 {
876 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
877 fn make_err<T>() -> Error {
878 Error::InvalidData(format!(
879 "data is too small to convert to {}",
880 std::any::type_name::<T>(),
881 ))
882 }
883 match endian {
884 Endianness::Big => {
885 let (int_bytes, _) = bs
886 .split_at_checked(std::mem::size_of::<Self>())
887 .ok_or_else(make_err::<Self>)?;
888 Ok(Self::from_be_bytes(
889 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
890 ))
891 }
892 Endianness::Little => {
893 let (int_bytes, _) = bs
894 .split_at_checked(std::mem::size_of::<Self>())
895 .ok_or_else(make_err::<Self>)?;
896 Ok(Self::from_le_bytes(
897 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
898 ))
899 }
900 Endianness::Native => unimplemented!(),
901 }
902 }
903}
904
905impl TryFromBytes for f32 {
906 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
907 fn make_err<T>() -> Error {
908 Error::InvalidData(format!(
909 "data is too small to convert to {}",
910 std::any::type_name::<T>(),
911 ))
912 }
913 match endian {
914 Endianness::Big => {
915 let (int_bytes, _) = bs
916 .split_at_checked(std::mem::size_of::<Self>())
917 .ok_or_else(make_err::<Self>)?;
918 Ok(Self::from_be_bytes(
919 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
920 ))
921 }
922 Endianness::Little => {
923 let (int_bytes, _) = bs
924 .split_at_checked(std::mem::size_of::<Self>())
925 .ok_or_else(make_err::<Self>)?;
926 Ok(Self::from_le_bytes(
927 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
928 ))
929 }
930 Endianness::Native => unimplemented!(),
931 }
932 }
933}
934
935impl TryFromBytes for f64 {
936 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
937 fn make_err<T>() -> Error {
938 Error::InvalidData(format!(
939 "data is too small to convert to {}",
940 std::any::type_name::<T>(),
941 ))
942 }
943 match endian {
944 Endianness::Big => {
945 let (int_bytes, _) = bs
946 .split_at_checked(std::mem::size_of::<Self>())
947 .ok_or_else(make_err::<Self>)?;
948 Ok(Self::from_be_bytes(
949 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
950 ))
951 }
952 Endianness::Little => {
953 let (int_bytes, _) = bs
954 .split_at_checked(std::mem::size_of::<Self>())
955 .ok_or_else(make_err::<Self>)?;
956 Ok(Self::from_le_bytes(
957 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
958 ))
959 }
960 Endianness::Native => unimplemented!(),
961 }
962 }
963}
964
965pub(crate) fn decode_rational<T: TryFromBytes>(
966 data: &[u8],
967 endian: Endianness,
968) -> Result<Rational<T>, Error> {
969 if data.len() < 8 {
970 return Err(Error::InvalidData(
971 "data is too small to decode a rational".to_string(),
972 ));
973 }
974
975 let numerator = T::try_from_bytes(data, endian)?;
976 let denominator = T::try_from_bytes(&data[4..], endian)?; Ok(Rational::<T>(numerator, denominator))
978}
979
980#[cfg(test)]
981mod tests {
982 use chrono::{Local, NaiveDateTime, TimeZone};
983
984 use super::*;
985
986 #[test]
987 fn test_parse_time() {
988 let s = "2023:07:09 20:36:33";
989 let t1 = NaiveDateTime::parse_from_str(s, "%Y:%m:%d %H:%M:%S").unwrap();
990 let t1 = Local.from_local_datetime(&t1).unwrap();
991
992 let tz = t1.format("%:z").to_string();
993
994 let s = format!("2023:07:09 20:36:33 {tz}");
995 let t2 = DateTime::parse_from_str(&s, "%Y:%m:%d %H:%M:%S %z").unwrap();
996
997 let t3 = t2.with_timezone(t2.offset());
998
999 assert_eq!(t1, t2);
1000 assert_eq!(t1, t3);
1001 }
1002
1003 #[test]
1004 fn test_iso_8601() {
1005 let s = "2023-11-02T19:58:34+0800";
1006 let t1 = DateTime::parse_from_str(s, "%+").unwrap();
1007
1008 let s = "2023-11-02T19:58:34+08:00";
1009 let t2 = DateTime::parse_from_str(s, "%+").unwrap();
1010
1011 let s = "2023-11-02T19:58:34.026490+08:00";
1012 let t3 = DateTime::parse_from_str(s, "%+").unwrap();
1013
1014 assert_eq!(t1, t2);
1015 assert!(t3 > t2);
1016 }
1017
1018 #[test]
1019 fn test_date_time_components() {
1020 let dt = DateTime::parse_from_str("2023-07-09T20:36:33+08:00", "%+").unwrap();
1021 let ndt =
1022 NaiveDateTime::parse_from_str("2023-07-09T20:36:33", "%Y-%m-%dT%H:%M:%S").unwrap();
1023 let offset = FixedOffset::east_opt(8 * 3600).unwrap();
1024
1025 let ev = EntryValue::Time(dt);
1026 assert_eq!(ev.as_time_components().unwrap(), (ndt, Some(offset)));
1027
1028 let recovered_dt = ndt.and_local_timezone(offset).unwrap();
1029 assert_eq!(recovered_dt, dt);
1030 let recovered_dt = offset.from_local_datetime(&ndt).unwrap();
1031 assert_eq!(recovered_dt, dt);
1032
1033 let ev = EntryValue::NaiveDateTime(ndt);
1034 assert_eq!(ev.as_time_components().unwrap(), (ndt, None));
1035 }
1036}