diff --git a/src/lib.rs b/src/lib.rs index 65fcf13..be74d38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,9 @@ //! images, which were distinguished by `bool` previously. Function //! parameters and struct members now take `In`s instead of `bool`s. //! `Field::thumbnail` was renamed to `Field::ifd_num` accordingly. +//! * The type of `Context` was changed from enum to struct. The variants +//! (e.g., `Context::Tiff`) were changed to associated constants and +//! they are now spelled in all uppercase (e.g., `Context::TIFF`). pub use error::Error; pub use jpeg::get_exif_attr as get_exif_attr_from_jpeg; diff --git a/src/tag.rs b/src/tag.rs index ce2fde0..139dc99 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -60,8 +60,8 @@ impl Tag { /// # Examples /// ``` /// use exif::{Context, Tag}; - /// assert_eq!(Tag::DateTime.context(), Context::Tiff); - /// assert_eq!(Tag::ExposureTime.context(), Context::Exif); + /// assert_eq!(Tag::DateTime.context(), Context::TIFF); + /// assert_eq!(Tag::ExposureTime.context(), Context::EXIF); /// ``` #[inline] pub fn context(self) -> Context { @@ -102,22 +102,75 @@ impl fmt::Display for Tag { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match get_tag_info(*self) { Some(ti) => f.pad(ti.name), - None => f.pad(&format!("{:?}", self)), + None => f.pad(&format!("Tag({}, {})", self.0, self.1)), } } } -/// An enum that indicates how a tag number is interpreted. +/// A type that indicates how a tag number is interpreted. +// This type is not an enum to keep API compatibilities when +// new contexts are introduced. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum Context { +pub struct Context(std::num::NonZeroU16); + +macro_rules! generate_context { + ( + $( + // Copy the doc attribute to the actual definition. + $( #[$attr:meta] )* + $name: ident = $num: expr, $disp: expr; + )+ + ) => ( + impl Context { + $( + $( #[$attr] )* + pub const $name: Self = Self( + unsafe { std::num::NonZeroU16::new_unchecked($num) }); + )+ + } + + fn display_context(ctx: &Context, f: &mut fmt::Formatter) + -> fmt::Result { + let name = match ctx.0.get() { + $( $num => $disp, )+ + _ => return fmt::Debug::fmt(ctx, f), + }; + f.write_str(name) + } + ) +} + +generate_context! { /// TIFF attributes defined in the TIFF Rev. 6.0 specification. - Tiff, // 0th/1st IFD + TIFF = 1, "TIFF"; // 0th/1st IFD (toplevel) /// Exif attributes. - Exif, // 0th/1st IFD -- Exif IFD + EXIF = 2, "Exif"; // -- Exif IFD /// GPS attributes. - Gps, // 0th/1st IFD -- GPS IFD + GPS = 3, "GPS"; // -- GPS IFD /// Interoperability attributes. - Interop, // 0th/1st IFD -- Exif IFD -- Interoperability IFD + INTEROP = 4, "Interop"; // -- Exif IFD -- Interoperability IFD +} + +impl Context { + // Compatibility with 0.3.x. + #[allow(non_upper_case_globals)] + #[deprecated(since = "0.4.0", note = "use Context::TIFF instead")] + pub const Tiff: Self = Self::TIFF; + #[allow(non_upper_case_globals)] + #[deprecated(since = "0.4.0", note = "use Context::EXIF instead")] + pub const Exif: Self = Self::EXIF; + #[allow(non_upper_case_globals)] + #[deprecated(since = "0.4.0", note = "use Context::GPS instead")] + pub const Gps: Self = Self::GPS; + #[allow(non_upper_case_globals)] + #[deprecated(since = "0.4.0", note = "use Context::INTEROP instead")] + pub const Interop: Self = Self::INTEROP; +} + +impl fmt::Display for Context { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + display_context(self, f) + } } #[derive(Debug)] @@ -215,7 +268,7 @@ macro_rules! generate_well_known_tag_constants { // the Exif field names: camel cases and all-capital acronyms. generate_well_known_tag_constants!( // Exif-specific IFDs [EXIF23 4.6.3]. - |Context::Tiff| + |Context::TIFF| /// A pointer to the Exif IFD. This is used for the internal structure /// of Exif data and will not be returned to the user. @@ -228,7 +281,7 @@ generate_well_known_tag_constants!( unit![], "GPS Info IFD pointer"), - |Context::Exif| + |Context::EXIF| /// A pointer to the interoperability IFD. This is used for the internal /// structure of Exif data and will not be returned to the user. @@ -238,7 +291,7 @@ generate_well_known_tag_constants!( // TIFF primary and thumbnail attributes [EXIF23 4.6.4 Table 4, // 4.6.8 Table 17, and 4.6.8 Table 21]. - |Context::Tiff| + |Context::TIFF| (ImageWidth, 0x100, DefaultValue::None, d_default, unit!["pixels"], @@ -340,7 +393,7 @@ generate_well_known_tag_constants!( "Copyright holder"), // Exif IFD attributes [EXIF23 4.6.5 Table 7 and 4.6.8 Table 18]. - |Context::Exif| + |Context::EXIF| (ExposureTime, 0x829a, DefaultValue::None, d_exptime, unit!["s"], @@ -582,7 +635,7 @@ generate_well_known_tag_constants!( "Gamma"), // GPS attributes [EXIF23 4.6.6 Table 15 and 4.6.8 Table 19]. - |Context::Gps| + |Context::GPS| // Depends on the Exif version. (GPSVersionID, 0x0, DefaultValue::ContextDependent, d_gpsver, @@ -683,7 +736,7 @@ generate_well_known_tag_constants!( "Horizontal positioning error"), // Interoperability attributes [EXIF23 4.6.7 Table 16 and 4.6.8 Table 20]. - |Context::Interop| + |Context::INTEROP| (InteroperabilityIndex, 0x1, DefaultValue::None, d_default, unit![], @@ -1456,12 +1509,12 @@ mod tests { #[test] fn tag_constant_in_pattern() { // Destructuring, which will always work. - match Tag(Context::Tiff, 0x132) { - Tag(Context::Tiff, 0x132) => {}, + match Tag(Context::TIFF, 0x132) { + Tag(Context::TIFF, 0x132) => {}, _ => panic!("failed to match Tag"), } // Matching against a constant. Test if this compiles. - match Tag(Context::Tiff, 0x132) { + match Tag(Context::TIFF, 0x132) { Tag::DateTime => {}, _ => panic!("failed to match Tag"), } @@ -1498,11 +1551,11 @@ mod tests { #[test] fn tag_fmt_display() { - let tag1 = Tag(Context::Tiff, 0x132); + let tag1 = Tag(Context::TIFF, 0x132); assert_eq!(format!("{:15}", tag1), "DateTime "); assert_eq!(format!("{:>15}", tag1), " DateTime"); assert_eq!(format!("{:5.6}", tag1), "DateTi"); - let tag2 = Tag(Context::Exif, 0); + let tag2 = Tag(Context::EXIF, 0); assert_eq!(format!("{:15}", tag2), "Tag(Exif, 0) "); assert_eq!(format!("{:>15}", tag2), " Tag(Exif, 0)"); assert_eq!(format!("{:5.6}", tag2), "Tag(Ex"); diff --git a/src/tiff.rs b/src/tiff.rs index e8bd99a..0112596 100644 --- a/src/tiff.rs +++ b/src/tiff.rs @@ -122,7 +122,7 @@ fn parse_exif_sub(data: &[u8]) return Err(Error::InvalidFormat("Limit the IFD count to 8")); } ifd_offset = parse_ifd::( - &mut fields, data, ifd_offset, Context::Tiff, ifd_num)?; + &mut fields, data, ifd_offset, Context::TIFF, ifd_num)?; ifd_num_ck = ifd_num.checked_add(1); } Ok(fields) @@ -168,11 +168,11 @@ fn parse_ifd<'a, E>(fields: &mut Vec>, data: &'a [u8], let tag = Tag(ctx, tag); match tag { Tag::ExifIFDPointer => parse_child_ifd::( - fields, data, &val, Context::Exif, ifd_num)?, + fields, data, &val, Context::EXIF, ifd_num)?, Tag::GPSInfoIFDPointer => parse_child_ifd::( - fields, data, &val, Context::Gps, ifd_num)?, + fields, data, &val, Context::GPS, ifd_num)?, Tag::InteropIFDPointer => parse_child_ifd::( - fields, data, &val, Context::Interop, ifd_num)?, + fields, data, &val, Context::INTEROP, ifd_num)?, _ => fields.push(Field { tag: tag, ifd_num: In(ifd_num), value: val }), } diff --git a/src/writer.rs b/src/writer.rs index 73f377e..f48f630 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -130,10 +130,11 @@ impl<'a> Writer<'a> { Field { tag: Tag(ctx, _), ifd_num, .. } => { let ifd = self.pick_ifd(ifd_num); match ctx { - Context::Tiff => ifd.tiff_fields.push(field), - Context::Exif => ifd.exif_fields.push(field), - Context::Gps => ifd.gps_fields.push(field), - Context::Interop => ifd.interop_fields.push(field), + Context::TIFF => ifd.tiff_fields.push(field), + Context::EXIF => ifd.exif_fields.push(field), + Context::GPS => ifd.gps_fields.push(field), + Context::INTEROP => ifd.interop_fields.push(field), + _ => unimplemented!(), } }, }