use super::{Qualifier, Reading};
use crate::reference::{LexicalSource, VisitError};
use crate::scalar::{UriValue, UriXml};
use crate::xml::{Access, AccessError, Xml};
use structured_asn1::memory::{MemoryError, Work};
use structured_asn1::value::OwnedOid;
#[derive(Debug)]
pub enum Meaning {
Uri,
Oid(OwnedOid),
}
#[derive(Debug)]
pub enum MeaningError<E, N> {
Access(AccessError<E, N>),
Value {
node: N,
fact: crate::scalar::Fact,
},
Qualifier {
node: N,
},
Oid {
node: N,
cause: structured_asn1::Error,
},
}
pub type MeaningResult<X> = Result<Meaning, MeaningError<<X as Xml>::Error, <X as Xml>::Id>>;
#[derive(Debug)]
pub enum CheckError<E, N> {
Structure(crate::properties::Error<E, N>),
Meaning(MeaningError<E, N>),
Scalar(crate::scalar::Error<E, N>),
}
impl<E, N> From<crate::properties::Error<E, N>> for CheckError<E, N> {
fn from(cause: crate::properties::Error<E, N>) -> Self {
Self::Structure(cause)
}
}
pub type CheckResult<X> = Result<Meaning, CheckError<<X as Xml>::Error, <X as Xml>::Id>>;
pub fn check<X: UriXml + crate::scalar::ScalarXml, W: Work>(
access: &Access<'_, X, W>,
reading: &Reading<X::Id>,
) -> CheckResult<X> {
let meaning = check_value(access, reading).map_err(CheckError::Meaning)?;
super::documentation_with(access, reading.documentation, |node| {
crate::scalar::check(
access,
node,
LexicalSource::SimpleContent,
crate::scalar::Datatype::AnyUri,
)
.map_err(CheckError::Scalar)
})?;
Ok(meaning)
}
pub fn check_value<X: UriXml, W: Work>(
access: &Access<'_, X, W>,
reading: &Reading<X::Id>,
) -> MeaningResult<X> {
let node = reading.identifier;
let result = access.query_fallible(node, |xml, snapshot, work| {
xml.with_uri(
snapshot,
node,
LexicalSource::SimpleContent,
work,
|value| {
use crate::scalar::Fact;
let text = match value {
UriValue::Valid(text) => text,
value => {
let fact = match value {
UriValue::Absent => Fact::Absent,
UriValue::ComplexContent => Fact::ComplexContent,
UriValue::Malformed => Fact::Malformed,
UriValue::Unsupported => Fact::Unsupported,
UriValue::Valid(_) => unreachable!("successful URI selected above"),
};
return Err(MeaningError::Value { node, fact });
}
};
match reading.qualifier {
None => Ok(Meaning::Uri),
Some(Qualifier::Uri) => {
if prefix(text, "urn:", work).map_err(|cause| resource(node, cause))? {
return Err(MeaningError::Qualifier { node });
}
Ok(Meaning::Uri)
}
Some(Qualifier::Urn) => {
if !prefix(text, "urn:oid:", work).map_err(|cause| resource(node, cause))? {
return Err(MeaningError::Qualifier { node });
}
OwnedOid::parse_in(&text[8..], work)
.map(Meaning::Oid)
.map_err(|cause| MeaningError::Oid { node, cause })
}
}
},
)
});
match result {
Ok(value) => Ok(value),
Err(AccessError::Xml(VisitError::Visitor(cause))) => Err(cause),
Err(AccessError::Xml(VisitError::Processing(cause))) => {
Err(MeaningError::Access(AccessError::Xml(cause)))
}
Err(AccessError::Changed { node }) => {
Err(MeaningError::Access(AccessError::Changed { node }))
}
Err(AccessError::Resource { node, cause }) => Err(resource(node, cause)),
}
}
fn prefix<W: Work>(text: &str, expected: &str, work: &W) -> Result<bool, MemoryError> {
if text.len() < expected.len() {
return Ok(false);
}
work.charge_work(expected.len() as u64)?;
Ok(text.as_bytes()[..expected.len()].eq_ignore_ascii_case(expected.as_bytes()))
}
fn resource<E, N>(node: N, cause: MemoryError) -> MeaningError<E, N> {
MeaningError::Access(AccessError::Resource { node, cause })
}