use super::{ReferenceField, ReferenceProperty};
use crate::{
binary::{BinaryXml, Location},
certificate::{self, Agreement},
properties::{DateTimeFact, DateTimeXml},
reference::LexicalSource,
scalar::{self, Datatype, ScalarXml},
xml::{Access, AccessError, Name, Xml},
};
use core::convert::Infallible;
use structured_asn1::memory::{MemoryError, Work};
#[derive(Debug)]
pub enum ReferenceFieldError<E, N, D, G> {
Foreign {
node: N,
},
Access(AccessError<E, N>),
Scalar(scalar::Error<E, N>),
Time {
node: N,
fact: DateTimeFact,
},
Binary {
location: Location<N, D, G>,
cause: AccessError<E, N>,
},
Method {
node: N,
cause: Agreement,
},
Resource {
node: N,
cause: MemoryError,
},
}
pub type ReferenceFieldResult<X> = Result<
(),
ReferenceFieldError<
<X as Xml>::Error,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>,
>;
pub fn check_reference_fields<X: ScalarXml + DateTimeXml + BinaryXml, W: Work>(
references: &ReferenceProperty<X::Id, X::Document, X::Generation>,
access: &Access<'_, X, W>,
) -> ReferenceFieldResult<X> {
let node = references.node();
if references.snapshot() != access.snapshot() {
return Err(ReferenceFieldError::Foreign { node });
}
access
.query(node, |_, _, _| Ok(()))
.map_err(ReferenceFieldError::Access)?;
let mut ordinal = 0;
while ordinal < references.entries().len() {
access
.execution()
.charge_work(1)
.map_err(|cause| ReferenceFieldError::Resource { node, cause })?;
let entry = &references.entries()[ordinal];
if let Some((method, _)) = entry.digest()
&& let Some(cause) = certificate::preflight_method(access, method)
.map_err(ReferenceFieldError::Access)?
{
return Err(ReferenceFieldError::Method {
node: method,
cause,
});
}
let fields = entry.fields();
for field in fields {
access
.execution()
.charge_work(1)
.map_err(|cause| ReferenceFieldError::Resource { node, cause })?;
if let Some(field) = field {
check_field(access, field)?;
}
}
ordinal += 1;
}
access
.query(node, |_, _, _| Ok(()))
.map_err(ReferenceFieldError::Access)
}
fn check_field<X: ScalarXml + DateTimeXml + BinaryXml, W: Work>(
access: &Access<'_, X, W>,
field: ReferenceField<X::Id>,
) -> ReferenceFieldResult<X> {
match field {
ReferenceField::Uri(node) => {
let uri = Name {
namespace: "",
local: "URI",
};
if access
.attribute(node, uri, |value| value.is_some())
.map_err(ReferenceFieldError::Access)?
{
scalar::check(
access,
node,
LexicalSource::Attribute(uri),
Datatype::AnyUri,
)
.map_err(ReferenceFieldError::Scalar)?;
}
}
ReferenceField::Integer(node) => {
scalar::check(
access,
node,
LexicalSource::SimpleContent,
Datatype::Integer,
)
.map_err(ReferenceFieldError::Scalar)?;
}
ReferenceField::DateTime(node) => {
let fact = access
.query(node, |xml, snapshot, work| {
xml.date_time(snapshot, node, work)
})
.map_err(ReferenceFieldError::Access)?;
match fact {
DateTimeFact::Valid { .. } => (),
fact => return Err(ReferenceFieldError::Time { node, fact }),
}
}
ReferenceField::Binary(node) => {
let location = Location {
snapshot: access.snapshot(),
node,
};
let checked = access
.query(node, |xml, snapshot, work| {
xml.with_octets(snapshot, node, work, |_| Ok::<_, Infallible>(()))
})
.map_err(|cause| ReferenceFieldError::Binary { location, cause })?;
checked.unwrap_or_else(|never| match never {});
}
}
Ok(())
}