use crate::error::{SchemaError, SchemaResult};
use crate::namespace::{is_ncname, NameTable};
use crate::parser::attrs::AttributeMap;
use crate::parser::location::SourceRef;
use crate::schema::XsdVersion;
use crate::types::facets::{normalize_whitespace, WhitespaceMode};
fn collapsed(value: &str) -> String {
normalize_whitespace(value, WhitespaceMode::Collapse)
}
#[derive(Debug, Clone)]
pub struct ValidationContext {
pub xsd_version: XsdVersion,
pub is_top_level: bool,
pub inside_complex_type: bool,
pub source: Option<SourceRef>,
}
impl Default for ValidationContext {
fn default() -> Self {
Self {
xsd_version: XsdVersion::V1_0,
is_top_level: false,
inside_complex_type: false,
source: None,
}
}
}
impl ValidationContext {
pub fn new(xsd_version: XsdVersion, is_top_level: bool) -> Self {
Self {
xsd_version,
is_top_level,
inside_complex_type: false,
source: None,
}
}
pub fn with_source(mut self, source: Option<SourceRef>) -> Self {
self.source = source;
self
}
}
pub fn validate_element_structure(
attrs: &AttributeMap,
name_table: &NameTable,
ctx: &ValidationContext,
) -> SchemaResult<()> {
let has_name = attrs.get_value_by_name(name_table, "name").is_some();
let has_ref = attrs.get_value_by_name(name_table, "ref").is_some();
if let Some(name_val) = attrs.get_value_by_name(name_table, "name") {
if !is_ncname(&collapsed(name_val)) {
return Err(SchemaError::structural(
"src-element",
format!("Element 'name' value '{}' is not a valid NCName", name_val),
None,
));
}
}
if ctx.is_top_level {
if !has_name {
return Err(SchemaError::structural(
"src-element",
"Top-level element declaration must have 'name' attribute",
None,
));
}
if has_ref {
return Err(SchemaError::structural(
"src-element",
"Top-level element declaration cannot have 'ref' attribute",
None,
));
}
for prohibited in &["minOccurs", "maxOccurs", "form", "targetNamespace"] {
if attrs.get_value_by_name(name_table, prohibited).is_some() {
return Err(SchemaError::structural(
"src-element",
format!(
"Top-level element declaration cannot have '{}' attribute",
prohibited
),
None,
));
}
}
} else {
if has_name && has_ref {
return Err(SchemaError::structural(
"src-element",
"Local element cannot have both 'name' and 'ref' attributes",
None,
));
}
if !has_name && !has_ref {
return Err(SchemaError::structural(
"src-element",
"Local element must have either 'name' or 'ref' attribute",
None,
));
}
if has_ref {
let ref_prohibited = [
"type",
"default",
"fixed",
"nillable",
"block",
"final",
"form",
"targetNamespace",
];
for prohibited in &ref_prohibited {
if attrs.get_value_by_name(name_table, prohibited).is_some() {
return Err(SchemaError::structural(
"src-element",
format!("Element reference cannot have '{}' attribute", prohibited),
None,
));
}
}
}
for prohibited in &["final", "abstract", "substitutionGroup"] {
if attrs.get_value_by_name(name_table, prohibited).is_some() {
return Err(SchemaError::structural(
"src-element",
format!(
"Local element declaration cannot have '{}' attribute",
prohibited
),
None,
));
}
}
let has_target_ns = attrs
.get_value_by_name(name_table, "targetNamespace")
.is_some();
if has_target_ns {
let has_form = attrs.get_value_by_name(name_table, "form").is_some();
if has_form {
return Err(SchemaError::structural(
"src-element",
"Local element with 'targetNamespace' cannot also have 'form' \
(src-element §3.3.3 clause 3.2.2)",
None,
));
}
if !ctx.inside_complex_type {
return Err(SchemaError::structural(
"src-element",
"Local element with 'targetNamespace' must have a <complexType> \
lexical ancestor (src-element §3.3.3 clause 4)",
None,
));
}
}
}
let has_default = attrs.get_value_by_name(name_table, "default").is_some();
let has_fixed = attrs.get_value_by_name(name_table, "fixed").is_some();
if has_default && has_fixed {
return Err(SchemaError::structural(
"cos-valid-default",
"Element cannot have both 'default' and 'fixed' attributes",
None,
));
}
if let Some(final_val) = attrs.get_value_by_name(name_table, "final") {
validate_derivation_set_tokens(
final_val,
&["extension", "restriction"],
"final",
"element",
)?;
}
if let Some(block_val) = attrs.get_value_by_name(name_table, "block") {
validate_derivation_set_tokens(
block_val,
&["extension", "restriction", "substitution"],
"block",
"element",
)?;
}
Ok(())
}
fn validate_derivation_set_tokens(
value: &str,
allowed: &[&str],
attr: &str,
elem: &str,
) -> SchemaResult<()> {
let trimmed = value.trim();
if trimmed == "#all" {
return Ok(());
}
for token in trimmed.split_whitespace() {
if !allowed.contains(&token) {
return Err(SchemaError::structural(
"sch-props-correct",
format!(
"'{}' on '{}' does not allow derivation method '{}'",
attr, elem, token
),
None,
));
}
}
Ok(())
}
pub fn validate_attribute_structure(
attrs: &AttributeMap,
name_table: &NameTable,
ctx: &ValidationContext,
) -> SchemaResult<()> {
let has_name = attrs.get_value_by_name(name_table, "name").is_some();
let has_ref = attrs.get_value_by_name(name_table, "ref").is_some();
if let Some(name_val) = attrs.get_value_by_name(name_table, "name") {
let collapsed_name = collapsed(name_val);
if !is_ncname(&collapsed_name) {
return Err(SchemaError::structural(
"src-attribute",
format!(
"Attribute 'name' value '{}' is not a valid NCName",
name_val
),
None,
));
}
if collapsed_name == "xmlns" {
return Err(SchemaError::structural(
"no-xmlns",
"Attribute declaration name must not be 'xmlns'",
None,
));
}
}
if ctx.is_top_level {
if !has_name {
return Err(SchemaError::structural(
"src-attribute",
"Top-level attribute declaration must have 'name' attribute",
None,
));
}
if has_ref {
return Err(SchemaError::structural(
"src-attribute",
"Top-level attribute declaration cannot have 'ref' attribute",
None,
));
}
for prohibited in &["use", "form", "targetNamespace"] {
if attrs.get_value_by_name(name_table, prohibited).is_some() {
return Err(SchemaError::structural(
"src-attribute",
format!(
"Top-level attribute declaration cannot have '{}' attribute",
prohibited
),
None,
));
}
}
} else {
if has_name && has_ref {
return Err(SchemaError::structural(
"src-attribute",
"Local attribute cannot have both 'name' and 'ref' attributes",
None,
));
}
if !has_name && !has_ref {
return Err(SchemaError::structural(
"src-attribute",
"Local attribute must have either 'name' or 'ref' attribute",
None,
));
}
if has_ref {
for prohibited in &["type", "form", "targetNamespace"] {
if attrs.get_value_by_name(name_table, prohibited).is_some() {
return Err(SchemaError::structural(
"src-attribute",
format!("Attribute reference cannot have '{}' attribute", prohibited),
None,
));
}
}
}
let has_target_ns = attrs
.get_value_by_name(name_table, "targetNamespace")
.is_some();
if has_target_ns {
let has_form = attrs.get_value_by_name(name_table, "form").is_some();
if has_form {
return Err(SchemaError::structural(
"src-attribute",
"Local attribute with 'targetNamespace' cannot also have 'form' \
(src-attribute §3.2.3 clause 6.2)",
None,
));
}
if !ctx.inside_complex_type {
return Err(SchemaError::structural(
"src-attribute",
"Local attribute with 'targetNamespace' must have a <complexType> \
lexical ancestor (src-attribute §3.2.3 clause 6)",
None,
));
}
}
}
let has_default = attrs.get_value_by_name(name_table, "default").is_some();
let has_fixed = attrs.get_value_by_name(name_table, "fixed").is_some();
if has_default && has_fixed {
return Err(SchemaError::structural(
"cos-valid-default",
"Attribute cannot have both 'default' and 'fixed' attributes",
None,
));
}
if has_default {
if let Some(use_val) = attrs.get_value_by_name(name_table, "use") {
if use_val != "optional" {
return Err(SchemaError::structural(
"src-attribute",
format!(
"Attribute with 'default' must have use='optional' (got '{}')",
use_val
),
None,
));
}
}
}
if let Some(use_val) = attrs.get_value_by_name(name_table, "use") {
if use_val == "prohibited" {
if has_fixed && ctx.xsd_version == XsdVersion::V1_1 {
return Err(SchemaError::structural(
"src-attribute",
"Prohibited attribute cannot have 'fixed' attribute",
None,
));
}
}
}
Ok(())
}
pub fn validate_simple_type_structure(
attrs: &AttributeMap,
name_table: &NameTable,
ctx: &ValidationContext,
) -> SchemaResult<()> {
let has_name = attrs.get_value_by_name(name_table, "name").is_some();
if ctx.is_top_level && !has_name {
return Err(SchemaError::structural(
"src-simple-type",
"Top-level simpleType must have 'name' attribute",
None,
));
}
if !ctx.is_top_level && has_name {
return Err(SchemaError::structural(
"src-simple-type",
"Inline simpleType cannot have 'name' attribute",
None,
));
}
if let Some(name_val) = attrs.get_value_by_name(name_table, "name") {
if !is_ncname(&collapsed(name_val)) {
return Err(SchemaError::structural(
"src-simple-type",
format!(
"simpleType 'name' value '{}' is not a valid NCName",
name_val
),
None,
));
}
}
if let Some(final_val) = attrs.get_value_by_name(name_table, "final") {
validate_derivation_set_tokens(
final_val,
&["restriction", "list", "union", "extension"],
"final",
"simpleType",
)?;
}
Ok(())
}
pub fn validate_complex_type_structure(
attrs: &AttributeMap,
name_table: &NameTable,
ctx: &ValidationContext,
) -> SchemaResult<()> {
let has_name = attrs.get_value_by_name(name_table, "name").is_some();
if ctx.is_top_level && !has_name {
return Err(SchemaError::structural(
"src-ct",
"Top-level complexType must have 'name' attribute",
None,
));
}
if !ctx.is_top_level && has_name {
return Err(SchemaError::structural(
"src-ct",
"Inline complexType cannot have 'name' attribute",
None,
));
}
if let Some(name_val) = attrs.get_value_by_name(name_table, "name") {
if !is_ncname(&collapsed(name_val)) {
return Err(SchemaError::structural(
"src-ct",
format!(
"complexType 'name' value '{}' is not a valid NCName",
name_val
),
None,
));
}
}
if let Some(final_val) = attrs.get_value_by_name(name_table, "final") {
validate_derivation_set_tokens(
final_val,
&["extension", "restriction"],
"final",
"complexType",
)?;
}
if let Some(block_val) = attrs.get_value_by_name(name_table, "block") {
validate_derivation_set_tokens(
block_val,
&["extension", "restriction"],
"block",
"complexType",
)?;
}
Ok(())
}
pub fn validate_restriction_structure(
attrs: &AttributeMap,
name_table: &NameTable,
has_inline_type: bool,
) -> SchemaResult<()> {
let has_base = attrs.get_value_by_name(name_table, "base").is_some();
if has_base && has_inline_type {
return Err(SchemaError::structural(
"src-restriction-base-or-simpleType",
"Restriction cannot have both 'base' attribute and inline type",
None,
));
}
Ok(())
}
pub fn validate_extension_structure(
attrs: &AttributeMap,
name_table: &NameTable,
) -> SchemaResult<()> {
let has_base = attrs.get_value_by_name(name_table, "base").is_some();
if !has_base {
return Err(SchemaError::structural(
"src-ct",
"Extension must have 'base' attribute",
None,
));
}
Ok(())
}
pub fn validate_list_structure(
attrs: &AttributeMap,
name_table: &NameTable,
has_inline_type: bool,
) -> SchemaResult<()> {
let has_item_type = attrs.get_value_by_name(name_table, "itemType").is_some();
if has_item_type && has_inline_type {
return Err(SchemaError::structural(
"src-list-itemType-or-simpleType",
"List cannot have both 'itemType' attribute and inline simpleType",
None,
));
}
if !has_item_type && !has_inline_type {
return Err(SchemaError::structural(
"src-list-itemType-or-simpleType",
"List must have either 'itemType' attribute or inline simpleType",
None,
));
}
Ok(())
}
pub fn validate_union_structure(
attrs: &AttributeMap,
name_table: &NameTable,
has_inline_types: bool,
) -> SchemaResult<()> {
let has_member_types = attrs.get_value_by_name(name_table, "memberTypes").is_some();
if !has_member_types && !has_inline_types {
return Err(SchemaError::structural(
"src-union-memberTypes-or-simpleTypes",
"Union must have 'memberTypes' attribute or inline simpleType children",
None,
));
}
Ok(())
}
pub fn validate_key_unique_structure(
attrs: &AttributeMap,
name_table: &NameTable,
) -> SchemaResult<()> {
let has_name = attrs.get_value_by_name(name_table, "name").is_some();
let has_ref = attrs.get_value_by_name(name_table, "ref").is_some();
if !has_name && !has_ref {
return Err(SchemaError::structural(
"src-identity-constraint",
"Identity constraint (key/unique) must have 'name' or 'ref' attribute",
None,
));
}
if let Some(name_val) = attrs.get_value_by_name(name_table, "name") {
if !is_ncname(&collapsed(name_val)) {
return Err(SchemaError::structural(
"src-identity-constraint",
format!(
"identity constraint 'name' value '{}' is not a valid NCName",
name_val
),
None,
));
}
}
Ok(())
}
pub fn validate_keyref_structure(attrs: &AttributeMap, name_table: &NameTable) -> SchemaResult<()> {
let has_name = attrs.get_value_by_name(name_table, "name").is_some();
let has_refer = attrs.get_value_by_name(name_table, "refer").is_some();
let has_ref = attrs.get_value_by_name(name_table, "ref").is_some();
if !has_name && !has_ref {
return Err(SchemaError::structural(
"src-identity-constraint",
"Keyref must have 'name' or 'ref' attribute",
None,
));
}
if has_name && !has_refer {
return Err(SchemaError::structural(
"src-identity-constraint",
"Keyref must have 'refer' attribute",
None,
));
}
if let Some(name_val) = attrs.get_value_by_name(name_table, "name") {
if !is_ncname(&collapsed(name_val)) {
return Err(SchemaError::structural(
"src-identity-constraint",
format!("keyref 'name' value '{}' is not a valid NCName", name_val),
None,
));
}
}
Ok(())
}
pub fn validate_group_structure(
attrs: &AttributeMap,
name_table: &NameTable,
ctx: &ValidationContext,
) -> SchemaResult<()> {
let has_name = attrs.get_value_by_name(name_table, "name").is_some();
let has_ref = attrs.get_value_by_name(name_table, "ref").is_some();
if ctx.is_top_level {
if !has_name {
return Err(SchemaError::structural(
"mgd-props-correct",
"Top-level group must have 'name' attribute",
None,
));
}
if has_ref {
return Err(SchemaError::structural(
"mgd-props-correct",
"Top-level group cannot have 'ref' attribute",
None,
));
}
for prohibited in &["minOccurs", "maxOccurs"] {
if attrs.get_value_by_name(name_table, prohibited).is_some() {
return Err(SchemaError::structural(
"mgd-props-correct",
format!("Top-level group cannot have '{}' attribute", prohibited),
None,
));
}
}
} else {
if has_name {
return Err(SchemaError::structural(
"mgd-props-correct",
"Non-top-level group must use 'ref', not 'name'",
None,
));
}
if !has_ref {
return Err(SchemaError::structural(
"mgd-props-correct",
"Non-top-level group must have 'ref' attribute",
None,
));
}
}
if let Some(name_val) = attrs.get_value_by_name(name_table, "name") {
if !is_ncname(&collapsed(name_val)) {
return Err(SchemaError::structural(
"mgd-props-correct",
format!("group 'name' value '{}' is not a valid NCName", name_val),
None,
));
}
}
Ok(())
}
pub fn validate_attribute_group_structure(
attrs: &AttributeMap,
name_table: &NameTable,
ctx: &ValidationContext,
) -> SchemaResult<()> {
let has_name = attrs.get_value_by_name(name_table, "name").is_some();
let has_ref = attrs.get_value_by_name(name_table, "ref").is_some();
if ctx.is_top_level {
if !has_name {
return Err(SchemaError::structural(
"src-attribute_group",
"Top-level attributeGroup must have 'name' attribute",
None,
));
}
if has_ref {
return Err(SchemaError::structural(
"src-attribute_group",
"Top-level attributeGroup cannot have 'ref' attribute",
None,
));
}
} else {
if has_name {
return Err(SchemaError::structural(
"src-attribute_group",
"Non-top-level attributeGroup must use 'ref', not 'name'",
None,
));
}
if !has_ref {
return Err(SchemaError::structural(
"src-attribute_group",
"Non-top-level attributeGroup must have 'ref' attribute",
None,
));
}
}
if let Some(name_val) = attrs.get_value_by_name(name_table, "name") {
if !is_ncname(&collapsed(name_val)) {
return Err(SchemaError::structural(
"src-attribute_group",
format!(
"attributeGroup 'name' value '{}' is not a valid NCName",
name_val
),
None,
));
}
}
Ok(())
}
pub const XSD_1_1_ELEMENTS: &[&str] = &[
"assert",
"assertion",
"alternative",
"openContent",
"defaultOpenContent",
"override",
"explicitTimezone",
];
pub const XSD_1_1_ATTRIBUTES: &[&str] = &[
"targetNamespace", "notNamespace", "notQName", "inheritable", "defaultAttributes", "defaultAttributesApply", "xpathDefaultNamespace", ];
pub fn validate_xsd_version_element(
element_name: &str,
ctx: &ValidationContext,
) -> SchemaResult<()> {
if ctx.xsd_version == XsdVersion::V1_0 && XSD_1_1_ELEMENTS.contains(&element_name) {
return Err(SchemaError::feature(
format!(
"Element '{}' requires XSD 1.1 but schema is in XSD 1.0 mode",
element_name
),
None,
));
}
Ok(())
}
pub fn validate_xsd_version_attribute(
attr_name: &str,
element_name: &str,
ctx: &ValidationContext,
) -> SchemaResult<()> {
if ctx.xsd_version == XsdVersion::V1_0 {
let is_xsd_1_1_attr = match (element_name, attr_name) {
("element", "targetNamespace") => true,
("attribute", "targetNamespace") => true,
("attribute", "inheritable") => true,
("complexType", "defaultAttributesApply") => true,
("complexType", "xpathDefaultNamespace") => true,
("any", "notNamespace") | ("any", "notQName") => true,
("anyAttribute", "notNamespace") | ("anyAttribute", "notQName") => true,
("schema", "defaultAttributes") => true,
("schema", "xpathDefaultNamespace") => true,
("selector", "xpathDefaultNamespace") => true,
("field", "xpathDefaultNamespace") => true,
("unique", "ref") | ("key", "ref") | ("keyref", "ref") => true,
("schema", "targetNamespace") => false,
_ => XSD_1_1_ATTRIBUTES.contains(&attr_name),
};
if is_xsd_1_1_attr {
return Err(SchemaError::feature(
format!(
"Attribute '{}' on '{}' requires XSD 1.1 but schema is in XSD 1.0 mode",
attr_name, element_name
),
None,
));
}
}
Ok(())
}
pub fn validate_notation_structure(
attrs: &AttributeMap,
name_table: &NameTable,
ctx: &ValidationContext,
) -> SchemaResult<()> {
let has_name = attrs.get_value_by_name(name_table, "name").is_some();
let has_public = attrs.get_value_by_name(name_table, "public").is_some();
let has_system = attrs.get_value_by_name(name_table, "system").is_some();
if !has_name {
return Err(SchemaError::structural(
"n-props-correct",
"Notation must have 'name' attribute",
None,
));
}
if let Some(name_val) = attrs.get_value_by_name(name_table, "name") {
if !is_ncname(&collapsed(name_val)) {
return Err(SchemaError::structural(
"n-props-correct",
format!("notation 'name' value '{}' is not a valid NCName", name_val),
None,
));
}
}
match ctx.xsd_version {
XsdVersion::V1_0 => {
if !has_public {
return Err(SchemaError::structural(
"n-props-correct",
"Notation must have 'public' attribute in XSD 1.0",
None,
));
}
}
XsdVersion::V1_1 => {
if !has_public && !has_system {
return Err(SchemaError::structural(
"n-props-correct",
"Notation must have 'public' or 'system' attribute in XSD 1.1",
None,
));
}
}
}
Ok(())
}
pub fn validate_include_structure(
attrs: &AttributeMap,
name_table: &NameTable,
) -> SchemaResult<()> {
let has_location = attrs
.get_value_by_name(name_table, "schemaLocation")
.is_some();
if !has_location {
return Err(SchemaError::structural(
"src-include",
"Include must have 'schemaLocation' attribute",
None,
));
}
Ok(())
}
pub fn validate_import_structure(attrs: &AttributeMap, name_table: &NameTable) -> SchemaResult<()> {
if let Some(ns) = attrs.get_value_by_name(name_table, "namespace") {
if ns.is_empty() {
return Err(SchemaError::structural(
"src-import",
"xs:import 'namespace' must not be the empty string",
None,
));
}
}
Ok(())
}
pub fn validate_schema_structure(attrs: &AttributeMap, name_table: &NameTable) -> SchemaResult<()> {
if let Some(tns) = attrs.get_value_by_name(name_table, "targetNamespace") {
if tns.is_empty() {
return Err(SchemaError::structural(
"sch-props-correct",
"xs:schema 'targetNamespace' must not be the empty string",
None,
));
}
}
Ok(())
}
pub fn validate_redefine_structure(
attrs: &AttributeMap,
name_table: &NameTable,
) -> SchemaResult<()> {
let has_location = attrs
.get_value_by_name(name_table, "schemaLocation")
.is_some();
if !has_location {
return Err(SchemaError::structural(
"src-redefine",
"Redefine must have 'schemaLocation' attribute",
None,
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::attrs::ParsedAttribute;
fn make_attr_map(name_table: &mut NameTable, attrs: &[(&str, &str)]) -> AttributeMap {
let parsed: Vec<ParsedAttribute> = attrs
.iter()
.map(|(name, value)| ParsedAttribute {
namespace: None,
local_name: name_table.add(name),
prefix: None,
value: value.to_string(),
source: None,
})
.collect();
AttributeMap::new(parsed)
}
#[test]
fn test_element_top_level_valid() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(&mut name_table, &[("name", "myElement")]);
let ctx = ValidationContext::new(XsdVersion::V1_0, true);
let result = validate_element_structure(&attrs, &name_table, &ctx);
assert!(result.is_ok());
}
#[test]
fn test_element_top_level_missing_name() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(&mut name_table, &[("type", "xs:string")]);
let ctx = ValidationContext::new(XsdVersion::V1_0, true);
let result = validate_element_structure(&attrs, &name_table, &ctx);
assert!(result.is_err());
}
#[test]
fn test_element_top_level_has_ref() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(&mut name_table, &[("name", "myElement"), ("ref", "other")]);
let ctx = ValidationContext::new(XsdVersion::V1_0, true);
let result = validate_element_structure(&attrs, &name_table, &ctx);
assert!(result.is_err());
}
#[test]
fn test_element_local_name_and_ref() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(&mut name_table, &[("name", "myElement"), ("ref", "other")]);
let ctx = ValidationContext::new(XsdVersion::V1_0, false);
let result = validate_element_structure(&attrs, &name_table, &ctx);
assert!(result.is_err());
}
#[test]
fn test_element_default_and_fixed() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(
&mut name_table,
&[("name", "myElement"), ("default", "a"), ("fixed", "b")],
);
let ctx = ValidationContext::new(XsdVersion::V1_0, true);
let result = validate_element_structure(&attrs, &name_table, &ctx);
assert!(result.is_err());
}
#[test]
fn test_attribute_prohibited_with_default() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(
&mut name_table,
&[("ref", "myAttr"), ("use", "prohibited"), ("default", "x")],
);
let ctx = ValidationContext::new(XsdVersion::V1_0, false);
let result = validate_attribute_structure(&attrs, &name_table, &ctx);
assert!(result.is_err());
}
#[test]
fn test_xsd_1_1_element_in_1_0_mode() {
let ctx = ValidationContext::new(XsdVersion::V1_0, false);
let result = validate_xsd_version_element("assert", &ctx);
assert!(result.is_err());
}
#[test]
fn test_xsd_1_1_element_in_1_1_mode() {
let ctx = ValidationContext::new(XsdVersion::V1_1, false);
let result = validate_xsd_version_element("assert", &ctx);
assert!(result.is_ok());
}
#[test]
fn test_keyref_requires_refer() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(&mut name_table, &[("name", "myKeyRef")]);
let result = validate_keyref_structure(&attrs, &name_table);
assert!(result.is_err());
}
#[test]
fn test_keyref_with_refer() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(&mut name_table, &[("name", "myKeyRef"), ("refer", "myKey")]);
let result = validate_keyref_structure(&attrs, &name_table);
assert!(result.is_ok());
}
#[test]
fn test_list_itemtype_and_inline() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(&mut name_table, &[("itemType", "xs:string")]);
let result = validate_list_structure(&attrs, &name_table, true);
assert!(result.is_err());
}
#[test]
fn test_list_neither_itemtype_nor_inline() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(&mut name_table, &[]);
let result = validate_list_structure(&attrs, &name_table, false);
assert!(result.is_err());
}
#[test]
fn test_extension_requires_base() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(&mut name_table, &[]);
let result = validate_extension_structure(&attrs, &name_table);
assert!(result.is_err());
}
#[test]
fn test_notation_requires_public_in_1_0() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(
&mut name_table,
&[("name", "myNotation"), ("system", "foo")],
);
let ctx = ValidationContext::new(XsdVersion::V1_0, true);
let result = validate_notation_structure(&attrs, &name_table, &ctx);
assert!(result.is_err());
}
#[test]
fn test_notation_system_ok_in_1_1() {
let mut name_table = NameTable::new();
let attrs = make_attr_map(
&mut name_table,
&[("name", "myNotation"), ("system", "foo")],
);
let ctx = ValidationContext::new(XsdVersion::V1_1, true);
let result = validate_notation_structure(&attrs, &name_table, &ctx);
assert!(result.is_ok());
}
#[test]
fn test_xpath_default_ns_on_selector_rejected_in_1_0() {
let ctx = ValidationContext::new(XsdVersion::V1_0, false);
let result = validate_xsd_version_attribute("xpathDefaultNamespace", "selector", &ctx);
assert!(result.is_err());
}
#[test]
fn test_xpath_default_ns_on_field_rejected_in_1_0() {
let ctx = ValidationContext::new(XsdVersion::V1_0, false);
let result = validate_xsd_version_attribute("xpathDefaultNamespace", "field", &ctx);
assert!(result.is_err());
}
#[test]
fn test_xpath_default_ns_on_schema_rejected_in_1_0() {
let ctx = ValidationContext::new(XsdVersion::V1_0, true);
let result = validate_xsd_version_attribute("xpathDefaultNamespace", "schema", &ctx);
assert!(result.is_err());
}
#[test]
fn test_target_namespace_on_schema_allowed_in_1_0() {
let ctx = ValidationContext::new(XsdVersion::V1_0, true);
let result = validate_xsd_version_attribute("targetNamespace", "schema", &ctx);
assert!(result.is_ok());
}
#[test]
fn test_xsd_1_0_rejects_default_attributes_on_schema() {
let ctx = ValidationContext::new(XsdVersion::V1_0, true);
let result = validate_xsd_version_attribute("defaultAttributes", "schema", &ctx);
assert!(result.is_err());
let ctx11 = ValidationContext::new(XsdVersion::V1_1, true);
let result11 = validate_xsd_version_attribute("defaultAttributes", "schema", &ctx11);
assert!(result11.is_ok());
}
#[test]
fn test_xpath_default_ns_on_selector_allowed_in_1_1() {
let ctx = ValidationContext::new(XsdVersion::V1_1, false);
let result = validate_xsd_version_attribute("xpathDefaultNamespace", "selector", &ctx);
assert!(result.is_ok());
}
#[test]
fn test_xpath_default_ns_on_field_allowed_in_1_1() {
let ctx = ValidationContext::new(XsdVersion::V1_1, false);
let result = validate_xsd_version_attribute("xpathDefaultNamespace", "field", &ctx);
assert!(result.is_ok());
}
}