use serde::de::DeserializeOwned;
use tatara_lisp::TataraDomain;
use crate::SpecError;
pub trait Spec: TataraDomain + DeserializeOwned + Sized {
const CANONICAL_LISP: &'static str;
fn load_canonical_all() -> Result<Vec<Self>, SpecError> {
crate::loader::load_all::<Self>(Self::CANONICAL_LISP)
}
fn load_named(name: &str) -> Result<Self, SpecError>
where
Self: HasName,
{
Self::load_canonical_all()?
.into_iter()
.find(|s| s.name() == name)
.ok_or_else(|| {
SpecError::Load(format!(
"no ({}) with :name {name:?}",
Self::KEYWORD,
))
})
}
}
pub trait HasName {
fn name(&self) -> &str;
}
impl Spec for crate::derivation::DerivationAlgorithm {
const CANONICAL_LISP: &'static str = crate::derivation::CPPNIX_INPUT_ADDRESSED_LISP;
}
impl HasName for crate::derivation::DerivationAlgorithm {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::flake::FlakeShape {
const CANONICAL_LISP: &'static str = crate::flake::CPPNIX_FLAKE_SHAPE_LISP;
}
impl HasName for crate::flake::FlakeShape {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::probe::Probe {
const CANONICAL_LISP: &'static str = crate::probe::CANONICAL_PROBES_LISP;
}
impl HasName for crate::probe::Probe {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::rebuild::RebuildProbe {
const CANONICAL_LISP: &'static str = crate::rebuild::CANONICAL_REBUILD_PROBES_LISP;
}
impl HasName for crate::rebuild::RebuildProbe {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::module_system::ModuleEvalAlgorithm {
const CANONICAL_LISP: &'static str = crate::module_system::CANONICAL_MODULE_SYSTEM_LISP;
}
impl HasName for crate::module_system::ModuleEvalAlgorithm {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::module_system::OptionTypeSpec {
const CANONICAL_LISP: &'static str = crate::module_system::CANONICAL_MODULE_SYSTEM_LISP;
}
impl HasName for crate::module_system::OptionTypeSpec {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::module_system::PriorityRank {
const CANONICAL_LISP: &'static str = crate::module_system::CANONICAL_MODULE_SYSTEM_LISP;
}
impl HasName for crate::module_system::PriorityRank {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::activation_script::ActivationScriptAlgorithm {
const CANONICAL_LISP: &'static str = crate::activation_script::CANONICAL_ACTIVATION_LISP;
}
impl HasName for crate::activation_script::ActivationScriptAlgorithm {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::fetcher::FetcherSpec {
const CANONICAL_LISP: &'static str = crate::fetcher::CANONICAL_FETCHERS_LISP;
}
impl HasName for crate::fetcher::FetcherSpec {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::substituter::SubstituterSpec {
const CANONICAL_LISP: &'static str = crate::substituter::CANONICAL_SUBSTITUTERS_LISP;
}
impl HasName for crate::substituter::SubstituterSpec {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::sandbox::SandboxSpec {
const CANONICAL_LISP: &'static str = crate::sandbox::CANONICAL_SANDBOX_LISP;
}
impl HasName for crate::sandbox::SandboxSpec {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::store_layout::StoreLayout {
const CANONICAL_LISP: &'static str = crate::store_layout::CANONICAL_STORE_LAYOUT_LISP;
}
impl HasName for crate::store_layout::StoreLayout {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::gc::GcAlgorithm {
const CANONICAL_LISP: &'static str = crate::gc::CANONICAL_GC_LISP;
}
impl HasName for crate::gc::GcAlgorithm {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::hash::HashAlgorithm {
const CANONICAL_LISP: &'static str = crate::hash::CANONICAL_HASH_LISP;
}
impl HasName for crate::hash::HashAlgorithm {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::hash::HashEncoding {
const CANONICAL_LISP: &'static str = crate::hash::CANONICAL_HASH_LISP;
}
impl HasName for crate::hash::HashEncoding {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::nar::NarFormat {
const CANONICAL_LISP: &'static str = crate::nar::CANONICAL_NAR_LISP;
}
impl HasName for crate::nar::NarFormat {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::narinfo::NarinfoFormat {
const CANONICAL_LISP: &'static str = crate::narinfo::CANONICAL_NARINFO_LISP;
}
impl HasName for crate::narinfo::NarinfoFormat {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::eval_cache::EvalCacheFormat {
const CANONICAL_LISP: &'static str = crate::eval_cache::CANONICAL_EVAL_CACHE_LISP;
}
impl HasName for crate::eval_cache::EvalCacheFormat {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::profile::ProfileFormat {
const CANONICAL_LISP: &'static str = crate::profile::CANONICAL_PROFILE_LISP;
}
impl HasName for crate::profile::ProfileFormat {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::realisation::RealisationFormat {
const CANONICAL_LISP: &'static str = crate::realisation::CANONICAL_REALISATION_LISP;
}
impl HasName for crate::realisation::RealisationFormat {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::lock_file::LockFileFormat {
const CANONICAL_LISP: &'static str = crate::lock_file::CANONICAL_LOCK_FILE_LISP;
}
impl HasName for crate::lock_file::LockFileFormat {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::registry::RegistryFormat {
const CANONICAL_LISP: &'static str = crate::registry::CANONICAL_REGISTRY_LISP;
}
impl HasName for crate::registry::RegistryFormat {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::trust_model::TrustModel {
const CANONICAL_LISP: &'static str = crate::trust_model::CANONICAL_TRUST_LISP;
}
impl HasName for crate::trust_model::TrustModel {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::worker_protocol::WorkerProtocol {
const CANONICAL_LISP: &'static str = crate::worker_protocol::CANONICAL_WORKER_PROTOCOL_LISP;
}
impl HasName for crate::worker_protocol::WorkerProtocol {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::worker_protocol::WorkerOpcode {
const CANONICAL_LISP: &'static str = crate::worker_protocol::CANONICAL_WORKER_PROTOCOL_LISP;
}
impl HasName for crate::worker_protocol::WorkerOpcode {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::catalog::SubstrateDomain {
const CANONICAL_LISP: &'static str = crate::catalog::CANONICAL_CATALOG_LISP;
}
impl HasName for crate::catalog::SubstrateDomain {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::cli_coverage::SuiCommand {
const CANONICAL_LISP: &'static str = crate::cli_coverage::CANONICAL_CLI_COVERAGE_LISP;
}
impl HasName for crate::cli_coverage::SuiCommand {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::store_ops::StoreSlice {
const CANONICAL_LISP: &'static str = crate::store_ops::CANONICAL_STORE_OPS_LISP;
}
impl HasName for crate::store_ops::StoreSlice {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::store_inventory::StoreInventoryProfile {
const CANONICAL_LISP: &'static str = crate::store_inventory::CANONICAL_STORE_INVENTORY_LISP;
}
impl HasName for crate::store_inventory::StoreInventoryProfile {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::store_transform::StoreTransform {
const CANONICAL_LISP: &'static str = crate::store_transform::CANONICAL_STORE_TRANSFORMS_LISP;
}
impl HasName for crate::store_transform::StoreTransform {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::store_recipe::StoreRecipe {
const CANONICAL_LISP: &'static str = crate::store_recipe::CANONICAL_STORE_RECIPES_LISP;
}
impl HasName for crate::store_recipe::StoreRecipe {
fn name(&self) -> &str { &self.name }
}
impl Spec for crate::store_query::StoreQuery {
const CANONICAL_LISP: &'static str = crate::store_query::CANONICAL_STORE_QUERIES_LISP;
}
impl HasName for crate::store_query::StoreQuery {
fn name(&self) -> &str { &self.name }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::probe::Probe;
#[test]
fn spec_load_canonical_all_works_through_trait() {
let probes = Probe::load_canonical_all().expect("load via trait");
assert!(!probes.is_empty());
}
#[test]
fn spec_load_named_finds_known_probe() {
let p = Probe::load_named("getflake-outPath").expect("load via trait");
assert_eq!(p.name(), "getflake-outPath");
}
#[test]
fn spec_load_named_errors_on_missing() {
let err = Probe::load_named("no-such-probe").expect_err("must error");
match err {
SpecError::Load(msg) => {
assert!(msg.contains("no-such-probe"));
assert!(msg.contains("defprobe"),
"error must mention the keyword");
}
_ => panic!("expected SpecError::Load"),
}
}
#[test]
fn spec_keyword_matches_attribute() {
assert_eq!(Probe::KEYWORD, "defprobe");
}
fn count_canonical<S: Spec>() -> usize {
S::load_canonical_all().map(|v| v.len()).unwrap_or(0)
}
#[test]
fn generic_count_over_spec_trait() {
let derivation_count = count_canonical::<crate::derivation::DerivationAlgorithm>();
let fetcher_count = count_canonical::<crate::fetcher::FetcherSpec>();
let catalog_count = count_canonical::<crate::catalog::SubstrateDomain>();
assert!(derivation_count >= 1);
assert!(fetcher_count >= 5);
assert!(catalog_count >= 15);
}
}