io_keyring/coroutines/
write.rs

1//! I/O-free coroutine for saving a keyring entry secret.
2
3use log::trace;
4use secrecy::SecretString;
5use thiserror::Error;
6
7use crate::{entry::KeyringEntry, io::KeyringIo};
8
9/// Errors that can occur during the coroutine progression.
10#[derive(Clone, Debug, Error)]
11pub enum WriteSecretError {
12    /// The coroutine received an invalid argument.
13    #[error("Invalid argument: expected {0}, got {1:?}")]
14    InvalidArgument(&'static str, KeyringIo),
15
16    /// The entry and secret were not ready.
17    #[error("Entry and secret not ready")]
18    NotReady,
19}
20
21/// Output emitted after a coroutine finishes its progression.
22#[derive(Clone, Debug)]
23pub enum WriteSecretResult {
24    /// The coroutine has successfully terminated its progression.
25    Ok(()),
26
27    /// A keyring I/O needs to be performed to make the coroutine progress.
28    Io(KeyringIo),
29
30    /// An error occurred during the coroutine progression.
31    Err(WriteSecretError),
32}
33
34/// I/O-free coroutine for saving a keyring entry secret.
35#[derive(Clone, Debug)]
36pub struct WriteSecret {
37    secret: Option<(KeyringEntry, SecretString)>,
38}
39
40impl WriteSecret {
41    /// Creates a new coroutine for the given entry and secret.
42    pub fn new(entry: KeyringEntry, secret: impl Into<SecretString>) -> Self {
43        let secret = Some((entry, secret.into()));
44        Self { secret }
45    }
46
47    /// Makes the coroutine progress.
48    pub fn resume(&mut self, arg: Option<KeyringIo>) -> WriteSecretResult {
49        let Some(arg) = arg else {
50            let Some(secret) = self.secret.take() else {
51                return WriteSecretResult::Err(WriteSecretError::NotReady);
52            };
53
54            trace!("need I/O to write secret into keyring entry");
55            return WriteSecretResult::Io(KeyringIo::Write(Err(secret)));
56        };
57
58        let KeyringIo::Write(io) = arg else {
59            let err = WriteSecretError::InvalidArgument("write output", arg);
60            return WriteSecretResult::Err(err);
61        };
62
63        if let Err((entry, secret)) = io {
64            return WriteSecretResult::Io(KeyringIo::Write(Err((entry, secret))));
65        }
66
67        trace!("resume after writing secret into keyring entry");
68        WriteSecretResult::Ok(())
69    }
70}