1use crate::ids::NameId;
6use crate::parser::location::SourceRef;
7use crate::schema::model::XsdVersion;
8
9#[derive(Debug, Clone, PartialEq, Eq, Default)]
13pub enum NamespaceConstraint {
14 #[default]
16 Any,
17
18 Other,
20
21 Enumeration(Vec<Option<NameId>>),
24
25 Not(Vec<Option<NameId>>),
27}
28
29impl NamespaceConstraint {
30 pub fn any() -> Self {
32 NamespaceConstraint::Any
33 }
34
35 pub fn other() -> Self {
37 NamespaceConstraint::Other
38 }
39
40 pub fn target_namespace(ns: Option<NameId>) -> Self {
42 NamespaceConstraint::Enumeration(vec![ns])
43 }
44
45 pub fn local() -> Self {
47 NamespaceConstraint::Enumeration(vec![None])
48 }
49
50 pub fn list(namespaces: Vec<Option<NameId>>) -> Self {
52 NamespaceConstraint::Enumeration(namespaces)
53 }
54
55 pub fn allows(
57 &self,
58 ns: Option<NameId>,
59 target_ns: Option<NameId>,
60 xsd_version: XsdVersion,
61 ) -> bool {
62 match self {
63 NamespaceConstraint::Any => true,
64 NamespaceConstraint::Other => {
65 crate::types::complex::other_matches_namespace(ns, target_ns, xsd_version)
66 }
67 NamespaceConstraint::Enumeration(allowed) => allowed.contains(&ns),
68 NamespaceConstraint::Not(disallowed) => !disallowed.contains(&ns),
69 }
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
77pub enum ProcessContents {
78 #[default]
80 Strict,
81
82 Lax,
84
85 Skip,
87}
88
89impl std::str::FromStr for ProcessContents {
90 type Err = ();
91
92 fn from_str(s: &str) -> Result<Self, Self::Err> {
93 match s {
94 "strict" => Ok(ProcessContents::Strict),
95 "lax" => Ok(ProcessContents::Lax),
96 "skip" => Ok(ProcessContents::Skip),
97 _ => Err(()),
98 }
99 }
100}
101
102impl ProcessContents {
103 pub fn as_str(&self) -> &'static str {
105 match self {
106 ProcessContents::Strict => "strict",
107 ProcessContents::Lax => "lax",
108 ProcessContents::Skip => "skip",
109 }
110 }
111}
112
113#[derive(Debug, Clone)]
117pub struct ElementWildcard {
118 pub namespace_constraint: NamespaceConstraint,
120
121 pub process_contents: ProcessContents,
123
124 pub min_occurs: u32,
126
127 pub max_occurs: Option<u32>,
129
130 pub source: Option<SourceRef>,
132
133 pub id: Option<String>,
135
136 pub not_qnames: Vec<QNameDisallowed>,
138}
139
140impl ElementWildcard {
141 pub fn new() -> Self {
143 Self {
144 namespace_constraint: NamespaceConstraint::Any,
145 process_contents: ProcessContents::Strict,
146 min_occurs: 1,
147 max_occurs: Some(1),
148 source: None,
149 id: None,
150 not_qnames: Vec::new(),
151 }
152 }
153
154 pub fn any_lax() -> Self {
156 Self {
157 namespace_constraint: NamespaceConstraint::Any,
158 process_contents: ProcessContents::Lax,
159 min_occurs: 0,
160 max_occurs: None,
161 source: None,
162 id: None,
163 not_qnames: Vec::new(),
164 }
165 }
166
167 pub fn is_optional(&self) -> bool {
169 self.min_occurs == 0
170 }
171
172 pub fn is_unbounded(&self) -> bool {
174 self.max_occurs.is_none()
175 }
176}
177
178impl Default for ElementWildcard {
179 fn default() -> Self {
180 Self::new()
181 }
182}
183
184#[derive(Debug, Clone)]
188pub struct AttributeWildcard {
189 pub namespace_constraint: NamespaceConstraint,
191
192 pub process_contents: ProcessContents,
194
195 pub source: Option<SourceRef>,
197
198 pub id: Option<String>,
200
201 pub not_qnames: Vec<QNameDisallowed>,
203}
204
205impl AttributeWildcard {
206 pub fn new() -> Self {
208 Self {
209 namespace_constraint: NamespaceConstraint::Any,
210 process_contents: ProcessContents::Strict,
211 source: None,
212 id: None,
213 not_qnames: Vec::new(),
214 }
215 }
216
217 pub fn any_lax() -> Self {
219 Self {
220 namespace_constraint: NamespaceConstraint::Any,
221 process_contents: ProcessContents::Lax,
222 source: None,
223 id: None,
224 not_qnames: Vec::new(),
225 }
226 }
227}
228
229impl Default for AttributeWildcard {
230 fn default() -> Self {
231 Self::new()
232 }
233}
234
235#[derive(Debug, Clone)]
237pub enum QNameDisallowed {
238 QName {
240 namespace: Option<NameId>,
241 local_name: NameId,
242 },
243 Defined,
245 DefinedSibling,
247}
248
249#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn test_namespace_constraint_any() {
259 let constraint = NamespaceConstraint::any();
260 assert!(constraint.allows(Some(NameId(1)), None, XsdVersion::V1_0));
261 assert!(constraint.allows(None, None, XsdVersion::V1_0));
262 }
263
264 #[test]
265 fn test_namespace_constraint_other() {
266 let constraint = NamespaceConstraint::other();
267 let target = Some(NameId(1));
268
269 assert!(!constraint.allows(target, target, XsdVersion::V1_0)); assert!(constraint.allows(Some(NameId(2)), target, XsdVersion::V1_0)); assert!(!constraint.allows(None, target, XsdVersion::V1_0));
275 assert!(!constraint.allows(None, target, XsdVersion::V1_1));
276 }
277
278 #[test]
279 fn test_namespace_constraint_enumeration() {
280 let constraint = NamespaceConstraint::list(vec![Some(NameId(1)), Some(NameId(2))]);
281
282 assert!(constraint.allows(Some(NameId(1)), None, XsdVersion::V1_0));
283 assert!(constraint.allows(Some(NameId(2)), None, XsdVersion::V1_0));
284 assert!(!constraint.allows(Some(NameId(3)), None, XsdVersion::V1_0));
285 }
286
287 #[test]
288 fn test_process_contents_parsing() {
289 assert_eq!("strict".parse(), Ok(ProcessContents::Strict));
290 assert_eq!("lax".parse(), Ok(ProcessContents::Lax));
291 assert_eq!("skip".parse(), Ok(ProcessContents::Skip));
292 assert_eq!("invalid".parse::<ProcessContents>(), Err(()));
293 }
294
295 #[test]
296 fn test_element_wildcard_default() {
297 let wildcard = ElementWildcard::new();
298 assert_eq!(wildcard.process_contents, ProcessContents::Strict);
299 assert_eq!(wildcard.min_occurs, 1);
300 assert_eq!(wildcard.max_occurs, Some(1));
301 }
302
303 #[test]
304 fn test_element_wildcard_any_lax() {
305 let wildcard = ElementWildcard::any_lax();
306 assert!(wildcard.is_optional());
307 assert!(wildcard.is_unbounded());
308 assert_eq!(wildcard.process_contents, ProcessContents::Lax);
309 }
310
311 #[test]
312 fn test_attribute_wildcard_default() {
313 let wildcard = AttributeWildcard::new();
314 assert_eq!(wildcard.process_contents, ProcessContents::Strict);
315 }
316}