//! `InterceptorServiceImpl` — the Postgres-backed §9.4 interceptor resolver //! injected into `Services`. It maps `(cx.app_id, service, op)` to the nearest //! interceptor marker on the calling app's chain and materializes its script //! **sealed to the declaring owner** (via [`crate::interceptor_repo`]). Running //! that script is the executor's job (the `invoke()` re-entry core), which keeps //! `executor-core` Postgres-free. use async_trait::async_trait; use picloud_shared::{ InterceptorResolution, InterceptorService, ResolvedScript, ScriptId, SdkCallCx, }; use sqlx::PgPool; pub struct InterceptorServiceImpl { pool: PgPool, } impl InterceptorServiceImpl { #[must_use] pub fn new(pool: PgPool) -> Self { Self { pool } } } #[async_trait] impl InterceptorService for InterceptorServiceImpl { async fn resolve_before( &self, cx: &SdkCallCx, service: &str, op: &str, ) -> Result { // `app_id` derives from `cx` (never a script arg) — the isolation // boundary; the chain walk then scopes resolution to this app's subtree. let sealed = crate::interceptor_repo::resolve_before(&self.pool, cx.app_id, service, op) .await .map_err(|e| e.to_string())?; let Some(sealed) = sealed else { return Ok(InterceptorResolution::None); }; // A marker exists but its sealed script is missing/disabled → dangling, // the executor fails closed (denies) rather than silently allowing. let Some((sid, source, updated_at)) = sealed.script.clone() else { return Ok(InterceptorResolution::Dangling(sealed.script_name)); }; // The execution boundary is always the CALLER's app; the `owner` is the // sealing (declaring) owner, the lexical origin for the interceptor's // own `import`s (§5.5) — a group marker's script runs its imports from // the group. Ok(InterceptorResolution::Run(Box::new(ResolvedScript { script_id: ScriptId::from(sid), app_id: cx.app_id, owner: Some(sealed.sealing_owner()), source, updated_at, name: sealed.script_name, }))) } }