use crate::ids::NameId;
use crate::parser::location::SourceRef;
use crate::schema::model::XsdVersion;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum NamespaceConstraint {
#[default]
Any,
Other,
Enumeration(Vec<Option<NameId>>),
Not(Vec<Option<NameId>>),
}
impl NamespaceConstraint {
pub fn any() -> Self {
NamespaceConstraint::Any
}
pub fn other() -> Self {
NamespaceConstraint::Other
}
pub fn target_namespace(ns: Option<NameId>) -> Self {
NamespaceConstraint::Enumeration(vec![ns])
}
pub fn local() -> Self {
NamespaceConstraint::Enumeration(vec![None])
}
pub fn list(namespaces: Vec<Option<NameId>>) -> Self {
NamespaceConstraint::Enumeration(namespaces)
}
pub fn allows(
&self,
ns: Option<NameId>,
target_ns: Option<NameId>,
xsd_version: XsdVersion,
) -> bool {
match self {
NamespaceConstraint::Any => true,
NamespaceConstraint::Other => {
crate::types::complex::other_matches_namespace(ns, target_ns, xsd_version)
}
NamespaceConstraint::Enumeration(allowed) => allowed.contains(&ns),
NamespaceConstraint::Not(disallowed) => !disallowed.contains(&ns),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ProcessContents {
#[default]
Strict,
Lax,
Skip,
}
impl std::str::FromStr for ProcessContents {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"strict" => Ok(ProcessContents::Strict),
"lax" => Ok(ProcessContents::Lax),
"skip" => Ok(ProcessContents::Skip),
_ => Err(()),
}
}
}
impl ProcessContents {
pub fn as_str(&self) -> &'static str {
match self {
ProcessContents::Strict => "strict",
ProcessContents::Lax => "lax",
ProcessContents::Skip => "skip",
}
}
}
#[derive(Debug, Clone)]
pub struct ElementWildcard {
pub namespace_constraint: NamespaceConstraint,
pub process_contents: ProcessContents,
pub min_occurs: u32,
pub max_occurs: Option<u32>,
pub source: Option<SourceRef>,
pub id: Option<String>,
pub not_qnames: Vec<QNameDisallowed>,
}
impl ElementWildcard {
pub fn new() -> Self {
Self {
namespace_constraint: NamespaceConstraint::Any,
process_contents: ProcessContents::Strict,
min_occurs: 1,
max_occurs: Some(1),
source: None,
id: None,
not_qnames: Vec::new(),
}
}
pub fn any_lax() -> Self {
Self {
namespace_constraint: NamespaceConstraint::Any,
process_contents: ProcessContents::Lax,
min_occurs: 0,
max_occurs: None,
source: None,
id: None,
not_qnames: Vec::new(),
}
}
pub fn is_optional(&self) -> bool {
self.min_occurs == 0
}
pub fn is_unbounded(&self) -> bool {
self.max_occurs.is_none()
}
}
impl Default for ElementWildcard {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct AttributeWildcard {
pub namespace_constraint: NamespaceConstraint,
pub process_contents: ProcessContents,
pub source: Option<SourceRef>,
pub id: Option<String>,
pub not_qnames: Vec<QNameDisallowed>,
}
impl AttributeWildcard {
pub fn new() -> Self {
Self {
namespace_constraint: NamespaceConstraint::Any,
process_contents: ProcessContents::Strict,
source: None,
id: None,
not_qnames: Vec::new(),
}
}
pub fn any_lax() -> Self {
Self {
namespace_constraint: NamespaceConstraint::Any,
process_contents: ProcessContents::Lax,
source: None,
id: None,
not_qnames: Vec::new(),
}
}
}
impl Default for AttributeWildcard {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub enum QNameDisallowed {
QName {
namespace: Option<NameId>,
local_name: NameId,
},
Defined,
DefinedSibling,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_namespace_constraint_any() {
let constraint = NamespaceConstraint::any();
assert!(constraint.allows(Some(NameId(1)), None, XsdVersion::V1_0));
assert!(constraint.allows(None, None, XsdVersion::V1_0));
}
#[test]
fn test_namespace_constraint_other() {
let constraint = NamespaceConstraint::other();
let target = Some(NameId(1));
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));
assert!(!constraint.allows(None, target, XsdVersion::V1_1));
}
#[test]
fn test_namespace_constraint_enumeration() {
let constraint = NamespaceConstraint::list(vec![Some(NameId(1)), Some(NameId(2))]);
assert!(constraint.allows(Some(NameId(1)), None, XsdVersion::V1_0));
assert!(constraint.allows(Some(NameId(2)), None, XsdVersion::V1_0));
assert!(!constraint.allows(Some(NameId(3)), None, XsdVersion::V1_0));
}
#[test]
fn test_process_contents_parsing() {
assert_eq!("strict".parse(), Ok(ProcessContents::Strict));
assert_eq!("lax".parse(), Ok(ProcessContents::Lax));
assert_eq!("skip".parse(), Ok(ProcessContents::Skip));
assert_eq!("invalid".parse::<ProcessContents>(), Err(()));
}
#[test]
fn test_element_wildcard_default() {
let wildcard = ElementWildcard::new();
assert_eq!(wildcard.process_contents, ProcessContents::Strict);
assert_eq!(wildcard.min_occurs, 1);
assert_eq!(wildcard.max_occurs, Some(1));
}
#[test]
fn test_element_wildcard_any_lax() {
let wildcard = ElementWildcard::any_lax();
assert!(wildcard.is_optional());
assert!(wildcard.is_unbounded());
assert_eq!(wildcard.process_contents, ProcessContents::Lax);
}
#[test]
fn test_attribute_wildcard_default() {
let wildcard = AttributeWildcard::new();
assert_eq!(wildcard.process_contents, ProcessContents::Strict);
}
}