From ce80cb4baa46ea2914e5f420cab978235b3e9ea9 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Sat, 22 Oct 2022 17:25:52 -0700 Subject: [PATCH] Add `XmpMeta::contains_struct_field` --- src/ffi.cpp | 17 +++++++++ src/ffi.rs | 8 +++++ src/tests/xmp_meta.rs | 82 +++++++++++++++++++++++++++++++++++++++++++ src/xmp_meta.rs | 39 ++++++++++++++++++++ 4 files changed, 146 insertions(+) diff --git a/src/ffi.cpp b/src/ffi.cpp index ed36800..d76fd3d 100644 --- a/src/ffi.cpp +++ b/src/ffi.cpp @@ -618,6 +618,23 @@ extern "C" { return 0; } + int CXmpMetaDoesStructFieldExist(CXmpMeta* m, + const char* schemaNS, + const char* structName, + const char* fieldNS, + const char* fieldName) { + #ifndef NOOP_FFI + try { + return (m->m.DoesStructFieldExist(schemaNS, structName, fieldNS, fieldName)) ? 1 : 0; + } + catch (...) { + // Intentional no-op. + } + #endif + + return 0; + } + const char* CXmpMetaGetArrayItem(CXmpMeta* m, CXmpError* outError, const char* schemaNS, diff --git a/src/ffi.rs b/src/ffi.rs index c54b006..be47df7 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -273,6 +273,14 @@ extern "C" { prop_name: *const c_char, ) -> c_int; + pub(crate) fn CXmpMetaDoesStructFieldExist( + meta: *const CXmpMeta, + schema_ns: *const c_char, + struct_name: *const c_char, + field_ns: *const c_char, + field_name: *const c_char, + ) -> c_int; + pub(crate) fn CXmpMetaGetArrayItem( meta: *mut CXmpMeta, out_error: *mut CXmpError, diff --git a/src/tests/xmp_meta.rs b/src/tests/xmp_meta.rs index 0fd44c1..625ffc5 100644 --- a/src/tests/xmp_meta.rs +++ b/src/tests/xmp_meta.rs @@ -194,6 +194,88 @@ mod contains_property { } } +mod contains_struct_field { + use std::str::FromStr; + + use crate::{xmp_ns, XmpMeta}; + + const STRUCT_EXAMPLE: &str = r#" + + + + + + + + "#; + + #[test] + fn exists() { + let m = XmpMeta::from_str(STRUCT_EXAMPLE).unwrap(); + assert!(m.contains_struct_field( + xmp_ns::IPTC_CORE, + "CreatorContactInfo", + xmp_ns::IPTC_CORE, + "CiAdrPcode" + )); + } + + #[test] + fn doesnt_exist() { + let m = XmpMeta::from_str(STRUCT_EXAMPLE).unwrap(); + assert!(!m.contains_struct_field( + xmp_ns::IPTC_CORE, + "CreatorContactInfo", + xmp_ns::IPTC_CORE, + "CiAdrPcodx" + )); + } + + #[test] + fn empty_namespace() { + let m = XmpMeta::from_str(STRUCT_EXAMPLE).unwrap(); + assert!(!m.contains_struct_field( + "", + "CreatorContactInfo", + xmp_ns::IPTC_CORE, + "CiAdrPcode" + )); + } + + #[test] + fn empty_struct_name() { + let m = XmpMeta::from_str(STRUCT_EXAMPLE).unwrap(); + assert!(!m.contains_struct_field(xmp_ns::IPTC_CORE, "", xmp_ns::IPTC_CORE, "CiAdrPcode")); + } + #[test] + fn empty_field_namespace() { + let m = XmpMeta::from_str(STRUCT_EXAMPLE).unwrap(); + assert!(!m.contains_struct_field( + xmp_ns::IPTC_CORE, + "CreatorContactInfo", + "", + "CiAdrPcode" + )); + } + + #[test] + fn empty_field_name() { + let m = XmpMeta::from_str(STRUCT_EXAMPLE).unwrap(); + assert!(!m.contains_struct_field( + xmp_ns::IPTC_CORE, + "CreatorContactInfo", + xmp_ns::IPTC_CORE, + "" + )); + } +} + mod property { use crate::{tests::fixtures::*, xmp_ns, XmpMeta, XmpValue}; diff --git a/src/xmp_meta.rs b/src/xmp_meta.rs index c782e6b..c9d9833 100644 --- a/src/xmp_meta.rs +++ b/src/xmp_meta.rs @@ -142,6 +142,45 @@ impl XmpMeta { r != 0 } + /// Returns `true` if the metadata block contains a struct field by this + /// name. + /// + /// ## Arguments + /// + /// * `struct_ns` and `struct_path`: See [Accessing + /// properties](#accessing-properties). + /// * `field_ns` and `field_name` take the same form (i.e. see [Accessing + /// properties](#accessing-properties) again.) + /// + /// ## Error handling + /// + /// Any errors (for instance, empty or invalid namespace or property name) + /// are ignored; the function will return `false` in such cases. + pub fn contains_struct_field( + &self, + struct_ns: &str, + struct_path: &str, + field_ns: &str, + field_name: &str, + ) -> bool { + let c_struct_ns = CString::new(struct_ns).unwrap_or_default(); + let c_struct_name = CString::new(struct_path).unwrap_or_default(); + let c_field_ns = CString::new(field_ns).unwrap_or_default(); + let c_field_name = CString::new(field_name).unwrap_or_default(); + + let r = unsafe { + ffi::CXmpMetaDoesStructFieldExist( + self.m, + c_struct_ns.as_ptr(), + c_struct_name.as_ptr(), + c_field_ns.as_ptr(), + c_field_name.as_ptr(), + ) + }; + + r != 0 + } + /// Gets a simple string property value. /// /// ## Arguments