fjall-cli 1.0.1

CLI for Fjall database
Documentation
use crate::{ByteEncoding, ByteEncodingDecodeError, PrefixKind, Suffix};
use errgonomic::{handle, handle_bool, handle_opt};
use fjall::{Database, KeyspaceCreateOptions};
use std::io;
use std::io::Write;
use std::process::ExitCode;
use thiserror::Error;

#[derive(clap::Parser, Clone, Debug)]
pub struct GetCommand {
    #[arg(value_name = "KEY")]
    key: String,

    #[arg(long, value_enum, default_value_t = ByteEncoding::String)]
    key_encoding: ByteEncoding,

    #[arg(long, value_enum)]
    value_prefix: Option<PrefixKind>,

    #[arg(long)]
    value_suffix: Option<Suffix>,
}

impl GetCommand {
    pub async fn run(self, db: &Database, keyspace: impl Into<String>) -> Result<ExitCode, GetCommandRunError> {
        use GetCommandRunError::*;
        let keyspace = keyspace.into();
        let Self {
            key,
            key_encoding,
            value_prefix,
            value_suffix,
        } = self;
        let key_bytes = handle!(key_encoding.decode(&key), DecodeKeyBytesFailed, key, key_encoding);
        handle_bool!(!db.keyspace_exists(&keyspace), KeyspaceNotFound, keyspace);
        let keyspace_handle = handle!(db.keyspace(&keyspace, KeyspaceCreateOptions::default), KeyspaceFailed, keyspace);
        let value_opt = handle!(keyspace_handle.get(&key_bytes), GetFailed, keyspace, key);
        let value = handle_opt!(value_opt, KeyNotFound, keyspace, key);
        let mut stdout = io::stdout().lock();
        if let Some(prefix) = value_prefix {
            let bytes = prefix.write(&value);
            handle!(stdout.write_all(&bytes), WriteAllFailed);
        }
        handle!(stdout.write_all(value.as_ref()), WriteAllFailed);
        if let Some(suffix) = value_suffix {
            handle!(stdout.write_all(suffix.as_bytes()), WriteAllFailed);
        }
        Ok(ExitCode::SUCCESS)
    }
}

#[derive(Error, Debug)]
pub enum GetCommandRunError {
    #[error("failed to decode key '{key}' with encoding '{key_encoding}'")]
    DecodeKeyBytesFailed { source: ByteEncodingDecodeError, key: String, key_encoding: ByteEncoding },

    #[error("keyspace '{keyspace}' not found")]
    KeyspaceNotFound { keyspace: String },

    #[error("failed to open keyspace '{keyspace}'")]
    KeyspaceFailed { source: fjall::Error, keyspace: String },

    #[error("failed to get key '{key}' from keyspace '{keyspace}'")]
    GetFailed { source: fjall::Error, keyspace: String, key: String },

    #[error("key '{key}' not found in keyspace '{keyspace}'")]
    KeyNotFound { keyspace: String, key: String },

    #[error("failed to write value to stdout")]
    WriteAllFailed { source: io::Error },
}