use chrono::NaiveDate;
use std::sync::OnceLock;
use super::{
DEFAULT_REPAIRABILITY_THRESHOLDS, RepairabilityRuleset, RepairabilityThresholds,
RepairabilityWeights,
};
use crate::error::CalcError;
use crate::repairability::parameters::RepairabilityInputs;
use crate::ruleset::{Effectivity, RegulatoryBasis, Ruleset, RulesetId, RulesetVersion};
static SMARTPHONE_WEIGHTS: RepairabilityWeights = RepairabilityWeights {
disassembly: 0.25,
spare_parts: 0.15,
repair_info: 0.15,
diagnostic_tools: 0.15,
software_updatability: 0.15,
customer_support: 0.15,
};
static SMARTPHONE_BASIS: RegulatoryBasis = RegulatoryBasis {
regulation: "Non-regulatory: simplified repairability heuristic (NOT EU 2023/1669 Annex IV)",
article: "Six-factor heuristic — disassembly, spare parts, repair info, \
diagnostic tools, software updates, customer support",
standard: None,
technical_study: None,
source_url: None,
superseded_by: None,
};
static SMARTPHONE_RULESET_ID: RulesetId = RulesetId("repairability-heuristic-v1");
static SMARTPHONE_RULESET_VERSION: RulesetVersion = RulesetVersion("1.0.0");
static SMARTPHONE_EFFECTIVITY: OnceLock<Effectivity> = OnceLock::new();
pub struct SimplifiedRepairabilityHeuristic;
impl Ruleset for SimplifiedRepairabilityHeuristic {
fn id(&self) -> &RulesetId {
&SMARTPHONE_RULESET_ID
}
fn version(&self) -> &RulesetVersion {
&SMARTPHONE_RULESET_VERSION
}
fn effectivity(&self) -> &Effectivity {
SMARTPHONE_EFFECTIVITY.get_or_init(|| {
Effectivity::open(NaiveDate::from_ymd_opt(2025, 6, 20).expect("valid date"))
})
}
fn regulatory_basis(&self) -> &RegulatoryBasis {
&SMARTPHONE_BASIS
}
}
impl RepairabilityRuleset for SimplifiedRepairabilityHeuristic {
fn weights(&self) -> &RepairabilityWeights {
&SMARTPHONE_WEIGHTS
}
fn thresholds(&self) -> &RepairabilityThresholds {
&DEFAULT_REPAIRABILITY_THRESHOLDS
}
fn validate_cross_fields(&self, inputs: &RepairabilityInputs) -> Result<(), CalcError> {
if inputs.disassembly == 0 && inputs.spare_parts > 0 {
return Err(CalcError::CrossFieldViolation(
"spare_parts requires disassembly ≥ 1: parts are inaccessible \
without disassembly instructions"
.into(),
));
}
Ok(())
}
}