ifc-resource 0.3.0

Construction resources: labour, equipment, material, crew, subcontract.
Documentation
//! `IfcActorRole`/`IfcPerson`/`IfcOrganization` projection checks.

mod support;

use ifc_resource::{ResourceError, ResourceView};
use ifc_schema::ifc4;

use support::{enumeration, model, named, refs, refs_text, text, GUID_A};

#[test]
fn person_projects_names_and_roles_and_enforces_identifiable_and_name_set_rules() {
    let schema = ifc4();
    let mut model = model("IFC4");
    let role = model.push(named(
        schema,
        "IfcActorRole",
        &[
            ("Role", enumeration("ARCHITECT")),
            ("Description", text("Lead architect")),
        ],
    ));
    let person = model.push(named(
        schema,
        "IfcPerson",
        &[
            ("FamilyName", text("Doe")),
            ("GivenName", text("Jane")),
            ("MiddleNames", refs_text(&["Q"])),
            ("Roles", refs(&[role])),
        ],
    ));

    let view = ResourceView::for_model(&model).unwrap();
    let projected = view.person(person).unwrap();
    assert_eq!(projected.family_name().unwrap(), Some("Doe"));
    assert_eq!(projected.given_name().unwrap(), Some("Jane"));
    assert_eq!(projected.middle_names().unwrap(), vec!["Q"]);
    let roles = projected.roles().unwrap();
    assert_eq!(roles.len(), 1);
    assert_eq!(roles[0].role().unwrap(), "ARCHITECT");
    assert_eq!(roles[0].description().unwrap(), Some("Lead architect"));
}

#[test]
fn person_rejects_missing_identity_and_middle_names_without_family_or_given() {
    let schema = ifc4();

    let mut no_identity = model("IFC4");
    let person = no_identity.push(named(schema, "IfcPerson", &[]));
    assert!(matches!(
        ResourceView::for_model(&no_identity)
            .unwrap()
            .person(person),
        Err(ResourceError::SemanticViolation { .. })
    ));

    let mut bad_middle = model("IFC4");
    let person = bad_middle.push(named(
        schema,
        "IfcPerson",
        &[
            ("Identification", text("P-1")),
            ("MiddleNames", refs_text(&["Q"])),
        ],
    ));
    assert!(matches!(
        ResourceView::for_model(&bad_middle).unwrap().person(person),
        Err(ResourceError::SemanticViolation { .. })
    ));
}

#[test]
fn actor_role_enforces_wr1_userdefined_requires_user_defined_role() {
    let schema = ifc4();

    let mut missing = model("IFC4");
    let role = missing.push(named(
        schema,
        "IfcActorRole",
        &[("Role", enumeration("USERDEFINED"))],
    ));
    assert!(matches!(
        ResourceView::for_model(&missing).unwrap().actor_role(role),
        Err(ResourceError::SemanticViolation { .. })
    ));

    let mut present = model("IFC4");
    let role = present.push(named(
        schema,
        "IfcActorRole",
        &[
            ("Role", enumeration("USERDEFINED")),
            ("UserDefinedRole", text("Owner's rep")),
        ],
    ));
    let projected = ResourceView::for_model(&present)
        .unwrap()
        .actor_role(role)
        .unwrap();
    assert_eq!(projected.role().unwrap(), "USERDEFINED");
    assert_eq!(projected.user_defined_role().unwrap(), Some("Owner's rep"));

    let mut unknown = model("IFC4");
    let role = unknown.push(named(
        schema,
        "IfcActorRole",
        &[("Role", enumeration("MAGIC"))],
    ));
    assert!(matches!(
        ResourceView::for_model(&unknown).unwrap().actor_role(role),
        Err(ResourceError::InvalidEnumeration { .. })
    ));
}

#[test]
fn organization_projects_name_roles_and_relationship_membership() {
    let schema = ifc4();
    let mut model = model("IFC4");
    let role = model.push(named(
        schema,
        "IfcActorRole",
        &[("Role", enumeration("OWNER"))],
    ));
    let parent = model.push(named(
        schema,
        "IfcOrganization",
        &[("Name", text("Acme Holding"))],
    ));
    let child = model.push(named(
        schema,
        "IfcOrganization",
        &[
            ("Name", text("Acme Construction")),
            ("Roles", refs(&[role])),
        ],
    ));
    let relation = model.push(named(
        schema,
        "IfcOrganizationRelationship",
        &[
            ("Name", text("Subsidiary")),
            ("RelatingOrganization", ifc_model::Value::Ref(parent)),
            ("RelatedOrganizations", refs(&[child])),
        ],
    ));

    let view = ResourceView::for_model(&model).unwrap();
    let org = view.organization(child).unwrap();
    assert_eq!(org.name().unwrap(), "Acme Construction");
    let roles = org.roles().unwrap();
    assert_eq!(roles.len(), 1);
    assert_eq!(roles[0].role().unwrap(), "OWNER");

    let relationship = view.organization_relationship(relation).unwrap();
    assert_eq!(relationship.name().unwrap(), Some("Subsidiary"));
    assert_eq!(relationship.relating_organization().unwrap(), parent);
    assert_eq!(relationship.related_organizations().unwrap(), vec![child]);
}

#[test]
fn person_and_organization_resolves_person_organization_and_roles() {
    let schema = ifc4();
    let mut model = model("IFC4");
    let person = model.push(named(schema, "IfcPerson", &[("FamilyName", text("Doe"))]));
    let organization = model.push(named(schema, "IfcOrganization", &[("Name", text("Acme"))]));
    let role = model.push(named(
        schema,
        "IfcActorRole",
        &[("Role", enumeration("ENGINEER"))],
    ));
    let combo = model.push(named(
        schema,
        "IfcPersonAndOrganization",
        &[
            ("ThePerson", ifc_model::Value::Ref(person)),
            ("TheOrganization", ifc_model::Value::Ref(organization)),
            ("Roles", refs(&[role])),
        ],
    ));

    let view = ResourceView::for_model(&model).unwrap();
    let combo = view.person_and_organization(combo).unwrap();
    assert_eq!(combo.person().unwrap(), person);
    assert_eq!(combo.organization().unwrap(), organization);
    assert_eq!(combo.role_ids().unwrap(), vec![role]);
}

#[test]
fn person_and_organization_rejects_wrong_reference_types() {
    let schema = ifc4();
    let mut model = model("IFC4");
    let wall = model.push(named(schema, "IfcWall", &[("GlobalId", text(GUID_A))]));
    let organization = model.push(named(schema, "IfcOrganization", &[("Name", text("Acme"))]));
    let combo = model.push(named(
        schema,
        "IfcPersonAndOrganization",
        &[
            ("ThePerson", ifc_model::Value::Ref(wall)),
            ("TheOrganization", ifc_model::Value::Ref(organization)),
        ],
    ));

    let view = ResourceView::for_model(&model).unwrap();
    assert!(matches!(
        view.person_and_organization(combo).unwrap().person(),
        Err(ResourceError::WrongReferenceType { .. })
    ));
}