use chrono::NaiveDate;
use serde::Serialize;
use datasynth_standards::registry::StandardRegistry;
#[derive(Debug, Clone, Serialize)]
pub struct ComplianceStandardRecord {
pub standard_id: String,
pub body: String,
pub number: String,
pub title: String,
pub category: String,
pub domain: String,
pub jurisdiction: String,
pub effective_date: String,
pub version: String,
pub is_active: bool,
pub superseded_by: Option<String>,
pub applicable_account_types: Vec<String>,
pub applicable_processes: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct CrossReferenceRecord {
pub from_standard: String,
pub to_standard: String,
pub relationship: String,
pub convergence_level: f64,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct JurisdictionRecord {
pub country_code: String,
pub country_name: String,
pub accounting_framework: String,
pub audit_framework: String,
pub standards_body: String,
pub statutory_tax_rate: f64,
pub standard_count: usize,
}
pub struct RegulationGenerator {
registry: StandardRegistry,
}
impl RegulationGenerator {
pub fn new() -> Self {
Self {
registry: StandardRegistry::with_built_in(),
}
}
pub fn with_registry(registry: StandardRegistry) -> Self {
Self { registry }
}
pub fn registry(&self) -> &StandardRegistry {
&self.registry
}
pub fn generate_standard_records(
&self,
jurisdictions: &[String],
reference_date: NaiveDate,
) -> Vec<ComplianceStandardRecord> {
let mut records = Vec::new();
for jurisdiction in jurisdictions {
let standards = self
.registry
.standards_for_jurisdiction(jurisdiction, reference_date);
for std in standards {
let active_version =
self.registry
.active_version_in(&std.id, jurisdiction, reference_date);
let (effective_date, version) = if let Some(v) = active_version {
(v.effective_from.to_string(), v.version_id.clone())
} else {
("unknown".to_string(), "unknown".to_string())
};
records.push(ComplianceStandardRecord {
standard_id: std.id.as_str().to_string(),
body: std.id.body().to_string(),
number: std.id.number().to_string(),
title: std.title.clone(),
category: format!("{}", std.category),
domain: format!("{}", std.domain),
jurisdiction: jurisdiction.clone(),
effective_date,
version,
is_active: true,
superseded_by: std.superseded_by.as_ref().map(|s| s.as_str().to_string()),
applicable_account_types: std.applicable_account_types.clone(),
applicable_processes: std.applicable_processes.clone(),
});
}
}
records
}
pub fn generate_cross_reference_records(&self) -> Vec<CrossReferenceRecord> {
self.registry
.cross_references()
.iter()
.map(|xr| CrossReferenceRecord {
from_standard: xr.from_standard.as_str().to_string(),
to_standard: xr.to_standard.as_str().to_string(),
relationship: format!("{}", xr.relationship),
convergence_level: xr.convergence_level,
description: xr.description.clone(),
})
.collect()
}
pub fn generate_jurisdiction_records(
&self,
jurisdictions: &[String],
reference_date: NaiveDate,
) -> Vec<JurisdictionRecord> {
jurisdictions
.iter()
.filter_map(|code| {
self.registry.jurisdiction(code).map(|jp| {
let standard_count = self
.registry
.standards_for_jurisdiction(code, reference_date)
.len();
JurisdictionRecord {
country_code: jp.country_code.clone(),
country_name: jp.country_name.clone(),
accounting_framework: format!("{:?}", jp.accounting_framework),
audit_framework: format!("{:?}", jp.audit_framework),
standards_body: jp.accounting_standards_body.clone(),
statutory_tax_rate: jp.corporate_tax_rate.unwrap_or(0.0),
standard_count,
}
})
})
.collect()
}
}
impl Default for RegulationGenerator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn test_generate_standard_records() {
let gen = RegulationGenerator::new();
let date = NaiveDate::from_ymd_opt(2025, 6, 30).unwrap();
let records = gen.generate_standard_records(&["US".to_string()], date);
assert!(!records.is_empty(), "Should have US standards");
}
#[test]
fn test_generate_cross_references() {
let gen = RegulationGenerator::new();
let records = gen.generate_cross_reference_records();
assert!(!records.is_empty(), "Should have cross-references");
}
#[test]
fn test_generate_jurisdiction_records() {
let gen = RegulationGenerator::new();
let date = NaiveDate::from_ymd_opt(2025, 6, 30).unwrap();
let records =
gen.generate_jurisdiction_records(&["US".to_string(), "DE".to_string()], date);
assert_eq!(records.len(), 2);
}
}