Skip to main content

xrust/
xmldecl.rs

1/*! Defines common features of XML documents.
2 */
3
4use std::collections::HashMap;
5use std::fmt;
6use std::fmt::Formatter;
7
8#[derive(Clone, Debug, PartialEq)]
9pub struct XMLDecl {
10    pub(crate) version: String,
11    pub(crate) encoding: Option<String>,
12    pub(crate) standalone: Option<String>,
13}
14
15impl XMLDecl {
16    pub fn new(version: String, encoding: Option<String>, standalone: Option<String>) -> Self {
17        XMLDecl {
18            version,
19            encoding,
20            standalone,
21        }
22    }
23    pub fn version(&self) -> String {
24        self.version.clone()
25    }
26    pub fn set_encoding(&mut self, e: String) {
27        self.encoding = Some(e)
28    }
29    pub fn encoding(&self) -> String {
30        self.encoding.as_ref().map_or(String::new(), |e| e.clone())
31    }
32    pub fn set_standalone(&mut self, s: String) {
33        self.standalone = Some(s)
34    }
35    pub fn standalone(&self) -> String {
36        self.standalone
37            .as_ref()
38            .map_or(String::new(), |e| e.clone())
39    }
40}
41
42impl fmt::Display for XMLDecl {
43    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
44        let mut result = String::from("<?xml version=\"");
45        result.push_str(self.version.as_str());
46        result.push('"');
47        if let Some(e) = self.encoding.as_ref() {
48            result.push_str(" encoding=\"");
49            result.push_str(e.as_str());
50            result.push('"');
51        };
52        if let Some(e) = self.standalone.as_ref() {
53            result.push_str(" standalone=\"");
54            result.push_str(e.as_str());
55            result.push('"');
56        };
57        f.write_str(result.as_str())
58    }
59}
60
61pub struct XMLDeclBuilder(XMLDecl);
62
63impl Default for XMLDeclBuilder {
64    fn default() -> Self {
65        Self::new()
66    }
67}
68
69impl XMLDeclBuilder {
70    pub fn new() -> Self {
71        XMLDeclBuilder(XMLDecl {
72            version: String::new(),
73            encoding: None,
74            standalone: None,
75        })
76    }
77    pub fn version(mut self, v: String) -> Self {
78        self.0.version = v;
79        self
80    }
81    pub fn encoding(mut self, v: String) -> Self {
82        self.0.encoding = Some(v);
83        self
84    }
85    pub fn standalone(mut self, v: String) -> Self {
86        self.0.standalone = Some(v);
87        self
88    }
89    pub fn build(self) -> XMLDecl {
90        self.0
91    }
92}
93
94/// DTD declarations.
95/// Data is not stored in any fashion conformant with any standard and will be adusted to meet
96/// the needs of any validators implemented.
97///
98/// NB. Names of elements and attributes cannot be stored as QName, since in the DTD it is not possible to resolve the Namespace URI.
99/// Instead, they are stored here as a (prefix, local-part) pair.
100#[derive(Clone, PartialEq, Debug)]
101pub struct DTD {
102    /// This struct is for internal consumption mainly, it holding the DTD in various incomplete forms
103    /// before construction into useful patterns for validation
104    pub(crate) elements: HashMap<(Option<String>, String), DTDPattern>,
105    pub(crate) attlists: HashMap<
106        (Option<String>, String),
107        HashMap<(Option<String>, String), (AttType, DefaultDecl, bool)>,
108    >, // Boolean for is_editable;
109    pub(crate) notations: HashMap<String, DTDDecl>,
110    pub(crate) generalentities: HashMap<String, (String, bool)>, // Boolean for is_editable;
111    pub(crate) paramentities: HashMap<String, (String, bool)>,   // Boolean for is_editable;
112    publicid: Option<String>,
113    systemid: Option<String>,
114    pub(crate) name: Option<(Option<String>, String)>,
115    pub(crate) patterns: HashMap<(Option<String>, String), DTDPattern>,
116}
117
118impl DTD {
119    pub fn new() -> DTD {
120        let default_entities = vec![
121            ("amp".to_string(), ("&".to_string(), false)),
122            ("gt".to_string(), (">".to_string(), false)),
123            ("lt".to_string(), ("<".to_string(), false)),
124            ("apos".to_string(), ("'".to_string(), false)),
125            ("quot".to_string(), ("\"".to_string(), false)),
126        ];
127        DTD {
128            elements: Default::default(),
129            attlists: Default::default(),
130            notations: Default::default(),
131            generalentities: default_entities.into_iter().collect(),
132            paramentities: HashMap::new(),
133            publicid: None,
134            systemid: None,
135            name: None,
136            patterns: HashMap::new(),
137        }
138    }
139}
140
141impl Default for DTD {
142    fn default() -> Self {
143        Self::new()
144    }
145}
146
147#[derive(Clone, Debug, Eq, PartialEq)]
148pub enum DTDDecl {
149    Notation((Option<String>, String), String),
150    GeneralEntity((Option<String>, String), String),
151    ParamEntity((Option<String>, String), String),
152}
153
154#[allow(clippy::upper_case_acronyms)]
155#[derive(Clone, Debug, Eq, PartialEq)]
156pub(crate) enum AttType {
157    CDATA,
158    ID,
159    IDREF,
160    IDREFS,
161    ENTITY,
162    ENTITIES,
163    NMTOKEN,
164    NMTOKENS,
165    NOTATION(Vec<String>),
166    ENUMERATION(Vec<String>),
167}
168
169#[allow(clippy::upper_case_acronyms)]
170#[derive(Clone, Debug, Eq, PartialEq)]
171pub(crate) enum DefaultDecl {
172    Required,
173    Implied,
174    FIXED(String),
175    Default(String),
176}
177
178#[derive(Clone, PartialEq, Debug)]
179pub(crate) enum DTDPattern {
180    Choice(Box<DTDPattern>, Box<DTDPattern>),
181    Interleave(Box<DTDPattern>, Box<DTDPattern>),
182    Group(Box<DTDPattern>, Box<DTDPattern>),
183    OneOrMore(Box<DTDPattern>),
184    After(Box<DTDPattern>, Box<DTDPattern>),
185    Empty,
186    NotAllowed,
187    Text,
188    Value(String),
189    Attribute((Option<String>, String), Box<DTDPattern>),
190    Element((Option<String>, String), Box<DTDPattern>),
191    Ref((Option<String>, String)),
192    Any,
193    /*
194       This Enum is never used, but it might see application when properly validating ENTITYs.
195    */
196    #[allow(dead_code)]
197    List(Box<DTDPattern>),
198}