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§
Sourceconst NAME: &'static str
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.
Required Associated Types§
Required Methods§
Sourcefn enable_in_project(cfg: &mut ProjectConfig)
fn enable_in_project(cfg: &mut ProjectConfig)
Mark the current project as using this service.
Sourcefn disable_in_project(cfg: &mut ProjectConfig)
fn disable_in_project(cfg: &mut ProjectConfig)
Remove this service’s entry from the current project config.
Sourcefn 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 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.
Sourcefn store_secrets(
secrets: &Self::Secrets,
scope: Scope<'_>,
) -> Result<Vec<SecretRef>, ZadError>
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.
Sourcefn delete_secrets(scope: Scope<'_>) -> Result<Vec<SecretRef>, ZadError>
fn delete_secrets(scope: Scope<'_>) -> Result<Vec<SecretRef>, ZadError>
Remove every keychain entry for this service at scope.
Idempotent.
Sourcefn inspect_secrets(scope: Scope<'_>) -> Result<Vec<SecretRef>, ZadError>
fn inspect_secrets(scope: Scope<'_>) -> Result<Vec<SecretRef>, ZadError>
Report keychain presence per account this service expects.
Sourcefn load_secrets(scope: Scope<'_>) -> Result<Option<Self::Secrets>, ZadError>
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.
Provided Methods§
Sourcefn post_create_hint(_cfg: &Self::Cfg) -> Option<String>
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".