Skip to main content

LifecycleService

Trait LifecycleService 

Source
pub trait LifecycleService:
    Send
    + Sync
    + 'static {
    type Cfg: Serialize + DeserializeOwned + Clone + Send + Sync + 'static;
    type Secrets: Send + Sync;

    const NAME: &'static str;
    const DISPLAY: &'static str;

    // Required methods
    fn enable_in_project(cfg: &mut ProjectConfig);
    fn disable_in_project(cfg: &mut ProjectConfig);
    fn validate<'life0, 'life1, 'async_trait>(
        cfg: &'life0 Self::Cfg,
        secrets: &'life1 mut Self::Secrets,
    ) -> Pin<Box<dyn Future<Output = Result<String, ZadError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             Self: 'async_trait;
    fn store_secrets(
        secrets: &Self::Secrets,
        scope: Scope<'_>,
    ) -> Result<Vec<SecretRef>, ZadError>;
    fn delete_secrets(scope: Scope<'_>) -> Result<Vec<SecretRef>, ZadError>;
    fn inspect_secrets(scope: Scope<'_>) -> Result<Vec<SecretRef>, ZadError>;
    fn load_secrets(scope: Scope<'_>) -> Result<Option<Self::Secrets>, ZadError>;
    fn cfg_human(cfg: &Self::Cfg) -> Vec<(&'static str, String)>;
    fn cfg_json(cfg: &Self::Cfg) -> Value;
    fn scopes_of(cfg: &Self::Cfg) -> &[String];

    // Provided method
    fn post_create_hint(_cfg: &Self::Cfg) -> Option<String> { ... }
}
Expand description

Library-shaped lifecycle plumbing for one service. Implement this when adding a new service so the typed create / enable / … driver functions below work for it.

The CLI extends this with CliLifecycle (in zad-cli) that adds the CreateArgs: clap::Args associated type plus an interactive resolve step. Library callers don’t need that — they construct (Cfg, Secrets) themselves and call create directly.

Required Associated Constants§

Source

const NAME: &'static str

Lowercase identifier used in paths, commands, and keychain account names ("discord", "telegram", …). Must match the entry in crate::service::registry::SERVICES.

Source

const DISPLAY: &'static str

Capitalized display name for human-facing output ("Discord").

Required Associated Types§

Source

type Cfg: Serialize + DeserializeOwned + Clone + Send + Sync + 'static

Non-secret per-service config persisted to the service’s config.toml. Anything that isn’t a credential belongs here.

Source

type Secrets: Send + Sync

Credential material held in the OS keychain. Shape is up to the service: one bot token, three OAuth fields, a PEM blob — whatever the provider needs.

Required Methods§

Source

fn enable_in_project(cfg: &mut ProjectConfig)

Mark the current project as using this service.

Source

fn disable_in_project(cfg: &mut ProjectConfig)

Remove this service’s entry from the current project config.

Source

fn validate<'life0, 'life1, 'async_trait>( cfg: &'life0 Self::Cfg, secrets: &'life1 mut Self::Secrets, ) -> Pin<Box<dyn Future<Output = Result<String, ZadError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Confirm the credentials work by pinging the provider. Returns a short identifier (bot username, GitHub App slug) on success.

secrets is mutable because the validate ping can itself cause a credential rotation: Spotify’s PKCE flow rotates the refresh token on every /api/token call. Impls that talk to rotating providers wire a crate::oauth::RefreshTokenStore into the underlying client and update secrets in place; impls that don’t simply ignore the mutability and behave as before. The driver uses the (possibly-mutated) secrets for the subsequent store_secrets call so the keychain ends up holding the latest token instead of the pre-rotation one.

Source

fn store_secrets( secrets: &Self::Secrets, scope: Scope<'_>, ) -> Result<Vec<SecretRef>, ZadError>

Write each piece of secret material to the OS keychain at scope. Returns one SecretRef per account written.

Source

fn delete_secrets(scope: Scope<'_>) -> Result<Vec<SecretRef>, ZadError>

Remove every keychain entry for this service at scope. Idempotent.

Source

fn inspect_secrets(scope: Scope<'_>) -> Result<Vec<SecretRef>, ZadError>

Report keychain presence per account this service expects.

Source

fn load_secrets(scope: Scope<'_>) -> Result<Option<Self::Secrets>, ZadError>

Load the full secret material from the keychain at scope. Returns Ok(None) if any required account is missing.

Source

fn cfg_human(cfg: &Self::Cfg) -> Vec<(&'static str, String)>

Human-readable non-secret fields, as (label, value) pairs.

Source

fn cfg_json(cfg: &Self::Cfg) -> Value

Non-secret fields rendered for --json.

Source

fn scopes_of(cfg: &Self::Cfg) -> &[String]

Declared scopes — stored verbatim in the TOML config’s scopes array.

Provided Methods§

Source

fn post_create_hint(_cfg: &Self::Cfg) -> Option<String>

Optional URL to surface immediately after create succeeds. Default: no hint.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§