use super::*;
impl State<'_> {
pub(super) fn finish(mut self) -> Result<Model, XmlError> {
if !self.seen_root || !self.stack.is_empty() {
return Err(XmlError::Root { found: None });
}
let mut resolved = Vec::with_capacity(self.forward_names.len());
for id in &self.forward_names {
let number = self
.ids
.get(id)
.copied()
.ok_or_else(|| XmlError::UnresolvedReference { id: id.clone() })?;
resolved.push(number);
}
let resolve = |number: u64| -> u64 {
if number >= PLACEHOLDER {
resolved[usize::try_from(number - PLACEHOLDER).unwrap_or(usize::MAX)]
} else {
number
}
};
if !resolved.is_empty() {
for pending in &mut self.entities {
for value in pending.slots.iter_mut().flatten() {
rewrite(value, &resolve);
}
}
}
for claim in &mut self.claims {
claim.target = resolve(claim.target);
}
for backfill in &mut self.backfills {
backfill.relationship = resolve(backfill.relationship);
}
let backfills = std::mem::take(&mut self.backfills);
for backfill in &backfills {
self.apply_backfill(backfill).map_err(|error| {
error.at(format!(
"{}/{}",
self.entity_path(backfill.parent),
backfill.inverse.name
))
})?;
}
let claims = std::mem::take(&mut self.claims);
for claim in &claims {
let target = &self.entities[index(claim.target)].layout;
if !self.schema.is_a(&target.name, &claim.claimed) {
return Err(XmlError::TypeMismatch {
declared: claim.claimed.to_string(),
found: format!("a reference to an entity `{}`", target.name),
}
.at(self.entity_path(claim.referrer)));
}
}
for (position, pending) in self.entities.iter().enumerate() {
self.check_references(pending)
.map_err(|error| error.at(self.entity_path(position as u64 + 1)))?;
}
let mut entities = std::mem::take(&mut self.entities);
let mut model = self.model;
for (position, pending) in entities.drain(..).enumerate() {
let values = pending
.slots
.into_iter()
.zip(&pending.layout.slots)
.map(|(value, slot)| match value {
_ if slot.derived => Value::Derived,
Some(value) => value,
None => Value::Null,
})
.collect();
model.insert(
EntityId(position as u64 + 1),
Entity::new(pending.layout.upper.clone(), values),
);
}
Ok(model)
}
pub(super) fn apply_backfill(&mut self, backfill: &Backfill) -> Result<(), XmlError> {
let relationship = &self.entities[index(backfill.relationship)];
let layout = relationship.layout.clone();
let inverse = &backfill.inverse;
let conflict = |detail: &str| XmlError::InverseConflict {
inverse: inverse.name.to_string(),
relationship: layout.name.to_string(),
attribute: inverse.for_attribute.to_string(),
detail: detail.into(),
};
if !self.schema.is_a(&layout.name, &inverse.entity) {
return Err(XmlError::TypeMismatch {
declared: inverse.entity.to_string(),
found: format!("an entity `{}` in inverse `{}`", layout.name, inverse.name),
});
}
let slot = layout
.slot(&inverse.for_attribute)
.ok_or_else(|| conflict("is not an explicit attribute of it"))?;
let shape = &layout.slots[slot].shape;
let parent = Value::Ref(EntityId(backfill.parent));
let current = &mut self.entities[index(backfill.relationship)].slots[slot];
if shape.levels.is_empty() {
match current {
None | Some(Value::Null) => *current = Some(parent),
Some(existing) if *existing == parent => {}
Some(_) => return Err(conflict("already names a different entity")),
}
return Ok(());
}
if shape.levels.len() != 1 {
return Err(conflict("is a nested aggregate"));
}
match current {
None | Some(Value::Null) => *current = Some(Value::List(vec![parent])),
Some(Value::List(items)) => {
if items.contains(&parent) {
return Ok(());
}
match shape.levels[0].kind {
AggregateKind::Set | AggregateKind::Bag => items.push(parent),
_ => return Err(conflict(
"is ordered, so the position of an entity implied by an inverse is unknown",
)),
}
}
Some(_) => return Err(conflict("holds a value that is not a list")),
}
Ok(())
}
pub(super) fn check_references(&self, pending: &Pending) -> Result<(), XmlError> {
let mut references = Vec::new();
for (value, slot) in pending.slots.iter().zip(&pending.layout.slots) {
let Some(value) = value else { continue };
references.clear();
typing::references(self.schema, &slot.shape, value, &mut references).map_err(
|error| error.at(format!("{}/{}", self.entity_path_of(pending), slot.name)),
)?;
for (EntityId(number), declared) in &references {
let target = &self.entities[index(*number)].layout;
if !self.schema.accepts_type(declared, &target.name) {
return Err(XmlError::TypeMismatch {
declared: declared.to_string(),
found: format!("a reference to an entity `{}`", target.name),
}
.at(format!(
"{}/{}",
self.entity_path_of(pending),
slot.name
)));
}
}
}
Ok(())
}
}
impl ValueFrame {
pub(super) fn new(
target: Target,
shape: Shape,
attribute: Arc<str>,
text_content: bool,
single: bool,
) -> Self {
Self {
target,
shape,
text_content,
single,
wrap: None,
items: Vec::new(),
nested_items: None,
text: String::new(),
array_size: None,
attribute,
}
}
pub(super) fn finish(self) -> Result<(Target, Value), XmlError> {
let value = if self.text_content {
if self.shape.levels.is_empty() {
if self.shape.leaf == Leaf::Binary {
let token = self.text.trim();
Value::Binary(typing::hex_binary(token).ok_or_else(|| {
XmlError::InvalidScalar {
kind: "BINARY".into(),
value: self.text.clone(),
}
})?)
} else {
typing::scalar(&self.shape.leaf, &self.text, Lexical::Xsd)?
}
} else {
typing::list_text(&self.shape, &self.text, Lexical::Xsd)?
}
} else if self.single {
let mut items = self.items;
if items.len() != 1 {
return Err(XmlError::Unsupported {
construct: format!("an empty SELECT value `{}`", self.attribute),
});
}
items.remove(0)
} else if self.nested_items == Some(true) {
Value::List(self.items)
} else {
typing::nest(&self.shape, self.items, self.array_size.as_deref())?
};
let value = match self.wrap {
Some(type_name) => Value::Typed {
type_name,
value: Box::new(value),
},
None => value,
};
Ok((self.target, value))
}
}