extern crate exempi_sys as c;
#[macro_use]
extern crate bitflags;
mod error;
mod xmp;
mod xmpfile;
mod xmpiterator;
mod xmpstring;
use std::cmp::Ordering;
use std::ffi::CString;
use std::mem::transmute;
use std::result;
use std::sync::Once;
pub use c::consts::*;
pub use c::FileType;
pub use c::TzSign;
pub use c::XmpError;
pub use error::Error;
pub use xmp::{PropFlags, SerialFlags, Xmp};
pub use xmpfile::{CloseFlags, FormatOptionFlags, OpenFlags, XmpFile};
pub use xmpiterator::{IterFlags, IterSkipFlags, XmpIterator};
pub use xmpstring::XmpString;
pub type Result<T> = result::Result<T, Error>;
static START: Once = Once::new();
pub(crate) fn init() -> bool {
let mut inited = true;
START.call_once(|| {
inited = unsafe { c::xmp_init() };
});
inited
}
pub(crate) fn get_error() -> Error {
let err = unsafe { c::xmp_get_error() };
Error::from(match err {
-15..=0 | -110..=-101 | -211..=-201 => unsafe { transmute::<i32, c::XmpError>(err) },
_ => c::XmpError::TBD,
})
}
pub fn register_namespace(uri: &str, prefix: &str) -> Result<XmpString> {
init();
let s_uri = CString::new(uri).unwrap();
let s_prefix = CString::new(prefix).unwrap();
let mut reg_prefix = XmpString::new();
if unsafe {
c::xmp_register_namespace(s_uri.as_ptr(), s_prefix.as_ptr(), reg_prefix.as_mut_ptr())
} {
Ok(reg_prefix)
} else {
Err(get_error())
}
}
pub fn namespace_prefix<U: AsRef<[u8]>>(uri: U) -> Result<XmpString> {
init();
let s = CString::new(uri.as_ref()).unwrap();
let mut prefix = XmpString::new();
if unsafe { c::xmp_namespace_prefix(s.as_ptr(), prefix.as_mut_ptr()) } {
Ok(prefix)
} else {
Err(get_error())
}
}
pub fn prefix_namespace<P: AsRef<[u8]>>(prefix: P) -> Result<XmpString> {
init();
let s = CString::new(prefix.as_ref()).unwrap();
let mut uri = XmpString::new();
if unsafe { c::xmp_prefix_namespace_uri(s.as_ptr(), uri.as_mut_ptr()) } {
Ok(uri)
} else {
Err(get_error())
}
}
#[derive(Clone, Debug, Default)]
pub struct DateTime(c::XmpDateTime);
impl DateTime {
pub fn new() -> Self {
DateTime::default()
}
pub fn as_ptr(&self) -> *const c::XmpDateTime {
&self.0 as *const c::XmpDateTime
}
pub fn as_mut_ptr(&mut self) -> *mut c::XmpDateTime {
&mut self.0 as *mut c::XmpDateTime
}
pub fn set_date(&mut self, year: i32, month: i32, day: i32) {
self.0.year = year;
self.0.month = month;
self.0.day = day;
}
pub fn set_time(&mut self, hour: i32, min: i32, sec: i32) {
self.0.hour = hour;
self.0.minute = min;
self.0.second = sec;
}
pub fn set_nano_second(&mut self, nano_second: i32) {
self.0.nano_second = nano_second;
}
pub fn set_timezone(&mut self, sign: TzSign, hour: i32, min: i32) {
self.0.tz_sign = sign;
self.0.tz_hour = hour;
self.0.tz_minute = min;
}
pub fn year(&self) -> i32 {
self.0.year
}
pub fn month(&self) -> i32 {
self.0.month
}
pub fn day(&self) -> i32 {
self.0.day
}
pub fn hour(&self) -> i32 {
self.0.hour
}
pub fn minute(&self) -> i32 {
self.0.minute
}
pub fn second(&self) -> i32 {
self.0.second
}
pub fn nano_second(&self) -> i32 {
self.0.nano_second
}
pub fn has_tz(&self) -> bool {
self.tz_sign() != TzSign::UTC && (self.tz_hours() != 0 || self.tz_minutes() != 0)
}
pub fn tz_sign(&self) -> TzSign {
self.0.tz_sign
}
pub fn tz_hours(&self) -> i32 {
self.0.tz_hour
}
pub fn tz_minutes(&self) -> i32 {
self.0.tz_minute
}
}
impl From<c::XmpDateTime> for DateTime {
fn from(d: c::XmpDateTime) -> DateTime {
DateTime(d)
}
}
impl PartialEq for DateTime {
fn eq(&self, other: &DateTime) -> bool {
unsafe {
c::xmp_datetime_compare(
&self.0 as *const c::XmpDateTime,
&other.0 as *const c::XmpDateTime,
) == 0
}
}
}
impl PartialOrd for DateTime {
fn partial_cmp(&self, other: &DateTime) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for DateTime {}
impl Ord for DateTime {
fn cmp(&self, other: &DateTime) -> Ordering {
match unsafe {
c::xmp_datetime_compare(
&self.0 as *const c::XmpDateTime,
&other.0 as *const c::XmpDateTime,
)
} {
n if n < 0 => Ordering::Less,
n if n > 0 => Ordering::Greater,
_ => Ordering::Equal,
}
}
}
#[cfg(test)]
mod test {
#[test]
fn test_xmpdate() {
let mut date = crate::DateTime::new();
date.set_date(2012, 02, 17);
date.set_time(11, 10, 49);
date.set_timezone(crate::TzSign::West, 4, 0);
assert_eq!(std::mem::offset_of!(crate::DateTime, 0.tz_sign), 24);
let mut xmp = crate::Xmp::new();
let r = xmp.set_property_date(
"http://ns.adobe.com/exif/1.0/",
"DateTimeOriginal",
&date,
crate::PropFlags::NONE,
);
assert!(r.is_ok());
let mut flags = crate::PropFlags::default();
let d = xmp
.get_property_date(
"http://ns.adobe.com/exif/1.0/",
"DateTimeOriginal",
&mut flags,
)
.unwrap();
assert_eq!(d, date);
let d = xmp
.get_property(
"http://ns.adobe.com/exif/1.0/",
"DateTimeOriginal",
&mut flags,
)
.unwrap();
assert_eq!(&d.to_string(), "2012-02-17T11:10:49-04:00");
}
}