use super::check_method::CheckMethod;
use super::reference::Reference;
#[derive(Debug, Clone)]
pub struct References {
pub check_method: Option<String>,
pub references: Vec<Reference>,
}
impl References {
#[must_use]
pub fn new() -> Self {
Self {
check_method: None,
references: Vec::new(),
}
}
#[must_use]
pub fn check_method(mut self, method: impl Into<String>) -> Self {
self.check_method = Some(method.into());
self
}
#[must_use]
pub fn set_check_method(mut self, method: CheckMethod) -> Self {
self.check_method = Some(method.as_str().to_owned());
self
}
#[must_use]
pub fn check_method_str(&self) -> Option<&str> {
self.check_method.as_deref()
}
#[must_use]
pub fn check_method_enum(&self) -> Option<CheckMethod> {
self.check_method
.as_deref()
.and_then(|s| CheckMethod::try_from_str(s).ok())
}
#[must_use]
pub fn add_reference(mut self, reference: Reference) -> Self {
self.references.push(reference);
self
}
pub fn has_file(&self, abs_loc: &str) -> bool {
self.references.iter().any(|r| r.file_ref == abs_loc)
}
#[must_use]
pub fn to_xml_string(&self) -> String {
let mut xml = String::from("<ofd:References");
if let Some(ref method) = self.check_method {
use std::fmt::Write;
let _ = write!(xml, r#" CheckMethod="{method}""#);
}
xml.push('>');
for r in &self.references {
xml.push('\n');
xml.push_str(&r.to_xml_string());
}
xml.push_str("\n</ofd:References>");
xml
}
}
impl Default for References {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_references_new() {
let refs = References::new();
assert!(refs.check_method.is_none());
assert!(refs.references.is_empty());
}
#[test]
fn test_references_builder() {
let refs = References::new()
.check_method("SM3")
.add_reference(Reference::new("/Doc_0/Document.xml", "abc"))
.add_reference(Reference::new("/Doc_0/Pages/Page_0/Content.xml", "def"));
assert_eq!(refs.check_method.as_deref(), Some("SM3"));
assert_eq!(refs.references.len(), 2);
}
#[test]
fn test_references_has_file() {
let refs = References::new().add_reference(Reference::new("/Doc_0/Document.xml", "abc"));
assert!(refs.has_file("/Doc_0/Document.xml"));
assert!(!refs.has_file("/Doc_0/Other.xml"));
}
#[test]
fn test_references_xml() {
let refs = References::new()
.check_method("SHA256")
.add_reference(Reference::new("/Doc_0/Document.xml", "hash1"));
let xml = refs.to_xml_string();
assert!(xml.contains("CheckMethod=\"SHA256\""));
assert!(xml.contains("FileRef=\"/Doc_0/Document.xml\""));
assert!(xml.contains("</ofd:References>"));
}
#[test]
fn test_references_default() {
let refs = References::default();
assert!(refs.references.is_empty());
}
#[test]
fn test_set_check_method_enum() {
let refs = References::new().set_check_method(CheckMethod::Sm3);
assert_eq!(refs.check_method.as_deref(), Some("SM3"));
assert_eq!(refs.check_method_str(), Some("SM3"));
}
#[test]
fn test_check_method_enum_roundtrip() {
for method in [
CheckMethod::Md5,
CheckMethod::Sha1,
CheckMethod::Sha256,
CheckMethod::Sm3,
] {
let refs = References::new().set_check_method(method);
assert_eq!(refs.check_method_enum(), Some(method));
}
}
#[test]
fn test_check_method_enum_none() {
let refs = References::new();
assert!(refs.check_method_enum().is_none());
}
#[test]
fn test_check_method_enum_unknown_string() {
let refs = References::new().check_method("UNKNOWN");
assert!(refs.check_method_enum().is_none());
}
#[test]
fn test_set_check_method_xml_unchanged() {
let refs_str = References::new().check_method("SM3");
let refs_enum = References::new().set_check_method(CheckMethod::Sm3);
assert_eq!(refs_str.to_xml_string(), refs_enum.to_xml_string());
}
}