From 3685e38fecb3702aca15fd12c37c1e185428f86c Mon Sep 17 00:00:00 2001 From: Jiangzhou He Date: Sat, 4 Oct 2025 13:49:43 -0700 Subject: [PATCH] feat(attachment): create `TargetSpecificAttachmentFactoryBase` --- src/ops/factory_bases.rs | 78 +++++++++++++++++++++++++++++++++++++++- src/ops/interface.rs | 4 +-- src/setup/states.rs | 6 ++-- 3 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/ops/factory_bases.rs b/src/ops/factory_bases.rs index a857a78df..2b0b19ae4 100644 --- a/src/ops/factory_bases.rs +++ b/src/ops/factory_bases.rs @@ -374,7 +374,7 @@ pub struct TypedResourceSetupChangeItem<'a, F: TargetFactoryBase + ?Sized> { } #[async_trait] -pub trait TargetFactoryBase: TargetFactory + Send + Sync + 'static { +pub trait TargetFactoryBase: Send + Sync + 'static { type Spec: DeserializeOwned + Send + Sync; type DeclarationSpec: DeserializeOwned + Send + Sync; @@ -635,3 +635,79 @@ fn from_json_combined_state( legacy_state_key: existing_states.legacy_state_key, }) } + +pub struct TypedTargetAttachmentState { + pub setup_key: F::SetupKey, + pub setup_state: F::SetupState, +} + +/// A factory for target-specific attachments. +pub trait TargetSpecificAttachmentFactoryBase: Send + Sync + 'static { + type TargetSpec: DeserializeOwned + Send + Sync; + type Spec: DeserializeOwned + Send + Sync; + type SetupKey: Debug + Clone + Serialize + DeserializeOwned + Eq + Hash + Send + Sync; + type SetupState: Debug + Clone + Serialize + DeserializeOwned + Send + Sync; + type SetupChange: interface::AttachmentSetupChange + Send + Sync; + + fn get_state( + &self, + target_name: &str, + target_spec: &Self::TargetSpec, + attachment_spec: Self::Spec, + ) -> Result>; + + fn diff_setup_states( + &self, + key: &serde_json::Value, + new_state: Option, + existing_states: setup::CombinedState, + ) -> Result>; + + /// Deserialize the setup key from a JSON value. + /// You can override this method to provide a custom deserialization logic, e.g. to perform backward compatible deserialization. + fn deserialize_setup_key(key: serde_json::Value) -> Result { + Ok(utils::deser::from_json_value(key)?) + } +} + +#[async_trait] +impl TargetAttachmentFactory for T { + fn normalize_setup_key(&self, key: &serde_json::Value) -> Result { + let key: T::SetupKey = Self::deserialize_setup_key(key.clone())?; + Ok(serde_json::to_value(key)?) + } + + fn get_state( + &self, + target_name: &str, + target_spec: &serde_json::Map, + attachment_spec: serde_json::Value, + ) -> Result { + let state = TargetSpecificAttachmentFactoryBase::get_state( + self, + target_name, + &utils::deser::from_json_value(serde_json::Value::Object(target_spec.clone()))?, + utils::deser::from_json_value(attachment_spec)?, + )?; + Ok(interface::TargetAttachmentState { + setup_key: serde_json::to_value(state.setup_key)?, + setup_state: serde_json::to_value(state.setup_state)?, + }) + } + + fn diff_setup_states( + &self, + key: &serde_json::Value, + new_state: Option, + existing_states: setup::CombinedState, + ) -> Result>> { + let setup_change = self.diff_setup_states( + &utils::deser::from_json_value(key.clone())?, + new_state + .map(|v| utils::deser::from_json_value(v)) + .transpose()?, + from_json_combined_state(existing_states)?, + )?; + Ok(setup_change.map(|s| Box::new(s) as Box)) + } +} diff --git a/src/ops/interface.rs b/src/ops/interface.rs index 8ea589d24..709a71ecc 100644 --- a/src/ops/interface.rs +++ b/src/ops/interface.rs @@ -322,7 +322,7 @@ pub struct TargetAttachmentState { } #[async_trait] -pub trait AttachmentSetupChangeAction { +pub trait AttachmentSetupChange { fn describe_change(&self) -> String; async fn apply_change(&self) -> Result<()>; @@ -346,7 +346,7 @@ pub trait TargetAttachmentFactory: Send + Sync { key: &serde_json::Value, new_state: Option, existing_states: setup::CombinedState, - ) -> Result>>; + ) -> Result>>; } #[derive(Clone)] diff --git a/src/setup/states.rs b/src/setup/states.rs index 5e838761e..8db1d6bbd 100644 --- a/src/setup/states.rs +++ b/src/setup/states.rs @@ -1,4 +1,4 @@ -use crate::ops::interface::AttachmentSetupChangeAction; +use crate::ops::interface::AttachmentSetupChange; /// Concepts: /// - Resource: some setup that needs to be tracked and maintained. /// - Setup State: current state of a resource. @@ -402,8 +402,8 @@ pub trait ObjectSetupChange { #[derive(Default)] pub struct AttachmentsSetupChange { - pub deletes: Vec>, - pub upserts: Vec>, + pub deletes: Vec>, + pub upserts: Vec>, } impl AttachmentsSetupChange {