ifc-classification 0.2.2

Classification systems, document references, libraries, external references.
Documentation
//! Typed failures while interpreting or authoring IFC external references.

use ifc_model::EntityId;
use ifc_schema::SchemaVersion;
use thiserror::Error;

/// Failure decoding, cross-checking, or authoring classification/document/library records.
#[derive(Debug, Clone, PartialEq, Error)]
#[non_exhaustive]
pub enum ClassificationError {
    /// Entity did not have the expected IFC type.
    #[error("expected {expected}, found {actual}")]
    WrongEntityType {
        /// IFC type name expected at this position.
        expected: &'static str,
        /// IFC type name actually found.
        actual: String,
    },
    /// A required attribute was unset (`$` or `*`) or absent.
    #[error("{entity} {id} is missing required attribute {attribute}")]
    MissingAttribute {
        /// IFC entity type of the offending instance.
        entity: &'static str,
        /// Id of the offending instance.
        id: EntityId,
        /// Name of the missing attribute.
        attribute: &'static str,
    },
    /// An attribute was present but failed to decode as its expected shape.
    #[error("{entity} {id} has invalid {attribute}: {value}")]
    InvalidValue {
        /// IFC entity type of the offending instance.
        entity: &'static str,
        /// Id of the offending instance.
        id: EntityId,
        /// Name of the invalid attribute.
        attribute: &'static str,
        /// Debug rendering of the offending value.
        value: String,
    },
    /// A referenced entity id does not exist in the model.
    #[error("entity {id} does not exist")]
    UnknownEntity {
        /// Id that could not be resolved.
        id: EntityId,
    },
    /// An attribute reference points at an id that is not present in the model.
    #[error("{entity} {id}.{attribute} reference {target} does not resolve")]
    DanglingReference {
        /// IFC entity type holding the dangling reference.
        entity: &'static str,
        /// Id of the entity holding the dangling reference.
        id: EntityId,
        /// Name of the attribute holding the dangling reference.
        attribute: &'static str,
        /// Id that could not be resolved.
        target: EntityId,
    },
    /// An attribute reference resolves but the target entity has the wrong type for its select.
    #[error("{entity} {id}.{attribute} reference {target} has type {actual}, expected {expected}")]
    ReferenceType {
        /// IFC entity type holding the mistyped reference.
        entity: &'static str,
        /// Id of the entity holding the mistyped reference.
        id: EntityId,
        /// Name of the attribute holding the mistyped reference.
        attribute: &'static str,
        /// Id of the mistyped target.
        target: EntityId,
        /// Select or type name the target was expected to satisfy.
        expected: &'static str,
        /// IFC type name the target actually has.
        actual: String,
    },
    /// Following `ReferencedSource` links revisited an already-seen entity.
    #[error("classification hierarchy contains a cycle: {path:?}")]
    Cycle {
        /// Entity ids on the cycle, in traversal order.
        path: Vec<EntityId>,
    },
    /// Hierarchy traversal exceeded the caller-supplied node or depth budget.
    #[error("classification hierarchy exceeded max_depth={max_depth} or max_nodes={max_nodes}")]
    BudgetExceeded {
        /// Maximum edge depth allowed.
        max_depth: usize,
        /// Maximum number of nodes allowed.
        max_nodes: usize,
    },
    /// An occurrence object is related to more than one `IfcRelDefinesByType` type.
    #[error("object {object} has {count} assigned IFC types")]
    AmbiguousType {
        /// The occurrence object with the ambiguous type assignment.
        object: EntityId,
        /// Number of types the object was found related to.
        count: usize,
    },
    /// A draft value failed schema validation before staging.
    #[error("cannot author {entity}.{attribute}: {value}")]
    AuthoringInvalid {
        /// IFC entity type being authored.
        entity: &'static str,
        /// Name of the offending attribute.
        attribute: &'static str,
        /// Description of why the value is invalid.
        value: String,
    },
    /// A draft reference resolves to a staged or committed entity of the wrong type.
    #[error("authoring reference {target} has type {actual}, expected {expected}")]
    AuthoringReferenceType {
        /// Id of the mistyped reference target.
        target: EntityId,
        /// Select or type name the target was expected to satisfy.
        expected: &'static str,
        /// IFC type name the target actually has.
        actual: String,
    },
    /// The header declares several schemas, so no single release can be
    /// bound to read or write the model against.
    #[error("the header declares {schemas} schemas; classification reads bind to exactly one")]
    MultipleSchemas {
        /// Number of `FILE_SCHEMA` declarations.
        schemas: usize,
    },
    /// The release the model is read against does not define this attribute
    /// or record, for example `Description` on an IFC2X3 `IfcClassification`
    /// or any `IfcExternalReferenceRelationship` in an IFC2X3 model. This is
    /// never reported as an unset (`None`) value.
    #[error("{entity} {id}.{attribute} is not defined by {schema:?}")]
    NotInSchema {
        /// IFC entity type of the instance.
        entity: &'static str,
        /// Id of the instance.
        id: EntityId,
        /// Attribute name, as this crate's accessor names it.
        attribute: &'static str,
        /// The release the model is read against.
        schema: SchemaVersion,
    },
    /// A text accessor met an attribute the bound release types as an entity
    /// record, such as an IFC2X3 `IfcCalendarDate` edition date. The value is
    /// valid; read it structurally through `target`.
    #[error("{entity} {id}.{attribute} is the record {target}, not text")]
    StructuredValue {
        /// IFC entity type of the instance.
        entity: &'static str,
        /// Id of the instance.
        id: EntityId,
        /// Attribute name.
        attribute: &'static str,
        /// The entity record holding the value.
        target: EntityId,
    },
    /// The header declares one schema this crate has no bundled table for,
    /// so no slot position can be trusted. Never read as IFC4.
    #[error("the header declares {schema}, which has no bundled schema table")]
    UnsupportedSchema {
        /// The `FILE_SCHEMA` token as written.
        schema: String,
    },
    /// An authoring call would write a record type the model's release
    /// cannot instantiate, such as an `IfcExternalReferenceRelationship`
    /// into an IFC2X3 model.
    #[error("cannot author {entity}: not an instantiable entity of {schema:?}")]
    EntityNotInSchema {
        /// The IFC entity type name.
        entity: &'static str,
        /// The release the model is bound to.
        schema: SchemaVersion,
    },
    /// An authoring call supplied a value for an attribute the model's
    /// release does not declare, such as a `Description` for an IFC2X3
    /// `IfcClassification`. The value is refused, never silently dropped.
    #[error("cannot author {entity}.{attribute}: not defined by {schema:?}")]
    AuthoringNotInSchema {
        /// The IFC entity type name being authored.
        entity: &'static str,
        /// The attribute, as this crate names it (IFC4).
        attribute: &'static str,
        /// The release the model is bound to.
        schema: SchemaVersion,
    },
    /// The model's release requires an attribute the authoring call leaves
    /// unset, such as the IFC2X3 `IfcRoot.OwnerHistory` or the IFC2X3
    /// `IfcClassification.Source`.
    #[error("cannot author {entity}: {schema:?} requires {attribute}")]
    AuthoringRequired {
        /// The IFC entity type name being authored.
        entity: &'static str,
        /// The required attribute, as the release names it.
        attribute: &'static str,
        /// The release the model is bound to.
        schema: SchemaVersion,
    },
    /// An authoring call supplied text for an attribute the model's release
    /// types as an entity record, such as an IFC2X3 `IfcCalendarDate`
    /// `EditionDate`. The text is refused rather than written into a slot
    /// that cannot hold it.
    #[error("cannot author {entity}.{attribute} as text: {schema:?} declares {declared}")]
    AuthoringValueType {
        /// The IFC entity type name being authored.
        entity: &'static str,
        /// The attribute, as this crate names it (IFC4).
        attribute: &'static str,
        /// The type the release declares for the attribute.
        declared: &'static str,
        /// The release the model is bound to.
        schema: SchemaVersion,
    },
}

/// Result of a classification/document/library query or authoring operation.
pub type ClassificationResult<T> = Result<T, ClassificationError>;