Module layers

Source
Expand description

Layer stacks: loading a main layer and, recursively, its imports.

A LayerStackBuilder asks a caller-supplied LayerResolver for each layer, loads every layer once, checks integrity where an import carries it, and returns the layers as a LayerStack in federation order: every layer after the layers it imports, so that a layer overrides its imports. The crate performs no network access. A filesystem resolver, FsResolver, is available behind the fs feature, and MemoryResolver serves layers held in memory.

§Layer order

A layer overrides the layers it imports, as a USD layer overrides its sublayers. The ifcx_alpha draft text states no priority, so this was asked upstream in buildingSMART/IFC5-development#144; on 2026-10-05 the maintainers agreed that “imported data should come ‘before’ the main data”. The stack lists the layers in that order:

  • every layer comes after its own imports, recursively;
  • sibling imports keep the order they are written in, so a later import overrides an earlier one;
  • each layer appears once, at the place where the depth-first walk from the main layer first reaches it; a later import of a layer that is already placed does not move it;
  • the main layer comes last and overrides everything.

For main importing a and b, and a importing c:

c, a, b, main

and a chain main → a → b gives b, a, main. When an earlier sibling also imports a later one, the later one is placed before that sibling, which must override what it imports: main → [a, b] with a → [b] gives b, a, main.

LayerStack::federate concatenates schemas and data in this order and takes the main layer’s header, and flatten lets later opinions win. ADR 0002’s “later opinions override earlier ones” therefore means: the importing layer wins over its imports.

Upstream’s IfcxLayerStackBuilder at 1a63082 still uses the opposite order (the main layer first, each layer before its imports, so an import overrides its importer); a pull request bringing it to the agreed order is pending. Until it lands, stacks whose imports carry data compose differently from upstream’s main branch. Upstream’s examples import only schemas, so their composed trees do not depend on the order.

§Cycles

Upstream silently skips an import of a layer that is already placed, so it accepts import cycles. This crate rejects them with LayerError::Cycle by default. With LayerStackBuilder::allow_cycles the import that closes a cycle is skipped instead: a layer still being loaded is already placed, so its importer comes before it and it overrides that importer. For main → a → main the order is a, main; the main layer stays last.

use openbim_ifcx::layers::{LayerStackBuilder, MemoryResolver};

let layer = |id: &str, imports: &str, value: &str| format!(r#"{{
    "header": {{"id": "{id}", "ifcxVersion": "ifcx_alpha", "dataVersion": "1.0.0",
               "author": "someone", "timestamp": "2026-10-03"}},
    "imports": [{imports}],
    "schemas": {{}},
    "data": [{{"path": "p1", "attributes": {{"demo::value": "{value}"}}}}]
}}"#);

let mut resolver = MemoryResolver::new();
resolver.insert("main", layer("main", r#"{"uri": "base"}"#, "main"));
resolver.insert("base", layer("base", "", "base"));

let stack = LayerStackBuilder::new(resolver).build("main")?;
assert_eq!(stack.keys().collect::<Vec<_>>(), ["base", "main"]);
assert_eq!(stack.main().key(), "main");

// Data stays in stack order; the main layer's opinion comes last and wins.
let federated = stack.federate();
assert_eq!(federated.header.id, "main");
assert_eq!(federated.data.len(), 2);
assert_eq!(federated.data[1].attributes.as_ref().unwrap()["demo::value"], "main");

Structs§

FsResolver
Loads layers from the local filesystem. Requires the fs feature.
Layer
One loaded layer of a LayerStack.
LayerStack
A main layer and everything it imports, each once, in federation order: every layer after its imports, the main layer last.
LayerStackBuilder
Builds a LayerStack from a main layer through a LayerResolver.
MemoryResolver
Serves layers held in memory, keyed by the exact import uri, like upstream’s InMemoryLayerProvider.

Enums§

FsError
Why FsResolver could not load a layer.
IntegrityError
Why an integrity value rejected a layer.
LayerError
Why a layer stack could not be built. E is the resolver’s error type.

Traits§

LayerResolver
Finds and loads layers for a LayerStackBuilder.

Functions§

check_integrity
Checks bytes against an import’s integrity value.
federate
Merges files in the order given, weakest first, as upstream’s Federate does: no imports, schemas from every file (a repeated id keeps its first position and takes the last value), and every data node in order. The header is the last file’s, the strongest one, as the main layer is last in a LayerStack. Top-level fields this crate does not model are not carried over.
federate_owned
federate for files that are not needed afterwards: their schemas and data move into the result instead of being copied. The result is the same.