diff --git a/Cargo.lock b/Cargo.lock index dcee6565b50..655fac76238 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2578,6 +2578,7 @@ dependencies = [ "thiserror", "time", "tokio", + "trait-variant", "type-system", "url", "utoipa", @@ -7378,6 +7379,17 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trait-variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "try-lock" version = "0.2.5" diff --git a/Cargo.toml b/Cargo.toml index 3e184065a45..11c6cd9efb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -209,6 +209,7 @@ tokio-stream = { version = "=0.1.16", default-features = false } tokio-test = { version = "=0.4.4", default-features = false } tower = { version = "=0.5.1", default-features = false } tower-test = { version = "=0.4.0", default-features = false } +trait-variant = { version = "=0.1.2", default-features = false } tracing = { version = "=0.1.40", default-features = false } tracing-error = { version = "=0.2.0", default-features = false } tracing-flame = { version = "=0.2.0", default-features = false } diff --git a/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/mod.rs b/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/mod.rs index f2cb745a594..c6f1c05b9e0 100644 --- a/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/mod.rs +++ b/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/mod.rs @@ -31,7 +31,7 @@ use graph_types::{ PropertyWithMetadataValue, visitor::EntityVisitor as _, }, }, - ontology::{DataTypeProvider, OntologyTypeProvider}, + ontology::{DataTypeLookup, OntologyTypeProvider}, owned_by_id::OwnedById, }; use hash_graph_store::{ @@ -60,7 +60,8 @@ use temporal_versioning::{ use tokio_postgres::{GenericClient as _, Row, error::SqlState}; use type_system::{ schema::{ - ClosedEntityType, ClosedMultiEntityType, EntityTypeUuid, InheritanceDepth, OntologyTypeUuid, + ClosedEntityType, ClosedMultiEntityType, DataTypeReference, EntityTypeUuid, + InheritanceDepth, OntologyTypeUuid, }, url::VersionedUrl, }; @@ -333,7 +334,7 @@ where Ok(()) } - async fn convert_entity_properties( + async fn convert_entity_properties( &self, provider: &P, entity: &mut PropertyWithMetadata, @@ -353,7 +354,10 @@ where }; let Ok(conversions) = provider - .find_conversion(source_data_type_id, target_data_type_id) + .find_conversion( + <&DataTypeReference>::from(&*source_data_type_id), + <&DataTypeReference>::from(target_data_type_id), + ) .await else { // If no conversion is found, we can ignore the property. @@ -375,7 +379,7 @@ where metadata.data_type_id = Some(target_data_type_id.clone()); } - async fn convert_entity( + async fn convert_entity( &self, provider: &P, entity: &mut Entity, diff --git a/apps/hash-graph/libs/graph/src/store/validation.rs b/apps/hash-graph/libs/graph/src/store/validation.rs index 00beb2a2c21..a9fc0e0ceda 100644 --- a/apps/hash-graph/libs/graph/src/store/validation.rs +++ b/apps/hash-graph/libs/graph/src/store/validation.rs @@ -14,7 +14,7 @@ use graph_types::{ account::AccountId, knowledge::entity::{Entity, EntityId}, ontology::{ - DataTypeProvider, DataTypeWithMetadata, EntityTypeProvider, EntityTypeWithMetadata, + DataTypeLookup, DataTypeWithMetadata, EntityTypeProvider, EntityTypeWithMetadata, OntologyTypeProvider, PropertyTypeProvider, PropertyTypeWithMetadata, }, }; @@ -28,9 +28,10 @@ use hash_graph_store::{ use tokio::sync::RwLock; use tokio_postgres::GenericClient as _; use type_system::{ + Valid, schema::{ - ClosedEntityType, ConversionDefinition, ConversionExpression, DataTypeUuid, EntityTypeUuid, - PropertyType, PropertyTypeUuid, + ClosedDataType, ClosedEntityType, ConversionDefinition, ConversionExpression, + DataTypeReference, DataTypeUuid, EntityTypeUuid, PropertyType, PropertyTypeUuid, }, url::{BaseUrl, VersionedUrl}, }; @@ -123,6 +124,7 @@ where #[derive(Debug, Default)] pub struct StoreCache { data_types: CacheHashMap, + closed_data_types: CacheHashMap, property_types: CacheHashMap, entity_types: CacheHashMap, entities: CacheHashMap, @@ -161,33 +163,32 @@ where } } -impl OntologyTypeProvider for StoreProvider<'_, PostgresStore> +impl DataTypeLookup for StoreProvider<'_, PostgresStore> where C: AsClient, A: AuthorizationApi, { - type Value = Arc; + type ClosedDataType = Arc; + type DataTypeWithMetadata = Arc; + type Error = QueryError; - #[expect(refining_impl_trait)] - async fn provide_type( + async fn lookup_data_type_by_uuid( &self, - type_id: &VersionedUrl, + data_type_uuid: DataTypeUuid, ) -> Result, Report> { - let data_type_id = DataTypeUuid::from_url(type_id); - - if let Some(cached) = self.cache.data_types.get(&data_type_id).await { + if let Some(cached) = self.cache.data_types.get(&data_type_uuid).await { return cached; } - if let Err(error) = self.authorize_data_type(data_type_id).await { - self.cache.data_types.deny(data_type_id).await; + if let Err(error) = self.authorize_data_type(data_type_uuid).await { + self.cache.data_types.deny(data_type_uuid).await; return Err(error); } let schema = self .store .read_one( - &Filter::::for_versioned_url(type_id), + &Filter::for_data_type_uuid(data_type_uuid), Some( &QueryTemporalAxesUnresolved::DecisionTime { pinned: PinnedTemporalAxisUnresolved::new(None), @@ -199,25 +200,51 @@ where ) .await?; - let schema = self.cache.data_types.grant(data_type_id, schema).await; + let schema = self.cache.data_types.grant(data_type_uuid, schema).await; + + Ok(schema) + } + + async fn lookup_closed_data_type_by_uuid( + &self, + data_type_uuid: DataTypeUuid, + ) -> Result, Report> { + if let Some(cached) = self.cache.closed_data_types.get(&data_type_uuid).await { + return cached; + } + + if let Err(error) = self.authorize_data_type(data_type_uuid).await { + self.cache.closed_data_types.deny(data_type_uuid).await; + return Err(error); + } + + let schema: Valid = self + .store + .as_client() + .query_one( + "SELECT closed_schema FROM data_types WHERE ontology_id = $1", + &[&data_type_uuid], + ) + .await + .change_context(QueryError)? + .get(0); + + let schema = self + .cache + .closed_data_types + .grant(data_type_uuid, schema.into_inner()) + .await; Ok(schema) } -} -impl DataTypeProvider for StoreProvider<'_, PostgresStore> -where - C: AsClient, - A: AuthorizationApi, -{ - #[expect(refining_impl_trait)] async fn is_parent_of( &self, - child: &VersionedUrl, + child: &DataTypeReference, parent: &BaseUrl, ) -> Result> { let client = self.store.as_client().client(); - let child = DataTypeUuid::from_url(child); + let child = DataTypeUuid::from_url(&child.url); Ok(client .query_one( @@ -237,16 +264,20 @@ where .get(0)) } - #[expect(refining_impl_trait)] async fn find_conversion( &self, - source_data_type_id: &VersionedUrl, - target_data_type_id: &VersionedUrl, + source: &DataTypeReference, + target: &DataTypeReference, ) -> Result>, Report> { - let source = DataTypeUuid::from_url(source_data_type_id); - let target = DataTypeUuid::from_url(target_data_type_id); + let source_uuid = DataTypeUuid::from_url(&source.url); + let target_uuid = DataTypeUuid::from_url(&target.url); - if let Some(cached) = self.cache.conversions.get(&(source, target)).await { + if let Some(cached) = self + .cache + .conversions + .get(&(source_uuid, target_uuid)) + .await + { return cached; } @@ -274,18 +305,18 @@ where AND target_data_type_base_url = $3 ;", &[ - &source, - &target, - &source_data_type_id.base_url, - &target_data_type_id.base_url, + &source_uuid, + &target_uuid, + &source.url.base_url, + &target.url.base_url, ], ) .await .change_context(QueryError) .attach_printable_lazy(|| { format!( - "Found none or more than one conversions between `{source_data_type_id}` and \ - `{target_data_type_id}`" + "Found none or more than one conversions between `{}` and `{}`", + source.url, target.url ) })? .get::<_, Vec>(0) @@ -296,7 +327,7 @@ where Ok(self .cache .conversions - .grant((source, target), expression) + .grant((source_uuid, target_uuid), expression) .await) } } diff --git a/apps/hash-graph/libs/store/src/filter/mod.rs b/apps/hash-graph/libs/store/src/filter/mod.rs index 664fe91bec4..0ea8d36fbf1 100644 --- a/apps/hash-graph/libs/store/src/filter/mod.rs +++ b/apps/hash-graph/libs/store/src/filter/mod.rs @@ -9,11 +9,11 @@ use derive_where::derive_where; use error_stack::{Report, ResultExt as _, bail}; use graph_types::{ knowledge::entity::{Entity, EntityId}, - ontology::{DataTypeProvider, DataTypeWithMetadata}, + ontology::{DataTypeLookup, DataTypeWithMetadata}, }; use serde::{Deserialize, de, de::IntoDeserializer as _}; use type_system::{ - schema::DataTypeUuid, + schema::{DataTypeReference, DataTypeUuid}, url::{BaseUrl, OntologyTypeVersion, VersionedUrl}, }; @@ -160,6 +160,19 @@ where } impl<'p> Filter<'p, DataTypeWithMetadata> { + #[must_use] + pub const fn for_data_type_uuid(data_type_uuid: DataTypeUuid) -> Self { + Self::Equal( + Some(FilterExpression::Path { + path: DataTypeQueryPath::OntologyId, + }), + Some(FilterExpression::Parameter { + parameter: Parameter::Uuid(data_type_uuid.into_uuid()), + convert: None, + }), + ) + } + #[must_use] pub fn for_data_type_parents( data_type_ids: &'p [DataTypeUuid], @@ -295,7 +308,7 @@ where data_type_provider: &P, ) -> Result<(), Report> where - P: DataTypeProvider + Sync, + P: DataTypeLookup + Sync, { match self { Self::All(filters) | Self::Any(filters) => { @@ -474,7 +487,7 @@ impl FilterExpression<'_, R> { provider: &D, ) -> Result<(), Report> where - D: DataTypeProvider + Sync, + D: DataTypeLookup + Sync, { if let Self::Parameter { parameter, convert } = self { if let Some(conversion) = convert.take() { @@ -486,7 +499,10 @@ impl FilterExpression<'_, R> { }; let conversions = provider - .find_conversion(&conversion.from, &conversion.to) + .find_conversion( + <&DataTypeReference>::from(&conversion.from), + <&DataTypeReference>::from(&conversion.to), + ) .await .change_context_lazy(|| ParameterConversionError::NoConversionFound { from: conversion.from.clone(), @@ -509,36 +525,49 @@ mod tests { use graph_types::{ knowledge::entity::{DraftId, EntityUuid}, - ontology::{DataTypeWithMetadata, OntologyTypeProvider}, + ontology::{DataTypeLookup, DataTypeWithMetadata}, owned_by_id::OwnedById, }; use serde_json::json; - use type_system::schema::ConversionExpression; + use type_system::schema::{ClosedDataType, ConversionExpression, DataTypeReference}; use uuid::Uuid; use super::*; struct TestDataTypeProvider; - #[expect(refining_impl_trait)] - impl OntologyTypeProvider for TestDataTypeProvider { - type Value = DataTypeWithMetadata; + impl DataTypeLookup for TestDataTypeProvider { + type ClosedDataType = ClosedDataType; + type DataTypeWithMetadata = DataTypeWithMetadata; + type Error = !; - async fn provide_type(&self, _: &VersionedUrl) -> Result> { + async fn lookup_data_type_by_uuid( + &self, + _: DataTypeUuid, + ) -> Result> { + unimplemented!() + } + + async fn lookup_closed_data_type_by_uuid( + &self, + _: DataTypeUuid, + ) -> Result> { unimplemented!() } - } - #[expect(refining_impl_trait)] - impl DataTypeProvider for TestDataTypeProvider { - async fn is_parent_of(&self, _: &VersionedUrl, _: &BaseUrl) -> Result> { + async fn is_parent_of( + &self, + _: &DataTypeReference, + _: &BaseUrl, + ) -> Result> { unimplemented!() } + #[expect(refining_impl_trait_internal)] async fn find_conversion( &self, - _: &VersionedUrl, - _: &VersionedUrl, + _: &DataTypeReference, + _: &DataTypeReference, ) -> Result, Report> { unimplemented!() } diff --git a/libs/@local/hash-graph-types/rust/Cargo.toml b/libs/@local/hash-graph-types/rust/Cargo.toml index 2f4d3177669..461169b68d5 100644 --- a/libs/@local/hash-graph-types/rust/Cargo.toml +++ b/libs/@local/hash-graph-types/rust/Cargo.toml @@ -31,6 +31,7 @@ time = { workspace = true, default-features = false, features = ["serde", "parsi url = { workspace = true, features = ["serde"] } utoipa = { workspace = true, optional = true } uuid = { workspace = true, default-features = false, features = ["serde"] } +trait-variant = { workspace = true } [dev-dependencies] graph-test-data = { workspace = true } diff --git a/libs/@local/hash-graph-types/rust/src/knowledge/property/visitor.rs b/libs/@local/hash-graph-types/rust/src/knowledge/property/visitor.rs index 650157dcd99..72ed7dddb30 100644 --- a/libs/@local/hash-graph-types/rust/src/knowledge/property/visitor.rs +++ b/libs/@local/hash-graph-types/rust/src/knowledge/property/visitor.rs @@ -17,9 +17,7 @@ use crate::{ PropertyWithMetadataValue, ValueMetadata, error::{Actual, Expected}, }, - ontology::{ - DataTypeProvider, DataTypeWithMetadata, OntologyTypeProvider, PropertyTypeProvider, - }, + ontology::{DataTypeLookup, DataTypeWithMetadata, OntologyTypeProvider, PropertyTypeProvider}, }; #[derive(Debug, thiserror::Error)] @@ -111,7 +109,7 @@ pub trait EntityVisitor: Sized + Send + Sync { type_provider: &P, ) -> impl Future>> + Send where - P: DataTypeProvider + Sync, + P: DataTypeLookup + Sync, { walk_value(self, data_type, value, metadata, type_provider) } @@ -126,7 +124,7 @@ pub trait EntityVisitor: Sized + Send + Sync { type_provider: &P, ) -> impl Future>> + Send where - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { walk_property(self, schema, property, type_provider) } @@ -142,7 +140,7 @@ pub trait EntityVisitor: Sized + Send + Sync { ) -> impl Future>> + Send where T: PropertyValueSchema + Sync, - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { walk_array(self, schema, array, type_provider) } @@ -158,7 +156,7 @@ pub trait EntityVisitor: Sized + Send + Sync { ) -> impl Future>> + Send where T: PropertyObjectSchema> + Sync, - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { walk_object(self, schema, object, type_provider) } @@ -173,7 +171,7 @@ pub trait EntityVisitor: Sized + Send + Sync { type_provider: &P, ) -> impl Future>> + Send where - P: DataTypeProvider + Sync, + P: DataTypeLookup + Sync, { walk_one_of_property_value(self, schema, property, type_provider) } @@ -188,7 +186,7 @@ pub trait EntityVisitor: Sized + Send + Sync { type_provider: &P, ) -> impl Future>> + Send where - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { walk_one_of_array(self, schema, array, type_provider) } @@ -203,7 +201,7 @@ pub trait EntityVisitor: Sized + Send + Sync { type_provider: &P, ) -> impl Future>> + Send where - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { walk_one_of_object(self, schema, object, type_provider) } @@ -225,13 +223,13 @@ pub async fn walk_value( ) -> Result<(), Report<[TraversalError]>> where V: EntityVisitor, - P: DataTypeProvider + Sync, + P: DataTypeLookup + Sync, { let mut status = ReportSink::new(); for parent in &data_type.schema.all_of { match type_provider - .provide_type(&parent.url) + .lookup_data_type_by_ref(parent) .await .change_context_lazy(|| TraversalError::DataTypeRetrieval { id: parent.clone() }) { @@ -270,7 +268,7 @@ pub async fn walk_property( ) -> Result<(), Report<[TraversalError]>> where V: EntityVisitor, - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { let mut status = ReportSink::new(); match property { @@ -316,7 +314,7 @@ pub async fn walk_array( where V: EntityVisitor, S: PropertyValueSchema + Sync, - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { let mut status = ReportSink::new(); @@ -380,7 +378,7 @@ pub async fn walk_object( where V: EntityVisitor, S: PropertyObjectSchema> + Sync, - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { let mut status = ReportSink::new(); @@ -467,7 +465,7 @@ pub async fn walk_one_of_property_value( ) -> Result<(), Report<[TraversalError]>> where V: EntityVisitor, - P: DataTypeProvider + Sync, + P: DataTypeLookup + Sync, { let mut status = ReportSink::new(); let mut passed: usize = 0; @@ -476,7 +474,7 @@ where match schema { PropertyValues::DataTypeReference(data_type_ref) => { let data_type = type_provider - .provide_type(&data_type_ref.url) + .lookup_data_type_by_ref(data_type_ref) .await .change_context_lazy(|| TraversalError::DataTypeRetrieval { id: data_type_ref.clone(), @@ -545,7 +543,7 @@ pub async fn walk_one_of_array( ) -> Result<(), Report<[TraversalError]>> where V: EntityVisitor, - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { let mut status = ReportSink::new(); let mut passed: usize = 0; @@ -609,7 +607,7 @@ pub async fn walk_one_of_object( ) -> Result<(), Report<[TraversalError]>> where V: EntityVisitor, - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { let mut status = ReportSink::new(); let mut passed: usize = 0; diff --git a/libs/@local/hash-graph-types/rust/src/ontology/data_type/lookup.rs b/libs/@local/hash-graph-types/rust/src/ontology/data_type/lookup.rs new file mode 100644 index 00000000000..c8aabab0360 --- /dev/null +++ b/libs/@local/hash-graph-types/rust/src/ontology/data_type/lookup.rs @@ -0,0 +1,46 @@ +use core::{borrow::Borrow, error::Error}; + +use error_stack::{FutureExt as _, Report}; +use type_system::{ + schema::{ClosedDataType, ConversionExpression, DataTypeReference, DataTypeUuid}, + url::BaseUrl, +}; + +use crate::ontology::DataTypeWithMetadata; + +#[trait_variant::make(Send)] +pub trait DataTypeLookup { + type DataTypeWithMetadata: Borrow + Send; + type ClosedDataType: Borrow + Send; + type Error: Error + Send + Sync + 'static; + + async fn lookup_data_type_by_ref( + &self, + data_type_ref: &DataTypeReference, + ) -> Result> { + self.lookup_data_type_by_uuid(DataTypeUuid::from_url(&data_type_ref.url)) + .attach_printable_lazy(|| data_type_ref.url.clone()) + } + + async fn lookup_data_type_by_uuid( + &self, + data_type_uuid: DataTypeUuid, + ) -> Result>; + + async fn lookup_closed_data_type_by_uuid( + &self, + data_type_uuid: DataTypeUuid, + ) -> Result>; + + async fn is_parent_of( + &self, + child: &DataTypeReference, + parent: &BaseUrl, + ) -> Result>; + + async fn find_conversion( + &self, + source: &DataTypeReference, + target: &DataTypeReference, + ) -> Result>, Report>; +} diff --git a/libs/@local/hash-graph-types/rust/src/ontology/data_type.rs b/libs/@local/hash-graph-types/rust/src/ontology/data_type/mod.rs similarity index 98% rename from libs/@local/hash-graph-types/rust/src/ontology/data_type.rs rename to libs/@local/hash-graph-types/rust/src/ontology/data_type/mod.rs index c21778d0d99..5651430e066 100644 --- a/libs/@local/hash-graph-types/rust/src/ontology/data_type.rs +++ b/libs/@local/hash-graph-types/rust/src/ontology/data_type/mod.rs @@ -1,3 +1,7 @@ +pub use self::lookup::DataTypeLookup; + +mod lookup; + use std::collections::HashMap; use serde::{Deserialize, Serialize}; diff --git a/libs/@local/hash-graph-types/rust/src/ontology/mod.rs b/libs/@local/hash-graph-types/rust/src/ontology/mod.rs index c2570b677a1..c89e3d69673 100644 --- a/libs/@local/hash-graph-types/rust/src/ontology/mod.rs +++ b/libs/@local/hash-graph-types/rust/src/ontology/mod.rs @@ -18,7 +18,7 @@ use type_system::{ }; pub use self::{ - data_type::{DataTypeMetadata, DataTypeWithMetadata, PartialDataTypeMetadata}, + data_type::{DataTypeLookup, DataTypeMetadata, DataTypeWithMetadata, PartialDataTypeMetadata}, entity_type::{ EntityTypeEmbedding, EntityTypeMetadata, EntityTypeWithMetadata, PartialEntityTypeMetadata, }, diff --git a/libs/@local/hash-validation/src/entity_type.rs b/libs/@local/hash-validation/src/entity_type.rs index 47b1cbb6697..0adfc7118ed 100644 --- a/libs/@local/hash-validation/src/entity_type.rs +++ b/libs/@local/hash-validation/src/entity_type.rs @@ -17,7 +17,7 @@ use graph_types::{ }, }, ontology::{ - DataTypeProvider, DataTypeWithMetadata, EntityTypeProvider, OntologyTypeProvider, + DataTypeLookup, DataTypeWithMetadata, EntityTypeProvider, OntologyTypeProvider, PropertyTypeProvider, }, }; @@ -61,7 +61,7 @@ where P: EntityProvider + EntityTypeProvider + OntologyTypeProvider - + OntologyTypeProvider + + DataTypeLookup + Sync, { type Error = EntityValidationError; @@ -124,7 +124,7 @@ where P: EntityProvider + EntityTypeProvider + OntologyTypeProvider - + DataTypeProvider + + DataTypeLookup + Sync, { type Error = EntityValidationError; @@ -328,7 +328,7 @@ impl EntityVisitor for ValueValidator { _: &P, ) -> Result<(), Report<[TraversalError]>> where - P: DataTypeProvider + Sync, + P: DataTypeLookup + Sync, { let mut status = ReportSink::new(); @@ -361,14 +361,15 @@ impl EntityVisitor for EntityPreprocessor { type_provider: &P, ) -> Result<(), Report<[TraversalError]>> where - P: DataTypeProvider + Sync, + P: DataTypeLookup + Sync, { let mut status = ReportSink::new(); if let Some(data_type_url) = &metadata.data_type_id { + let data_type_ref: &DataTypeReference = data_type_url.into(); if data_type.schema.id != *data_type_url { let is_compatible = type_provider - .is_parent_of(data_type_url, &data_type.schema.id.base_url) + .is_parent_of(data_type_ref, &data_type.schema.id.base_url) .await .change_context_lazy(|| TraversalError::DataTypeRetrieval { id: DataTypeReference { @@ -384,7 +385,7 @@ impl EntityVisitor for EntityPreprocessor { } let desired_data_type = type_provider - .provide_type(data_type_url) + .lookup_data_type_by_ref(data_type_ref) .await .change_context_lazy(|| TraversalError::DataTypeRetrieval { id: DataTypeReference { @@ -430,7 +431,7 @@ impl EntityVisitor for EntityPreprocessor { type_provider: &P, ) -> Result<(), Report<[TraversalError]>> where - P: DataTypeProvider + Sync, + P: DataTypeLookup + Sync, { let mut status = ReportSink::new(); @@ -445,7 +446,7 @@ impl EntityVisitor for EntityPreprocessor { if let PropertyValues::DataTypeReference(data_type_ref) = values { let Some(data_type) = infer_status.attempt( type_provider - .provide_type(&data_type_ref.url) + .lookup_data_type_by_ref(data_type_ref) .await .change_context_lazy(|| TraversalError::DataTypeRetrieval { id: data_type_ref.clone(), @@ -494,17 +495,16 @@ impl EntityVisitor for EntityPreprocessor { &property.metadata.original_data_type_id, &property.metadata.data_type_id, ) { - if source_data_type_id != target_data_type_id { + let source_data_type_ref: &DataTypeReference = source_data_type_id.into(); + let target_data_type_ref: &DataTypeReference = target_data_type_id.into(); + + if source_data_type_ref != target_data_type_ref { let conversions = type_provider - .find_conversion(source_data_type_id, target_data_type_id) + .find_conversion(source_data_type_ref, target_data_type_ref) .await .change_context_lazy(|| TraversalError::ConversionRetrieval { - current: DataTypeReference { - url: source_data_type_id.clone(), - }, - target: DataTypeReference { - url: target_data_type_id.clone(), - }, + current: source_data_type_ref.clone(), + target: target_data_type_ref.clone(), })?; if let Some(mut value) = property.value.as_f64() { @@ -528,7 +528,7 @@ impl EntityVisitor for EntityPreprocessor { .insert(data_type_id.base_url.clone(), property.value.clone()); let data_type_result = type_provider - .provide_type(data_type_id) + .lookup_data_type_by_ref(<&DataTypeReference>::from(data_type_id)) .await .change_context_lazy(|| TraversalError::DataTypeRetrieval { id: DataTypeReference { @@ -620,7 +620,7 @@ impl EntityVisitor for EntityPreprocessor { ) -> Result<(), Report<[TraversalError]>> where T: PropertyValueSchema + Sync, - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { let mut status = ReportSink::new(); if let Err(error) = walk_array(self, schema, array, type_provider).await { @@ -658,7 +658,7 @@ impl EntityVisitor for EntityPreprocessor { ) -> Result<(), Report<[TraversalError]>> where T: PropertyObjectSchema> + Sync, - P: DataTypeProvider + PropertyTypeProvider + Sync, + P: DataTypeLookup + PropertyTypeProvider + Sync, { let mut status = ReportSink::new(); if let Err(error) = walk_object(self, schema, object, type_provider).await { diff --git a/libs/@local/hash-validation/src/lib.rs b/libs/@local/hash-validation/src/lib.rs index 4b2d55cb39c..4684036d1b4 100644 --- a/libs/@local/hash-validation/src/lib.rs +++ b/libs/@local/hash-validation/src/lib.rs @@ -111,7 +111,7 @@ mod tests { visitor::{EntityVisitor as _, TraversalError}, }, ontology::{ - DataTypeMetadata, DataTypeProvider, DataTypeWithMetadata, EntityTypeProvider, + DataTypeLookup, DataTypeMetadata, DataTypeWithMetadata, EntityTypeProvider, OntologyEditionProvenance, OntologyProvenance, OntologyTemporalMetadata, OntologyTypeClassificationMetadata, OntologyTypeProvider, OntologyTypeRecordId, PropertyTypeProvider, ProvidedOntologyEditionProvenance, @@ -123,8 +123,9 @@ mod tests { use thiserror::Error; use type_system::{ schema::{ - ClosedEntityType, ClosedMultiEntityType, ConversionExpression, DataType, EntityType, - EntityTypeUuid, OntologyTypeResolver, PropertyType, + ClosedDataType, ClosedEntityType, ClosedMultiEntityType, ConversionExpression, + DataType, DataTypeReference, DataTypeUuid, EntityType, EntityTypeUuid, + OntologyTypeResolver, PropertyType, }, url::{BaseUrl, VersionedUrl}, }; @@ -163,7 +164,7 @@ mod tests { entities: HashMap, entity_types: HashMap>, property_types: HashMap>, - data_types: HashMap>, + data_types: HashMap>, } impl Provider { fn new( @@ -189,7 +190,7 @@ mod tests { .into_iter() .map(|schema| { ( - schema.id.clone(), + DataTypeUuid::from_url(&schema.id), Arc::new(generate_data_type_metadata(schema)), ) }) @@ -216,10 +217,8 @@ mod tests { id: VersionedUrl, } #[derive(Debug, Error)] - #[error("data type was not found: `{id}`")] - struct InvalidDataType { - id: VersionedUrl, - } + #[error("data type was not found")] + struct InvalidDataType; impl EntityProvider for Provider { #[expect(refining_impl_trait)] @@ -294,44 +293,47 @@ mod tests { impl PropertyTypeProvider for Provider {} - impl OntologyTypeProvider for Provider { - type Value = Arc; + impl DataTypeLookup for Provider { + type ClosedDataType = Arc; + type DataTypeWithMetadata = Arc; + type Error = InvalidDataType; - #[expect(refining_impl_trait)] - async fn provide_type( + async fn lookup_data_type_by_uuid( &self, - type_id: &VersionedUrl, + data_type_uuid: DataTypeUuid, ) -> Result, Report> { - self.data_types.get(type_id).map(Arc::clone).ok_or_else(|| { - Report::new(InvalidDataType { - id: type_id.clone(), - }) - }) + self.data_types + .get(&data_type_uuid) + .map(Arc::clone) + .ok_or_else(|| Report::new(InvalidDataType)) + } + + async fn lookup_closed_data_type_by_uuid( + &self, + _: DataTypeUuid, + ) -> Result> { + unimplemented!() } - } - impl DataTypeProvider for Provider { - #[expect(refining_impl_trait)] async fn is_parent_of( &self, - child: &VersionedUrl, + child: &DataTypeReference, parent: &BaseUrl, ) -> Result> { - Ok( - OntologyTypeProvider::::provide_type(self, child) - .await? - .schema - .all_of - .iter() - .any(|id| id.url.base_url == *parent), - ) + Ok(self + .lookup_data_type_by_ref(child) + .await? + .schema + .all_of + .iter() + .any(|id| id.url.base_url == *parent)) } #[expect(refining_impl_trait)] async fn find_conversion( &self, - _: &VersionedUrl, - _: &VersionedUrl, + _: &DataTypeReference, + _: &DataTypeReference, ) -> Result, Report> { Ok(Vec::new()) }