Change the type of Context from enum to struct.

This commit is contained in:
KAMADA Ken'ichi 2019-10-21 23:12:58 +09:00
parent b584930a78
commit f81d2f6e6a
4 changed files with 85 additions and 28 deletions

View File

@ -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;

View File

@ -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");

View File

@ -122,7 +122,7 @@ fn parse_exif_sub<E>(data: &[u8])
return Err(Error::InvalidFormat("Limit the IFD count to 8"));
}
ifd_offset = parse_ifd::<E>(
&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<Field<'a>>, data: &'a [u8],
let tag = Tag(ctx, tag);
match tag {
Tag::ExifIFDPointer => parse_child_ifd::<E>(
fields, data, &val, Context::Exif, ifd_num)?,
fields, data, &val, Context::EXIF, ifd_num)?,
Tag::GPSInfoIFDPointer => parse_child_ifd::<E>(
fields, data, &val, Context::Gps, ifd_num)?,
fields, data, &val, Context::GPS, ifd_num)?,
Tag::InteropIFDPointer => parse_child_ifd::<E>(
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 }),
}

View File

@ -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!(),
}
},
}