1use std::{
2 fmt::{Display, LowerHex},
3 string::FromUtf8Error,
4};
5
6use chrono::{DateTime, FixedOffset, NaiveDateTime};
7
8use nom::{multi::many_m_n, number::Endianness, AsChar, Parser};
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize, Serializer};
11
12use crate::{error::EntryError, ExifTag};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum ExifDateTime {
17 Aware(DateTime<FixedOffset>),
19 Naive(NaiveDateTime),
21}
22
23impl ExifDateTime {
24 pub fn aware(&self) -> Option<DateTime<FixedOffset>> {
26 match self {
27 ExifDateTime::Aware(dt) => Some(*dt),
28 ExifDateTime::Naive(_) => None,
29 }
30 }
31
32 pub fn into_naive(self) -> NaiveDateTime {
34 match self {
35 ExifDateTime::Aware(dt) => dt.naive_local(),
36 ExifDateTime::Naive(ndt) => ndt,
37 }
38 }
39
40 pub fn or_offset(self, fallback: FixedOffset) -> DateTime<FixedOffset> {
42 match self {
43 ExifDateTime::Aware(dt) => dt,
44 ExifDateTime::Naive(ndt) => ndt
45 .and_local_timezone(fallback)
46 .single()
47 .unwrap_or_else(|| ndt.and_utc().with_timezone(&fallback)),
48 }
49 }
50}
51
52#[derive(Debug, Clone, PartialEq)]
54#[non_exhaustive]
55pub enum EntryValue {
56 Text(String),
57 URational(URational),
58 IRational(IRational),
59
60 U8(u8),
61 U16(u16),
62 U32(u32),
63 U64(u64),
64
65 I8(i8),
66 I16(i16),
67 I32(i32),
68 I64(i64),
69
70 F32(f32),
71 F64(f64),
72
73 DateTime(DateTime<FixedOffset>),
74 NaiveDateTime(NaiveDateTime),
75 Undefined(Vec<u8>),
76
77 URationalArray(Vec<URational>),
78 IRationalArray(Vec<IRational>),
79
80 U8Array(Vec<u8>),
81 U16Array(Vec<u16>),
82 U32Array(Vec<u32>),
83}
84
85#[derive(Clone, Debug, PartialEq, Eq)]
86pub(crate) struct EntryData<'a> {
87 pub endian: Endianness,
88 pub tag: u16,
89 pub data: &'a [u8],
90 pub data_format: DataFormat,
91 pub components_num: u32,
92}
93
94impl EntryData<'_> {
95 fn try_as_rationals<T: TryFromBytes + Copy>(&self) -> Result<Vec<Rational<T>>, EntryError> {
97 if self.components_num == 0 {
98 return Err(EntryError::InvalidShape {
99 format: self.data_format as u16,
100 count: self.components_num,
101 });
102 }
103
104 let mut vec = Vec::with_capacity(self.components_num as usize);
105 for i in 0..self.components_num {
106 let rational = decode_rational::<T>(&self.data[i as usize * 8..], self.endian)?;
107 vec.push(rational);
108 }
109 Ok(vec)
110 }
111}
112
113impl EntryValue {
114 pub(crate) fn parse(entry: &EntryData, tz: &Option<String>) -> Result<EntryValue, EntryError> {
136 if entry.data.is_empty() {
137 return Err(EntryError::InvalidShape {
138 format: entry.data_format as u16,
139 count: entry.components_num,
140 });
141 }
142
143 let endian = entry.endian;
144 let tag = entry.tag;
145 let data_format = entry.data_format;
146 let data = entry.data;
147 let components_num = entry.components_num;
148
149 if data.is_empty() || components_num == 0 {
150 return Ok(EntryValue::variant_default(data_format));
151 }
152
153 let exif_tag = ExifTag::from_code(tag);
154 if let Some(tag) = exif_tag {
155 if tag == ExifTag::DateTimeOriginal
156 || tag == ExifTag::CreateDate
157 || tag == ExifTag::ModifyDate
158 {
159 let s = get_cstr(data).map_err(|_| EntryError::InvalidValue("invalid utf-8"))?;
160
161 let t = if let Some(tz) = tz {
162 let tz = repair_tz_str(tz);
163 let ss = format!("{s} {tz}");
164 match DateTime::parse_from_str(&ss, "%Y:%m:%d %H:%M:%S %z") {
165 Ok(t) => t,
166 Err(_) => return Ok(EntryValue::NaiveDateTime(parse_naive_time(s)?)),
167 }
168 } else {
169 return Ok(EntryValue::NaiveDateTime(parse_naive_time(s)?));
170 };
171
172 return Ok(EntryValue::DateTime(t));
173 }
174 }
175
176 match data_format {
177 DataFormat::U8 => match components_num {
178 1 => Ok(Self::U8(data[0])),
179 _ => Ok(Self::U8Array(data.into())),
180 },
181 DataFormat::Text => Ok(EntryValue::Text(
182 get_cstr(data).map_err(|_| EntryError::InvalidValue("invalid utf-8"))?,
183 )),
184 DataFormat::U16 => {
185 if components_num == 1 {
186 Ok(Self::U16(u16::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::u16(endian),
192 )
193 .parse(data)
194 .map_err(|_| EntryError::InvalidShape {
195 format: DataFormat::U16 as u16,
196 count: components_num,
197 })?;
198 Ok(Self::U16Array(v))
199 }
200 }
201 DataFormat::U32 => {
202 if components_num == 1 {
203 Ok(Self::U32(u32::try_from_bytes(data, endian)?))
204 } else {
205 let (_, v) = many_m_n::<_, nom::error::Error<_>, _>(
206 components_num as usize,
207 components_num as usize,
208 nom::number::complete::u32(endian),
209 )
210 .parse(data)
211 .map_err(|_| EntryError::InvalidShape {
212 format: DataFormat::U32 as u16,
213 count: components_num,
214 })?;
215 Ok(Self::U32Array(v))
216 }
217 }
218 DataFormat::URational => {
219 let rationals = entry.try_as_rationals::<u32>()?;
220 if rationals.len() == 1 {
221 Ok(Self::URational(rationals[0]))
222 } else {
223 Ok(Self::URationalArray(rationals))
224 }
225 }
226 DataFormat::I8 => match components_num {
227 1 => Ok(Self::I8(data[0] as i8)),
228 x => Err(EntryError::InvalidShape {
229 format: data_format as u16,
230 count: x,
231 }),
232 },
233 DataFormat::Undefined => Ok(Self::Undefined(data.to_vec())),
234 DataFormat::I16 => match components_num {
235 1 => Ok(Self::I16(i16::try_from_bytes(data, endian)?)),
236 x => Err(EntryError::InvalidShape {
237 format: data_format as u16,
238 count: x,
239 }),
240 },
241 DataFormat::I32 => match components_num {
242 1 => Ok(Self::I32(i32::try_from_bytes(data, endian)?)),
243 x => Err(EntryError::InvalidShape {
244 format: data_format as u16,
245 count: x,
246 }),
247 },
248 DataFormat::IRational => {
249 let rationals = entry.try_as_rationals::<i32>()?;
250 if rationals.len() == 1 {
251 Ok(Self::IRational(rationals[0]))
252 } else {
253 Ok(Self::IRationalArray(rationals))
254 }
255 }
256 DataFormat::F32 => match components_num {
257 1 => Ok(Self::F32(f32::try_from_bytes(data, endian)?)),
258 x => Err(EntryError::InvalidShape {
259 format: data_format as u16,
260 count: x,
261 }),
262 },
263 DataFormat::F64 => match components_num {
264 1 => Ok(Self::F64(f64::try_from_bytes(data, endian)?)),
265 x => Err(EntryError::InvalidShape {
266 format: data_format as u16,
267 count: x,
268 }),
269 },
270 }
271 }
272
273 fn variant_default(data_format: DataFormat) -> EntryValue {
274 match data_format {
275 DataFormat::U8 => Self::U8(0),
276 DataFormat::Text => Self::Text(String::default()),
277 DataFormat::U16 => Self::U16(0),
278 DataFormat::U32 => Self::U32(0),
279 DataFormat::URational => Self::URational(URational::default()),
280 DataFormat::I8 => Self::I8(0),
281 DataFormat::Undefined => Self::Undefined(Vec::default()),
282 DataFormat::I16 => Self::I16(0),
283 DataFormat::I32 => Self::I32(0),
284 DataFormat::IRational => Self::IRational(IRational::default()),
285 DataFormat::F32 => Self::F32(0.0),
286 DataFormat::F64 => Self::F64(0.0),
287 }
288 }
289
290 pub fn as_str(&self) -> Option<&str> {
291 match self {
292 EntryValue::Text(v) => Some(v),
293 _ => None,
294 }
295 }
296
297 pub fn as_datetime(&self) -> Option<ExifDateTime> {
317 match self {
318 EntryValue::DateTime(v) => Some(ExifDateTime::Aware(*v)),
319 EntryValue::NaiveDateTime(v) => Some(ExifDateTime::Naive(*v)),
320 _ => None,
321 }
322 }
323
324 pub fn as_u8(&self) -> Option<u8> {
325 match self {
326 EntryValue::U8(v) => Some(*v),
327 _ => None,
328 }
329 }
330
331 pub fn as_i8(&self) -> Option<i8> {
332 match self {
333 EntryValue::I8(v) => Some(*v),
334 _ => None,
335 }
336 }
337
338 pub fn as_u16(&self) -> Option<u16> {
339 match self {
340 EntryValue::U16(v) => Some(*v),
341 _ => None,
342 }
343 }
344
345 pub fn as_i16(&self) -> Option<i16> {
346 match self {
347 EntryValue::I16(v) => Some(*v),
348 _ => None,
349 }
350 }
351
352 pub fn as_u64(&self) -> Option<u64> {
353 match self {
354 EntryValue::U64(v) => Some(*v),
355 _ => None,
356 }
357 }
358
359 pub fn as_u32(&self) -> Option<u32> {
360 match self {
361 EntryValue::U32(v) => Some(*v),
362 _ => None,
363 }
364 }
365
366 pub fn as_i32(&self) -> Option<i32> {
367 match self {
368 EntryValue::I32(v) => Some(*v),
369 _ => None,
370 }
371 }
372
373 pub fn as_i64(&self) -> Option<i64> {
374 if let EntryValue::I64(v) = self {
375 Some(*v)
376 } else {
377 None
378 }
379 }
380
381 pub fn as_f64(&self) -> Option<f64> {
382 if let EntryValue::F64(v) = self {
383 Some(*v)
384 } else {
385 None
386 }
387 }
388
389 pub fn try_as_integer(&self) -> Option<i64> {
392 match self {
393 EntryValue::U8(v) => Some(*v as i64),
394 EntryValue::U16(v) => Some(*v as i64),
395 EntryValue::U32(v) => Some(*v as i64),
396 EntryValue::U64(v) => i64::try_from(*v).ok(),
397 EntryValue::I8(v) => Some(*v as i64),
398 EntryValue::I16(v) => Some(*v as i64),
399 EntryValue::I32(v) => Some(*v as i64),
400 EntryValue::I64(v) => Some(*v),
401 _ => None,
402 }
403 }
404
405 pub fn try_as_float(&self) -> Option<f64> {
408 match self {
409 EntryValue::F32(v) => Some(*v as f64),
410 EntryValue::F64(v) => Some(*v),
411 EntryValue::URational(v) => v.to_f64(),
412 EntryValue::IRational(v) => v.to_f64(),
413 v => v.try_as_integer().map(|x| x as f64),
414 }
415 }
416
417 pub fn as_urational(&self) -> Option<URational> {
418 if let EntryValue::URational(v) = self {
419 Some(*v)
420 } else {
421 None
422 }
423 }
424
425 pub fn as_irational(&self) -> Option<IRational> {
426 if let EntryValue::IRational(v) = self {
427 Some(*v)
428 } else {
429 None
430 }
431 }
432
433 pub fn as_urational_slice(&self) -> Option<&[URational]> {
434 if let EntryValue::URationalArray(v) = self {
435 Some(v)
436 } else {
437 None
438 }
439 }
440
441 pub fn as_irational_slice(&self) -> Option<&[IRational]> {
442 if let EntryValue::IRationalArray(v) = self {
443 Some(v)
444 } else {
445 None
446 }
447 }
448
449 pub fn as_u8_slice(&self) -> Option<&[u8]> {
450 if let EntryValue::U8Array(v) = self {
451 Some(v)
452 } else {
453 None
454 }
455 }
456
457 pub fn as_u16_slice(&self) -> Option<&[u16]> {
458 if let EntryValue::U16Array(v) = self {
459 Some(v)
460 } else {
461 None
462 }
463 }
464
465 pub fn as_u32_slice(&self) -> Option<&[u32]> {
466 if let EntryValue::U32Array(v) = self {
467 Some(v)
468 } else {
469 None
470 }
471 }
472
473 pub fn as_undefined(&self) -> Option<&[u8]> {
474 if let EntryValue::Undefined(v) = self {
475 Some(v)
476 } else {
477 None
478 }
479 }
480}
481
482impl From<(NaiveDateTime, Option<FixedOffset>)> for EntryValue {
484 fn from(value: (NaiveDateTime, Option<FixedOffset>)) -> Self {
485 if let Some(offset) = value.1 {
486 EntryValue::DateTime(value.0.and_local_timezone(offset).unwrap())
487 } else {
488 EntryValue::NaiveDateTime(value.0)
489 }
490 }
491}
492
493fn parse_naive_time(s: String) -> Result<NaiveDateTime, EntryError> {
494 NaiveDateTime::parse_from_str(&s, "%Y:%m:%d %H:%M:%S")
495 .map_err(|_| EntryError::InvalidValue("invalid time format"))
496}
497
498fn repair_tz_str(tz: &str) -> String {
499 if let Some(idx) = tz.find(":") {
500 if tz[idx..].len() < 3 {
501 return format!("{tz}0");
503 }
504 }
505 tz.into()
506}
507
508#[repr(u16)]
524#[derive(Clone, Copy, Debug, PartialEq, Eq)]
525#[allow(unused)]
526pub(crate) enum DataFormat {
527 U8 = 1,
528 Text = 2,
529 U16 = 3,
530 U32 = 4,
531 URational = 5,
532 I8 = 6,
533 Undefined = 7,
534 I16 = 8,
535 I32 = 9,
536 IRational = 10,
537 F32 = 11,
538 F64 = 12,
539}
540
541impl DataFormat {
542 pub fn component_size(&self) -> usize {
543 match self {
544 Self::U8 | Self::I8 | Self::Text | Self::Undefined => 1,
545 Self::U16 | Self::I16 => 2,
546 Self::U32 | Self::I32 | Self::F32 => 4,
547 Self::URational | Self::IRational | Self::F64 => 8,
548 }
549 }
550}
551
552impl TryFrom<u16> for DataFormat {
553 type Error = u16;
556 fn try_from(v: u16) -> Result<Self, Self::Error> {
557 if v >= Self::U8 as u16 && v <= Self::F64 as u16 {
558 Ok(unsafe { std::mem::transmute::<u16, Self>(v) })
559 } else {
560 Err(v)
561 }
562 }
563}
564
565#[cfg(feature = "serde")]
566impl Serialize for EntryValue {
567 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
574 where
575 S: Serializer,
576 {
577 use serde::ser::SerializeSeq;
578 match self {
579 EntryValue::Text(s) => serializer.serialize_str(s),
580 EntryValue::URational(r) => r.serialize(serializer),
581 EntryValue::IRational(r) => r.serialize(serializer),
582 EntryValue::U8(v) => serializer.serialize_u8(*v),
583 EntryValue::U16(v) => serializer.serialize_u16(*v),
584 EntryValue::U32(v) => serializer.serialize_u32(*v),
585 EntryValue::U64(v) => serializer.serialize_u64(*v),
586 EntryValue::I8(v) => serializer.serialize_i8(*v),
587 EntryValue::I16(v) => serializer.serialize_i16(*v),
588 EntryValue::I32(v) => serializer.serialize_i32(*v),
589 EntryValue::I64(v) => serializer.serialize_i64(*v),
590 EntryValue::F32(v) => serializer.serialize_f32(*v),
591 EntryValue::F64(v) => serializer.serialize_f64(*v),
592 EntryValue::DateTime(t) => serializer.serialize_str(&t.to_rfc3339()),
593 EntryValue::NaiveDateTime(t) => {
594 serializer.serialize_str(&t.format("%Y-%m-%d %H:%M:%S").to_string())
595 }
596 EntryValue::Undefined(bytes) => {
597 let mut hex = String::with_capacity(bytes.len() * 2);
598 for b in bytes {
599 use std::fmt::Write;
600 let _ = write!(&mut hex, "{b:02x}");
601 }
602 serializer.serialize_str(&hex)
603 }
604 EntryValue::URationalArray(v) => {
605 let mut seq = serializer.serialize_seq(Some(v.len()))?;
606 for r in v {
607 seq.serialize_element(r)?;
608 }
609 seq.end()
610 }
611 EntryValue::IRationalArray(v) => {
612 let mut seq = serializer.serialize_seq(Some(v.len()))?;
613 for r in v {
614 seq.serialize_element(r)?;
615 }
616 seq.end()
617 }
618 EntryValue::U8Array(v) => v.serialize(serializer),
619 EntryValue::U16Array(v) => v.serialize(serializer),
620 EntryValue::U32Array(v) => v.serialize(serializer),
621 }
622 }
623}
624
625impl Display for EntryValue {
632 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
633 match self {
634 EntryValue::Text(v) => v.fmt(f),
635 EntryValue::URational(v) => format!(
636 "{}/{} ({:.04})",
637 v.numerator(),
638 v.denominator(),
639 v.numerator() as f64 / v.denominator() as f64
640 )
641 .fmt(f),
642 EntryValue::IRational(v) => format!(
643 "{}/{} ({:.04})",
644 v.numerator(),
645 v.denominator(),
646 v.numerator() as f64 / v.denominator() as f64
647 )
648 .fmt(f),
649 EntryValue::U32(v) => Display::fmt(&v, f),
650 EntryValue::U16(v) => Display::fmt(&v, f),
651 EntryValue::U64(v) => Display::fmt(&v, f),
652 EntryValue::I16(v) => Display::fmt(&v, f),
653 EntryValue::I32(v) => Display::fmt(&v, f),
654 EntryValue::I64(v) => Display::fmt(&v, f),
655 EntryValue::F32(v) => Display::fmt(&v, f),
656 EntryValue::F64(v) => Display::fmt(&v, f),
657 EntryValue::U8(v) => Display::fmt(&v, f),
658 EntryValue::I8(v) => Display::fmt(&v, f),
659 EntryValue::DateTime(v) => Display::fmt(&v.to_rfc3339(), f),
660 EntryValue::NaiveDateTime(v) => Display::fmt(&v.format("%Y-%m-%d %H:%M:%S"), f),
661 EntryValue::Undefined(v) => fmt_undefined(v, f),
662 EntryValue::URationalArray(v) => {
663 format!("URationalArray[{}]", rationals_to_string::<u32>(v)).fmt(f)
664 }
665 EntryValue::IRationalArray(v) => {
666 format!("IRationalArray[{}]", rationals_to_string::<i32>(v)).fmt(f)
667 }
668 EntryValue::U8Array(v) => fmt_array_to_string("U8Array", v, f),
669 EntryValue::U32Array(v) => fmt_array_to_string("U32Array", v, f),
670 EntryValue::U16Array(v) => fmt_array_to_string("U16Array", v, f),
671 }
672 }
673}
674
675pub(crate) fn fmt_array_to_string<T: Display + LowerHex>(
676 name: &str,
677 v: &[T],
678 f: &mut std::fmt::Formatter,
679) -> Result<(), std::fmt::Error> {
680 array_to_string(name, v).fmt(f)
681}
682
683pub(crate) fn array_to_string<T: Display + LowerHex>(name: &str, v: &[T]) -> String {
684 let s = v
685 .iter()
686 .map(|x| format!("0x{x:02x}"))
687 .collect::<Vec<String>>()
688 .join(", ");
689 format!("{name}[{s}]")
690}
691
692fn rationals_to_string<T>(rationals: &[Rational<T>]) -> String
693where
694 T: Display + Into<f64> + Copy,
695{
696 rationals
697 .iter()
698 .map(|x| {
699 format!(
700 "{}/{} ({:.04})",
701 x.numerator(),
702 x.denominator(),
703 x.numerator().into() / x.denominator().into()
704 )
705 })
706 .collect::<Vec<String>>()
707 .join(", ")
708}
709
710fn fmt_undefined(v: &[u8], f: &mut std::fmt::Formatter) -> std::fmt::Result {
718 if !v.is_empty() && v.iter().all(|b| (0x20..=0x7e).contains(b)) {
719 let s = std::str::from_utf8(v).expect("ASCII subset is valid UTF-8");
720 write!(f, "\"{s}\"")
721 } else {
722 f.write_str("0x")?;
723 for b in v {
724 write!(f, "{b:02x}")?;
725 }
726 Ok(())
727 }
728}
729
730impl From<DateTime<FixedOffset>> for EntryValue {
731 fn from(value: DateTime<FixedOffset>) -> Self {
732 EntryValue::DateTime(value)
733 }
734}
735
736impl From<u8> for EntryValue {
737 fn from(value: u8) -> Self {
738 EntryValue::U8(value)
739 }
740}
741impl From<u16> for EntryValue {
742 fn from(value: u16) -> Self {
743 EntryValue::U16(value)
744 }
745}
746impl From<u32> for EntryValue {
747 fn from(value: u32) -> Self {
748 EntryValue::U32(value)
749 }
750}
751impl From<u64> for EntryValue {
752 fn from(value: u64) -> Self {
753 EntryValue::U64(value)
754 }
755}
756
757impl From<i8> for EntryValue {
758 fn from(value: i8) -> Self {
759 EntryValue::I8(value)
760 }
761}
762impl From<i16> for EntryValue {
763 fn from(value: i16) -> Self {
764 EntryValue::I16(value)
765 }
766}
767impl From<i32> for EntryValue {
768 fn from(value: i32) -> Self {
769 EntryValue::I32(value)
770 }
771}
772impl From<i64> for EntryValue {
773 fn from(value: i64) -> Self {
774 EntryValue::I64(value)
775 }
776}
777
778impl From<f32> for EntryValue {
779 fn from(value: f32) -> Self {
780 EntryValue::F32(value)
781 }
782}
783impl From<f64> for EntryValue {
784 fn from(value: f64) -> Self {
785 EntryValue::F64(value)
786 }
787}
788
789impl From<String> for EntryValue {
790 fn from(value: String) -> Self {
791 EntryValue::Text(value)
792 }
793}
794
795impl From<&str> for EntryValue {
796 fn from(value: &str) -> Self {
797 value.to_owned().into()
798 }
799}
800
801pub type URational = Rational<u32>;
802pub type IRational = Rational<i32>;
803
804#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
805#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
806pub struct Rational<T> {
807 numerator: T,
808 denominator: T,
809}
810
811impl<T: Copy> Rational<T> {
812 pub const fn new(numerator: T, denominator: T) -> Self {
813 Self {
814 numerator,
815 denominator,
816 }
817 }
818
819 pub const fn numerator(&self) -> T {
820 self.numerator
821 }
822
823 pub const fn denominator(&self) -> T {
824 self.denominator
825 }
826}
827
828impl<T: Copy + Into<f64> + PartialEq + Default> Rational<T> {
829 #[allow(clippy::wrong_self_convention)]
831 pub fn to_f64(&self) -> Option<f64> {
832 if self.denominator == T::default() {
833 None
834 } else {
835 Some(self.numerator.into() / self.denominator.into())
836 }
837 }
838}
839
840impl TryFrom<IRational> for URational {
841 type Error = crate::ConvertError;
842 fn try_from(value: IRational) -> Result<Self, Self::Error> {
843 let n = value.numerator();
844 let d = value.denominator();
845 if n < 0 || d < 0 {
846 Err(crate::ConvertError::NegativeRational)
847 } else {
848 Ok(URational::new(n as u32, d as u32))
849 }
850 }
851}
852
853pub(crate) fn get_cstr(data: &[u8]) -> std::result::Result<String, FromUtf8Error> {
854 let vec = filter_zero(data);
855 if let Ok(s) = String::from_utf8(vec) {
856 Ok(s)
857 } else {
858 Ok(filter_zero(data)
859 .into_iter()
860 .map(|x| x.as_char())
861 .collect::<String>())
862 }
863}
864
865pub(crate) fn filter_zero(data: &[u8]) -> Vec<u8> {
866 data.iter()
867 .skip_while(|b| **b == 0)
869 .take_while(|b| **b != 0)
871 .cloned()
872 .collect::<Vec<u8>>()
873}
874
875pub(crate) trait TryFromBytes: Sized {
876 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, EntryError>;
877}
878
879macro_rules! impl_try_from_bytes {
880 ($type:ty) => {
881 impl TryFromBytes for $type {
882 fn try_from_bytes(bs: &[u8], endian: Endianness) -> Result<Self, EntryError> {
883 fn make_err<T>(available: usize) -> EntryError {
884 EntryError::Truncated {
885 needed: std::mem::size_of::<T>(),
886 available,
887 }
888 }
889 match endian {
890 Endianness::Big => {
891 let (int_bytes, _) = bs
892 .split_at_checked(std::mem::size_of::<Self>())
893 .ok_or_else(|| make_err::<Self>(bs.len()))?;
894 Ok(Self::from_be_bytes(
895 int_bytes
896 .try_into()
897 .map_err(|_| make_err::<Self>(bs.len()))?,
898 ))
899 }
900 Endianness::Little => {
901 let (int_bytes, _) = bs
902 .split_at_checked(std::mem::size_of::<Self>())
903 .ok_or_else(|| make_err::<Self>(bs.len()))?;
904 Ok(Self::from_le_bytes(
905 int_bytes
906 .try_into()
907 .map_err(|_| make_err::<Self>(bs.len()))?,
908 ))
909 }
910 Endianness::Native => unimplemented!(),
911 }
912 }
913 }
914 };
915}
916
917impl_try_from_bytes!(u32);
918impl_try_from_bytes!(i32);
919impl_try_from_bytes!(u16);
920impl_try_from_bytes!(i16);
921impl_try_from_bytes!(f32);
922impl_try_from_bytes!(f64);
923
924pub(crate) fn decode_rational<T: TryFromBytes + Copy>(
925 data: &[u8],
926 endian: Endianness,
927) -> Result<Rational<T>, EntryError> {
928 if data.len() < 8 {
929 return Err(EntryError::Truncated {
930 needed: 8,
931 available: data.len(),
932 });
933 }
934
935 let numerator = T::try_from_bytes(data, endian)?;
936 let denominator = T::try_from_bytes(&data[4..], endian)?; Ok(Rational::<T>::new(numerator, denominator))
938}
939
940#[cfg(test)]
941mod tests {
942 use chrono::{Local, NaiveDateTime, TimeZone};
943
944 use super::*;
945
946 #[test]
947 fn test_parse_time() {
948 let s = "2023:07:09 20:36:33";
949 let t1 = NaiveDateTime::parse_from_str(s, "%Y:%m:%d %H:%M:%S").unwrap();
950 let t1 = Local.from_local_datetime(&t1).unwrap();
951
952 let tz = t1.format("%:z").to_string();
953
954 let s = format!("2023:07:09 20:36:33 {tz}");
955 let t2 = DateTime::parse_from_str(&s, "%Y:%m:%d %H:%M:%S %z").unwrap();
956
957 let t3 = t2.with_timezone(t2.offset());
958
959 assert_eq!(t1, t2);
960 assert_eq!(t1, t3);
961 }
962
963 #[test]
964 fn test_iso_8601() {
965 let s = "2023-11-02T19:58:34+0800";
966 let t1 = DateTime::parse_from_str(s, "%+").unwrap();
967
968 let s = "2023-11-02T19:58:34+08:00";
969 let t2 = DateTime::parse_from_str(s, "%+").unwrap();
970
971 let s = "2023-11-02T19:58:34.026490+08:00";
972 let t3 = DateTime::parse_from_str(s, "%+").unwrap();
973
974 assert_eq!(t1, t2);
975 assert!(t3 > t2);
976 }
977
978 #[test]
979 fn test_date_time_components() {
980 let dt = DateTime::parse_from_str("2023-07-09T20:36:33+08:00", "%+").unwrap();
981 let ndt =
982 NaiveDateTime::parse_from_str("2023-07-09T20:36:33", "%Y-%m-%dT%H:%M:%S").unwrap();
983 let offset = FixedOffset::east_opt(8 * 3600).unwrap();
984
985 let ev = EntryValue::DateTime(dt);
986 let edt = ev.as_datetime().unwrap();
987 assert_eq!(edt.aware(), Some(dt));
988 assert_eq!(edt.into_naive(), ndt);
989 assert_eq!(edt.or_offset(FixedOffset::east_opt(0).unwrap()), dt);
990
991 let ev = EntryValue::NaiveDateTime(ndt);
992 let edt = ev.as_datetime().unwrap();
993 assert_eq!(edt.aware(), None);
994 assert_eq!(edt.into_naive(), ndt);
995 assert_eq!(edt.or_offset(offset), dt);
996 }
997
998 #[test]
999 fn rational_to_f64_normal() {
1000 let r = URational::new(1, 2);
1001 assert_eq!(r.numerator(), 1);
1002 assert_eq!(r.denominator(), 2);
1003 assert_eq!(r.to_f64(), Some(0.5));
1004 }
1005
1006 #[test]
1007 fn rational_to_f64_zero_denominator() {
1008 let r = URational::new(1, 0);
1009 assert_eq!(r.to_f64(), None);
1010
1011 let r = IRational::new(-1, 0);
1012 assert_eq!(r.to_f64(), None);
1013 }
1014
1015 #[test]
1016 fn rational_default() {
1017 let r = URational::default();
1018 assert_eq!(r.numerator(), 0);
1019 assert_eq!(r.denominator(), 0);
1020 }
1021
1022 #[test]
1023 fn irational_to_urational_positive() {
1024 let i = IRational::new(3, 4);
1025 let u: URational = i.try_into().unwrap();
1026 assert_eq!(u, URational::new(3, 4));
1027 }
1028
1029 #[test]
1030 fn irational_to_urational_negative_numerator() {
1031 let i = IRational::new(-3, 4);
1032 let err = URational::try_from(i).unwrap_err();
1033 assert!(matches!(err, crate::ConvertError::NegativeRational));
1034 }
1035
1036 #[test]
1037 fn irational_to_urational_negative_denominator() {
1038 let i = IRational::new(3, -4);
1039 let err = URational::try_from(i).unwrap_err();
1040 assert!(matches!(err, crate::ConvertError::NegativeRational));
1041 }
1042
1043 #[test]
1044 fn entry_value_as_i64_f64() {
1045 assert_eq!(EntryValue::I64(-7).as_i64(), Some(-7));
1046 assert_eq!(EntryValue::F64(2.5).as_f64(), Some(2.5));
1047 assert_eq!(EntryValue::I32(7).as_i64(), None);
1048 assert_eq!(EntryValue::F32(2.5).as_f64(), None);
1049 }
1050
1051 #[test]
1052 fn entry_value_try_as_integer() {
1053 assert_eq!(EntryValue::U8(7).try_as_integer(), Some(7));
1054 assert_eq!(
1055 EntryValue::U32(0xffff_ffff).try_as_integer(),
1056 Some(0xffff_ffff_i64)
1057 );
1058 assert_eq!(EntryValue::I32(-7).try_as_integer(), Some(-7));
1059 assert_eq!(EntryValue::U64(u64::MAX).try_as_integer(), None);
1060 assert_eq!(EntryValue::Text("x".into()).try_as_integer(), None);
1061 }
1062
1063 #[test]
1064 fn entry_value_try_as_float() {
1065 assert_eq!(EntryValue::U8(7).try_as_float(), Some(7.0));
1066 assert_eq!(EntryValue::F32(1.5).try_as_float(), Some(1.5));
1067 assert_eq!(
1068 EntryValue::URational(URational::new(1, 2)).try_as_float(),
1069 Some(0.5)
1070 );
1071 assert_eq!(
1072 EntryValue::URational(URational::new(1, 0)).try_as_float(),
1073 None
1074 );
1075 assert_eq!(EntryValue::Text("x".into()).try_as_float(), None);
1076 }
1077
1078 #[test]
1079 fn entry_value_slice_accessors() {
1080 assert_eq!(
1081 EntryValue::U8Array(vec![1, 2]).as_u8_slice(),
1082 Some(&[1u8, 2][..])
1083 );
1084 assert_eq!(
1085 EntryValue::U16Array(vec![1, 2]).as_u16_slice(),
1086 Some(&[1u16, 2][..])
1087 );
1088 assert_eq!(
1089 EntryValue::U32Array(vec![1, 2]).as_u32_slice(),
1090 Some(&[1u32, 2][..])
1091 );
1092 assert_eq!(
1093 EntryValue::Undefined(vec![1, 2]).as_undefined(),
1094 Some(&[1u8, 2][..])
1095 );
1096 let r = URational::new(1, 2);
1097 assert_eq!(
1098 EntryValue::URationalArray(vec![r]).as_urational_slice(),
1099 Some(&[r][..])
1100 );
1101 }
1102}