sneakerweb 1.2.0

A parallel web transported by physical media
use crate::{
    serve::domain_is_empty,
    util::{
        BASE32_ALPHABET, SNEAKERWEB_NAMESPACE_ID_BYTES, domain_style, secret_style, sneakerweb_dir,
    },
};
use anyhow::Result;
use clap::{Args, Subcommand};
use rand::rngs::OsRng;
use std::path::PathBuf;
use ufotofu::IntoConsumer;
use willow25::{
    entry::{NamespaceId, randomly_generate_subspace},
    storage::PersistentStore,
};

#[derive(Args)]
pub struct DomainArgs {
    #[command(subcommand)]
    pub command: Option<DomainCommands>,
}

#[derive(Subcommand)]
pub enum DomainCommands {
    /// List all domains in your local sneakerweb collection.
    ///
    /// The base-32 domains are printed to stdout, one per line.
    List(ListArgs),
}

#[derive(Args)]
pub struct ListArgs {
    /// Show domains with no content.
    ///
    /// Sites which are overwritten with an empty directory are effectively deleted. Pass this to
    /// show them anyways.
    #[arg(long)]
    pub show_empty: bool,
    /// The path to the collection whose domains should be listed.
    ///
    /// If no such path is specified, domains from the default collection (stored at ~/.sneakerweb)
    /// are listed.
    #[arg(short, long)]
    collection: Option<PathBuf>,
}

pub fn generate_domain(_args: &DomainArgs) {
    let mut csprng = OsRng;
    let (subspace_id, subspace_secret) = randomly_generate_subspace(&mut csprng);

    let b32_id = base32::encode(BASE32_ALPHABET, subspace_id.as_bytes());

    let b32_secret = base32::encode(BASE32_ALPHABET, subspace_secret.as_bytes());

    println!("Domain: {}", domain_style(&b32_id));
    println!("Secret: {}", secret_style(&b32_secret));
    println!(
        "{} It is the password for publishing to this domain.",
        secret_style("Keep this secret somewhere safe."),
    );
}

pub async fn list_domains(args: &ListArgs) -> Result<()> {
    let sneakerweb_fs_path = sneakerweb_dir(args.collection.as_ref()).await?;
    let mut store = PersistentStore::new(sneakerweb_fs_path).await?;

    let namespace = NamespaceId::from_bytes(&SNEAKERWEB_NAMESPACE_ID_BYTES);
    let mut subspace_consumer = vec![].into_consumer();

    store.subspaces(&namespace, &mut subspace_consumer).await?;

    for domain in Vec::from(subspace_consumer) {
        if args.show_empty || !domain_is_empty(&mut store, &domain).await? {
            println!("{}", base32::encode(BASE32_ALPHABET, domain.as_bytes()));
        }
    }

    Ok(())
}