mk 0.7.10

Yet another simple task runner 🦀
Documentation
use std::fs::{
  self,
  File,
};
use std::path::Path;

use clap::Args;
use console::style;
use pgp::composed::{
  Deserializable as _,
  SignedSecretKey,
};
use pgp::types::KeyDetails as _;
use prettytable::format::consts;
use prettytable::{
  row,
  Table,
};

use crate::secrets::context::Context;

use super::KEY_LOCATION_HELP;

#[derive(Debug, Args)]
pub struct ListKeys {
  /// The location to store the private key
  #[arg(short, long, help = KEY_LOCATION_HELP)]
  location: Option<String>,
}

impl ListKeys {
  pub fn execute(&self, context: &Context) -> anyhow::Result<()> {
    let location: &str = &self.location.clone().unwrap_or_else(|| context.keys_location());

    assert!(!location.is_empty(), "Location must be provided");

    let path = Path::new(location);
    if path.exists() && path.is_dir() {
      let entries = fs::read_dir(path)?
        .filter_map(Result::ok)
        .filter(|entry| entry.path().extension().and_then(|ext| ext.to_str()) == Some("key"))
        .map(|entry| {
          entry
            .path()
            .file_stem()
            .and_then(|stem| stem.to_str())
            .unwrap_or("")
            .to_string()
        })
        .collect::<Vec<_>>();

      if entries.is_empty() {
        println!(
          "No keys found at '{}'. Generate one with: mk secrets key gen",
          location
        );
        return Ok(());
      }

      let mut table = Table::new();
      table.set_format(*consts::FORMAT_CLEAN);
      table.set_titles(row![Fbb->"Name", Fbb->"Key ID", Fbb->"Fingerprint"]);
      for entry in entries {
        let key_name: &str = &entry.clone();
        let filename_with_ext = format!("{key_name}.key");
        let key_path = Path::new(location).join(filename_with_ext);
        let mut secret_key_string = File::open(key_path)?;
        let (signed_secret_key, _) = SignedSecretKey::from_armor_single(&mut secret_key_string)?;
        signed_secret_key.verify_bindings()?;

        let key_id = hex::encode(signed_secret_key.legacy_key_id());
        let fingerprint = hex::encode(signed_secret_key.fingerprint().as_bytes());

        table.add_row(row![b->&key_name, Fg->&key_id, Fg->&fingerprint]);
      }
      let msg = style("Available keys:").bold().cyan();
      println!();
      println!("{msg}");
      println!();
      table.printstd();
    } else {
      println!(
        "Keys directory '{}' does not exist. Generate a key first with: mk secrets key gen",
        location
      );
    }

    Ok(())
  }
}