revault_lockbox_api 0.0.4

reVault lockbox API to create and manage lockboxes
Documentation
use revault_lockbox_api::{
    FormFieldDefinition, FormFieldKind, FormValue, Lockbox, LockboxOpen, LockboxPath,
    LockboxProtection, OwnerSigningKeyPair, SecretString,
};

fn main() -> revault_lockbox_api::Result<()> {
    let root = example_root("forms")?;
    let lockbox_file = root.join("forms.lbox");
    let pass_phrase = pass_phrase()?;
    let signing_key = OwnerSigningKeyPair::generate()?;
    let password = SecretString::try_from_bytes(b"example-service-password".to_vec())?;

    let mut lockbox = Lockbox::create_file(
        &lockbox_file,
        LockboxProtection::Password(&pass_phrase),
        &signing_key,
    )?;
    let definition = lockbox.define_form(
        "login",
        "Login",
        vec![
            field("url", "URL", FormFieldKind::Url, true),
            field("username", "Username", FormFieldKind::Text, true),
            field("password", "Password", FormFieldKind::Secret, true),
        ],
    )?;
    lockbox.create_form_record(&path("/accounts/example")?, "login", "Example")?;
    lockbox.set_form_field_normal(&path("/accounts/example")?, "url", "https://example.com")?;
    lockbox.set_form_field_normal(&path("/accounts/example")?, "username", "alice")?;
    lockbox.set_form_field_secret(&path("/accounts/example")?, "password", &password)?;
    lockbox.commit()?;

    let opened = Lockbox::open(&lockbox_file, LockboxOpen::Password(&pass_phrase))?;
    let record = opened
        .get_form_record(&path("/accounts/example")?)?
        .expect("record exists");

    println!(
        "{} uses form {} revision {}",
        record.name, definition.alias, record.definition_revision
    );
    for field_value in record.values {
        match field_value.value {
            FormValue::Normal(value) => println!("{}={value}", field_value.field_id),
            FormValue::Secret(secret) => {
                let len = secret.with_str(|value| value.len())?;
                println!("{}=<secret, {len} chars>", field_value.field_id);
            }
        }
    }
    Ok(())
}

fn field(id: &str, label: &str, kind: FormFieldKind, required: bool) -> FormFieldDefinition {
    FormFieldDefinition {
        id: id.to_string(),
        label: label.to_string(),
        kind,
        required,
    }
}

fn path(value: &str) -> revault_lockbox_api::Result<LockboxPath> {
    LockboxPath::new(value)
}

fn pass_phrase() -> revault_lockbox_api::Result<SecretString> {
    Ok(SecretString::try_from_bytes(
        b"correct horse battery staple".to_vec(),
    )?)
}

fn example_root(name: &str) -> revault_lockbox_api::Result<std::path::PathBuf> {
    let root = std::env::temp_dir()
        .join("lockbox-core-examples")
        .join(name);
    match std::fs::remove_dir_all(&root) {
        Ok(()) => {}
        Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
        Err(err) => return Err(revault_lockbox_api::Error::Io(err.to_string())),
    }
    std::fs::create_dir_all(&root)
        .map_err(|err| revault_lockbox_api::Error::Io(err.to_string()))?;
    Ok(root)
}