1use std::{fmt::Display, string::FromUtf8Error};
2
3use chrono::{
4 offset::LocalResult, DateTime, FixedOffset, Local, NaiveDateTime, Offset, TimeZone as _, Utc,
5};
6
7use nom::{multi::many_m_n, number::Endianness};
8#[cfg(feature = "json_dump")]
9use serde::{Deserialize, Serialize, Serializer};
10use thiserror::Error;
11
12use crate::ExifTag;
13
14#[derive(Debug, Clone, PartialEq)]
16pub enum EntryValue {
17 Text(String),
18 URational(URational),
19 IRational(IRational),
20
21 U8(u8),
22 U16(u16),
23 U32(u32),
24 U64(u64),
25
26 I8(i8),
27 I16(i16),
28 I32(i32),
29 I64(i64),
30
31 F32(f32),
32 F64(f64),
33
34 Time(DateTime<FixedOffset>),
35 Undefined(Vec<u8>),
36
37 URationalArray(Vec<URational>),
38 IRationalArray(Vec<IRational>),
39
40 U16Array(Vec<u16>),
41 U32Array(Vec<u32>),
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
45pub(crate) struct EntryData<'a> {
46 pub endian: Endianness,
47 pub tag: u16,
48 pub data: &'a [u8],
49 pub data_format: DataFormat,
50 pub components_num: u32,
51}
52
53#[derive(Debug, Clone, Error)]
54pub(crate) enum ParseEntryError {
55 #[error("size is too big")]
56 EntrySizeTooBig,
57
58 #[error("data is invalid: {0}")]
59 InvalidData(String),
60
61 #[error("data format is unsupported (please file a bug): {0}")]
62 Unsupported(String),
63}
64
65impl From<chrono::ParseError> for ParseEntryError {
66 fn from(value: chrono::ParseError) -> Self {
67 ParseEntryError::InvalidData(format!("invalid time format: {value}"))
68 }
69}
70
71use ParseEntryError as Error;
72
73impl EntryData<'_> {
74 fn try_as_rationals<T: TryFromBytes>(&self) -> Result<Vec<Rational<T>>, Error> {
76 if self.components_num == 0 {
77 return Err(Error::InvalidData("components is 0".to_string()));
78 }
79
80 let mut vec = Vec::with_capacity(self.components_num as usize);
81 for i in 0..self.components_num {
82 let rational = decode_rational::<T>(&self.data[i as usize * 8..], self.endian)?;
83 vec.push(rational);
84 }
85 Ok(vec)
86 }
87}
88
89impl EntryValue {
90 pub(crate) fn parse(entry: &EntryData, tz: &Option<String>) -> Result<EntryValue, Error> {
112 if entry.data.is_empty() {
113 return Err(Error::InvalidData(
114 "invalid DirectoryEntry: entry data is empty".into(),
115 ));
116 }
117
118 let endian = entry.endian;
119 let tag = entry.tag;
120 let data_format = entry.data_format;
121 let data = entry.data;
122 let components_num = entry.components_num;
123
124 if data.is_empty() || components_num == 0 {
125 return Ok(EntryValue::variant_default(data_format));
126 }
127
128 let exif_tag: Result<ExifTag, _> = tag.try_into();
129 if let Ok(tag) = exif_tag {
130 if tag == ExifTag::DateTimeOriginal
131 || tag == ExifTag::CreateDate
132 || tag == ExifTag::ModifyDate
133 {
134 let s = get_cstr(data).map_err(|e| Error::InvalidData(e.to_string()))?;
141
142 let t = if let Some(tz) = tz {
143 let s = format!("{s} {tz}");
144 DateTime::parse_from_str(&s, "%Y:%m:%d %H:%M:%S %z")?
145 } else {
146 let t = NaiveDateTime::parse_from_str(&s, "%Y:%m:%d %H:%M:%S")?;
147 let t = Local.from_local_datetime(&t);
148 let t = if let LocalResult::Single(t) = t {
149 Ok(t)
150 } else {
151 Err(Error::InvalidData(format!("parse time failed: {s}")))
152 }?;
153
154 t.with_timezone(t.offset())
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 x => Err(Error::Unsupported(format!(
165 "usigned byte with {x} components"
166 ))),
167 },
168 DataFormat::Text => Ok(EntryValue::Text(
169 get_cstr(data).map_err(|e| Error::InvalidData(e.to_string()))?,
170 )),
171 DataFormat::U16 => {
172 if components_num == 1 {
173 Ok(Self::U16(u16::try_from_bytes(data, endian)?))
174 } else {
175 let (_, v) = many_m_n::<_, _, nom::error::Error<_>, _>(
176 components_num as usize,
177 components_num as usize,
178 nom::number::complete::u16(endian),
179 )(data)
180 .map_err(|e| {
181 ParseEntryError::InvalidData(format!("parse U16Array error: {e:?}"))
182 })?;
183 Ok(Self::U16Array(v))
184 }
185 }
186 DataFormat::U32 => {
187 if components_num == 1 {
188 Ok(Self::U32(u32::try_from_bytes(data, endian)?))
189 } else {
190 let (_, v) = many_m_n::<_, _, nom::error::Error<_>, _>(
191 components_num as usize,
192 components_num as usize,
193 nom::number::complete::u32(endian),
194 )(data)
195 .map_err(|e| {
196 ParseEntryError::InvalidData(format!("parse U32Array error: {e:?}"))
197 })?;
198 Ok(Self::U32Array(v))
199 }
200 }
201 DataFormat::URational => {
202 let rationals = entry.try_as_rationals::<u32>()?;
203 if rationals.len() == 1 {
204 Ok(Self::URational(rationals[0]))
205 } else {
206 Ok(Self::URationalArray(rationals))
207 }
208 }
209 DataFormat::I8 => match components_num {
210 1 => Ok(Self::I8(data[0] as i8)),
211 x => Err(Error::Unsupported(format!(
212 "signed byte with {x} components"
213 ))),
214 },
215 DataFormat::Undefined => Ok(Self::Undefined(data.to_vec())),
216 DataFormat::I16 => match components_num {
217 1 => Ok(Self::I16(i16::try_from_bytes(data, endian)?)),
218 x => Err(Error::Unsupported(format!(
219 "signed short with {x} components"
220 ))),
221 },
222 DataFormat::I32 => match components_num {
223 1 => Ok(Self::I32(i32::try_from_bytes(data, endian)?)),
224 x => Err(Error::Unsupported(format!(
225 "signed long with {x} components"
226 ))),
227 },
228 DataFormat::IRational => {
229 let rationals = entry.try_as_rationals::<i32>()?;
230 if rationals.len() == 1 {
231 Ok(Self::IRational(rationals[0]))
232 } else {
233 Ok(Self::IRationalArray(rationals))
234 }
235 }
236 DataFormat::F32 => match components_num {
237 1 => Ok(Self::F32(f32::try_from_bytes(data, endian)?)),
238 x => Err(Error::Unsupported(format!("float with {x} components"))),
239 },
240 DataFormat::F64 => match components_num {
241 1 => Ok(Self::F64(f64::try_from_bytes(data, endian)?)),
242 x => Err(Error::Unsupported(format!("double with {x} components"))),
243 },
244 }
245 }
246
247 fn variant_default(data_format: DataFormat) -> EntryValue {
248 match data_format {
249 DataFormat::U8 => Self::U8(0),
250 DataFormat::Text => Self::Text(String::default()),
251 DataFormat::U16 => Self::U16(0),
252 DataFormat::U32 => Self::U32(0),
253 DataFormat::URational => Self::URational(URational::default()),
254 DataFormat::I8 => Self::I8(0),
255 DataFormat::Undefined => Self::Undefined(Vec::default()),
256 DataFormat::I16 => Self::I16(0),
257 DataFormat::I32 => Self::I32(0),
258 DataFormat::IRational => Self::IRational(IRational::default()),
259 DataFormat::F32 => Self::F32(0.0),
260 DataFormat::F64 => Self::F64(0.0),
261 }
262 }
263
264 pub fn as_str(&self) -> Option<&str> {
265 match self {
266 EntryValue::Text(v) => Some(v),
267 _ => None,
268 }
269 }
270
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_u8(&self) -> Option<u8> {
279 match self {
280 EntryValue::U8(v) => Some(*v),
281 _ => None,
282 }
283 }
284
285 pub fn as_i8(&self) -> Option<i8> {
286 match self {
287 EntryValue::I8(v) => Some(*v),
288 _ => None,
289 }
290 }
291
292 pub fn as_u16(&self) -> Option<u16> {
293 match self {
294 EntryValue::U16(v) => Some(*v),
295 _ => None,
296 }
297 }
298
299 pub fn as_i16(&self) -> Option<i16> {
300 match self {
301 EntryValue::I16(v) => Some(*v),
302 _ => None,
303 }
304 }
305
306 pub fn as_u64(&self) -> Option<u64> {
307 match self {
308 EntryValue::U64(v) => Some(*v),
309 _ => None,
310 }
311 }
312
313 pub fn as_u32(&self) -> Option<u32> {
314 match self {
315 EntryValue::U32(v) => Some(*v),
316 _ => None,
317 }
318 }
319
320 pub fn as_i32(&self) -> Option<i32> {
321 match self {
322 EntryValue::I32(v) => Some(*v),
323 _ => None,
324 }
325 }
326
327 pub fn as_urational(&self) -> Option<URational> {
328 if let EntryValue::URational(v) = self {
329 Some(*v)
330 } else {
331 None
332 }
333 }
334
335 pub fn as_irational(&self) -> Option<IRational> {
336 if let EntryValue::IRational(v) = self {
337 Some(*v)
338 } else {
339 None
340 }
341 }
342
343 pub fn as_urational_array(&self) -> Option<&[URational]> {
344 if let EntryValue::URationalArray(v) = self {
345 Some(v)
346 } else {
347 None
348 }
349 }
350
351 pub fn as_irational_array(&self) -> Option<&[IRational]> {
352 if let EntryValue::IRationalArray(v) = self {
353 Some(v)
354 } else {
355 None
356 }
357 }
358}
359
360#[repr(u16)]
376#[derive(Clone, Copy, Debug, PartialEq, Eq)]
377#[allow(unused)]
378pub(crate) enum DataFormat {
379 U8 = 1,
380 Text = 2,
381 U16 = 3,
382 U32 = 4,
383 URational = 5,
384 I8 = 6,
385 Undefined = 7,
386 I16 = 8,
387 I32 = 9,
388 IRational = 10,
389 F32 = 11,
390 F64 = 12,
391}
392
393impl DataFormat {
394 pub fn component_size(&self) -> usize {
395 match self {
396 Self::U8 | Self::I8 | Self::Text | Self::Undefined => 1,
397 Self::U16 | Self::I16 => 2,
398 Self::U32 | Self::I32 | Self::F32 => 4,
399 Self::URational | Self::IRational | Self::F64 => 8,
400 }
401 }
402}
403
404impl TryFrom<u16> for DataFormat {
405 type Error = Error;
406 fn try_from(v: u16) -> Result<Self, Self::Error> {
407 if v >= Self::U8 as u16 && v <= Self::F64 as u16 {
408 Ok(unsafe { std::mem::transmute::<u16, Self>(v) })
409 } else {
410 Err(Error::InvalidData(format!("data format {v}")))
411 }
412 }
413}
414
415#[cfg(feature = "json_dump")]
416impl Serialize for EntryValue {
417 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
418 where
419 S: Serializer,
420 {
421 serializer.serialize_str(&self.to_string())
422 }
423}
424
425impl Display for EntryValue {
426 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427 match self {
428 EntryValue::Text(v) => v.fmt(f),
429 EntryValue::URational(v) => {
430 format!("{}/{} ({:.04})", v.0, v.1, v.0 as f64 / v.1 as f64).fmt(f)
431 }
432 EntryValue::IRational(v) => {
433 format!("{}/{} ({:.04})", v.0, v.1, v.0 as f64 / v.1 as f64).fmt(f)
434 }
435 EntryValue::U32(v) => Display::fmt(&v, f),
436 EntryValue::U16(v) => Display::fmt(&v, f),
437 EntryValue::U64(v) => Display::fmt(&v, f),
438 EntryValue::I16(v) => Display::fmt(&v, f),
439 EntryValue::I32(v) => Display::fmt(&v, f),
440 EntryValue::I64(v) => Display::fmt(&v, f),
441 EntryValue::F32(v) => Display::fmt(&v, f),
442 EntryValue::F64(v) => Display::fmt(&v, f),
443 EntryValue::U8(v) => Display::fmt(&v, f),
444 EntryValue::I8(v) => Display::fmt(&v, f),
445 EntryValue::Time(v) => Display::fmt(&v.to_rfc3339(), f),
446 EntryValue::Undefined(v) => {
447 const MAX_DISPLAY_NUM: usize = 8;
449 let s = v
450 .iter()
451 .map(|x| format!("0x{x:02x}"))
452 .take(MAX_DISPLAY_NUM + 1)
453 .enumerate()
454 .map(|(i, x)| {
455 if i >= MAX_DISPLAY_NUM {
456 "...".to_owned()
457 } else {
458 x
459 }
460 })
461 .collect::<Vec<String>>()
462 .join(", ");
463 format!("Undefined[{}]", s).fmt(f)
464 }
465 EntryValue::URationalArray(v) => {
466 format!("URationalArray[{}]", rationals_to_string::<u32>(v)).fmt(f)
467 }
468 EntryValue::IRationalArray(v) => {
469 format!("IRationalArray[{}]", rationals_to_string::<i32>(v)).fmt(f)
470 }
471 EntryValue::U32Array(v) => array_to_string("U32Array", v, f),
472 EntryValue::U16Array(v) => array_to_string("U16Array", v, f),
473 }
474 }
475}
476
477fn array_to_string<T: Display>(
478 name: &str,
479 v: &[T],
480 f: &mut std::fmt::Formatter,
481) -> Result<(), std::fmt::Error> {
482 format!(
483 "{}[{}]",
484 name,
485 v.iter()
486 .map(|x| x.to_string())
487 .collect::<Vec<String>>()
488 .join(", ")
489 )
490 .fmt(f)
491}
492
493fn rationals_to_string<T>(rationals: &[Rational<T>]) -> String
494where
495 T: Display + Into<f64> + Copy,
496{
497 const MAX_DISPLAY_NUM: usize = 3;
499 rationals
500 .iter()
501 .map(|x| format!("{}/{} ({:.04})", x.0, x.1, x.0.into() / x.1.into()))
502 .take(MAX_DISPLAY_NUM + 1)
503 .enumerate()
504 .map(|(i, x)| {
505 if i >= MAX_DISPLAY_NUM {
506 "...".to_owned()
507 } else {
508 x
509 }
510 })
511 .collect::<Vec<String>>()
512 .join(", ")
513}
514
515impl From<DateTime<Utc>> for EntryValue {
516 fn from(value: DateTime<Utc>) -> Self {
517 assert_eq!(value.offset().fix(), FixedOffset::east_opt(0).unwrap());
518 EntryValue::Time(value.fixed_offset())
519 }
520}
521
522impl From<DateTime<FixedOffset>> for EntryValue {
523 fn from(value: DateTime<FixedOffset>) -> Self {
524 EntryValue::Time(value)
525 }
526}
527
528impl From<u8> for EntryValue {
529 fn from(value: u8) -> Self {
530 EntryValue::U8(value)
531 }
532}
533impl From<u16> for EntryValue {
534 fn from(value: u16) -> Self {
535 EntryValue::U16(value)
536 }
537}
538impl From<u32> for EntryValue {
539 fn from(value: u32) -> Self {
540 EntryValue::U32(value)
541 }
542}
543impl From<u64> for EntryValue {
544 fn from(value: u64) -> Self {
545 EntryValue::U64(value)
546 }
547}
548
549impl From<i8> for EntryValue {
550 fn from(value: i8) -> Self {
551 EntryValue::I8(value)
552 }
553}
554impl From<i16> for EntryValue {
555 fn from(value: i16) -> Self {
556 EntryValue::I16(value)
557 }
558}
559impl From<i32> for EntryValue {
560 fn from(value: i32) -> Self {
561 EntryValue::I32(value)
562 }
563}
564impl From<i64> for EntryValue {
565 fn from(value: i64) -> Self {
566 EntryValue::I64(value)
567 }
568}
569
570impl From<f32> for EntryValue {
571 fn from(value: f32) -> Self {
572 EntryValue::F32(value)
573 }
574}
575impl From<f64> for EntryValue {
576 fn from(value: f64) -> Self {
577 EntryValue::F64(value)
578 }
579}
580
581impl From<String> for EntryValue {
582 fn from(value: String) -> Self {
583 EntryValue::Text(value)
584 }
585}
586
587impl From<&String> for EntryValue {
588 fn from(value: &String) -> Self {
589 EntryValue::Text(value.to_owned())
590 }
591}
592
593impl From<&str> for EntryValue {
594 fn from(value: &str) -> Self {
595 value.to_owned().into()
596 }
597}
598
599impl From<(u32, u32)> for EntryValue {
600 fn from(value: (u32, u32)) -> Self {
601 Self::URational(value.into())
602 }
603}
604
605impl From<(i32, i32)> for EntryValue {
606 fn from(value: (i32, i32)) -> Self {
607 Self::IRational((value.0, value.1).into())
608 }
609}
610
611pub type URational = Rational<u32>;
616pub type IRational = Rational<i32>;
617
618#[cfg_attr(feature = "json_dump", derive(Serialize, Deserialize))]
619#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
620pub struct Rational<T>(pub T, pub T);
621
622impl<T> Rational<T>
623where
624 T: Copy + Into<f64>,
625{
626 pub fn as_float(&self) -> f64 {
627 std::convert::Into::<f64>::into(self.0) / std::convert::Into::<f64>::into(self.1)
628 }
629}
630
631impl<T> From<(T, T)> for Rational<T>
632where
633 T: Copy,
634{
635 fn from(value: (T, T)) -> Self {
636 Self(value.0, value.1)
637 }
638}
639
640impl<T> From<Rational<T>> for (T, T)
641where
642 T: Copy,
643{
644 fn from(value: Rational<T>) -> Self {
645 (value.0, value.1)
646 }
647}
648
649impl From<IRational> for URational {
650 fn from(value: IRational) -> Self {
651 Self(value.0 as u32, value.1 as u32)
652 }
653}
654
655fn get_cstr(data: &[u8]) -> std::result::Result<String, FromUtf8Error> {
656 String::from_utf8(
657 data.iter()
658 .take_while(|b| **b != 0)
659 .filter(|b| **b != 0)
660 .cloned()
661 .collect::<Vec<u8>>(),
662 )
663}
664
665pub(crate) trait TryFromBytes: Sized {
666 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error>;
667}
668
669impl TryFromBytes for u32 {
670 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
671 fn make_err<T>() -> Error {
672 Error::InvalidData(format!(
673 "data is too small to convert to {}",
674 std::any::type_name::<T>(),
675 ))
676 }
677 match endian {
678 Endianness::Big => {
679 let (int_bytes, _) = bs
680 .split_at_checked(std::mem::size_of::<Self>())
681 .ok_or_else(make_err::<Self>)?;
682 Ok(Self::from_be_bytes(
683 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
684 ))
685 }
686 Endianness::Little => {
687 let (int_bytes, _) = bs
688 .split_at_checked(std::mem::size_of::<Self>())
689 .ok_or_else(make_err::<Self>)?;
690 Ok(Self::from_le_bytes(
691 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
692 ))
693 }
694 Endianness::Native => unimplemented!(),
695 }
696 }
697}
698
699impl TryFromBytes for i32 {
700 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, Error> {
701 fn make_err<T>() -> Error {
702 Error::InvalidData(format!(
703 "data is too small to convert to {}",
704 std::any::type_name::<T>(),
705 ))
706 }
707 match endian {
708 Endianness::Big => {
709 let (int_bytes, _) = bs
710 .split_at_checked(std::mem::size_of::<Self>())
711 .ok_or_else(make_err::<Self>)?;
712 Ok(Self::from_be_bytes(
713 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
714 ))
715 }
716 Endianness::Little => {
717 let (int_bytes, _) = bs
718 .split_at_checked(std::mem::size_of::<Self>())
719 .ok_or_else(make_err::<Self>)?;
720 Ok(Self::from_le_bytes(
721 int_bytes.try_into().map_err(|_| make_err::<Self>())?,
722 ))
723 }
724 Endianness::Native => unimplemented!(),
725 }
726 }
727}
728
729impl TryFromBytes for u16 {
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 i16 {
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 f32 {
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 f64 {
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
849pub(crate) fn decode_rational<T: TryFromBytes>(
850 data: &[u8],
851 endian: Endianness,
852) -> Result<Rational<T>, Error> {
853 if data.len() < 8 {
854 return Err(Error::InvalidData(
855 "data is too small to decode a rational".to_string(),
856 ));
857 }
858
859 let numerator = T::try_from_bytes(data, endian)?;
860 let denominator = T::try_from_bytes(&data[4..], endian)?; Ok(Rational::<T>(numerator, denominator))
862}
863
864#[cfg(test)]
865mod tests {
866 use chrono::{Local, NaiveDateTime, TimeZone};
867
868 use super::*;
869
870 #[test]
871 fn test_parse_time() {
872 let tz = Local::now().format("%:z").to_string();
873
874 let s = format!("2023:07:09 20:36:33 {tz}");
875 let t1 = DateTime::parse_from_str(&s, "%Y:%m:%d %H:%M:%S %z").unwrap();
876
877 let s = "2023:07:09 20:36:33";
878 let t2 = NaiveDateTime::parse_from_str(s, "%Y:%m:%d %H:%M:%S").unwrap();
879 let t2 = Local.from_local_datetime(&t2).unwrap();
880
881 let t3 = t2.with_timezone(t2.offset());
882
883 assert_eq!(t1, t2);
884 assert_eq!(t1, t3);
885 }
886
887 #[test]
888 fn test_iso_8601() {
889 let s = "2023-11-02T19:58:34+0800";
890 let t1 = DateTime::parse_from_str(s, "%+").unwrap();
891
892 let s = "2023-11-02T19:58:34+08:00";
893 let t2 = DateTime::parse_from_str(s, "%+").unwrap();
894
895 let s = "2023-11-02T19:58:34.026490+08:00";
896 let t3 = DateTime::parse_from_str(s, "%+").unwrap();
897
898 assert_eq!(t1, t2);
899 assert!(t3 > t2);
900 }
901}