use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use crate::error::{Error, Result};
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct Config {
pub manifest: ManifestConfig,
pub domains: AllowlistConfig,
pub kind: AllowlistConfig,
pub layout: LayoutConfig,
pub index: IndexConfig,
pub branding: BrandingConfig,
pub coupling: CouplingConfig,
pub provenance: ProvenanceConfig,
pub frontmatter: FrontmatterConfig,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct ManifestConfig {
pub metadata_namespace: String,
}
impl Default for ManifestConfig {
fn default() -> Self {
ManifestConfig {
metadata_namespace: "spec-spine".to_string(),
}
}
}
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct AllowlistConfig {
pub allowed: Vec<String>,
}
impl AllowlistConfig {
pub fn is_disabled(&self) -> bool {
self.allowed.is_empty()
}
pub fn permits(&self, value: &str) -> bool {
self.is_disabled() || self.allowed.iter().any(|a| a == value)
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct LayoutConfig {
pub specs_dir: String,
pub derived_dir: String,
pub standards_dir: String,
pub schemas_dir: String,
pub cargo_workspace: String,
pub npm_workspaces: Vec<String>,
pub standalone_rust_workspaces: Vec<String>,
pub standalone_npm_packages: Vec<String>,
}
impl Default for LayoutConfig {
fn default() -> Self {
LayoutConfig {
specs_dir: "specs".to_string(),
derived_dir: ".derived".to_string(),
standards_dir: "standards/spec".to_string(),
schemas_dir: "standards/schemas".to_string(),
cargo_workspace: "Cargo.toml".to_string(),
npm_workspaces: vec![
"package.json".to_string(),
"pnpm-workspace.yaml".to_string(),
],
standalone_rust_workspaces: Vec::new(),
standalone_npm_packages: Vec::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct IndexConfig {
pub extra_hashed_inputs: Vec<String>,
pub resolver_exclusions: Vec<String>,
}
impl Default for IndexConfig {
fn default() -> Self {
IndexConfig {
extra_hashed_inputs: vec![
"standards/**".to_string(),
".github/workflows/**".to_string(),
],
resolver_exclusions: vec![
"target".to_string(),
"node_modules".to_string(),
".derived".to_string(),
"dist".to_string(),
"build".to_string(),
".next".to_string(),
],
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct BrandingConfig {
pub compiler_id: String,
pub indexer_id: String,
}
impl Default for BrandingConfig {
fn default() -> Self {
BrandingConfig {
compiler_id: "spec-spine".to_string(),
indexer_id: "spec-spine".to_string(),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct CouplingConfig {
pub bypass_prefixes: Vec<String>,
pub waiver_keyword: String,
}
impl Default for CouplingConfig {
fn default() -> Self {
CouplingConfig {
bypass_prefixes: Vec::new(),
waiver_keyword: "Spec-Drift-Waiver:".to_string(),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct ProvenanceConfig {
pub uri_schemes: BTreeMap<String, String>,
}
impl Default for ProvenanceConfig {
fn default() -> Self {
let mut uri_schemes = BTreeMap::new();
uri_schemes.insert("knowledge".to_string(), "knowledge://".to_string());
uri_schemes.insert("code-fingerprint".to_string(), "fingerprint://".to_string());
ProvenanceConfig { uri_schemes }
}
}
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct FrontmatterConfig {
pub extra_known_keys: Vec<String>,
}
pub fn load_config(toml_src: &str) -> Result<Config> {
toml::from_str(toml_src).map_err(|e| Error::Config(e.to_string()))
}