sbol 0.2.0

Rust implementation of the SBOL 3.1.0 specification.
Documentation
use crate::Object;
use crate::validation::helpers::component_contains;
use crate::validation::resolver::{ConstraintEngine, RelationOutcome};
use crate::validation::validator::Validator;
use crate::vocab::*;

impl<'a> Validator<'a> {
    pub(crate) fn validate_constraint(&mut self, object: &Object) {
        let Some(component) = self
            .ownership
            .single_parent(object.identity(), SBOL_HAS_CONSTRAINT)
            .cloned()
        else {
            return;
        };
        if let Some(subject) = object.first_resource(SBOL_SUBJECT)
            && !component_contains(&component, self.document, SBOL_HAS_FEATURE, subject)
        {
            self.error(
                "sbol3-11701",
                object,
                Some(SBOL_SUBJECT),
                "Constraint subject must be a Feature of the containing Component",
            );
        }
        if let Some(constrained_object) = object.first_resource(SBOL_OBJECT)
            && !component_contains(
                &component,
                self.document,
                SBOL_HAS_FEATURE,
                constrained_object,
            )
        {
            self.error(
                "sbol3-11702",
                object,
                Some(SBOL_OBJECT),
                "Constraint object must be a Feature of the containing Component",
            );
        }
        if object.first_resource(SBOL_SUBJECT) == object.first_resource(SBOL_OBJECT) {
            self.error(
                "sbol3-11703",
                object,
                Some(SBOL_OBJECT),
                "Constraint subject and object must not be the same Feature",
            );
        }

        let Some(restriction) = object.first_iri(SBOL_RESTRICTION) else {
            return;
        };
        if !CONSTRAINT_RESTRICTION_IRIS.contains(&restriction.as_str()) {
            self.warning(
                "sbol3-11704",
                object,
                Some(SBOL_RESTRICTION),
                format!(
                    "Constraint restriction `{restriction}` is not a recommended SBOL relation"
                ),
            );
        }

        if let (Some(subject), Some(constrained_object)) = (
            object.first_resource(SBOL_SUBJECT),
            object.first_resource(SBOL_OBJECT),
        ) {
            let (table8, table10) = {
                let engine = ConstraintEngine::new(self.document, &self.ownership);
                (
                    engine.table8_relation(restriction.as_str(), subject, constrained_object),
                    engine.table10_relation(restriction.as_str(), subject, constrained_object),
                )
            };
            if matches!(table8, RelationOutcome::Contradicted { .. }) {
                self.error(
                    "sbol3-11705",
                    object,
                    Some(SBOL_RESTRICTION),
                    format!(
                        "Constraint restriction `{restriction}` is contradicted by resolved Feature semantics"
                    ),
                );
            }
            if let RelationOutcome::Contradicted {
                subject_location: Some(subject_location),
                object_location: Some(object_location),
            } = table10
            {
                self.error(
                    "sbol3-11706",
                    object,
                    Some(SBOL_RESTRICTION),
                    format!(
                        "Constraint restriction `{restriction}` is contradicted by locations `{subject_location}` and `{object_location}` on the same Sequence"
                    ),
                );
            }
        }
    }
}