fiscal_core/
tax_element.rs1#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct TaxField {
11 pub name: String,
13 pub value: String,
15}
16
17impl TaxField {
18 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
20 Self {
21 name: name.into(),
22 value: value.into(),
23 }
24 }
25}
26
27#[derive(Debug, Clone)]
29pub struct TaxElement {
30 pub outer_tag: Option<String>,
32 pub outer_fields: Vec<TaxField>,
34 pub variant_tag: String,
36 pub fields: Vec<TaxField>,
38}
39
40pub fn optional_field(name: &str, value: Option<&str>) -> Option<TaxField> {
42 value.map(|v| TaxField::new(name, v))
43}
44
45pub fn required_field(name: &str, value: Option<&str>) -> Result<TaxField, crate::FiscalError> {
51 match value {
52 Some(v) => Ok(TaxField::new(name, v)),
53 None => Err(crate::FiscalError::MissingRequiredField {
54 field: name.to_string(),
55 }),
56 }
57}
58
59pub fn filter_fields(fields: Vec<Option<TaxField>>) -> Vec<TaxField> {
61 fields.into_iter().flatten().collect()
62}
63
64fn escape_xml_value(s: &str) -> String {
66 let mut result = String::with_capacity(s.len());
67 for ch in s.chars() {
68 match ch {
69 '&' => result.push_str("&"),
70 '<' => result.push_str("<"),
71 '>' => result.push_str(">"),
72 '"' => result.push_str("""),
73 c => result.push(c),
74 }
75 }
76 result
77}
78
79fn serialize_field(field: &TaxField) -> String {
81 format!(
82 "<{name}>{value}</{name}>",
83 name = field.name,
84 value = escape_xml_value(&field.value)
85 )
86}
87
88pub fn serialize_tax_element(element: &TaxElement) -> String {
90 let inner_content: String = element.fields.iter().map(serialize_field).collect();
91 let variant_xml = format!("<{tag}>{inner_content}</{tag}>", tag = element.variant_tag,);
92
93 match &element.outer_tag {
94 None => variant_xml,
95 Some(outer) => {
96 let outer_fields_xml: String =
97 element.outer_fields.iter().map(serialize_field).collect();
98 format!("<{outer}>{outer_fields_xml}{variant_xml}</{outer}>")
99 }
100 }
101}