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, mainand 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
fsfeature. - Layer
- One loaded layer of a
LayerStack. - Layer
Stack - A main layer and everything it imports, each once, in federation order: every layer after its imports, the main layer last.
- Layer
Stack Builder - Builds a
LayerStackfrom a main layer through aLayerResolver. - Memory
Resolver - Serves layers held in memory, keyed by the exact import
uri, like upstream’sInMemoryLayerProvider.
Enums§
- FsError
- Why
FsResolvercould not load a layer. - Integrity
Error - Why an
integrityvalue rejected a layer. - Layer
Error - Why a layer stack could not be built.
Eis the resolver’s error type.
Traits§
- Layer
Resolver - Finds and loads layers for a
LayerStackBuilder.
Functions§
- check_
integrity - Checks
bytesagainst an import’sintegrityvalue. - federate
- Merges
filesin the order given, weakest first, as upstream’sFederatedoes: 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 aLayerStack. Top-level fields this crate does not model are not carried over. - federate_
owned federatefor files that are not needed afterwards: their schemas and data move into the result instead of being copied. The result is the same.