use serde::{Deserialize, Serialize};
use tatara_lisp::DeriveTataraDomain;
use crate::SpecError;
#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone)]
#[tatara(keyword = "defsui-command")]
pub struct SuiCommand {
pub name: String,
#[serde(rename = "nixEquivalent")]
pub nix_equivalent: String,
pub maturity: SuiCommandMaturity,
#[serde(default)]
pub substrate: Vec<String>,
pub notes: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SuiCommandMaturity {
Working,
Partial,
Stub,
Missing,
SuiNative,
}
impl SuiCommandMaturity {
#[must_use]
pub fn name(self) -> &'static str {
match self {
Self::Working => "Working",
Self::Partial => "Partial",
Self::Stub => "Stub",
Self::Missing => "Missing",
Self::SuiNative => "SuiNative",
}
}
#[must_use]
pub fn counts_as_replacing_nix(self) -> bool {
matches!(self, Self::Working)
}
#[must_use]
pub fn is_queued_task(self) -> bool {
matches!(self, Self::Partial | Self::Stub | Self::Missing)
}
}
pub const CANONICAL_CLI_COVERAGE_LISP: &str =
include_str!("../specs/cli_coverage.lisp");
pub fn load_canonical() -> Result<Vec<SuiCommand>, SpecError> {
crate::loader::load_all::<SuiCommand>(CANONICAL_CLI_COVERAGE_LISP)
}
pub fn maturity_histogram()
-> Result<Vec<(SuiCommandMaturity, usize)>, SpecError>
{
use std::collections::BTreeMap;
let cat = load_canonical()?;
let mut counts: BTreeMap<String, usize> = BTreeMap::new();
for c in &cat {
*counts.entry(c.maturity.name().to_string()).or_default() += 1;
}
let order = [
SuiCommandMaturity::Working,
SuiCommandMaturity::Partial,
SuiCommandMaturity::Stub,
SuiCommandMaturity::Missing,
SuiCommandMaturity::SuiNative,
];
Ok(order
.into_iter()
.map(|m| (m, *counts.get(m.name()).unwrap_or(&0)))
.collect())
}
pub fn replacement_percentage() -> Result<f64, SpecError> {
let cat = load_canonical()?;
let total_nix: usize = cat.iter()
.filter(|c| c.maturity != SuiCommandMaturity::SuiNative)
.count();
let working: usize = cat.iter()
.filter(|c| c.maturity == SuiCommandMaturity::Working)
.count();
if total_nix == 0 {
return Ok(0.0);
}
Ok(working as f64 / total_nix as f64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn canonical_catalog_parses() {
let cat = load_canonical().expect("catalog must parse");
assert!(!cat.is_empty(), "catalog must have ≥1 entry");
}
#[test]
fn every_command_has_unique_name() {
let cat = load_canonical().unwrap();
let mut seen = std::collections::HashSet::new();
for c in &cat {
assert!(seen.insert(c.name.clone()),
"duplicate sui command name `{}`", c.name);
}
}
#[test]
fn histogram_sums_to_total() {
let cat = load_canonical().unwrap();
let hist = maturity_histogram().unwrap();
let total: usize = hist.iter().map(|(_, n)| n).sum();
assert_eq!(total, cat.len());
}
#[test]
fn replacement_percentage_is_in_range() {
let pct = replacement_percentage().unwrap();
assert!((0.0..=1.0).contains(&pct));
}
#[test]
fn every_substrate_ref_points_at_a_real_domain() {
let cat = load_canonical().unwrap();
let domains = crate::catalog::load_canonical().unwrap();
let names: std::collections::HashSet<String> = domains
.iter()
.map(|d| d.name.clone())
.collect();
for c in &cat {
for s in &c.substrate {
assert!(
names.contains(s),
"command `{}` references substrate `{}` which has no catalog entry",
c.name, s,
);
}
}
}
}