Add DateTime parser.
This change introduces a new variant to Error and breaks the API compatibility.
This commit is contained in:
parent
7f66863d84
commit
5600fb205f
|
@ -38,6 +38,10 @@ pub enum Error {
|
|||
Io(io::Error),
|
||||
/// Exif attribute information was not found.
|
||||
NotFound(&'static str),
|
||||
/// The value of the field is blank. Some fields have blank values
|
||||
/// whose meanings are defined as "unknown". Such a blank value
|
||||
/// should be treated the same as the absence of the field.
|
||||
BlankValue(&'static str),
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
|
@ -52,6 +56,7 @@ impl fmt::Display for Error {
|
|||
Error::InvalidFormat(msg) => f.write_str(msg),
|
||||
Error::Io(ref err) => err.fmt(f),
|
||||
Error::NotFound(msg) => f.write_str(msg),
|
||||
Error::BlankValue(msg) => f.write_str(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +67,7 @@ impl error::Error for Error {
|
|||
Error::InvalidFormat(msg) => msg,
|
||||
Error::Io(ref err) => err.description(),
|
||||
Error::NotFound(msg) => msg,
|
||||
Error::BlankValue(msg) => msg,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,6 +76,7 @@ impl error::Error for Error {
|
|||
Error::InvalidFormat(_) => None,
|
||||
Error::Io(ref err) => Some(err),
|
||||
Error::NotFound(_) => None,
|
||||
Error::BlankValue(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ pub use jpeg::get_exif_attr as get_exif_attr_from_jpeg;
|
|||
pub use reader::Reader;
|
||||
pub use tag_priv::{Context, Tag};
|
||||
pub use tag_priv::constants as tag;
|
||||
pub use tiff::Field;
|
||||
pub use tiff::{DateTime, Field};
|
||||
pub use tiff::parse_exif;
|
||||
pub use value::Value;
|
||||
pub use value::{Rational, SRational};
|
||||
|
|
35
src/tiff.rs
35
src/tiff.rs
|
@ -30,6 +30,7 @@ use tag;
|
|||
use tag_priv::{Context, Tag};
|
||||
use value::Value;
|
||||
use value::get_type_info;
|
||||
use util::atou16;
|
||||
|
||||
// TIFF header magic numbers [EXIF23 4.5.2].
|
||||
const TIFF_BE: u16 = 0x4d4d;
|
||||
|
@ -155,6 +156,40 @@ pub fn is_tiff(buf: &[u8]) -> bool {
|
|||
buf.starts_with(&TIFF_BE_SIG) || buf.starts_with(&TIFF_LE_SIG)
|
||||
}
|
||||
|
||||
/// A struct used to parse a DateTime field.
|
||||
#[derive(Debug)]
|
||||
pub struct DateTime {
|
||||
pub year: u16,
|
||||
pub month: u8,
|
||||
pub day: u8,
|
||||
pub hour: u8,
|
||||
pub minute: u8,
|
||||
pub second: u8,
|
||||
}
|
||||
|
||||
impl DateTime {
|
||||
/// Parse an ASCII data of a DateTime field. The range of a number
|
||||
/// is not validated, so, for example, 13 may be returned as the month.
|
||||
pub fn from_ascii(data: &[u8]) -> Result<DateTime, Error> {
|
||||
if data == b" : : : : " || data == b" " {
|
||||
return Err(Error::BlankValue("DateTime is blank"));
|
||||
} else if data.len() < 19 {
|
||||
return Err(Error::InvalidFormat("DateTime too short"));
|
||||
} else if !(data[4] == b':' && data[7] == b':' && data[10] == b' ' &&
|
||||
data[13] == b':' && data[16] == b':') {
|
||||
return Err(Error::InvalidFormat("Invalid DateTime delimiter"));
|
||||
}
|
||||
Ok(DateTime {
|
||||
year: try!(atou16(&data[0..4])),
|
||||
month: try!(atou16(&data[5..7])) as u8,
|
||||
day: try!(atou16(&data[8..10])) as u8,
|
||||
hour: try!(atou16(&data[11..13])) as u8,
|
||||
minute: try!(atou16(&data[14..16])) as u8,
|
||||
second: try!(atou16(&data[17..19])) as u8,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use error::Error;
|
||||
|
|
34
src/util.rs
34
src/util.rs
|
@ -26,6 +26,8 @@
|
|||
|
||||
use std::io;
|
||||
|
||||
use error::Error;
|
||||
|
||||
pub fn read8<R>(reader: &mut R) -> Result<u8, io::Error> where R: io::Read {
|
||||
let mut buf: [u8; 1] = unsafe { ::std::mem::uninitialized() };
|
||||
reader.read_exact(&mut buf).and(Ok(buf[0]))
|
||||
|
@ -37,6 +39,27 @@ pub fn read16<R>(reader: &mut R) -> Result<u16, io::Error> where R: io::Read {
|
|||
Ok(((buf[0] as u16) << 8) + buf[1] as u16)
|
||||
}
|
||||
|
||||
// This function must not be called with more than 4 bytes.
|
||||
pub fn atou16(bytes: &[u8]) -> Result<u16, Error> {
|
||||
const ASCII_0: u8 = 0x30;
|
||||
const ASCII_9: u8 = 0x39;
|
||||
|
||||
if cfg!(debug_assertions) && bytes.len() >= 5 {
|
||||
panic!("atou16 accepts up to 4 bytes");
|
||||
}
|
||||
if bytes.len() == 0 {
|
||||
return Err(Error::InvalidFormat("Not a number"));
|
||||
}
|
||||
let mut n = 0;
|
||||
for &c in bytes {
|
||||
if c < ASCII_0 || ASCII_9 < c {
|
||||
return Err(Error::InvalidFormat("Not a number"));
|
||||
}
|
||||
n = n * 10 + (c - ASCII_0) as u16;
|
||||
}
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
|
@ -71,4 +94,15 @@ mod tests {
|
|||
assert_ok!(reader.read_to_end(&mut buf), 1);
|
||||
assert_eq!(buf, [0x03]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atou16_misc() {
|
||||
assert_ok!(atou16(b"0"), 0);
|
||||
assert_ok!(atou16(b"0010"), 10);
|
||||
assert_ok!(atou16(b"9999"), 9999);
|
||||
assert_err_pat!(atou16(b""), Error::InvalidFormat(_));
|
||||
assert_err_pat!(atou16(b"/"), Error::InvalidFormat(_));
|
||||
assert_err_pat!(atou16(b":"), Error::InvalidFormat(_));
|
||||
assert_err_pat!(atou16(b"-1"), Error::InvalidFormat(_));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue