io_keyring/coroutines/
read.rs

1//! I/O-free coroutine for reading a secret from a keyring entry.
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 ReadSecretError {
12    /// The coroutine received an invalid argument.
13    ///
14    /// Occurs when the coroutine receives an I/O response from
15    /// another coroutine, which should not happen if the runtime maps
16    /// correctly the arguments.
17    #[error("Invalid argument: expected {0}, got {1:?}")]
18    InvalidArgument(&'static str, KeyringIo),
19
20    /// The entry was not ready.
21    #[error("Entry not ready")]
22    NotReady,
23}
24
25/// Output emitted after a coroutine finishes its progression.
26#[derive(Clone, Debug)]
27pub enum ReadSecretResult {
28    /// The coroutine has successfully terminated its progression.
29    Ok(SecretString),
30
31    /// A keyring I/O needs to be performed to make the coroutine progress.
32    Io(KeyringIo),
33
34    /// An error occurred during the coroutine progression.
35    Err(ReadSecretError),
36}
37
38/// I/O-free coroutine for reading a secret from a keyring entry.
39#[derive(Clone, Debug)]
40pub struct ReadSecret {
41    entry: Option<KeyringEntry>,
42}
43
44impl ReadSecret {
45    /// Creates a new coroutine for the given entry.
46    pub fn new(entry: KeyringEntry) -> Self {
47        Self { entry: Some(entry) }
48    }
49
50    /// Makes the coroutine progress.
51    pub fn resume(&mut self, arg: Option<KeyringIo>) -> ReadSecretResult {
52        let Some(arg) = arg else {
53            let Some(entry) = self.entry.take() else {
54                return ReadSecretResult::Err(ReadSecretError::NotReady);
55            };
56
57            trace!("need I/O to read secret from keyring entry");
58            return ReadSecretResult::Io(KeyringIo::Read(Err(entry)));
59        };
60
61        let KeyringIo::Read(io) = arg else {
62            return ReadSecretResult::Err(ReadSecretError::InvalidArgument("read output", arg));
63        };
64
65        let secret = match io {
66            Ok(secret) => secret,
67            Err(entry) => return ReadSecretResult::Io(KeyringIo::Read(Err(entry))),
68        };
69
70        trace!("resume after reading keyring entry");
71        ReadSecretResult::Ok(secret)
72    }
73}