pub mod app_properties;
pub mod core_properties;
pub mod custom_properties;
pub mod odt_properties;
pub use app_properties::{
DocxAppProperties, PptxAppProperties, XlsxAppProperties, extract_docx_app_properties, extract_pptx_app_properties,
extract_xlsx_app_properties,
};
pub use core_properties::{CoreProperties, extract_core_properties};
pub use custom_properties::{CustomProperties, extract_custom_properties};
pub use odt_properties::{OdtProperties, extract_odt_properties};
use crate::error::{KreuzbergError, Result};
use roxmltree::Node;
use std::io::Read;
use zip::ZipArchive;
pub(crate) fn read_zip_entry_to_string<R: Read + std::io::Seek>(
archive: &mut ZipArchive<R>,
entry_path: &str,
display_name: &str,
) -> Result<Option<String>> {
match archive.by_name(entry_path) {
Ok(mut file) => {
let mut content = String::new();
file.read_to_string(&mut content)
.map_err(|e| KreuzbergError::parsing(format!("Failed to read {display_name}: {e}")))?;
Ok(Some(content))
}
Err(_) => Ok(None),
}
}
pub(crate) fn parse_xml_text(node: Node, name: &str) -> Option<String> {
node.descendants()
.find(|n| n.has_tag_name(name))
.and_then(|n| n.text())
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(String::from)
}
pub(crate) fn parse_xml_int(node: Node, name: &str) -> Option<i32> {
node.descendants()
.find(|n| n.has_tag_name(name))
.and_then(|n| n.text())
.and_then(|s| s.trim().parse::<i32>().ok())
}
pub(crate) fn parse_xml_bool(node: Node, name: &str) -> Option<bool> {
node.descendants()
.find(|n| n.has_tag_name(name))
.and_then(|n| n.text())
.map(|s| s.trim())
.and_then(|s| match s.to_lowercase().as_str() {
"true" => Some(true),
"false" => Some(false),
_ => None,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_xml_text() {
let xml = r#"<root><title>Test Document</title></root>"#;
let doc = roxmltree::Document::parse(xml).unwrap();
let root = doc.root_element();
assert_eq!(parse_xml_text(root, "title"), Some("Test Document".to_string()));
assert_eq!(parse_xml_text(root, "missing"), None);
}
#[test]
fn test_parse_xml_text_empty() {
let xml = r#"<root><title></title></root>"#;
let doc = roxmltree::Document::parse(xml).unwrap();
let root = doc.root_element();
assert_eq!(parse_xml_text(root, "title"), None);
}
#[test]
fn test_parse_xml_int() {
let xml = r#"<root><count>42</count></root>"#;
let doc = roxmltree::Document::parse(xml).unwrap();
let root = doc.root_element();
assert_eq!(parse_xml_int(root, "count"), Some(42));
assert_eq!(parse_xml_int(root, "missing"), None);
}
#[test]
fn test_parse_xml_bool() {
let xml = r#"<root><flag>true</flag><other>false</other></root>"#;
let doc = roxmltree::Document::parse(xml).unwrap();
let root = doc.root_element();
assert_eq!(parse_xml_bool(root, "flag"), Some(true));
assert_eq!(parse_xml_bool(root, "other"), Some(false));
assert_eq!(parse_xml_bool(root, "missing"), None);
}
}