//! `Services` — bundle of stateful SDK service handles plumbed from the //! host binary into every Rhai execution. //! //! Constructed once at startup in the picloud binary; cloned (cheap — //! every field is an `Arc`) into the per-call sdk bridge so script //! invocations don't need to re-resolve dependencies. The bundle is //! handed to `executor-core::sdk::register_all` alongside an //! `SdkCallCx` to wire each `::` namespace. //! //! v1.1.0 shipped this empty; v1.1.1 added the first two service fields //! (`kv`, `dead_letters`) plus the `events` emitter that bound services //! use to publish events into the triggers outbox. v1.1.3 adds the //! `modules` field — the `ModuleSource` consulted by the per-call //! `PicloudModuleResolver` to load `import`ed module scripts. //! //! `#[non_exhaustive]` so adding fields is a non-breaking change for //! consumers that only *pattern-match* a `&Services`; only crates that //! *construct* a `Services` (the picloud binary and tests) update. use std::sync::Arc; use crate::{ DeadLetterService, DocsService, EmailService, FilesService, HttpService, KvService, ModuleSource, NoopDeadLetterService, NoopDocsService, NoopEmailService, NoopEventEmitter, NoopFilesService, NoopHttpService, NoopKvService, NoopModuleSource, NoopPubsubService, NoopSecretsService, PubsubService, SecretsService, ServiceEventEmitter, }; /// SDK service bundle. See module docs for the lifecycle and the v1.1.x /// expansion plan. #[non_exhaustive] pub struct Services { /// KV store (v1.1.1). Backed by Postgres in the picloud binary; /// in-memory in tests. pub kv: Arc, /// Document store (v1.1.2). Backed by Postgres in the picloud /// binary; in-memory in tests. pub docs: Arc, /// Dead-letter management (v1.1.1). Scripts get /// `dead_letters::replay(id)` and `dead_letters::resolve(id, reason)`. pub dead_letters: Arc, /// Event emitter for the triggers outbox. Mutating service methods /// (`KvService::set/delete`, `DocsService::create/update/delete`, /// future `files::*`, etc.) call `events.emit(cx, event)` after /// the write succeeds. The outbox-backed impl in /// `manager-core::outbox_event_emitter` replaces v1.1.0's /// `NoopEventEmitter`. pub events: Arc, /// Module source (v1.1.3). The `PicloudModuleResolver` consults /// this to load `kind = 'module'` scripts that other scripts /// `import`. Backed by Postgres in the picloud binary; in-memory /// fakes in resolver tests. pub modules: Arc, /// Outbound HTTP (v1.1.4). Scripts get `http::{get,post,…}`. /// Backed by a reqwest client with the SSRF deny-list resolver in /// the picloud binary; `NoopHttpService` in tests that don't make /// network calls. pub http: Arc, /// Filesystem-backed blob storage (v1.1.5). Scripts get /// `files::collection(name).{create,head,get,update,delete,list}`. /// Backed by a Postgres-metadata + on-disk-bytes repo in the /// picloud binary; `NoopFilesService` in tests that don't touch /// files. pub files: Arc, /// Durable pub/sub (v1.1.5). Scripts get /// `pubsub::publish_durable(topic, message)`. Backed by a /// publish-time outbox fan-out in the picloud binary; /// `NoopPubsubService` in tests that don't publish. pub pubsub: Arc, /// Encrypted per-app secrets (v1.1.7). Scripts get /// `secrets::{get,set,delete,list}(name)`. Backed by an /// AES-256-GCM-at-rest Postgres repo in the picloud binary; /// `NoopSecretsService` in tests that don't touch secrets. pub secrets: Arc, /// Outbound email (v1.1.7). Scripts get `email::{send,send_html}`. /// Backed by an SMTP relay (lettre) in the picloud binary; /// `NoopEmailService` (always `NotConfigured`) in tests that don't /// send mail. pub email: Arc, } impl Services { /// Construct a bundle from already-constructed `Arc` handles. /// The picloud binary's `main` wires this up after the DB pool is /// open; tests build it from in-memory fakes. #[must_use] #[allow(clippy::too_many_arguments)] // one Arc per stateful service; a builder would just move the noise pub fn new( kv: Arc, docs: Arc, dead_letters: Arc, events: Arc, modules: Arc, http: Arc, files: Arc, pubsub: Arc, secrets: Arc, email: Arc, ) -> Self { Self { kv, docs, dead_letters, events, modules, http, files, pubsub, secrets, email, } } /// All-noop bundle for tests that build an `Engine` but don't /// exercise the stateful services. Returns the same shape as /// `Services::new` so callers can't accidentally rely on a stub /// silently doing the right thing — every call into a noop /// service surfaces an explicit error. #[must_use] pub fn with_noop_services() -> Self { Self::new( Arc::new(NoopKvService), Arc::new(NoopDocsService), Arc::new(NoopDeadLetterService), Arc::new(NoopEventEmitter), Arc::new(NoopModuleSource), Arc::new(NoopHttpService), Arc::new(NoopFilesService), Arc::new(NoopPubsubService), Arc::new(NoopSecretsService), Arc::new(NoopEmailService), ) } } impl Default for Services { fn default() -> Self { Self::with_noop_services() } }