fix(manager-core): F-Q-011 blanket Arc<T: ScriptRepository> impl; delete PostgresScriptRepoHandle
A 70-line newtype lived in picloud/src/lib.rs wrapping Arc<PostgresScriptRepository> and re-implementing every ScriptRepository method as `self.0.method(...).await`. Hand-delegation invited silent skew when the trait gained a method, and forced 8 call sites to know about the wrapper. Add a blanket impl<T: ScriptRepository + ?Sized> ScriptRepository for Arc<T> in manager-core::repo. The implementations forward via (**self).method(...) so any owner of an Arc<dyn ScriptRepository> (or Arc<ConcreteImpl>) can be passed where the trait is expected. Migrate the 7 picloud-binary call sites: Arc::new(PostgresScriptRepoHandle(script_repo.clone())) → script_repo.clone() Delete the newtype + its hand-delegated impl. Leaves a 6-line comment documenting why the boilerplate is gone. AUDIT.md anchor: F-Q-011. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,68 @@ pub trait ScriptRepository: Send + Sync {
|
||||
-> Result<Vec<Script>, ScriptRepositoryError>;
|
||||
}
|
||||
|
||||
/// F-Q-011: blanket impl for `Arc<T: ScriptRepository>`. Lets callers
|
||||
/// pass a shared `Arc<PostgresScriptRepository>` directly anywhere a
|
||||
/// `dyn ScriptRepository` is wanted — replacing the hand-delegated
|
||||
/// `PostgresScriptRepoHandle` newtype that previously had to be
|
||||
/// rewritten in lockstep every time the trait gained a method.
|
||||
#[async_trait::async_trait]
|
||||
impl<T: ScriptRepository + ?Sized> ScriptRepository for std::sync::Arc<T> {
|
||||
async fn get(&self, id: ScriptId) -> Result<Option<Script>, ScriptRepositoryError> {
|
||||
(**self).get(id).await
|
||||
}
|
||||
async fn get_by_name(
|
||||
&self,
|
||||
app_id: AppId,
|
||||
name: &str,
|
||||
) -> Result<Option<Script>, ScriptRepositoryError> {
|
||||
(**self).get_by_name(app_id, name).await
|
||||
}
|
||||
async fn list(&self) -> Result<Vec<Script>, ScriptRepositoryError> {
|
||||
(**self).list().await
|
||||
}
|
||||
async fn list_for_app(&self, app_id: AppId) -> Result<Vec<Script>, ScriptRepositoryError> {
|
||||
(**self).list_for_app(app_id).await
|
||||
}
|
||||
async fn list_for_user(
|
||||
&self,
|
||||
user_id: AdminUserId,
|
||||
) -> Result<Vec<Script>, ScriptRepositoryError> {
|
||||
(**self).list_for_user(user_id).await
|
||||
}
|
||||
async fn create(&self, input: NewScript) -> Result<Script, ScriptRepositoryError> {
|
||||
(**self).create(input).await
|
||||
}
|
||||
async fn update(
|
||||
&self,
|
||||
id: ScriptId,
|
||||
patch: ScriptPatch,
|
||||
) -> Result<Script, ScriptRepositoryError> {
|
||||
(**self).update(id, patch).await
|
||||
}
|
||||
async fn delete(&self, id: ScriptId) -> Result<(), ScriptRepositoryError> {
|
||||
(**self).delete(id).await
|
||||
}
|
||||
async fn count_routes_for_script(
|
||||
&self,
|
||||
script_id: ScriptId,
|
||||
) -> Result<i64, ScriptRepositoryError> {
|
||||
(**self).count_routes_for_script(script_id).await
|
||||
}
|
||||
async fn count_triggers_for_script(
|
||||
&self,
|
||||
script_id: ScriptId,
|
||||
) -> Result<i64, ScriptRepositoryError> {
|
||||
(**self).count_triggers_for_script(script_id).await
|
||||
}
|
||||
async fn list_imports(
|
||||
&self,
|
||||
script_id: ScriptId,
|
||||
) -> Result<Vec<Script>, ScriptRepositoryError> {
|
||||
(**self).list_imports(script_id).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Inbound shape for create. Defaults match the migration's CHECK
|
||||
/// constraints; the repo enforces them in the DB regardless.
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
Reference in New Issue
Block a user