hennzau

21 commits
Updated 2026-04-25 12:39:37
hnz/src
hnz/src/main.rs
use std::{ops::Deref, path::PathBuf, sync::Arc};

use clap::{ArgMatches, Command, arg};

use jj_lib::{
    backend::{Backend, BackendLoadError},
    config::StackedConfig,
    default_index::DefaultIndexStore,
    default_submodule_store::DefaultSubmoduleStore,
    git_backend::GitBackend,
    repo::{ReadonlyRepo, RepoLoader, RepoLoaderError, StoreFactories, StoreLoadError},
    settings::UserSettings,
    simple_op_heads_store::SimpleOpHeadsStore,
    simple_op_store::SimpleOpStore,
};

mod back;
mod front;
mod sync;

pub struct Help<'a>(&'a ArgMatches);

impl<'a> Deref for Help<'a> {
    type Target = ArgMatches;

    fn deref(&self) -> &Self::Target {
        self.0
    }
}

fn main() {
    let matches = Command::new("hnz")
        .version("1.0")
        .about("Manage hennzau's jj hub")
        .subcommand_required(true)
        .subcommand(
            Command::new("gen-front")
                .about("Generate the correct .md file for a repo")
                .arg(
                    arg!(<REPO_PATH>)
                        .required(true)
                        .value_parser(clap::value_parser!(PathBuf))
                        .help("Root path of a jj repository"),
                )
                .arg(
                    arg!(<OUTPUT>)
                        .required(true)
                        .value_parser(clap::value_parser!(PathBuf))
                        .help("Path where the <REPO_NAME>.md will be created"),
                ),
        )
        .subcommand(
            Command::new("gen-back")
                .about("Generate the correct .json file for a repo")
                .arg(
                    arg!(<REPO_PATH>)
                        .required(true)
                        .value_parser(clap::value_parser!(PathBuf))
                        .help("Root path of a jj repository"),
                )
                .arg(
                    arg!(<OUTPUT>)
                        .required(true)
                        .value_parser(clap::value_parser!(PathBuf))
                        .help("Path where the <REPO_NAME>.json will be created"),
                ),
        )
        .subcommand(
            Command::new("sync")
                .about("Make sure to remove front and back files for non existing repositories")
                .arg(
                    arg!(<BASE_PATH>)
                        .required(true)
                        .value_parser(clap::value_parser!(PathBuf))
                        .help("Root path of a jj hub"),
                )
                .arg(
                    arg!(<MD_PATH>)
                        .required(true)
                        .value_parser(clap::value_parser!(PathBuf))
                        .help("Path where the .md files must be sync"),
                )
                .arg(
                    arg!(<JSON_PATH>)
                        .required(true)
                        .value_parser(clap::value_parser!(PathBuf))
                        .help("Path where the .json files must be sync"),
                ),
        )
        .get_matches();

    match matches.subcommand() {
        Some(("gen-front", args)) => {
            if let Err(e) = front::gen_front(Help(args)) {
                println!("{e:?}")
            }
        }
        Some(("gen-back", args)) => {
            if let Err(e) = back::gen_back(Help(args)) {
                println!("{e:?}")
            }
        }
        Some(("sync", args)) => {
            if let Err(e) = sync::sync(Help(args)) {
                println!("{e:?}")
            }
        }
        _ => unreachable!(),
    }
}

impl Help<'_> {
    pub fn load(
        &self,
        path: PathBuf,
    ) -> Result<Result<Arc<ReadonlyRepo>, RepoLoaderError>, StoreLoadError> {
        let mut store_factories = StoreFactories::empty();
        store_factories.add_backend(
            "git",
            Box::new(|a, b| {
                GitBackend::load(a, b)
                    .map(|git| Box::new(git) as Box<dyn Backend>)
                    .map_err(|e| BackendLoadError(Box::new(*e)))
            }),
        );
        store_factories.add_op_store(
            "simple_op_store",
            Box::new(|_, b, c| Ok(Box::new(SimpleOpStore::load(b, c)))),
        );
        store_factories.add_op_heads_store(
            "simple_op_heads_store",
            Box::new(|_, b| Ok(Box::new(SimpleOpHeadsStore::load(b)))),
        );
        store_factories.add_index_store(
            "default",
            Box::new(|_, b| Ok(Box::new(DefaultIndexStore::load(b)))),
        );
        store_factories.add_submodule_store(
            "default",
            Box::new(|_, b| Ok(Box::new(DefaultSubmoduleStore::load(b)))),
        );

        let config = StackedConfig::with_defaults();
        let settings = UserSettings::from_config(config)
            .expect("UserSettings from default config should never fail");

        RepoLoader::init_from_file_system(&settings, &path, &store_factories)
            .map(|loader| loader.load_at_head())
    }
}