nom_exif/
values.rs

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/// Represent a parsed entry value.
16#[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    // Ensure that the returned Vec is not empty.
79    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    /// Parse an IFD entry value.
95    ///
96    /// # Structure of IFD Entry
97    ///
98    /// ```txt
99    /// | 2   | 2           | 4              | 4                      |
100    /// | tag | data format | components num | data (value or offset) |
101    /// ```
102    ///
103    /// # Data size
104    ///
105    /// `data_size = components_num * bytes_per_component`
106    ///
107    /// `bytes_per_component` is determined by tag & data format.
108    ///
109    /// If data_size > 4, then the data area of entry stores the offset of the
110    /// value, not the value itself.
111    ///
112    /// # Data format
113    ///
114    /// See: [`DataFormat`].
115    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                // assert_eq!(data_format, 2);
139                // if data_format != 2 {
140                //     return Err(Error::InvalidData(
141                //         "invalid DirectoryEntry: date format is invalid".into(),
142                //     ));
143                // }
144                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    pub fn as_time(&self) -> Option<DateTime<FixedOffset>> {
270        match self {
271            EntryValue::Time(v) => Some(*v),
272            _ => None,
273        }
274    }
275
276    pub fn as_u8(&self) -> Option<u8> {
277        match self {
278            EntryValue::U8(v) => Some(*v),
279            _ => None,
280        }
281    }
282
283    pub fn as_i8(&self) -> Option<i8> {
284        match self {
285            EntryValue::I8(v) => Some(*v),
286            _ => None,
287        }
288    }
289
290    pub fn as_u16(&self) -> Option<u16> {
291        match self {
292            EntryValue::U16(v) => Some(*v),
293            _ => None,
294        }
295    }
296
297    pub fn as_i16(&self) -> Option<i16> {
298        match self {
299            EntryValue::I16(v) => Some(*v),
300            _ => None,
301        }
302    }
303
304    pub fn as_u64(&self) -> Option<u64> {
305        match self {
306            EntryValue::U64(v) => Some(*v),
307            _ => None,
308        }
309    }
310
311    pub fn as_u32(&self) -> Option<u32> {
312        match self {
313            EntryValue::U32(v) => Some(*v),
314            _ => None,
315        }
316    }
317
318    pub fn as_i32(&self) -> Option<i32> {
319        match self {
320            EntryValue::I32(v) => Some(*v),
321            _ => None,
322        }
323    }
324
325    pub fn as_urational(&self) -> Option<URational> {
326        if let EntryValue::URational(v) = self {
327            Some(*v)
328        } else {
329            None
330        }
331    }
332
333    pub fn as_irational(&self) -> Option<IRational> {
334        if let EntryValue::IRational(v) = self {
335            Some(*v)
336        } else {
337            None
338        }
339    }
340
341    pub fn as_urational_array(&self) -> Option<&[URational]> {
342        if let EntryValue::URationalArray(v) = self {
343            Some(v)
344        } else {
345            None
346        }
347    }
348
349    pub fn as_irational_array(&self) -> Option<&[IRational]> {
350        if let EntryValue::IRationalArray(v) = self {
351            Some(v)
352        } else {
353            None
354        }
355    }
356
357    pub fn as_u8array(&self) -> Option<&[u8]> {
358        if let EntryValue::U8Array(v) = self {
359            Some(v)
360        } else {
361            None
362        }
363    }
364    pub fn to_u8array(self) -> Option<Vec<u8>> {
365        if let EntryValue::U8Array(v) = self {
366            Some(v)
367        } else {
368            None
369        }
370    }
371}
372fn parse_naive_time(s: String) -> Result<NaiveDateTime, ParseEntryError> {
373    let t = NaiveDateTime::parse_from_str(&s, "%Y:%m:%d %H:%M:%S")?;
374    Ok(t)
375}
376// fn parse_time_with_local_tz(s: String) -> Result<DateTime<FixedOffset>, ParseEntryError> {
377//     let t = NaiveDateTime::parse_from_str(&s, "%Y:%m:%d %H:%M:%S")?;
378//     let t = Local.from_local_datetime(&t);
379//     let t = if let LocalResult::Single(t) = t {
380//         Ok(t)
381//     } else {
382//         Err(Error::InvalidData(format!("parse time failed: {s}")))
383//     }?;
384//     Ok(t.with_timezone(t.offset()))
385// }
386
387fn repair_tz_str(tz: &str) -> String {
388    if let Some(idx) = tz.find(":") {
389        if tz[idx..].len() < 3 {
390            // Add tailed 0
391            return format!("{tz}0");
392        }
393    }
394    tz.into()
395}
396
397/// # Exif Data format
398///
399/// ```txt
400/// | Value           |             1 |             2 |              3 |               4 |                 5 |            6 |
401/// |-----------------+---------------+---------------+----------------+-----------------+-------------------+--------------|
402/// | Format          | unsigned byte | ascii strings | unsigned short |   unsigned long | unsigned rational |  signed byte |
403/// | Bytes/component |             1 |             1 |              2 |               4 |                 8 |            1 |
404///
405/// | Value           |             7 |             8 |              9 |              10 |                11 |           12 |
406/// |-----------------+---------------+---------------+----------------+-----------------+-------------------+--------------|
407/// | Format          |     undefined |  signed short |    signed long | signed rational |      single float | double float |
408/// | Bytes/component |             1 |             2 |              4 |               8 |                 4 |            8 |
409/// ```
410///
411/// See: [Exif](https://www.media.mit.edu/pia/Research/deepview/exif.html).
412#[repr(u16)]
413#[derive(Clone, Copy, Debug, PartialEq, Eq)]
414#[allow(unused)]
415pub(crate) enum DataFormat {
416    U8 = 1,
417    Text = 2,
418    U16 = 3,
419    U32 = 4,
420    URational = 5,
421    I8 = 6,
422    Undefined = 7,
423    I16 = 8,
424    I32 = 9,
425    IRational = 10,
426    F32 = 11,
427    F64 = 12,
428}
429
430impl DataFormat {
431    pub fn component_size(&self) -> usize {
432        match self {
433            Self::U8 | Self::I8 | Self::Text | Self::Undefined => 1,
434            Self::U16 | Self::I16 => 2,
435            Self::U32 | Self::I32 | Self::F32 => 4,
436            Self::URational | Self::IRational | Self::F64 => 8,
437        }
438    }
439}
440
441impl TryFrom<u16> for DataFormat {
442    type Error = Error;
443    fn try_from(v: u16) -> Result<Self, Self::Error> {
444        if v >= Self::U8 as u16 && v <= Self::F64 as u16 {
445            Ok(unsafe { std::mem::transmute::<u16, Self>(v) })
446        } else {
447            Err(Error::InvalidData(format!("data format 0x{v:02x}")))
448        }
449    }
450}
451
452#[cfg(feature = "json_dump")]
453impl Serialize for EntryValue {
454    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
455    where
456        S: Serializer,
457    {
458        serializer.serialize_str(&self.to_string())
459    }
460}
461
462// impl std::fmt::Debug for EntryValue {
463//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
464//         Display::fmt(self, f)
465//     }
466// }
467
468impl Display for EntryValue {
469    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
470        match self {
471            EntryValue::Text(v) => v.fmt(f),
472            EntryValue::URational(v) => {
473                format!("{}/{} ({:.04})", v.0, v.1, v.0 as f64 / v.1 as f64).fmt(f)
474            }
475            EntryValue::IRational(v) => {
476                format!("{}/{} ({:.04})", v.0, v.1, v.0 as f64 / v.1 as f64).fmt(f)
477            }
478            EntryValue::U32(v) => Display::fmt(&v, f),
479            EntryValue::U16(v) => Display::fmt(&v, f),
480            EntryValue::U64(v) => Display::fmt(&v, f),
481            EntryValue::I16(v) => Display::fmt(&v, f),
482            EntryValue::I32(v) => Display::fmt(&v, f),
483            EntryValue::I64(v) => Display::fmt(&v, f),
484            EntryValue::F32(v) => Display::fmt(&v, f),
485            EntryValue::F64(v) => Display::fmt(&v, f),
486            EntryValue::U8(v) => Display::fmt(&v, f),
487            EntryValue::I8(v) => Display::fmt(&v, f),
488            EntryValue::Time(v) => Display::fmt(&v.to_rfc3339(), f),
489            EntryValue::NaiveDateTime(v) => Display::fmt(&v.format("%Y-%m-%d %H:%M:%S"), f),
490            EntryValue::Undefined(v) => fmt_array_to_string("Undefined", v, f),
491            EntryValue::URationalArray(v) => {
492                format!("URationalArray[{}]", rationals_to_string::<u32>(v)).fmt(f)
493            }
494            EntryValue::IRationalArray(v) => {
495                format!("IRationalArray[{}]", rationals_to_string::<i32>(v)).fmt(f)
496            }
497            EntryValue::U8Array(v) => fmt_array_to_string("U8Array", v, f),
498            EntryValue::U32Array(v) => fmt_array_to_string("U32Array", v, f),
499            EntryValue::U16Array(v) => fmt_array_to_string("U16Array", v, f),
500        }
501    }
502}
503
504pub(crate) fn fmt_array_to_string<T: Display + LowerHex>(
505    name: &str,
506    v: &[T],
507    f: &mut std::fmt::Formatter,
508) -> Result<(), std::fmt::Error> {
509    array_to_string(name, v).fmt(f)
510    // format!(
511    //     "{}[{}]",
512    //     name,
513    //     v.iter()
514    //         .map(|x| x.to_string())
515    //         .collect::<Vec<String>>()
516    //         .join(", ")
517    // )
518    // .fmt(f)
519}
520
521pub(crate) fn array_to_string<T: Display + LowerHex>(name: &str, v: &[T]) -> String {
522    // Display up to MAX_DISPLAY_NUM components, and replace the rest with ellipsis
523    const MAX_DISPLAY_NUM: usize = 8;
524    let s = v
525        .iter()
526        .map(|x| format!("0x{x:02x}"))
527        .take(MAX_DISPLAY_NUM + 1)
528        .enumerate()
529        .map(|(i, x)| {
530            if i >= MAX_DISPLAY_NUM {
531                "...".to_owned()
532            } else {
533                x
534            }
535        })
536        .collect::<Vec<String>>()
537        .join(", ");
538    format!("{}[{}]", name, s)
539}
540
541fn rationals_to_string<T>(rationals: &[Rational<T>]) -> String
542where
543    T: Display + Into<f64> + Copy,
544{
545    // Display up to MAX_DISPLAY_NUM components, and replace the rest with ellipsis
546    const MAX_DISPLAY_NUM: usize = 3;
547    rationals
548        .iter()
549        .map(|x| format!("{}/{} ({:.04})", x.0, x.1, x.0.into() / x.1.into()))
550        .take(MAX_DISPLAY_NUM + 1)
551        .enumerate()
552        .map(|(i, x)| {
553            if i >= MAX_DISPLAY_NUM {
554                "...".to_owned()
555            } else {
556                x
557            }
558        })
559        .collect::<Vec<String>>()
560        .join(", ")
561}
562
563impl From<DateTime<Utc>> for EntryValue {
564    fn from(value: DateTime<Utc>) -> Self {
565        assert_eq!(value.offset().fix(), FixedOffset::east_opt(0).unwrap());
566        EntryValue::Time(value.fixed_offset())
567    }
568}
569
570impl From<DateTime<FixedOffset>> for EntryValue {
571    fn from(value: DateTime<FixedOffset>) -> Self {
572        EntryValue::Time(value)
573    }
574}
575
576impl From<u8> for EntryValue {
577    fn from(value: u8) -> Self {
578        EntryValue::U8(value)
579    }
580}
581impl From<u16> for EntryValue {
582    fn from(value: u16) -> Self {
583        EntryValue::U16(value)
584    }
585}
586impl From<u32> for EntryValue {
587    fn from(value: u32) -> Self {
588        EntryValue::U32(value)
589    }
590}
591impl From<u64> for EntryValue {
592    fn from(value: u64) -> Self {
593        EntryValue::U64(value)
594    }
595}
596
597impl From<i8> for EntryValue {
598    fn from(value: i8) -> Self {
599        EntryValue::I8(value)
600    }
601}
602impl From<i16> for EntryValue {
603    fn from(value: i16) -> Self {
604        EntryValue::I16(value)
605    }
606}
607impl From<i32> for EntryValue {
608    fn from(value: i32) -> Self {
609        EntryValue::I32(value)
610    }
611}
612impl From<i64> for EntryValue {
613    fn from(value: i64) -> Self {
614        EntryValue::I64(value)
615    }
616}
617
618impl From<f32> for EntryValue {
619    fn from(value: f32) -> Self {
620        EntryValue::F32(value)
621    }
622}
623impl From<f64> for EntryValue {
624    fn from(value: f64) -> Self {
625        EntryValue::F64(value)
626    }
627}
628
629impl From<String> for EntryValue {
630    fn from(value: String) -> Self {
631        EntryValue::Text(value)
632    }
633}
634
635impl From<&String> for EntryValue {
636    fn from(value: &String) -> Self {
637        EntryValue::Text(value.to_owned())
638    }
639}
640
641impl From<&str> for EntryValue {
642    fn from(value: &str) -> Self {
643        value.to_owned().into()
644    }
645}
646
647impl From<(u32, u32)> for EntryValue {
648    fn from(value: (u32, u32)) -> Self {
649        Self::URational(value.into())
650    }
651}
652
653impl From<(i32, i32)> for EntryValue {
654    fn from(value: (i32, i32)) -> Self {
655        Self::IRational((value.0, value.1).into())
656    }
657}
658
659// #[cfg_attr(feature = "json_dump", derive(Serialize, Deserialize))]
660// #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
661// pub struct URational(pub u32, pub u32);
662
663pub type URational = Rational<u32>;
664pub type IRational = Rational<i32>;
665
666#[cfg_attr(feature = "json_dump", derive(Serialize, Deserialize))]
667#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
668pub struct Rational<T>(pub T, pub T);
669
670impl<T> Rational<T>
671where
672    T: Copy + Into<f64>,
673{
674    pub fn as_float(&self) -> f64 {
675        std::convert::Into::<f64>::into(self.0) / std::convert::Into::<f64>::into(self.1)
676    }
677}
678
679impl<T> From<(T, T)> for Rational<T>
680where
681    T: Copy,
682{
683    fn from(value: (T, T)) -> Self {
684        Self(value.0, value.1)
685    }
686}
687
688impl<T> From<Rational<T>> for (T, T)
689where
690    T: Copy,
691{
692    fn from(value: Rational<T>) -> Self {
693        (value.0, value.1)
694    }
695}
696
697impl From<IRational> for URational {
698    fn from(value: IRational) -> Self {
699        Self(value.0 as u32, value.1 as u32)
700    }
701}
702
703pub(crate) fn get_cstr(data: &[u8]) -> std::result::Result<String, FromUtf8Error> {
704    let vec = filter_zero(data);
705    if let Ok(s) = String::from_utf8(vec) {
706        Ok(s)
707    } else {
708        Ok(filter_zero(data)
709            .into_iter()
710            .map(|x| x.as_char())
711            .collect::<String>())
712    }
713}
714
715pub(crate) fn filter_zero(data: &[u8]) -> Vec<u8> {
716    data.iter()
717        // skip leading zero bytes
718        .skip_while(|b| **b == 0)
719        // ignore tailing zero bytes, and all bytes after zero bytes
720        .take_while(|b| **b != 0)
721        .cloned()
722        .collect::<Vec<u8>>()
723}
724
725pub(crate) trait TryFromBytes: Sized {
726    fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error>;
727}
728
729impl TryFromBytes for u32 {
730    fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
731        fn make_err<T>() -> Error {
732            Error::InvalidData(format!(
733                "data is too small to convert to {}",
734                std::any::type_name::<T>(),
735            ))
736        }
737        match endian {
738            Endianness::Big => {
739                let (int_bytes, _) = bs
740                    .split_at_checked(std::mem::size_of::<Self>())
741                    .ok_or_else(make_err::<Self>)?;
742                Ok(Self::from_be_bytes(
743                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
744                ))
745            }
746            Endianness::Little => {
747                let (int_bytes, _) = bs
748                    .split_at_checked(std::mem::size_of::<Self>())
749                    .ok_or_else(make_err::<Self>)?;
750                Ok(Self::from_le_bytes(
751                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
752                ))
753            }
754            Endianness::Native => unimplemented!(),
755        }
756    }
757}
758
759impl TryFromBytes for i32 {
760    fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
761        fn make_err<T>() -> Error {
762            Error::InvalidData(format!(
763                "data is too small to convert to {}",
764                std::any::type_name::<T>(),
765            ))
766        }
767        match endian {
768            Endianness::Big => {
769                let (int_bytes, _) = bs
770                    .split_at_checked(std::mem::size_of::<Self>())
771                    .ok_or_else(make_err::<Self>)?;
772                Ok(Self::from_be_bytes(
773                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
774                ))
775            }
776            Endianness::Little => {
777                let (int_bytes, _) = bs
778                    .split_at_checked(std::mem::size_of::<Self>())
779                    .ok_or_else(make_err::<Self>)?;
780                Ok(Self::from_le_bytes(
781                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
782                ))
783            }
784            Endianness::Native => unimplemented!(),
785        }
786    }
787}
788
789impl TryFromBytes for u16 {
790    fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
791        fn make_err<T>() -> Error {
792            Error::InvalidData(format!(
793                "data is too small to convert to {}",
794                std::any::type_name::<T>(),
795            ))
796        }
797        match endian {
798            Endianness::Big => {
799                let (int_bytes, _) = bs
800                    .split_at_checked(std::mem::size_of::<Self>())
801                    .ok_or_else(make_err::<Self>)?;
802                Ok(Self::from_be_bytes(
803                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
804                ))
805            }
806            Endianness::Little => {
807                let (int_bytes, _) = bs
808                    .split_at_checked(std::mem::size_of::<Self>())
809                    .ok_or_else(make_err::<Self>)?;
810                Ok(Self::from_le_bytes(
811                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
812                ))
813            }
814            Endianness::Native => unimplemented!(),
815        }
816    }
817}
818
819impl TryFromBytes for i16 {
820    fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
821        fn make_err<T>() -> Error {
822            Error::InvalidData(format!(
823                "data is too small to convert to {}",
824                std::any::type_name::<T>(),
825            ))
826        }
827        match endian {
828            Endianness::Big => {
829                let (int_bytes, _) = bs
830                    .split_at_checked(std::mem::size_of::<Self>())
831                    .ok_or_else(make_err::<Self>)?;
832                Ok(Self::from_be_bytes(
833                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
834                ))
835            }
836            Endianness::Little => {
837                let (int_bytes, _) = bs
838                    .split_at_checked(std::mem::size_of::<Self>())
839                    .ok_or_else(make_err::<Self>)?;
840                Ok(Self::from_le_bytes(
841                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
842                ))
843            }
844            Endianness::Native => unimplemented!(),
845        }
846    }
847}
848
849impl TryFromBytes for f32 {
850    fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
851        fn make_err<T>() -> Error {
852            Error::InvalidData(format!(
853                "data is too small to convert to {}",
854                std::any::type_name::<T>(),
855            ))
856        }
857        match endian {
858            Endianness::Big => {
859                let (int_bytes, _) = bs
860                    .split_at_checked(std::mem::size_of::<Self>())
861                    .ok_or_else(make_err::<Self>)?;
862                Ok(Self::from_be_bytes(
863                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
864                ))
865            }
866            Endianness::Little => {
867                let (int_bytes, _) = bs
868                    .split_at_checked(std::mem::size_of::<Self>())
869                    .ok_or_else(make_err::<Self>)?;
870                Ok(Self::from_le_bytes(
871                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
872                ))
873            }
874            Endianness::Native => unimplemented!(),
875        }
876    }
877}
878
879impl TryFromBytes for f64 {
880    fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
881        fn make_err<T>() -> Error {
882            Error::InvalidData(format!(
883                "data is too small to convert to {}",
884                std::any::type_name::<T>(),
885            ))
886        }
887        match endian {
888            Endianness::Big => {
889                let (int_bytes, _) = bs
890                    .split_at_checked(std::mem::size_of::<Self>())
891                    .ok_or_else(make_err::<Self>)?;
892                Ok(Self::from_be_bytes(
893                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
894                ))
895            }
896            Endianness::Little => {
897                let (int_bytes, _) = bs
898                    .split_at_checked(std::mem::size_of::<Self>())
899                    .ok_or_else(make_err::<Self>)?;
900                Ok(Self::from_le_bytes(
901                    int_bytes.try_into().map_err(|_| make_err::<Self>())?,
902                ))
903            }
904            Endianness::Native => unimplemented!(),
905        }
906    }
907}
908
909pub(crate) fn decode_rational<T: TryFromBytes>(
910    data: &[u8],
911    endian: Endianness,
912) -> Result<Rational<T>, Error> {
913    if data.len() < 8 {
914        return Err(Error::InvalidData(
915            "data is too small to decode a rational".to_string(),
916        ));
917    }
918
919    let numerator = T::try_from_bytes(data, endian)?;
920    let denominator = T::try_from_bytes(&data[4..], endian)?; // Safe-slice
921    Ok(Rational::<T>(numerator, denominator))
922}
923
924#[cfg(test)]
925mod tests {
926    use chrono::{Local, NaiveDateTime, TimeZone};
927
928    use super::*;
929
930    #[test]
931    fn test_parse_time() {
932        let tz = Local::now().format("%:z").to_string();
933
934        let s = format!("2023:07:09 20:36:33 {tz}");
935        let t1 = DateTime::parse_from_str(&s, "%Y:%m:%d %H:%M:%S %z").unwrap();
936
937        let s = "2023:07:09 20:36:33";
938        let t2 = NaiveDateTime::parse_from_str(s, "%Y:%m:%d %H:%M:%S").unwrap();
939        let t2 = Local.from_local_datetime(&t2).unwrap();
940
941        let t3 = t2.with_timezone(t2.offset());
942
943        assert_eq!(t1, t2);
944        assert_eq!(t1, t3);
945    }
946
947    #[test]
948    fn test_iso_8601() {
949        let s = "2023-11-02T19:58:34+0800";
950        let t1 = DateTime::parse_from_str(s, "%+").unwrap();
951
952        let s = "2023-11-02T19:58:34+08:00";
953        let t2 = DateTime::parse_from_str(s, "%+").unwrap();
954
955        let s = "2023-11-02T19:58:34.026490+08:00";
956        let t3 = DateTime::parse_from_str(s, "%+").unwrap();
957
958        assert_eq!(t1, t2);
959        assert!(t3 > t2);
960    }
961}