diff --git a/Cargo.lock b/Cargo.lock index 0907e55ce7..19490dc11e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4209,6 +4209,7 @@ dependencies = [ "serde_json", "si-data-pg", "si-hash", + "si-id", "si-jwt-public-key", "si-pkg", "si-posthog", @@ -6570,7 +6571,6 @@ dependencies = [ "si-id", "strum", "thiserror 1.0.69", - "ulid", "xxhash-rust", ] @@ -6619,6 +6619,7 @@ version = "0.1.0" dependencies = [ "derive_more", "postgres-types", + "sea-orm", "serde", "ulid", ] @@ -6660,6 +6661,7 @@ dependencies = [ "si-data-nats", "si-data-pg", "si-events", + "si-id", "si-runtime", "si-std", "strum", diff --git a/lib/dal/src/workspace_snapshot/vector_clock.rs b/lib/dal/src/workspace_snapshot/vector_clock.rs index 9f75665a2c..143f9e16fc 100644 --- a/lib/dal/src/workspace_snapshot/vector_clock.rs +++ b/lib/dal/src/workspace_snapshot/vector_clock.rs @@ -54,8 +54,7 @@ impl VectorClock { &self, change_set_id_filter: Option, ) -> Option<(VectorClockId, LamportClock)> { - let maybe_change_set_id = change_set_id_filter - .map(|change_set_id| VectorClockChangeSetId::new(change_set_id.into_inner().into())); + let maybe_change_set_id = change_set_id_filter.map(VectorClockChangeSetId::from); self.entries .iter() .filter(|(clock_id, _)| { diff --git a/lib/module-index-server/BUCK b/lib/module-index-server/BUCK index c3745d278f..d548217827 100644 --- a/lib/module-index-server/BUCK +++ b/lib/module-index-server/BUCK @@ -8,6 +8,7 @@ rust_library( "//lib/module-index-types:module-index-types", "//lib/si-data-pg:si-data-pg", "//lib/si-hash:si-hash", + "//lib/si-id:si-id", "//lib/si-jwt-public-key:si-jwt-public-key", "//lib/si-pkg:si-pkg", "//lib/si-posthog-rs:si-posthog", diff --git a/lib/module-index-server/Cargo.toml b/lib/module-index-server/Cargo.toml index 46039d4501..c9a86596e7 100644 --- a/lib/module-index-server/Cargo.toml +++ b/lib/module-index-server/Cargo.toml @@ -14,6 +14,7 @@ buck2-resources = { path = "../../lib/buck2-resources" } module-index-types = { path = "../../lib/module-index-types" } si-data-pg = { path = "../../lib/si-data-pg" } si-hash = { path = "../../lib/si-hash" } +si-id = { path = "../../lib/si-id" } si-jwt-public-key = { path = "../../lib/si-jwt-public-key" } si-pkg = { path = "../../lib/si-pkg" } si-posthog = { path = "../../lib/si-posthog-rs" } diff --git a/lib/module-index-server/src/models/si_module.rs b/lib/module-index-server/src/models/si_module.rs index 8a5e95de58..69fac904f8 100644 --- a/lib/module-index-server/src/models/si_module.rs +++ b/lib/module-index-server/src/models/si_module.rs @@ -3,13 +3,9 @@ use sea_orm::{entity::prelude::*, sea_query, TryGetError}; use serde::{Deserialize, Serialize}; use std::str::FromStr; -pub mod module_id; -pub mod schema_id; -pub mod schema_variant_id; - -pub use module_id::ModuleId; -pub use schema_id::SchemaId; -pub use schema_variant_id::SchemaVariantId; +pub use si_id::ModuleIndexModuleId as ModuleId; +pub use si_id::SchemaId; +pub use si_id::SchemaVariantId; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/lib/module-index-server/src/models/si_module/module_id.rs b/lib/module-index-server/src/models/si_module/module_id.rs deleted file mode 100644 index b13f6d7a0e..0000000000 --- a/lib/module-index-server/src/models/si_module/module_id.rs +++ /dev/null @@ -1,79 +0,0 @@ -use sea_orm::{entity::prelude::*, sea_query, TryGetError}; -use serde::{Deserialize, Serialize}; -use ulid::Ulid; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ModuleId(pub Ulid); - -impl From for Value { - fn from(source: ModuleId) -> Self { - Value::String(Some(Box::new(source.0.to_string()))) - } -} - -impl std::fmt::Display for ModuleId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl TryFrom for ModuleId { - type Error = sea_orm::DbErr; - fn try_from(s: String) -> Result { - Ok(ModuleId( - Ulid::from_string(&s).map_err(|err| DbErr::Type(err.to_string()))?, - )) - } -} - -impl sea_orm::TryFromU64 for ModuleId { - fn try_from_u64(_: u64) -> Result { - Err(sea_orm::DbErr::Exec(sea_orm::RuntimeErr::Internal( - format!("{} cannot be converted from u64", stringify!(ModuleId)), - ))) - } -} - -impl From for String { - fn from(val: ModuleId) -> Self { - val.0.to_string() - } -} - -impl sea_orm::sea_query::Nullable for ModuleId { - fn null() -> sea_orm::Value { - sea_orm::Value::String(None) - } -} - -impl sea_orm::TryGetable for ModuleId { - fn try_get_by(res: &QueryResult, idx: I) -> Result { - let json_str: String = res.try_get_by(idx).map_err(TryGetError::DbErr)?; - Ulid::from_string(&json_str) - .map_err(|e| TryGetError::DbErr(DbErr::Type(e.to_string()))) - .map(ModuleId) - } -} - -impl sea_query::ValueType for ModuleId { - fn try_from(v: Value) -> Result { - match v { - Value::String(Some(x)) => Ok(ModuleId( - Ulid::from_string(&x).map_err(|_| sea_query::ValueTypeErr)?, - )), - _ => Err(sea_query::ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(ModuleId).to_owned() - } - - fn array_type() -> sea_orm::sea_query::ArrayType { - sea_orm::sea_query::ArrayType::String - } - - fn column_type() -> sea_query::ColumnType { - sea_query::ColumnType::String(StringLen::None) - } -} diff --git a/lib/module-index-server/src/models/si_module/schema_id.rs b/lib/module-index-server/src/models/si_module/schema_id.rs deleted file mode 100644 index 57aa035b1a..0000000000 --- a/lib/module-index-server/src/models/si_module/schema_id.rs +++ /dev/null @@ -1,84 +0,0 @@ -use sea_orm::{entity::prelude::*, sea_query, TryGetError}; -use serde::{Deserialize, Serialize}; -use ulid::Ulid; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct SchemaId(pub Ulid); - -impl From for Value { - fn from(source: SchemaId) -> Self { - Value::String(Some(Box::new(source.0.to_string()))) - } -} - -impl std::fmt::Display for SchemaId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl TryFrom for SchemaId { - type Error = sea_orm::DbErr; - fn try_from(s: String) -> Result { - Ok(SchemaId( - Ulid::from_string(&s).map_err(|err| DbErr::Type(err.to_string()))?, - )) - } -} - -impl sea_orm::TryFromU64 for SchemaId { - fn try_from_u64(_: u64) -> Result { - Err(sea_orm::DbErr::Exec(sea_orm::RuntimeErr::Internal( - format!("{} cannot be converted from u64", stringify!(SchemaId)), - ))) - } -} - -impl From for String { - fn from(val: SchemaId) -> Self { - val.0.to_string() - } -} - -impl sea_orm::sea_query::Nullable for SchemaId { - fn null() -> sea_orm::Value { - sea_orm::Value::String(None) - } -} - -impl sea_orm::TryGetable for SchemaId { - fn try_get_by(res: &QueryResult, idx: I) -> Result { - let json_str: String = - res.try_get_by(idx) - .map_err(TryGetError::DbErr) - .and_then(|opt: Option| { - opt.ok_or(sea_orm::TryGetError::Null("null".to_string())) - })?; - Ulid::from_string(&json_str) - .map_err(|e| TryGetError::DbErr(DbErr::Type(e.to_string()))) - .map(SchemaId) - } -} - -impl sea_query::ValueType for SchemaId { - fn try_from(v: Value) -> Result { - match v { - Value::String(Some(x)) => Ok(SchemaId( - Ulid::from_string(&x).map_err(|_| sea_query::ValueTypeErr)?, - )), - _ => Err(sea_query::ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(SchemaId).to_owned() - } - - fn array_type() -> sea_orm::sea_query::ArrayType { - sea_orm::sea_query::ArrayType::String - } - - fn column_type() -> sea_query::ColumnType { - sea_query::ColumnType::String(StringLen::None) - } -} diff --git a/lib/module-index-server/src/models/si_module/schema_variant_id.rs b/lib/module-index-server/src/models/si_module/schema_variant_id.rs deleted file mode 100644 index 77a6fd5669..0000000000 --- a/lib/module-index-server/src/models/si_module/schema_variant_id.rs +++ /dev/null @@ -1,87 +0,0 @@ -use sea_orm::{entity::prelude::*, sea_query, TryGetError}; -use serde::{Deserialize, Serialize}; -use ulid::Ulid; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct SchemaVariantId(pub Ulid); - -impl From for Value { - fn from(source: SchemaVariantId) -> Self { - Value::String(Some(Box::new(source.0.to_string()))) - } -} - -impl std::fmt::Display for SchemaVariantId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl TryFrom for SchemaVariantId { - type Error = sea_orm::DbErr; - fn try_from(s: String) -> Result { - Ok(SchemaVariantId( - Ulid::from_string(&s).map_err(|err| DbErr::Type(err.to_string()))?, - )) - } -} - -impl sea_orm::TryFromU64 for SchemaVariantId { - fn try_from_u64(_: u64) -> Result { - Err(sea_orm::DbErr::Exec(sea_orm::RuntimeErr::Internal( - format!( - "{} cannot be converted from u64", - stringify!(SchemaVariantId) - ), - ))) - } -} - -impl From for String { - fn from(val: SchemaVariantId) -> Self { - val.0.to_string() - } -} - -impl sea_orm::sea_query::Nullable for SchemaVariantId { - fn null() -> sea_orm::Value { - sea_orm::Value::String(None) - } -} - -impl sea_orm::TryGetable for SchemaVariantId { - fn try_get_by(res: &QueryResult, idx: I) -> Result { - let json_str: String = - res.try_get_by(idx) - .map_err(TryGetError::DbErr) - .and_then(|opt: Option| { - opt.ok_or(sea_orm::TryGetError::Null("null".to_string())) - })?; - Ulid::from_string(&json_str) - .map_err(|e| TryGetError::DbErr(DbErr::Type(e.to_string()))) - .map(SchemaVariantId) - } -} - -impl sea_query::ValueType for SchemaVariantId { - fn try_from(v: Value) -> Result { - match v { - Value::String(Some(x)) => Ok(SchemaVariantId( - Ulid::from_string(&x).map_err(|_| sea_query::ValueTypeErr)?, - )), - _ => Err(sea_query::ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(SchemaVariantId).to_owned() - } - - fn array_type() -> sea_orm::sea_query::ArrayType { - sea_orm::sea_query::ArrayType::String - } - - fn column_type() -> sea_query::ColumnType { - sea_query::ColumnType::String(StringLen::None) - } -} diff --git a/lib/module-index-server/src/routes/upsert_module_route.rs b/lib/module-index-server/src/routes/upsert_module_route.rs index 5661313027..5ffcd5b08b 100644 --- a/lib/module-index-server/src/routes/upsert_module_route.rs +++ b/lib/module-index-server/src/routes/upsert_module_route.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use axum::{ extract::{multipart::MultipartError, Multipart}, response::{IntoResponse, Response}, @@ -18,7 +20,6 @@ use serde::{Deserialize, Serialize}; use si_pkg::{SiPkg, SiPkgError, SiPkgKind}; use telemetry::prelude::*; use thiserror::Error; -use ulid::Ulid; use crate::{ extract::{Authorization, DbConnection, ExtractedS3Bucket}, @@ -121,11 +122,11 @@ pub async fn upsert_module_route( SiPkgKind::Module => ModuleKind::Module, }; - let new_schema_id = Some(SchemaId(Ulid::new())); + let new_schema_id = Some(SchemaId::new()); let schema_id = match module_kind { ModuleKind::WorkspaceBackup => None, ModuleKind::Module => match module_schema_id { - Some(schema_id_string) => Some(SchemaId(Ulid::from_string(&schema_id_string)?)), + Some(schema_id_string) => Some(SchemaId::from_str(&schema_id_string)?), None => match module_based_on_hash { None => new_schema_id, Some(based_on_hash) => { @@ -156,7 +157,7 @@ pub async fn upsert_module_route( }; if let Some(schema_id) = schema_id { - info!("module gets schema id: {}", schema_id.0); + info!("module gets schema id: {}", schema_id.as_raw_id()); } let schemas: Vec = loaded_module @@ -177,9 +178,9 @@ pub async fn upsert_module_route( let schema_variant_id = match module_kind { ModuleKind::WorkspaceBackup => None, ModuleKind::Module => match module_schema_variant_id { - Some(schema_variant_id_string) => Some(SchemaVariantId(Ulid::from_string( - &schema_variant_id_string, - )?)), + Some(schema_variant_id_string) => { + Some(SchemaVariantId::from_str(&schema_variant_id_string)?) + } _ => None, }, }; diff --git a/lib/si-events-rs/BUCK b/lib/si-events-rs/BUCK index a586c69c13..33dd3ff81d 100644 --- a/lib/si-events-rs/BUCK +++ b/lib/si-events-rs/BUCK @@ -21,7 +21,6 @@ rust_library( "//third-party/rust:serde_json", "//third-party/rust:strum", "//third-party/rust:thiserror", - "//third-party/rust:ulid", "//third-party/rust:xxhash-rust", ], srcs = glob([ diff --git a/lib/si-events-rs/Cargo.toml b/lib/si-events-rs/Cargo.toml index ee87ef01cd..037d4ca110 100644 --- a/lib/si-events-rs/Cargo.toml +++ b/lib/si-events-rs/Cargo.toml @@ -23,5 +23,4 @@ serde = { workspace = true } serde_json = { workspace = true } strum = { workspace = true } thiserror = { workspace = true } -ulid = { workspace = true } xxhash-rust = { workspace = true } diff --git a/lib/si-events-rs/src/func_run.rs b/lib/si-events-rs/src/func_run.rs index d7ab5db935..5255a168d0 100644 --- a/lib/si-events-rs/src/func_run.rs +++ b/lib/si-events-rs/src/func_run.rs @@ -157,9 +157,9 @@ pub struct FuncRun { #[builder(default)] schema_name: Option, #[builder(default)] - action_or_func_id: Option, + action_or_func_id: Option, #[builder(default)] - prototype_id: Option, + prototype_id: Option, #[builder(default)] action_kind: Option, #[builder(default)] diff --git a/lib/si-events-rs/src/func_run_log.rs b/lib/si-events-rs/src/func_run_log.rs index 338f7f0dfe..2f09969647 100644 --- a/lib/si-events-rs/src/func_run_log.rs +++ b/lib/si-events-rs/src/func_run_log.rs @@ -1,59 +1,9 @@ -use std::str::FromStr; - use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; -use ulid::{Ulid, ULID_LEN}; use crate::{FuncRunId, Tenancy}; -#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct FuncRunLogId(Ulid); - -impl FuncRunLogId { - pub fn new() -> FuncRunLogId { - FuncRunLogId(Ulid::new()) - } - - pub fn array_to_str<'buf>(&self, buf: &'buf mut [u8; ULID_LEN]) -> &'buf mut str { - self.0.array_to_str(buf) - } - - pub fn into_inner(self) -> Ulid { - self.0 - } -} - -impl From for Ulid { - fn from(id: FuncRunLogId) -> Self { - id.0 - } -} - -impl Default for FuncRunLogId { - fn default() -> Self { - Self::new() - } -} - -impl FromStr for FuncRunLogId { - type Err = ulid::DecodeError; - - fn from_str(s: &str) -> Result { - Ok(Self(Ulid::from_str(s)?)) - } -} - -impl From for FuncRunLogId { - fn from(value: ulid::Ulid) -> Self { - Self(value) - } -} - -impl std::fmt::Display for FuncRunLogId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} +pub use si_id::FuncRunLogId; /// A one-to-one mapping of cyclone's "OutputStream" type. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] diff --git a/lib/si-events-rs/src/vector_clock_id.rs b/lib/si-events-rs/src/vector_clock_id.rs index b4fa0dcd92..540b5c1f8e 100644 --- a/lib/si-events-rs/src/vector_clock_id.rs +++ b/lib/si-events-rs/src/vector_clock_id.rs @@ -1,68 +1,7 @@ -use serde::{Deserialize, Serialize}; - use crate::ulid::Ulid; -#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, Copy, Debug)] -pub struct VectorClockChangeSetId(Ulid); - -impl VectorClockChangeSetId { - pub fn new(ulid: Ulid) -> Self { - Self(ulid) - } - - pub fn into_inner(self) -> Ulid { - self.0 - } -} - -impl From for VectorClockChangeSetId { - fn from(value: ulid::Ulid) -> Self { - Self(value.into()) - } -} - -impl From for VectorClockChangeSetId { - fn from(value: Ulid) -> Self { - Self(value) - } -} - -impl std::fmt::Display for VectorClockChangeSetId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, Copy, Debug)] -pub struct VectorClockActorId(Ulid); - -impl VectorClockActorId { - pub fn new(ulid: Ulid) -> Self { - Self(ulid) - } - - pub fn into_inner(self) -> Ulid { - self.0 - } -} - -impl From for VectorClockActorId { - fn from(value: Ulid) -> Self { - Self(value) - } -} - -impl From for VectorClockActorId { - fn from(value: ulid::Ulid) -> Self { - Self(value.into()) - } -} - -impl std::fmt::Display for VectorClockActorId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} +pub use si_id::VectorClockActorId; +pub use si_id::VectorClockChangeSetId; #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] pub struct VectorClockId { @@ -166,8 +105,8 @@ mod tests { let actor_ulid = Ulid::from_string(actor_text).expect("make actor from string"); let vector_clock_id = VectorClockId::new( - VectorClockChangeSetId(change_set_ulid), - VectorClockActorId(actor_ulid), + VectorClockChangeSetId::from(change_set_ulid), + VectorClockActorId::from(actor_ulid), ); let expected = format!("(ChangeSet({cs_text}), Actor({actor_text}))"); diff --git a/lib/si-id/BUCK b/lib/si-id/BUCK index 224ffcd199..4bf1642f35 100644 --- a/lib/si-id/BUCK +++ b/lib/si-id/BUCK @@ -5,6 +5,7 @@ rust_library( deps = [ "//third-party/rust:derive_more", "//third-party/rust:postgres-types", + "//third-party/rust:sea-orm", "//third-party/rust:serde", "//third-party/rust:ulid", ], diff --git a/lib/si-id/Cargo.toml b/lib/si-id/Cargo.toml index 9007e02268..de1fab72c9 100644 --- a/lib/si-id/Cargo.toml +++ b/lib/si-id/Cargo.toml @@ -11,5 +11,6 @@ publish.workspace = true [dependencies] derive_more = { workspace = true } postgres-types = { workspace = true } +sea-orm = { workspace = true } serde = { workspace = true } ulid = { workspace = true } diff --git a/lib/si-id/src/conversions.rs b/lib/si-id/src/conversions.rs index b8a2077d44..36259de123 100644 --- a/lib/si-id/src/conversions.rs +++ b/lib/si-id/src/conversions.rs @@ -1,4 +1,7 @@ -use crate::{AttributeValueId, PropId, PropertyEditorPropId, PropertyEditorValueId}; +use crate::{ + AttributeValueId, ChangeSetId, PropId, PropertyEditorPropId, PropertyEditorValueId, + VectorClockChangeSetId, +}; impl From for PropertyEditorPropId { fn from(prop_id: PropId) -> Self { @@ -23,3 +26,9 @@ impl From for AttributeValueId { Self::from(::ulid::Ulid::from(id)) } } + +impl From for VectorClockChangeSetId { + fn from(id: ChangeSetId) -> Self { + Self::from(::ulid::Ulid::from(id)) + } +} diff --git a/lib/si-id/src/lib.rs b/lib/si-id/src/lib.rs index 2f231eb6ff..fefb0a7bc8 100644 --- a/lib/si-id/src/lib.rs +++ b/lib/si-id/src/lib.rs @@ -30,8 +30,11 @@ pub(crate) mod macros; pub(crate) mod conversions; pub mod ulid; +pub use ::ulid as ulid_upstream; + // Please keep these alphabetically sorted! id!(ActionPrototypeId); +id!(ActivityId); id!(AttributePrototypeArgumentId); id!(AttributePrototypeId); id!(AuthenticationPrototypeId); @@ -39,9 +42,11 @@ id!(DeprecatedVectorClockId); id!(EventSessionId); id!(FuncArgumentId); id!(FuncExecutionPk); +id!(FuncRunLogId); id!(GeometryId); id!(HistoryEventPk); id!(InputSocketId); +id!(LayeredEventId); id!(ManagementPrototypeId); id!(ModuleId); id!(NaxumApiTypesRequestId); @@ -52,6 +57,8 @@ id!(PropertyEditorValueId); id!(SecretId); id!(StaticArgumentValueId); id!(ValidationOutputId); +id!(VectorClockActorId); +id!(VectorClockChangeSetId); id!(ViewId); id!(WorkspaceSnapshotNodeId); @@ -62,12 +69,15 @@ id_with_pg_types!(ChangeSetId); id_with_pg_types!(ComponentId); id_with_pg_types!(FuncId); id_with_pg_types!(FuncRunId); -id_with_pg_types!(SchemaId); id_with_pg_types!(UserPk); id_with_pg_types!(WorkspaceIntegrationId); // Please keep these alphabetically sorted! -id_with_none!(SchemaVariantId); +id_with_pg_and_sea_orm_types!(ModuleIndexModuleId); +id_with_pg_and_sea_orm_types!(SchemaId); + +// Please keep these alphabetically sorted! +id_with_none_and_sea_orm_types!(SchemaVariantId); // Please keep these alphabetically sorted! id_with_none_and_pg_types!(AttributeValueId); diff --git a/lib/si-id/src/macros.rs b/lib/si-id/src/macros.rs index 15f53b7769..af33bbd084 100644 --- a/lib/si-id/src/macros.rs +++ b/lib/si-id/src/macros.rs @@ -68,12 +68,135 @@ macro_rules! id_with_pg_types { }; } +/// Provides PostgreSQL type conversion and SeaORM implementations in addition to standard [`id!`] functionality. +macro_rules! id_with_pg_and_sea_orm_types { + ( + $(#[$($attrs:tt)*])* + $name:ident + ) => { + do_not_use_directly_id_inner!( + $(#[$($attrs)*])* + $name + ); + + impl<'a> postgres_types::FromSql<'a> for $name { + fn from_sql( + ty: &postgres_types::Type, + raw: &'a [u8], + ) -> Result> { + let id: String = postgres_types::FromSql::from_sql(ty, raw)?; + Ok(Self(::ulid::Ulid::from_string(&id)?)) + } + + fn accepts(ty: &postgres_types::Type) -> bool { + ty == &postgres_types::Type::BPCHAR + || ty.kind() == &postgres_types::Kind::Domain(postgres_types::Type::BPCHAR) + } + } + + impl postgres_types::ToSql for $name { + fn to_sql( + &self, + ty: &postgres_types::Type, + out: &mut postgres_types::private::BytesMut, + ) -> Result> + where + Self: Sized, + { + postgres_types::ToSql::to_sql(&self.0.to_string(), ty, out) + } + + fn accepts(ty: &postgres_types::Type) -> bool + where + Self: Sized, + { + ty == &postgres_types::Type::BPCHAR + || ty.kind() == &postgres_types::Kind::Domain(postgres_types::Type::BPCHAR) + } + + fn to_sql_checked( + &self, + ty: &postgres_types::Type, + out: &mut postgres_types::private::BytesMut, + ) -> Result> { + postgres_types::ToSql::to_sql(&self.0.to_string(), ty, out) + } + } + + impl From<$name> for sea_orm::Value { + fn from(source: $name) -> Self { + sea_orm::Value::String(Some(Box::new(source.0.to_string()))) + } + } + + impl TryFrom for $name { + type Error = sea_orm::DbErr; + fn try_from(s: String) -> Result { + Ok($name( + ::ulid::Ulid::from_string(&s).map_err(|err| sea_orm::DbErr::Type(err.to_string()))?, + )) + } + } + + impl sea_orm::TryFromU64 for $name { + fn try_from_u64(_: u64) -> Result { + Err(sea_orm::DbErr::Exec(sea_orm::RuntimeErr::Internal( + format!("{} cannot be converted from u64", stringify!($name)), + ))) + } + } + + impl sea_orm::sea_query::Nullable for $name { + fn null() -> sea_orm::Value { + sea_orm::Value::String(None) + } + } + + impl sea_orm::TryGetable for $name { + fn try_get_by(res: &sea_orm::QueryResult, idx: I) -> Result { + let json_str: String = + res.try_get_by(idx) + .map_err(sea_orm::TryGetError::DbErr) + .and_then(|opt: Option| { + opt.ok_or(sea_orm::TryGetError::Null("null".to_string())) + })?; + ::ulid::Ulid::from_string(&json_str) + .map_err(|e| sea_orm::TryGetError::DbErr(sea_orm::DbErr::Type(e.to_string()))) + .map($name) + } + } + + impl sea_orm::sea_query::ValueType for $name { + fn try_from(v: sea_orm::Value) -> Result { + match v { + sea_orm::Value::String(Some(x)) => Ok($name( + ::ulid::Ulid::from_string(&x).map_err(|_| sea_orm::sea_query::ValueTypeErr)?, + )), + _ => Err(sea_orm::sea_query::ValueTypeErr), + } + } + + fn type_name() -> String { + stringify!($name).to_owned() + } + + fn array_type() -> sea_orm::sea_query::ArrayType { + sea_orm::sea_query::ArrayType::String + } + + fn column_type() -> sea_orm::sea_query::ColumnType { + sea_orm::sea_query::ColumnType::String(sea_orm::entity::prelude::StringLen::None) + } + } + }; +} + /// **Deprecated:** this macro provides additional functionality for using the "nil" ID on top of the standard `id!` -/// macro. +/// macro. It also adds support for SeaORM type conversions. /// /// This absolutely should not be used for any new IDs. Nick will be mad. No, I'm not writing this in third /// person, why do you ask? -macro_rules! id_with_none { +macro_rules! id_with_none_and_sea_orm_types { ( $(#[$($attrs:tt)*])* $name:ident @@ -93,6 +216,72 @@ macro_rules! id_with_none { /// The "nil" value for this ID. pub const NONE: Self = Self(::ulid::Ulid::nil()); } + + impl From<$name> for sea_orm::Value { + fn from(source: $name) -> Self { + sea_orm::Value::String(Some(Box::new(source.0.to_string()))) + } + } + + impl TryFrom for $name { + type Error = sea_orm::DbErr; + fn try_from(s: String) -> Result { + Ok($name( + ::ulid::Ulid::from_string(&s).map_err(|err| sea_orm::DbErr::Type(err.to_string()))?, + )) + } + } + + impl sea_orm::TryFromU64 for $name { + fn try_from_u64(_: u64) -> Result { + Err(sea_orm::DbErr::Exec(sea_orm::RuntimeErr::Internal( + format!("{} cannot be converted from u64", stringify!($name)), + ))) + } + } + + impl sea_orm::sea_query::Nullable for $name { + fn null() -> sea_orm::Value { + sea_orm::Value::String(None) + } + } + + impl sea_orm::TryGetable for $name { + fn try_get_by(res: &sea_orm::QueryResult, idx: I) -> Result { + let json_str: String = + res.try_get_by(idx) + .map_err(sea_orm::TryGetError::DbErr) + .and_then(|opt: Option| { + opt.ok_or(sea_orm::TryGetError::Null("null".to_string())) + })?; + ::ulid::Ulid::from_string(&json_str) + .map_err(|e| sea_orm::TryGetError::DbErr(sea_orm::DbErr::Type(e.to_string()))) + .map($name) + } + } + + impl sea_orm::sea_query::ValueType for $name { + fn try_from(v: sea_orm::Value) -> Result { + match v { + sea_orm::Value::String(Some(x)) => Ok($name( + ::ulid::Ulid::from_string(&x).map_err(|_| sea_orm::sea_query::ValueTypeErr)?, + )), + _ => Err(sea_orm::sea_query::ValueTypeErr), + } + } + + fn type_name() -> String { + stringify!($name).to_owned() + } + + fn array_type() -> sea_orm::sea_query::ArrayType { + sea_orm::sea_query::ArrayType::String + } + + fn column_type() -> sea_orm::sea_query::ColumnType { + sea_orm::sea_query::ColumnType::String(sea_orm::entity::prelude::StringLen::None) + } + } }; } diff --git a/lib/si-layer-cache/BUCK b/lib/si-layer-cache/BUCK index 2514d66b92..ccba57b790 100644 --- a/lib/si-layer-cache/BUCK +++ b/lib/si-layer-cache/BUCK @@ -10,6 +10,7 @@ rust_library( "//lib/si-data-nats:si-data-nats", "//lib/si-data-pg:si-data-pg", "//lib/si-events-rs:si-events", + "//lib/si-id:si-id", "//lib/si-runtime-rs:si-runtime", "//lib/si-std:si-std", "//lib/telemetry-rs:telemetry", diff --git a/lib/si-layer-cache/Cargo.toml b/lib/si-layer-cache/Cargo.toml index 6e83ce1734..3c718b20be 100644 --- a/lib/si-layer-cache/Cargo.toml +++ b/lib/si-layer-cache/Cargo.toml @@ -12,6 +12,7 @@ publish.workspace = true si-data-nats = { path = "../../lib/si-data-nats" } si-data-pg = { path = "../../lib/si-data-pg" } si-events = { path = "../../lib/si-events-rs" } +si-id = { path = "../../lib/si-id" } si-runtime = { path = "../../lib/si-runtime-rs" } si-std = { path = "../../lib/si-std" } telemetry = { path = "../../lib/telemetry-rs" } diff --git a/lib/si-layer-cache/src/activities.rs b/lib/si-layer-cache/src/activities.rs index 22197e2549..ee8f4f1c0b 100644 --- a/lib/si-layer-cache/src/activities.rs +++ b/lib/si-layer-cache/src/activities.rs @@ -1,5 +1,5 @@ use std::time::Duration; -use std::{collections::HashMap, fmt, str::FromStr, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; use futures::StreamExt; use serde::{Deserialize, Serialize}; @@ -14,7 +14,7 @@ use tokio::sync::{ Mutex, }; use tokio_util::{sync::CancellationToken, task::TaskTracker}; -use ulid::{Ulid, ULID_LEN}; +use ulid::Ulid; use crate::{ db::serialize, @@ -44,48 +44,7 @@ pub mod test; //pub static SENT_BROADCAST_ERROR_COUNTER: AtomicI32 = AtomicI32::new(0); const MAX_BYTES: i64 = 1024 * 1024; // mirrors settings in Synadia NATs -#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct ActivityId(Ulid); - -impl ActivityId { - pub fn new() -> ActivityId { - ActivityId(Ulid::new()) - } - - pub fn array_to_str<'buf>(&self, buf: &'buf mut [u8; ULID_LEN]) -> &'buf mut str { - self.0.array_to_str(buf) - } - - pub fn into_inner(self) -> Ulid { - self.0 - } -} - -impl Default for ActivityId { - fn default() -> Self { - Self::new() - } -} - -impl FromStr for ActivityId { - type Err = ulid::DecodeError; - - fn from_str(s: &str) -> Result { - Ok(Self(Ulid::from_str(s)?)) - } -} - -impl From for ActivityId { - fn from(value: ulid::Ulid) -> Self { - Self(value) - } -} - -impl fmt::Display for ActivityId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - std::fmt::Display::fmt(&self.0, f) - } -} +pub use si_id::ActivityId; #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub struct Activity { diff --git a/lib/si-layer-cache/src/event.rs b/lib/si-layer-cache/src/event.rs index f0738cda23..0e98f8ff18 100644 --- a/lib/si-layer-cache/src/event.rs +++ b/lib/si-layer-cache/src/event.rs @@ -60,32 +60,7 @@ impl LayeredEventMetadata { } } -#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct LayeredEventId(Ulid); - -impl Default for LayeredEventId { - fn default() -> Self { - Self::new() - } -} - -impl LayeredEventId { - pub fn new() -> Self { - LayeredEventId(Ulid::new()) - } - - pub fn into_inner(self) -> Ulid { - self.0 - } -} - -impl std::str::FromStr for LayeredEventId { - type Err = ulid::DecodeError; - - fn from_str(s: &str) -> Result { - Ok(Self(Ulid::from_str(s)?)) - } -} +pub use si_id::LayeredEventId; #[remain::sorted] #[derive(AsRefStr, Debug, Serialize, Deserialize)]