git-remote-iroh 0.2.0

Git remote protocol support for https://www.iroh.computer
Documentation
use std::path::PathBuf;

use iroh::{PublicKey, SecretKey};
use iroh_rings::{Permission, RedbRegistry, Registry};
use tracing::{debug, instrument};

use crate::{GitOp, keys::load_or_create_key};

#[instrument(err)]
pub(super) fn open_db(config_dir: &PathBuf) -> Result<RedbRegistry, anyhow::Error> {
    let db_path = config_dir.join("auth.db");
    debug!(?db_path, "opening auth db");
    Ok(RedbRegistry::open(db_path)?)
}

pub const SINGLE_REPO_RESOURCE: &'static str = "<repo>";

pub(super) struct AuthAdmin {
    reg: RedbRegistry,
    pub(super) key: SecretKey,
    pub(super) key_file: PathBuf,
}

impl AuthAdmin {
    pub(super) fn new(config_dir: &PathBuf, git_dir: &PathBuf) -> Result<Self, anyhow::Error> {
        let reg = open_db(&config_dir)?;
        let (key_file, key) = load_or_create_key(config_dir, git_dir)?;
        Ok(Self { reg, key, key_file })
    }

    #[instrument(skip(self), err)]
    pub(super) fn add_peer(
        &self,
        ring_name: &str,
        peer_name: &str,
        id: &str,
    ) -> Result<(), anyhow::Error> {
        match self.reg.create_ring(ring_name) {
            Ok(()) => {
                self.reg.add_ring_to_resource(
                    GitOp::ClientPushServerFetch.resource_id(SINGLE_REPO_RESOURCE),
                    ring_name,
                    &[Permission::Write],
                )?;
                self.reg.add_ring_to_resource(
                    GitOp::ClientFetchServerPush.resource_id(SINGLE_REPO_RESOURCE),
                    ring_name,
                    &[Permission::Read],
                )?;
                Ok(())
            }
            Err(iroh_rings::Error::RingAlreadyExists(_)) => Ok(()),
            res => res,
        }?;
        self.reg
            .add_peer_to_ring(ring_name, PublicKey::from_z32(id)?, Some(peer_name))?;
        Ok(())
    }

    #[instrument(skip(self), err)]
    pub(super) fn list_peers(&self, ring_name: &str) -> Result<(), anyhow::Error> {
        let unnamed = "?".to_string();
        for (public_key, name) in self.reg.list_ring_peers(ring_name)?.iter() {
            println!(
                "{} {}",
                name.as_ref().unwrap_or(&unnamed),
                public_key.to_z32()
            );
        }
        Ok(())
    }

    #[instrument(skip(self), err)]
    pub(super) fn list_rings(&self) -> Result<(), anyhow::Error> {
        for ring in self.reg.list_rings()?.iter() {
            println!("{}", ring.as_str());
        }
        Ok(())
    }
}