rskit_config/sink/contract.rs
1use std::fmt;
2
3use rskit_errors::AppResult;
4use rskit_util::SecretString;
5
6/// Adapter contract for writable configuration backends.
7///
8/// A `ConfigSink` persists or patches configuration values back to a backend — a file,
9/// an in-memory store, or a future remote backend such as Vault, SSM, or a Kubernetes secret.
10/// `rskit-config` owns the contract; concrete backends live in their own adapter crates
11/// and depend only on this trait.
12///
13/// Sinks are injected explicitly (never via a global registry) and are object-safe,
14/// so callers can hold a `Box<dyn ConfigSink>`.
15///
16/// Values flow as [`SecretString`] end-to-end. Implementations must never log the plaintext value;
17/// only the key may appear in diagnostics.
18/// Writing the plaintext to the backing store (e.g. a file) is the sink's intended,
19/// explicit persistence — not a leak.
20pub trait ConfigSink: fmt::Debug + Send + Sync + 'static {
21 /// Set or replace the value stored at `key`.
22 ///
23 /// # Errors
24 ///
25 /// Returns a typed [`AppError`](rskit_errors::AppError) (cause preserved) if the backend rejects the write
26 /// or is unreachable.
27 fn set(&self, key: &str, value: SecretString) -> AppResult<()>;
28
29 /// Remove the value stored at `key`.
30 ///
31 /// Removing a missing key succeeds (idempotent), unless the backend distinguishes absence as an error.
32 ///
33 /// # Errors
34 ///
35 /// Returns a typed [`AppError`](rskit_errors::AppError) (cause preserved) if the backend rejects the removal
36 /// or is unreachable.
37 fn remove(&self, key: &str) -> AppResult<()>;
38
39 /// Set many key/value pairs.
40 ///
41 /// The default applies each entry with [`set`](ConfigSink::set) in order
42 /// and fails fast on the first error, leaving earlier writes applied. Adapters with native batch
43 /// or transactional writes should override this for atomicity and efficiency.
44 ///
45 /// # Errors
46 ///
47 /// Returns the first per-key error encountered.
48 fn set_many(&self, entries: Vec<(String, SecretString)>) -> AppResult<()> {
49 for (key, value) in entries {
50 self.set(&key, value)?;
51 }
52 Ok(())
53 }
54}