sql-hummus 0.1.0

Log files and key-value stores in SQLite.
Documentation
use std::{io::Read, process::ExitCode};

use anyhow::Result;
use clap::Parser as _;
use sql_hummus::{Kv, Log};

use crate::cli::{Command, KvCommand, LogCommand};

mod cli;

fn truthy_code() -> ExitCode {
    0.into()
}

fn falsy_code() -> ExitCode {
    1.into()
}

fn err_code() -> ExitCode {
    2.into()
}

fn main() -> ExitCode {
    match inner_main() {
        Ok(true) => truthy_code(),
        Ok(false) => falsy_code(),
        Err(e) => {
            eprintln!("{e}");
            err_code()
        }
    }
}

fn inner_main() -> Result<bool> {
    let cli = cli::Cli::try_parse()?;
    match cli.cmd {
        Command::Kv { cmd, path } => match cmd {
            KvCommand::ContainsKey { key } => match Kv::new(path)?.contains_key(key)? {
                true => {
                    println!("true");
                    Ok(true)
                }
                false => {
                    println!("false");
                    Ok(false)
                }
            },
            KvCommand::Get { key } => match Kv::new(path)?.get(key)? {
                Some(value) => {
                    println!("{value}");
                    Ok(true)
                }
                None => {
                    eprintln!("Key not in KV file");
                    Ok(false)
                }
            },
            KvCommand::Insert { key, value } => {
                Kv::new(path)?.insert(key, value)?;
                Ok(true)
            }
            KvCommand::WithPrefix { prefix } => {
                let kv = Kv::new(path)?;
                let cursor = kv.with_prefix(&prefix)?;
                for row in cursor {
                    let (k, v) = row?;
                    println!("{}", serde_json::to_string(&(k, v))?);
                }
                Ok(true)
            }
        },
        Command::Log { cmd, path } => match cmd {
            LogCommand::Get { index } => {
                let index = match index {
                    cli::LogIndex::DateRange(..) => todo!(),
                    cli::LogIndex::DateScalar(..) => todo!(),
                    cli::LogIndex::NumberRange(..) => todo!(),
                    cli::LogIndex::NumberScalar(index) => index,
                };
                let Some(line) = Log::new(&path)?.get_by_index(index)? else {
                    eprintln!("{path:?} has no element at index {index}");
                    return Ok(false);
                };
                println!("{}", serde_json::to_string(&line)?);
                Ok(true)
            }
            LogCommand::Iter => {
                let log = Log::new(path)?;
                let cursor = log.iter()?;
                for row in cursor {
                    let line = row?;
                    println!("{}", serde_json::to_string(&line)?);
                }
                Ok(true)
            }
            LogCommand::Push { content } => {
                let content = match content {
                    Some(x) => x,
                    None => {
                        let mut buf = Vec::with_capacity(1_024 * 1_024);
                        std::io::stdin().read_to_end(&mut buf)?;
                        String::from_utf8(buf)?
                    }
                };
                let index = Log::new(path)?.push(content)?;
                println!("{index}");
                Ok(true)
            }
        },
    }
}