io-keyring 0.0.2

Set of I/O-free Rust coroutines and runtimes to manage keyring entries
Documentation
#![cfg(feature = "std")]

use std::{
    env,
    io::{stdin, stdout, Write},
};

use io_keyring::{
    coroutines::{
        delete::{DeleteSecret, DeleteSecretResult},
        read::{ReadSecret, ReadSecretResult},
        write::{WriteSecret, WriteSecretResult},
    },
    entry::KeyringEntry,
    runtimes::std::handle,
};
use secrecy::ExposeSecret;

fn main() {
    env_logger::init();

    let service = match env::var("SERVICE") {
        Ok(service) => service,
        Err(_) => read_line("Keyring service?"),
    };

    let name = match env::var("NAME") {
        Ok(name) => name,
        Err(_) => read_line("Keyring entry name?"),
    };

    let password = match env::var("PASSWORD") {
        Ok(password) => password,
        Err(_) => read_line("Keyring entry password?"),
    };

    let entry = KeyringEntry::new(name).with_service(service);

    let mut arg = None;
    let mut read = ReadSecret::new(entry.clone());

    let secret = loop {
        match read.resume(arg.take()) {
            ReadSecretResult::Ok(secret) => break Ok(secret),
            ReadSecretResult::Io(io) => match handle(io) {
                Ok(io) => arg = Some(io),
                Err(err) => break Err(err),
            },
            ReadSecretResult::Err(err) => panic!("{err}"),
        }
    };

    println!("first read: {secret:?}");

    println!("store new password");

    let mut arg = None;
    let mut write = WriteSecret::new(entry.clone(), password);

    loop {
        match write.resume(arg.take()) {
            WriteSecretResult::Ok(()) => break,
            WriteSecretResult::Io(io) => arg = Some(handle(io).unwrap()),
            WriteSecretResult::Err(err) => panic!("{err}"),
        }
    }

    let mut arg = None;
    let mut read = ReadSecret::new(entry.clone());

    let secret = loop {
        match read.resume(arg.take()) {
            ReadSecretResult::Ok(secret) => break secret,
            ReadSecretResult::Io(io) => arg = Some(handle(io).unwrap()),
            ReadSecretResult::Err(err) => panic!("{err}"),
        }
    };

    println!("second read: {:?}", secret.expose_secret());

    println!("delete entry");

    let mut arg = None;
    let mut delete = DeleteSecret::new(entry.clone());

    while let DeleteSecretResult::Io(io) = delete.resume(arg) {
        arg = Some(handle(io).unwrap());
    }

    let mut arg = None;
    let mut read = ReadSecret::new(entry);

    let secret = loop {
        match read.resume(arg.take()) {
            ReadSecretResult::Ok(secret) => break Ok(secret),
            ReadSecretResult::Io(io) => match handle(io) {
                Ok(io) => arg = Some(io),
                Err(err) => break Err(err),
            },
            ReadSecretResult::Err(err) => panic!("{err}"),
        }
    };

    println!("no third read possible: {secret:?}");
}

fn read_line(prompt: &str) -> String {
    print!("{prompt} ");
    stdout().flush().unwrap();

    let mut line = String::new();
    stdin().read_line(&mut line).unwrap();

    line.trim().to_owned()
}