prolly-store-postgres 0.3.0

PostgreSQL store adapter for prolly-map.
Documentation
use std::env;
use std::error::Error;
use std::io;

use prolly::{AsyncProlly, Config, RemoteProllyStore};
use prolly_store_postgres::PostgresBackend;

fn main() -> Result<(), Box<dyn Error>> {
    let mut args = env::args().skip(1);
    let operation = required(&mut args, "operation")?;
    let database_url = required(&mut args, "database URL")?;
    let root = required(&mut args, "root name")?.into_bytes();
    let key = required(&mut args, "key")?.into_bytes();
    let value = required(&mut args, "value")?.into_bytes();
    if args.next().is_some() {
        return Err(io::Error::new(io::ErrorKind::InvalidInput, "too many arguments").into());
    }

    tokio::runtime::Builder::new_multi_thread()
        .worker_threads(2)
        .enable_all()
        .build()?
        .block_on(async {
            let backend = PostgresBackend::connect(&database_url).await?;
            backend.initialize_schema().await?;
            let prolly = AsyncProlly::new(RemoteProllyStore::new(backend), Config::default());
            match operation.as_str() {
                "write" => {
                    let tree = prolly.put(&prolly.create(), key, value).await?;
                    prolly.publish_named_root(&root, &tree).await?;
                }
                "verify" => {
                    let tree = prolly.load_named_root(&root).await?.ok_or_else(|| {
                        io::Error::new(io::ErrorKind::NotFound, "named root is missing")
                    })?;
                    let actual = prolly.get(&tree, &key).await?.ok_or_else(|| {
                        io::Error::new(io::ErrorKind::NotFound, "tree key is missing")
                    })?;
                    if actual != value {
                        return Err(io::Error::new(
                            io::ErrorKind::InvalidData,
                            "tree value differs",
                        )
                        .into());
                    }
                }
                _ => {
                    return Err(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        "operation must be write or verify",
                    )
                    .into());
                }
            }
            Ok::<(), Box<dyn Error>>(())
        })
}

fn required(args: &mut impl Iterator<Item = String>, name: &str) -> io::Result<String> {
    args.next().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidInput,
            format!("missing {name} argument"),
        )
    })
}