easyofd_core/signatures/
signature.rs1use std::fmt::Write;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum SigType {
12 Seal,
14 Sign,
16}
17
18impl SigType {
19 #[must_use]
21 pub fn as_str(&self) -> &'static str {
22 match self {
23 Self::Seal => "Seal",
24 Self::Sign => "Sign",
25 }
26 }
27
28 #[allow(clippy::should_implement_trait)]
30 pub fn from_str(s: &str) -> Result<Self, String> {
31 match s {
32 "Seal" => Ok(Self::Seal),
33 "Sign" => Ok(Self::Sign),
34 _ => Err(format!("未知的签名类型: {s}")),
35 }
36 }
37}
38
39impl std::fmt::Display for SigType {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 f.write_str(self.as_str())
42 }
43}
44
45#[derive(Debug, Clone)]
50pub struct Signature {
51 pub id: String,
53 pub sig_type: Option<SigType>,
55 pub relative: Option<String>,
58 pub base_loc: String,
60}
61
62impl Signature {
63 #[must_use]
65 pub fn new(id: impl Into<String>, base_loc: impl Into<String>) -> Self {
66 Self {
67 id: id.into(),
68 sig_type: None,
69 relative: None,
70 base_loc: base_loc.into(),
71 }
72 }
73
74 #[must_use]
76 pub fn sig_type(mut self, sig_type: SigType) -> Self {
77 self.sig_type = Some(sig_type);
78 self
79 }
80
81 #[must_use]
83 pub fn relative(mut self, id: impl Into<String>) -> Self {
84 self.relative = Some(id.into());
85 self
86 }
87
88 #[must_use]
90 pub fn to_xml_string(&self) -> String {
91 let mut xml = format!(r#"<ofd:Signature ID="{}""#, self.id);
92 if let Some(ref sig_type) = self.sig_type {
93 let _ = write!(xml, r#" Type="{}""#, sig_type.as_str());
94 }
95 if let Some(ref rel) = self.relative {
96 let _ = write!(xml, r#" Relative="{rel}""#);
97 }
98 let _ = write!(xml, r#" BaseLoc="{}""#, self.base_loc);
99 xml.push_str(" />");
100 xml
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_signature_new() {
110 let sig = Signature::new("s1", "/Doc_0/Signs/Sign_0/Signature.xml");
111 assert_eq!(sig.id, "s1");
112 assert_eq!(sig.base_loc, "/Doc_0/Signs/Sign_0/Signature.xml");
113 assert!(sig.sig_type.is_none());
114 assert!(sig.relative.is_none());
115 }
116
117 #[test]
118 fn test_signature_builder() {
119 let sig = Signature::new("s2", "/Doc_0/Signs/Sign_1/Signature.xml")
120 .sig_type(SigType::Sign)
121 .relative("s1");
122 assert_eq!(sig.sig_type, Some(SigType::Sign));
123 assert_eq!(sig.relative.as_deref(), Some("s1"));
124 }
125
126 #[test]
127 fn test_signature_xml_full() {
128 let sig = Signature::new("s1", "/Doc_0/Signs/Sign_0/Signature.xml").sig_type(SigType::Seal);
129 let xml = sig.to_xml_string();
130 assert!(xml.contains(r#"ID="s1""#));
131 assert!(xml.contains(r#"Type="Seal""#));
132 assert!(xml.contains(r#"BaseLoc="/Doc_0/Signs/Sign_0/Signature.xml""#));
133 assert!(!xml.contains("Relative"));
134 }
135
136 #[test]
137 fn test_sig_type_display() {
138 assert_eq!(SigType::Seal.to_string(), "Seal");
139 assert_eq!(SigType::Sign.to_string(), "Sign");
140 assert_eq!(SigType::from_str("Seal").unwrap(), SigType::Seal);
141 assert!(SigType::from_str("Unknown").is_err());
142 }
143}