Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
## Build, Test, and Development Commands
- Build workspace: `cargo build --workspace`
- Run all tests: `cargo test --workspace`
- Format (check): `cargo fmt --all -- --check` | Fix: `cargo fmt --all`
- Format (check): `cargo +stable fmt --all -- --check` | Fix: `cargo +stable fmt --all`
- Lint: `cargo clippy -p schemaview -p linkml_runtime -p linkml_tools -p linkml_runtime_python -p linkml_wasm --all-targets --all-features -- -D warnings --no-deps` (excludes autogenerated `linkml_meta`)
- Run a CLI (example):
- Validate: `cargo run -p linkml_tools --bin linkml-validate -- src/runtime/tests/data/schema.yaml Person src/runtime/tests/data/person_valid.yaml`
Expand Down
239 changes: 123 additions & 116 deletions src/metamodel/src/lib.rs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/schemaview/src/schemaview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,12 @@ impl SchemaView {
self.data.schema_definitions.iter()
}

/// Returns a converter built from every schema loaded into this view.
///
/// Note that prefix collisions across schemas will resolve to whichever
/// expansion appears last; avoid this helper if you expect conflicting
/// CURIE mappings and instead use `converter_for_schema` with a specific
/// schema URI.
pub fn converter(&self) -> Converter {
converter_from_schemas(self.data.schema_definitions.values())
}
Expand Down
18 changes: 14 additions & 4 deletions src/schemaview/src/slotview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,32 @@ impl RangeInfo {
e: &SlotExpressionOrSubtype,
slotview: &SlotView,
) -> Option<ClassView> {
let conv = slotview.sv.converter_for_schema(&slotview.schema_uri)?;
e.range().and_then(|r| {
if let Some(conv) = slotview.sv.converter_for_schema(&slotview.schema_uri) {
if let Ok(Some(cv)) = slotview.sv.get_class(&Identifier::new(r), conv) {
return Some(cv);
}
}
let conv = slotview.sv.converter();
slotview
.sv
.get_class(&Identifier::new(r), conv)
.get_class(&Identifier::new(r), &conv)
.ok()
.flatten()
})
}

fn determine_range_enum(e: &SlotExpressionOrSubtype, slotview: &SlotView) -> Option<EnumView> {
let conv = slotview.sv.converter_for_schema(&slotview.schema_uri)?;
e.range().and_then(|r| {
if let Some(conv) = slotview.sv.converter_for_schema(&slotview.schema_uri) {
if let Ok(Some(ev)) = slotview.sv.get_enum(&Identifier::new(r), conv) {
return Some(ev);
}
}
let conv = slotview.sv.converter();
slotview
.sv
.get_enum(&Identifier::new(r), conv)
.get_enum(&Identifier::new(r), &conv)
.ok()
.flatten()
})
Expand Down
15 changes: 15 additions & 0 deletions src/schemaview/tests/data/slot_usage_base.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
id: http://example.org/base
name: base
prefixes:
base: http://example.org/base/
default_prefix: base
default_range: string

classes:
BaseThing:
slots:
- shared_slot

slots:
shared_slot:
range: string
26 changes: 26 additions & 0 deletions src/schemaview/tests/data/slot_usage_specialized.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
id: http://example.org/specialized
name: specialized
prefixes:
specialized: http://example.org/specialized/
base: http://example.org/base/
default_prefix: specialized
default_range: string
imports:
- http://example.org/base

classes:
TargetClass:
slots:
- target_slot

SpecializedThing:
is_a: base:BaseThing
slots:
- shared_slot
slot_usage:
shared_slot:
range: specialized:TargetClass

slots:
target_slot:
range: string
44 changes: 44 additions & 0 deletions src/schemaview/tests/slot_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,47 @@ fn slot_lookup_and_class_slots() {
assert_eq!(map.get("symbol"), Some(&1usize));
assert_eq!(map.get("exact mappings"), Some(&2usize));
}

#[test]
fn slot_usage_overrides_range_across_schemas() {
let base_schema = from_yaml(Path::new(&data_path("slot_usage_base.yaml"))).unwrap();
let specialized_schema =
from_yaml(Path::new(&data_path("slot_usage_specialized.yaml"))).unwrap();

let mut sv = SchemaView::new();
sv.add_schema(base_schema.clone()).unwrap();
sv.add_schema(specialized_schema.clone()).unwrap();

let conv = converter_from_schemas([&base_schema, &specialized_schema]);

let base_class = sv
.get_class(&Identifier::new("base:BaseThing"), &conv)
.unwrap()
.unwrap();
let base_slot = base_class
.slots()
.iter()
.find(|s| s.name == "shared_slot")
.expect("shared_slot not found");

assert_eq!(base_slot.definition().range.as_deref(), Some("string"));

let class = sv
.get_class(&Identifier::new("specialized:SpecializedThing"), &conv)
.unwrap()
.unwrap();
let slot = class
.slots()
.iter()
.find(|s| s.name == "shared_slot")
.expect("shared_slot not found");

assert_eq!(
slot.definition().range.as_deref(),
Some("specialized:TargetClass")
);
assert_eq!(
slot.get_range_class().map(|cv| cv.name().to_string()),
Some("TargetClass".to_string())
);
}
Loading