#![allow(clippy::needless_doctest_main)]
#![crate_type = "lib"]
#![crate_name = "rexiv2"]
extern crate gexiv2_sys as gexiv2;
pub use gexiv2::GExiv2LogLevel as LogLevel;
use std::ffi;
use std::ptr;
use std::str;
#[derive(Debug, PartialEq, Eq)]
pub enum Rexiv2Error {
NoValue,
Utf8(str::Utf8Error),
Internal(Option<String>),
}
impl std::fmt::Display for Rexiv2Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Rexiv2Error::NoValue => write!(f, "No value found"),
Rexiv2Error::Utf8(ref err) => write!(f, "IO error: {err}"),
Rexiv2Error::Internal(Some(ref msg)) => write!(f, "Internal error: {msg}"),
Rexiv2Error::Internal(None) => write!(f, "Unknown internal error"),
}
}
}
impl std::error::Error for Rexiv2Error {
fn cause(&self) -> Option<&dyn std::error::Error> {
match *self {
Rexiv2Error::NoValue => None,
Rexiv2Error::Utf8(ref err) => Some(err),
Rexiv2Error::Internal(_) => None,
}
}
}
impl From<str::Utf8Error> for Rexiv2Error {
fn from(err: str::Utf8Error) -> Rexiv2Error {
Rexiv2Error::Utf8(err)
}
}
pub type Result<T> = std::result::Result<T, Rexiv2Error>;
#[derive(Debug, PartialEq, Eq)]
pub struct Metadata {
raw: *mut gexiv2::GExiv2Metadata,
}
#[derive(Debug, PartialEq, Eq)]
pub struct PreviewImage<'a> {
raw: *mut gexiv2::GExiv2PreviewProperties,
metadata: &'a Metadata, }
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct GpsInfo {
pub longitude: f64,
pub latitude: f64,
pub altitude: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TagType {
UnsignedByte,
AsciiString,
UnsignedShort,
UnsignedLong,
UnsignedRational,
SignedByte,
Undefined,
SignedShort,
SignedLong,
SignedRational,
TiffFloat,
TiffDouble,
TiffIfd,
String,
Date,
Time,
Comment,
Directory,
XmpText,
XmpAlt,
XmpBag,
XmpSeq,
LangAlt,
Invalid,
Unknown,
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum MediaType {
Bmp,
CanonCr2,
CanonCrw,
Eps,
FujiRaf,
Gif,
Jp2,
Jpeg,
MinoltaMrw,
OlympusOrf,
Png,
Psd,
PanasonicRw2,
Tga,
Tiff,
Other(String),
}
impl<'a> std::convert::From<&'a MediaType> for String {
fn from(t: &MediaType) -> String {
match *t {
MediaType::Bmp => "image/x-ms-bmp".to_string(),
MediaType::CanonCr2 => "image/x-canon-cr2".to_string(),
MediaType::CanonCrw => "image/x-canon-crw".to_string(),
MediaType::Eps => "application/postscript".to_string(),
MediaType::FujiRaf => "image/x-fuji-raf".to_string(),
MediaType::Gif => "image/gif".to_string(),
MediaType::Jp2 => "image/jp2".to_string(),
MediaType::Jpeg => "image/jpeg".to_string(),
MediaType::MinoltaMrw => "image/x-minolta-mrw".to_string(),
MediaType::OlympusOrf => "image/x-olympus-orf".to_string(),
MediaType::Png => "image/png".to_string(),
MediaType::Psd => "image/x-photoshop".to_string(),
MediaType::PanasonicRw2 => "image/x-panasonic-rw2".to_string(),
MediaType::Tga => "image/targa".to_string(),
MediaType::Tiff => "image/tiff".to_string(),
MediaType::Other(ref s) => s.clone(),
}
}
}
impl<'a> std::convert::From<&'a str> for MediaType {
fn from(t: &str) -> MediaType {
match t {
"image/x-ms-bmp" => MediaType::Bmp,
"image/x-canon-cr2" => MediaType::CanonCr2,
"image/x-canon-crw" => MediaType::CanonCrw,
"application/postscript" => MediaType::Eps,
"image/x-fuji-raf" => MediaType::FujiRaf,
"image/gif" => MediaType::Gif,
"image/jp2" => MediaType::Jp2,
"image/jpeg" => MediaType::Jpeg,
"image/x-minolta-mrw" => MediaType::MinoltaMrw,
"image/x-olympus-orf" => MediaType::OlympusOrf,
"image/png" => MediaType::Png,
"image/x-photoshop" => MediaType::Psd,
"image/x-panasonic-rw2" => MediaType::PanasonicRw2,
"image/targa" => MediaType::Tga,
"image/tiff" => MediaType::Tiff,
_ => MediaType::Other(t.to_string()),
}
}
}
impl std::fmt::Display for MediaType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", String::from(self))
}
}
pub use gexiv2::Orientation;
impl Metadata {
pub fn new_from_path<S: AsRef<ffi::OsStr>>(path: S) -> Result<Metadata> {
let c_str_path = os_str_to_c_string(path)?;
let mut err: *mut gexiv2::GError = ptr::null_mut();
unsafe {
let metadata = gexiv2::gexiv2_metadata_new();
let ok = gexiv2::gexiv2_metadata_open_path(metadata, c_str_path.as_ptr(), &mut err);
if ok != 1 {
let err_msg = ffi::CStr::from_ptr((*err).message).to_str();
return Err(Rexiv2Error::Internal(
err_msg.ok().map(|msg| msg.to_string()),
));
}
Ok(Metadata { raw: metadata })
}
}
pub fn new_from_app1_segment(data: &[u8]) -> Result<Metadata> {
let mut err: *mut gexiv2::GError = ptr::null_mut();
unsafe {
let metadata = gexiv2::gexiv2_metadata_new();
let ok = gexiv2::gexiv2_metadata_from_app1_segment(
metadata,
data.as_ptr(),
data.len() as libc::c_long,
&mut err,
);
if ok != 1 {
let err_msg = ffi::CStr::from_ptr((*err).message).to_str();
return Err(Rexiv2Error::Internal(
err_msg.ok().map(|msg| msg.to_string()),
));
}
Ok(Metadata { raw: metadata })
}
}
pub fn new_from_buffer(data: &[u8]) -> Result<Metadata> {
let mut err: *mut gexiv2::GError = ptr::null_mut();
unsafe {
let metadata = gexiv2::gexiv2_metadata_new();
let ok = gexiv2::gexiv2_metadata_open_buf(
metadata,
data.as_ptr(),
data.len() as libc::c_long,
&mut err,
);
if ok != 1 {
let err_msg = ffi::CStr::from_ptr((*err).message).to_str();
return Err(Rexiv2Error::Internal(
err_msg.ok().map(|msg| msg.to_string()),
));
}
Ok(Metadata { raw: metadata })
}
}
pub fn save_to_file<S: AsRef<ffi::OsStr>>(&self, path: S) -> Result<()> {
let c_str_path = os_str_to_c_string(path)?;
let mut err: *mut gexiv2::GError = ptr::null_mut();
unsafe {
let ok = gexiv2::gexiv2_metadata_save_file(self.raw, c_str_path.as_ptr(), &mut err);
if ok != 1 {
let err_msg = ffi::CStr::from_ptr((*err).message).to_str();
return Err(Rexiv2Error::Internal(
err_msg.ok().map(|msg| msg.to_string()),
));
}
Ok(())
}
}
pub fn supports_exif(&self) -> bool {
unsafe { gexiv2::gexiv2_metadata_get_supports_exif(self.raw) == 1 }
}
pub fn supports_iptc(&self) -> bool {
unsafe { gexiv2::gexiv2_metadata_get_supports_iptc(self.raw) == 1 }
}
pub fn supports_xmp(&self) -> bool {
unsafe { gexiv2::gexiv2_metadata_get_supports_xmp(self.raw) == 1 }
}
pub fn get_media_type(&self) -> Result<MediaType> {
unsafe {
let c_str_val = gexiv2::gexiv2_metadata_get_mime_type(self.raw);
if c_str_val.is_null() {
return Err(Rexiv2Error::NoValue);
}
Ok(MediaType::from(ffi::CStr::from_ptr(c_str_val).to_str()?))
}
}
pub fn get_pixel_width(&self) -> i32 {
unsafe { gexiv2::gexiv2_metadata_get_pixel_width(self.raw) }
}
pub fn get_pixel_height(&self) -> i32 {
unsafe { gexiv2::gexiv2_metadata_get_pixel_height(self.raw) }
}
pub fn has_tag(&self, tag: &str) -> bool {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe { gexiv2::gexiv2_metadata_has_tag(self.raw, c_str_tag.as_ptr()) == 1 }
}
pub fn clear_tag(&self, tag: &str) -> bool {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe { gexiv2::gexiv2_metadata_clear_tag(self.raw, c_str_tag.as_ptr()) == 1 }
}
pub fn clear(&self) {
unsafe { gexiv2::gexiv2_metadata_clear(self.raw) }
}
pub fn has_exif(&self) -> bool {
unsafe { gexiv2::gexiv2_metadata_has_exif(self.raw) == 1 }
}
pub fn clear_exif(&self) {
unsafe { gexiv2::gexiv2_metadata_clear_exif(self.raw) }
}
pub fn get_exif_tags(&self) -> Result<Vec<String>> {
let mut tags = vec![];
unsafe {
let c_tags = gexiv2::gexiv2_metadata_get_exif_tags(self.raw);
let mut cur_offset = 0;
while !(*c_tags.offset(cur_offset)).is_null() {
let tag = ffi::CStr::from_ptr(*c_tags.offset(cur_offset)).to_str();
match tag {
Ok(v) => tags.push(v.to_string()),
Err(e) => {
free_array_of_pointers(c_tags as *mut *mut libc::c_void);
return Err(Rexiv2Error::from(e));
}
}
cur_offset += 1;
}
free_array_of_pointers(c_tags as *mut *mut libc::c_void);
}
Ok(tags)
}
pub fn has_xmp(&self) -> bool {
unsafe { gexiv2::gexiv2_metadata_has_xmp(self.raw) == 1 }
}
pub fn clear_xmp(&self) {
unsafe { gexiv2::gexiv2_metadata_clear_xmp(self.raw) }
}
pub fn get_xmp_tags(&self) -> Result<Vec<String>> {
let mut tags = vec![];
unsafe {
let c_tags = gexiv2::gexiv2_metadata_get_xmp_tags(self.raw);
let mut cur_offset = 0;
while !(*c_tags.offset(cur_offset)).is_null() {
let tag = ffi::CStr::from_ptr(*c_tags.offset(cur_offset)).to_str();
match tag {
Ok(v) => tags.push(v.to_string()),
Err(e) => {
free_array_of_pointers(c_tags as *mut *mut libc::c_void);
return Err(Rexiv2Error::from(e));
}
}
cur_offset += 1;
}
free_array_of_pointers(c_tags as *mut *mut libc::c_void);
}
Ok(tags)
}
pub fn has_iptc(&self) -> bool {
unsafe { gexiv2::gexiv2_metadata_has_iptc(self.raw) == 1 }
}
pub fn clear_iptc(&self) {
unsafe { gexiv2::gexiv2_metadata_clear_iptc(self.raw) }
}
pub fn get_iptc_tags(&self) -> Result<Vec<String>> {
let mut tags = vec![];
unsafe {
let c_tags = gexiv2::gexiv2_metadata_get_iptc_tags(self.raw);
let mut cur_offset = 0;
while !(*c_tags.offset(cur_offset)).is_null() {
let tag = ffi::CStr::from_ptr(*c_tags.offset(cur_offset)).to_str();
match tag {
Ok(v) => tags.push(v.to_string()),
Err(e) => {
free_array_of_pointers(c_tags as *mut *mut libc::c_void);
return Err(Rexiv2Error::from(e));
}
}
cur_offset += 1;
}
free_array_of_pointers(c_tags as *mut *mut libc::c_void);
}
Ok(tags)
}
pub fn get_tag_string(&self, tag: &str) -> Result<String> {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe {
let c_str_val = gexiv2::gexiv2_metadata_get_tag_string(self.raw, c_str_tag.as_ptr());
if c_str_val.is_null() {
return Err(Rexiv2Error::NoValue);
}
let value = ffi::CStr::from_ptr(c_str_val).to_str()?.to_string();
libc::free(c_str_val as *mut libc::c_void);
Ok(value)
}
}
pub fn set_tag_string(&self, tag: &str, value: &str) -> Result<()> {
let c_str_tag = ffi::CString::new(tag).unwrap();
let c_str_val = ffi::CString::new(value).unwrap();
unsafe {
int_bool_to_result(gexiv2::gexiv2_metadata_set_tag_string(
self.raw,
c_str_tag.as_ptr(),
c_str_val.as_ptr(),
))
}
}
pub fn get_tag_interpreted_string(&self, tag: &str) -> Result<String> {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe {
let c_str_val =
gexiv2::gexiv2_metadata_get_tag_interpreted_string(self.raw, c_str_tag.as_ptr());
if c_str_val.is_null() {
return Err(Rexiv2Error::NoValue);
}
let value = ffi::CStr::from_ptr(c_str_val).to_str()?.to_string();
libc::free(c_str_val as *mut libc::c_void);
Ok(value)
}
}
pub fn get_tag_multiple_strings(&self, tag: &str) -> Result<Vec<String>> {
let c_str_tag = ffi::CString::new(tag).unwrap();
let mut vals = vec![];
unsafe {
let c_vals = gexiv2::gexiv2_metadata_get_tag_multiple(self.raw, c_str_tag.as_ptr());
if c_vals.is_null() {
return Err(Rexiv2Error::NoValue);
}
let mut cur_offset = 0;
while !(*c_vals.offset(cur_offset)).is_null() {
let value = ffi::CStr::from_ptr(*c_vals.offset(cur_offset)).to_str();
match value {
Ok(v) => vals.push(v.to_string()),
Err(e) => {
free_array_of_pointers(c_vals as *mut *mut libc::c_void);
return Err(Rexiv2Error::from(e));
}
}
cur_offset += 1;
}
free_array_of_pointers(c_vals as *mut *mut libc::c_void);
}
Ok(vals)
}
pub fn set_tag_multiple_strings(&self, tag: &str, values: &[&str]) -> Result<()> {
let c_str_tag = ffi::CString::new(tag).unwrap();
let c_strs: std::result::Result<Vec<_>, _> =
values.iter().map(|&s| ffi::CString::new(s)).collect();
let c_strs = c_strs.unwrap();
let mut ptrs: Vec<_> = c_strs.iter().map(|c| c.as_ptr()).collect();
ptrs.push(ptr::null());
unsafe {
int_bool_to_result(gexiv2::gexiv2_metadata_set_tag_multiple(
self.raw,
c_str_tag.as_ptr(),
ptrs.as_mut_ptr(),
))
}
}
pub fn get_tag_numeric(&self, tag: &str) -> i32 {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe { gexiv2::gexiv2_metadata_get_tag_long(self.raw, c_str_tag.as_ptr()) as i32 }
}
pub fn set_tag_numeric(&self, tag: &str, value: i32) -> Result<()> {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe {
int_bool_to_result(gexiv2::gexiv2_metadata_set_tag_long(
self.raw,
c_str_tag.as_ptr(),
value as libc::c_long,
))
}
}
pub fn get_tag_rational(&self, tag: &str) -> Option<num_rational::Ratio<i32>> {
let c_str_tag = ffi::CString::new(tag).unwrap();
let num = &mut 0;
let den = &mut 0;
match unsafe {
gexiv2::gexiv2_metadata_get_exif_tag_rational(self.raw, c_str_tag.as_ptr(), num, den)
} {
0 => None,
_ => {
if *den != 0 {
Some(num_rational::Ratio::new(*num, *den))
} else {
None
}
}
}
}
pub fn set_tag_rational(&self, tag: &str, value: &num_rational::Ratio<i32>) -> Result<()> {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe {
int_bool_to_result(gexiv2::gexiv2_metadata_set_exif_tag_rational(
self.raw,
c_str_tag.as_ptr(),
*value.numer(),
*value.denom(),
))
}
}
#[cfg(feature = "raw-tag-access")]
pub fn get_tag_raw(&self, tag: &str) -> Result<Vec<u8>> {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe {
let raw_tag_value = gexiv2::gexiv2_metadata_get_tag_raw(self.raw, c_str_tag.as_ptr());
let size = &mut 0;
let ptr = glib_sys::g_bytes_get_data(raw_tag_value, size) as *const u8;
let result = if ptr.is_null() {
Err(Rexiv2Error::NoValue)
} else {
Ok(std::slice::from_raw_parts(ptr, *size).to_owned())
};
glib_sys::g_bytes_unref(raw_tag_value);
result
}
}
pub fn get_orientation(&self) -> Orientation {
unsafe { gexiv2::gexiv2_metadata_get_orientation(self.raw) }
}
pub fn set_orientation(&self, orientation: Orientation) {
unsafe { gexiv2::gexiv2_metadata_set_orientation(self.raw, orientation) }
}
pub fn get_exposure_time(&self) -> Option<num_rational::Ratio<i32>> {
let num = &mut 0;
let den = &mut 0;
match unsafe { gexiv2::gexiv2_metadata_get_exposure_time(self.raw, num, den) } {
0 => None,
_ => Some(num_rational::Ratio::new(*num, *den)),
}
}
pub fn get_fnumber(&self) -> Option<f64> {
match unsafe { gexiv2::gexiv2_metadata_get_fnumber(self.raw) } {
error_value if error_value < 0.0 => None, fnumber => Some(fnumber),
}
}
pub fn get_focal_length(&self) -> Option<f64> {
match unsafe { gexiv2::gexiv2_metadata_get_focal_length(self.raw) } {
error_value if error_value < 0.0 => None, focal => Some(focal),
}
}
pub fn get_iso_speed(&self) -> Option<i32> {
match unsafe { gexiv2::gexiv2_metadata_get_iso_speed(self.raw) } {
0 => None,
speed => Some(speed),
}
}
pub fn get_thumbnail(&self) -> Option<&[u8]> {
let mut data: *mut u8 = ptr::null_mut();
let mut size: libc::c_int = 0;
unsafe {
match gexiv2::gexiv2_metadata_get_exif_thumbnail(self.raw, &mut data, &mut size) {
0 => None,
_ => Some(std::slice::from_raw_parts_mut(data, size as usize)),
}
}
}
pub fn erase_thumbnail(&self) {
unsafe { gexiv2::gexiv2_metadata_erase_exif_thumbnail(self.raw) }
}
pub fn set_thumbnail_from_file<S: AsRef<ffi::OsStr>>(&self, path: S) -> Result<()> {
let c_str_path = os_str_to_c_string(path)?;
let mut err: *mut gexiv2::GError = ptr::null_mut();
unsafe {
let ok = gexiv2::gexiv2_metadata_set_exif_thumbnail_from_file(
self.raw,
c_str_path.as_ptr(),
&mut err,
);
if ok != 1 {
let err_msg = ffi::CStr::from_ptr((*err).message).to_str();
return Err(Rexiv2Error::Internal(
err_msg.ok().map(|msg| msg.to_string()),
));
}
Ok(())
}
}
pub fn set_thumbnail_from_buffer(&self, data: &[u8]) {
unsafe {
gexiv2::gexiv2_metadata_set_exif_thumbnail_from_buffer(
self.raw,
data.as_ptr(),
data.len() as libc::c_int,
)
}
}
pub fn get_preview_images(&self) -> Option<Vec<PreviewImage>> {
unsafe {
let ptr = gexiv2::gexiv2_metadata_get_preview_properties(self.raw);
if ptr.is_null() {
return None;
}
let mut previews: Vec<PreviewImage> = vec![];
let mut n = 0;
while !(*ptr.offset(n)).is_null() {
let preview_prop = *ptr.offset(n);
previews.push(PreviewImage { raw: preview_prop, metadata: self });
n += 1;
}
Some(previews)
}
}
pub fn get_gps_info(&self) -> Option<GpsInfo> {
let lon = &mut 0.0;
let lat = &mut 0.0;
let alt = &mut 0.0;
match unsafe { gexiv2::gexiv2_metadata_get_gps_info(self.raw, lon, lat, alt) } {
0 => None,
_ => Some(GpsInfo { longitude: *lon, latitude: *lat, altitude: *alt }),
}
}
pub fn set_gps_info(&self, gps: &GpsInfo) -> Result<()> {
unsafe {
int_bool_to_result(gexiv2::gexiv2_metadata_set_gps_info(
self.raw,
gps.longitude,
gps.latitude,
gps.altitude,
))
}
}
pub fn delete_gps_info(&self) {
unsafe { gexiv2::gexiv2_metadata_delete_gps_info(self.raw) }
}
}
impl Drop for Metadata {
fn drop(&mut self) {
unsafe { gexiv2::gexiv2_metadata_free(self.raw) }
}
}
impl PreviewImage<'_> {
pub fn get_size(&self) -> u32 {
unsafe { gexiv2::gexiv2_preview_properties_get_size(self.raw) }
}
pub fn get_width(&self) -> u32 {
unsafe { gexiv2::gexiv2_preview_properties_get_width(self.raw) }
}
pub fn get_height(&self) -> u32 {
unsafe { gexiv2::gexiv2_preview_properties_get_height(self.raw) }
}
pub fn get_media_type(&self) -> Result<MediaType> {
unsafe {
let c_str_val = gexiv2::gexiv2_preview_properties_get_mime_type(self.raw);
if c_str_val.is_null() {
return Err(Rexiv2Error::NoValue);
}
Ok(MediaType::from(ffi::CStr::from_ptr(c_str_val).to_str()?))
}
}
pub fn get_extension(&self) -> Result<String> {
unsafe {
let c_str_val = gexiv2::gexiv2_preview_properties_get_extension(self.raw);
if c_str_val.is_null() {
return Err(Rexiv2Error::NoValue);
}
Ok((ffi::CStr::from_ptr(c_str_val).to_str())?.to_string())
}
}
pub fn get_data(&self) -> Result<Vec<u8>> {
let image =
unsafe { gexiv2::gexiv2_metadata_get_preview_image(self.metadata.raw, self.raw) };
let mut size: libc::c_uint = 0;
unsafe {
let data = gexiv2::gexiv2_preview_image_get_data(image, &mut size);
let result = if data.is_null() {
Err(Rexiv2Error::NoValue)
} else {
Ok(std::slice::from_raw_parts_mut(data as *mut u8, size as usize).to_vec())
};
gexiv2::gexiv2_preview_image_free(image);
result
}
}
pub fn save_to_file<S: AsRef<ffi::OsStr>>(&self, path: S) -> Result<()> {
let c_str_path = os_str_to_c_string(path)?;
let image =
unsafe { gexiv2::gexiv2_metadata_get_preview_image(self.metadata.raw, self.raw) };
unsafe {
let ok = gexiv2::gexiv2_preview_image_write_file(image, c_str_path.as_ptr());
gexiv2::gexiv2_preview_image_free(image);
let expected = self.get_size() as libc::c_long;
if ok != expected {
Err(Rexiv2Error::Internal(None))
} else {
Ok(())
}
}
}
}
pub fn is_exif_tag(tag: &str) -> bool {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe { gexiv2::gexiv2_metadata_is_exif_tag(c_str_tag.as_ptr()) == 1 }
}
pub fn is_iptc_tag(tag: &str) -> bool {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe { gexiv2::gexiv2_metadata_is_iptc_tag(c_str_tag.as_ptr()) == 1 }
}
pub fn is_xmp_tag(tag: &str) -> bool {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe { gexiv2::gexiv2_metadata_is_xmp_tag(c_str_tag.as_ptr()) == 1 }
}
pub fn get_tag_label(tag: &str) -> Result<String> {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe {
let c_str_val = gexiv2::gexiv2_metadata_get_tag_label(c_str_tag.as_ptr());
if c_str_val.is_null() {
return Err(Rexiv2Error::NoValue);
}
Ok(ffi::CStr::from_ptr(c_str_val).to_str()?.to_string())
}
}
pub fn get_tag_description(tag: &str) -> Result<String> {
let c_str_tag = ffi::CString::new(tag).unwrap();
unsafe {
let c_str_val = gexiv2::gexiv2_metadata_get_tag_description(c_str_tag.as_ptr());
if c_str_val.is_null() {
return Err(Rexiv2Error::NoValue);
}
Ok(ffi::CStr::from_ptr(c_str_val).to_str()?.to_string())
}
}
pub fn get_tag_type(tag: &str) -> Result<TagType> {
let c_str_tag = ffi::CString::new(tag).unwrap();
let tag_type = unsafe {
let c_str_val = gexiv2::gexiv2_metadata_get_tag_type(c_str_tag.as_ptr());
if c_str_val.is_null() {
return Err(Rexiv2Error::NoValue);
}
ffi::CStr::from_ptr(c_str_val).to_str()?
};
match tag_type {
"Byte" => Ok(TagType::UnsignedByte),
"Ascii" => Ok(TagType::AsciiString),
"Short" => Ok(TagType::UnsignedShort),
"Long" => Ok(TagType::UnsignedLong),
"Rational" => Ok(TagType::UnsignedRational),
"SByte" => Ok(TagType::SignedByte),
"Undefined" => Ok(TagType::Undefined),
"SShort" => Ok(TagType::SignedShort),
"SLong" => Ok(TagType::SignedLong),
"SRational" => Ok(TagType::SignedRational),
"Float" => Ok(TagType::TiffFloat),
"Double" => Ok(TagType::TiffDouble),
"Ifd" => Ok(TagType::TiffIfd),
"String" => Ok(TagType::String),
"Date" => Ok(TagType::Date),
"Time" => Ok(TagType::Time),
"Comment" => Ok(TagType::Comment),
"Directory" => Ok(TagType::Directory),
"XmpText" => Ok(TagType::XmpText),
"XmpAlt" => Ok(TagType::XmpAlt),
"XmpBag" => Ok(TagType::XmpBag),
"XmpSeq" => Ok(TagType::XmpSeq),
"LangAlt" => Ok(TagType::LangAlt),
"Invalid" => Ok(TagType::Invalid),
_ => Ok(TagType::Unknown),
}
}
pub fn initialize() -> Result<()> {
unsafe { int_bool_to_result(gexiv2::gexiv2_initialize()) }
}
pub fn register_xmp_namespace(name: &str, prefix: &str) -> Result<()> {
let c_str_name = ffi::CString::new(name).unwrap();
let c_str_prefix = ffi::CString::new(prefix).unwrap();
unsafe {
int_bool_to_result(gexiv2::gexiv2_metadata_register_xmp_namespace(
c_str_name.as_ptr(),
c_str_prefix.as_ptr(),
))
}
}
pub fn unregister_xmp_namespace(name: &str) -> Result<()> {
let c_str_name = ffi::CString::new(name).unwrap();
unsafe {
int_bool_to_result(gexiv2::gexiv2_metadata_unregister_xmp_namespace(
c_str_name.as_ptr(),
))
}
}
pub fn unregister_all_xmp_namespaces() {
unsafe { gexiv2::gexiv2_metadata_unregister_all_xmp_namespaces() }
}
pub fn get_log_level() -> LogLevel {
unsafe { gexiv2::gexiv2_log_get_level() }
}
pub fn set_log_level(level: LogLevel) {
unsafe { gexiv2::gexiv2_log_set_level(level) }
}
fn free_array_of_pointers(list: *mut *mut libc::c_void) {
unsafe {
let mut idx = 0;
while !(*list.offset(idx)).is_null() {
libc::free(*list.offset(idx));
idx += 1;
}
libc::free(list as *mut libc::c_void);
}
}
fn int_bool_to_result(success: libc::c_int) -> Result<()> {
match success {
0 => Err(Rexiv2Error::Internal(None)),
_ => Ok(()),
}
}
fn os_str_to_c_string<S: AsRef<ffi::OsStr>>(path: S) -> Result<ffi::CString> {
let path_as_utf8_result = path.as_ref().to_str();
if path_as_utf8_result.is_none() {
return Err(Rexiv2Error::Internal(Some(
"Could not convert path to UTF-8".to_string(),
)));
}
if let Ok(path_as_c_string) = ffi::CString::new(path_as_utf8_result.unwrap().as_bytes()) {
return Ok(path_as_c_string);
}
Err(Rexiv2Error::Internal(Some(
"Could not construct CString from UTF-8 path".to_string(),
)))
}