[niepce/lr-import: 18/20] npc-fwk: improve exempi support




commit 6d9e1839c7abf6c7d9dffcb3ede5e917691e40e2
Author: Hubert Figuière <hub figuiere net>
Date:   Tue Dec 14 22:58:19 2021 -0500

    npc-fwk: improve exempi support
    
    - Add more test
    - Fix datetime support to use fixed offset timezone
    - Update to exempi2 0.1.2

 Cargo.lock                              | 102 ++++++++++++++++----------------
 crates/npc-engine/Cargo.toml            |   2 +-
 crates/npc-engine/src/db/libmetadata.rs |  74 +++++++++++++++++++++--
 crates/npc-fwk/Cargo.toml               |   2 +-
 crates/npc-fwk/src/base/date.rs         |  41 ++++++++++++-
 crates/npc-fwk/src/utils/exempi.rs      |  29 ++++-----
 6 files changed, 172 insertions(+), 78 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 6576eb4..92c6c9d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -24,18 +24,18 @@ dependencies = [
 
 [[package]]
 name = "ansi_term"
-version = "0.11.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
 dependencies = [
  "winapi",
 ]
 
 [[package]]
 name = "anyhow"
-version = "1.0.45"
+version = "1.0.51"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7"
+checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
 
 [[package]]
 name = "async-channel"
@@ -186,9 +186,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "2.33.3"
+version = "2.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
 dependencies = [
  "ansi_term",
  "atty",
@@ -222,9 +222,9 @@ checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
 
 [[package]]
 name = "exempi-sys"
-version = "2.5.0"
+version = "2.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "092ed317836115f2a074e1fcc8ed9b2ced33bc7af1e71512cd4aa3a5be696e37"
+checksum = "feb66d7287eca6ff9fcdfcf8da289fd72c398693afff64f608d041431d24fc4f"
 dependencies = [
  "libc",
  "pkg-config",
@@ -232,9 +232,9 @@ dependencies = [
 
 [[package]]
 name = "exempi2"
-version = "0.1.1"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1deb5527e7a45f3ac714fc6cb78fa40e2be21a39824c93280deb71877216aa7d"
+checksum = "760d769a5bf6f641cd1ba6140c10d94ce4d5fa813656ea4fb1f482177cebd1c6"
 dependencies = [
  "bitflags",
  "exempi-sys",
@@ -265,24 +265,24 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888"
+checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27"
 dependencies = [
  "futures-core",
 ]
 
 [[package]]
 name = "futures-core"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
+checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c"
+checksum = "7b808bf53348a36cab739d7e04755909b9fcaaa69b7d7e588b37b6ec62704c97"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -291,23 +291,22 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
+checksum = "e481354db6b5c353246ccf6a728b0c5511d752c08da7260546fc0933869daa11"
 
 [[package]]
 name = "futures-task"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
+checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12"
 
 [[package]]
 name = "futures-util"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
+checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e"
 dependencies = [
- "autocfg",
  "futures-core",
  "futures-task",
  "pin-project-lite",
@@ -609,18 +608,18 @@ dependencies = [
 
 [[package]]
 name = "itertools"
-version = "0.10.1"
+version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
+checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
 dependencies = [
  "either",
 ]
 
 [[package]]
 name = "itoa"
-version = "0.4.8"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
 
 [[package]]
 name = "lazy_static"
@@ -630,9 +629,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.107"
+version = "0.2.112"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
+checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
 
 [[package]]
 name = "libopenraw-rs"
@@ -650,9 +649,9 @@ dependencies = [
 
 [[package]]
 name = "libsqlite3-sys"
-version = "0.23.1"
+version = "0.23.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "abd5850c449b40bacb498b2bbdfaff648b1b055630073ba8db499caf2d0ea9f2"
+checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58"
 dependencies = [
  "pkg-config",
  "vcpkg",
@@ -714,9 +713,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
 
 [[package]]
 name = "memoffset"
-version = "0.6.4"
+version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
 dependencies = [
  "autocfg",
 ]
@@ -956,9 +955,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
 [[package]]
 name = "pkg-config"
-version = "0.3.22"
+version = "0.3.24"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
+checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
 
 [[package]]
 name = "ppv-lite86"
@@ -1002,9 +1001,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.32"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
+checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a"
 dependencies = [
  "unicode-xid",
 ]
@@ -1130,9 +1129,9 @@ dependencies = [
 
 [[package]]
 name = "ryu"
-version = "1.0.5"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
 
 [[package]]
 name = "semver"
@@ -1154,18 +1153,18 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.130"
+version = "1.0.131"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
+checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.130"
+version = "1.0.131"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
+checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1174,9 +1173,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.69"
+version = "1.0.73"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8"
+checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
 dependencies = [
  "itoa",
  "ryu",
@@ -1221,9 +1220,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.81"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
+checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1299,11 +1298,12 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.1.43"
+version = "0.1.44"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
+checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
 dependencies = [
  "libc",
+ "wasi",
  "winapi",
 ]
 
@@ -1375,9 +1375,9 @@ checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
 
 [[package]]
 name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
+version = "0.10.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
 
 [[package]]
 name = "winapi"
diff --git a/crates/npc-engine/Cargo.toml b/crates/npc-engine/Cargo.toml
index a4a2041..595c957 100644
--- a/crates/npc-engine/Cargo.toml
+++ b/crates/npc-engine/Cargo.toml
@@ -13,7 +13,7 @@ license = "GPL-3.0"
 async-channel = "1.6.1"
 clap = { version = "2.33.3", optional = true }
 chrono = "0.4.19"
-exempi2 = { version = "0.1.1" }
+exempi2 = { version = "0.1.2" }
 gettext-rs = "0.7.0"
 gdk-pixbuf-sys = "*"
 gdk-pixbuf = "*"
diff --git a/crates/npc-engine/src/db/libmetadata.rs b/crates/npc-engine/src/db/libmetadata.rs
index 30e71aa..973b598 100644
--- a/crates/npc-engine/src/db/libmetadata.rs
+++ b/crates/npc-engine/src/db/libmetadata.rs
@@ -17,15 +17,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-use chrono::Utc;
-
 use super::libfile::FileType;
 use super::props;
 use super::NiepceProperties as Np;
 use super::{FromDb, LibraryId};
 use crate::{NiepcePropertyBag, NiepcePropertySet};
 use npc_fwk::utils::exempi::{NS_DC, NS_XAP};
-use npc_fwk::{xmp_date_from, PropertyBag, PropertySet, PropertyValue, XmpMeta};
+use npc_fwk::{xmp_date_from, Date, PropertyBag, PropertySet, PropertyValue, XmpMeta};
 
 #[derive(Clone)]
 pub struct LibMetadata {
@@ -229,14 +227,14 @@ impl LibMetadata {
                     }
                 }
                 Np::Index(NpIptcKeywordsProp) => {
-                    let mut iter = exempi2::XmpIterator::new(
+                    let iter = exempi2::XmpIterator::new(
                         &self.xmp_meta.xmp,
                         NS_DC,
                         "subject",
                         exempi2::IterFlags::JUST_LEAF_NODES,
                     );
                     let mut keywords: Vec<String> = vec![];
-                    while let Some(v) = iter.next() {
+                    for v in iter {
                         keywords.push(String::from(&v.value));
                     }
                     props.set_value(*prop_id, PropertyValue::StringArray(keywords));
@@ -268,7 +266,8 @@ impl LibMetadata {
     }
 
     pub fn touch(&mut self) -> bool {
-        let xmpdate = xmp_date_from(&Utc::now());
+        let local = chrono::Local::now();
+        let xmpdate = xmp_date_from(&Date::from(local));
         self.xmp_meta
             .xmp
             .set_property_date(NS_XAP, "MetadataDate", &xmpdate, exempi2::PropFlags::NONE)
@@ -317,3 +316,66 @@ pub extern "C" fn engine_libmetadata_to_properties(
     let result = Box::new(meta.to_properties(propset));
     Box::into_raw(result)
 }
+
+#[cfg(test)]
+mod test {
+
+    use super::{LibMetadata, Np};
+    use crate::NiepcePropertyIdx::*;
+    use chrono::TimeZone;
+    use npc_fwk::{PropertySet, PropertyValue, XmpMeta};
+
+    const XMP_PACKET: &[u8] = include_bytes!("../../tests/test.xmp");
+
+    #[test]
+    fn test_libmetadata() {
+        let xmp = exempi2::Xmp::from_buffer(XMP_PACKET);
+        assert!(xmp.is_ok());
+
+        let xmp = xmp.unwrap();
+        let xmp_meta = XmpMeta::new_with_xmp(xmp);
+        let libmetadata = LibMetadata::new_with_xmp(1, xmp_meta);
+        let mut propset = PropertySet::new();
+        propset.insert(Np::Index(NpIptcKeywordsProp));
+        propset.insert(Np::Index(NpTiffOrientationProp));
+        propset.insert(Np::Index(NpExifDateTimeOriginalProp));
+
+        let bag = libmetadata.to_properties(&propset);
+        assert_eq!(bag.len(), 3);
+
+        let keywords = bag.get(&Np::Index(NpIptcKeywordsProp));
+        assert!(keywords.is_some());
+
+        if let PropertyValue::StringArray(keywords) = keywords.unwrap() {
+            assert_eq!(keywords.len(), 5);
+            assert_eq!(keywords[0], "choir");
+            assert_eq!(keywords[1], "night");
+            assert_eq!(keywords[2], "ontario");
+            assert_eq!(keywords[3], "ottawa");
+            assert_eq!(keywords[4], "parliament of canada");
+        } else {
+            unreachable!();
+        }
+
+        let orientation = bag.get(&Np::Index(NpTiffOrientationProp));
+        assert!(orientation.is_some());
+
+        if let PropertyValue::Int(orientation) = orientation.unwrap() {
+            assert_eq!(orientation, &1);
+        } else {
+            unreachable!();
+        }
+
+        let creation_date = bag.get(&Np::Index(NpExifDateTimeOriginalProp));
+        assert!(creation_date.is_some());
+
+        if let PropertyValue::Date(creation_date) = creation_date.unwrap() {
+            let date = chrono::FixedOffset::west(5 * 3600)
+                .ymd(2006, 12, 07)
+                .and_hms(23, 37, 30);
+            assert_eq!(creation_date, &date);
+        } else {
+            unreachable!();
+        }
+    }
+}
diff --git a/crates/npc-fwk/Cargo.toml b/crates/npc-fwk/Cargo.toml
index 34003f1..a653e8a 100644
--- a/crates/npc-fwk/Cargo.toml
+++ b/crates/npc-fwk/Cargo.toml
@@ -11,7 +11,7 @@ build = "build.rs"
 async-channel = "1.6.1"
 cairo-rs = "*"
 chrono = "0.4.0"
-exempi2 = { version = "0.1.1" }
+exempi2 = { version = "0.1.2" }
 gio-sys = "*"
 gio = "^0.14.0"
 glib-sys = "*"
diff --git a/crates/npc-fwk/src/base/date.rs b/crates/npc-fwk/src/base/date.rs
index 7e07f7c..c7d54dd 100644
--- a/crates/npc-fwk/src/base/date.rs
+++ b/crates/npc-fwk/src/base/date.rs
@@ -22,13 +22,25 @@ use std::ffi::CString;
 use chrono::{Datelike, Timelike};
 
 pub type Time = i64;
-pub type Date = chrono::DateTime<chrono::Utc>;
+pub type Date = chrono::DateTime<chrono::FixedOffset>;
+
+/// Convert an `exempi2::DateTime` to a `chrono::DateTime<FixedOffset>`
+pub fn xmp_date_from(d: &Date) -> exempi2::DateTime {
+    use exempi2::TzSign;
 
-pub fn xmp_date_from(d: &chrono::DateTime<chrono::Utc>) -> exempi2::DateTime {
     let mut xmp_date = exempi2::DateTime::new();
     xmp_date.set_date(d.year(), d.month() as i32, d.day() as i32);
     xmp_date.set_time(d.hour() as i32, d.minute() as i32, d.second() as i32);
-    xmp_date.set_timezone(exempi2::TzSign::UTC, 0, 0);
+    let offset = d.offset().local_minus_utc();
+    let sign = if offset == 0 {
+        TzSign::UTC
+    } else if offset > 0 {
+        TzSign::East
+    } else {
+        TzSign::West
+    };
+    let offset = offset.abs();
+    xmp_date.set_timezone(sign, offset / 3600, offset / 60);
 
     xmp_date
 }
@@ -48,3 +60,26 @@ pub extern "C" fn fwk_date_to_string(date: &Date) -> *mut libc::c_char {
         .unwrap()
         .into_raw()
 }
+
+#[cfg(test)]
+mod test {
+    use chrono::TimeZone;
+
+    use super::xmp_date_from;
+
+    #[test]
+    fn test_xmp_date_from() {
+        let date = chrono::FixedOffset::west(5 * 3600)
+            .ymd(2021, 12, 25)
+            .and_hms(10, 42, 12);
+        let xmp_date = xmp_date_from(&date);
+        assert_eq!(xmp_date.year(), 2021);
+        assert_eq!(xmp_date.month(), 12);
+        assert_eq!(xmp_date.day(), 25);
+
+        assert_eq!(xmp_date.hour(), 10);
+
+        assert_eq!(xmp_date.tz_hours(), 5);
+        assert_eq!(xmp_date.tz_sign(), exempi2::TzSign::West);
+    }
+}
diff --git a/crates/npc-fwk/src/utils/exempi.rs b/crates/npc-fwk/src/utils/exempi.rs
index 3ac5e91..52b8f65 100644
--- a/crates/npc-fwk/src/utils/exempi.rs
+++ b/crates/npc-fwk/src/utils/exempi.rs
@@ -23,10 +23,11 @@ use std::fs::File;
 use std::io::prelude::*;
 use std::path::Path;
 
-use chrono::{DateTime, Utc};
+use chrono::DateTime;
 use exempi2::Xmp;
 
 use super::exiv2;
+use crate::Date;
 
 pub const NIEPCE_XMP_NAMESPACE: &str = "http://xmlns.figuiere.net/ns/niepce/1.0";;
 pub const NIEPCE_XMP_NS_PREFIX: &str = "niepce";
@@ -343,7 +344,7 @@ impl XmpMeta {
             .ok()
     }
 
-    pub fn creation_date(&self) -> Option<DateTime<Utc>> {
+    pub fn creation_date(&self) -> Option<Date> {
         let mut flags: exempi2::PropFlags = exempi2::PropFlags::default();
         let xmpstring = self
             .xmp
@@ -353,7 +354,7 @@ impl XmpMeta {
             .to_str()
             .and_then(|s| DateTime::parse_from_rfc3339(s).ok())?;
 
-        Some(date.with_timezone(&Utc))
+        Some(date)
     }
 
     pub fn creation_date_str(&self) -> Option<String> {
@@ -367,7 +368,7 @@ impl XmpMeta {
 
     /// Get the date property and return a Option<DateTime<Utc>> parsed
     /// from the string value.
-    pub fn get_date_property(&self, ns: &str, property: &str) -> Option<DateTime<Utc>> {
+    pub fn get_date_property(&self, ns: &str, property: &str) -> Option<Date> {
         let mut flags: exempi2::PropFlags = exempi2::PropFlags::default();
         let property = self.xmp.get_property(ns, property, &mut flags);
         if property.is_err() {
@@ -375,28 +376,24 @@ impl XmpMeta {
             return None;
         }
         let xmpstring = property.as_ref().ok().and_then(|s| s.to_str()).unwrap();
-        let parsed = DateTime::parse_from_rfc3339(xmpstring);
-        if parsed.is_err() {
-            err_out!(
-                "Error parsing property value '{}': {:?}",
-                xmpstring,
-                parsed.err()
-            );
-            return None;
+        match DateTime::parse_from_rfc3339(xmpstring) {
+            Ok(parsed) => Some(parsed),
+            Err(err) => {
+                err_out!("Error parsing property value '{}': {:?}", xmpstring, err);
+                None
+            }
         }
-        let date = parsed.unwrap();
-        Some(date.with_timezone(&Utc))
     }
 
     pub fn keywords(&mut self) -> &Vec<String> {
         if !self.keywords_fetched {
-            let mut iter = exempi2::XmpIterator::new(
+            let iter = exempi2::XmpIterator::new(
                 &self.xmp,
                 NS_DC,
                 "subject",
                 exempi2::IterFlags::JUST_LEAF_NODES,
             );
-            while let Some(v) = iter.next() {
+            for v in iter {
                 self.keywords.push(String::from(&v.value));
             }
             self.keywords_fetched = true;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]