1#![allow(clippy::too_many_arguments)]
2
3use async_trait::async_trait;
140use legalis_core::Statute;
141use legalis_i18n::{Jurisdiction, LegalSystem, Locale};
142use serde::{Deserialize, Serialize};
143use std::collections::HashMap;
144use thiserror::Error;
145
146#[derive(Debug, Error)]
148pub enum PortingError {
149 #[error("Source jurisdiction not found: {0}")]
150 SourceNotFound(String),
151
152 #[error("Target jurisdiction not found: {0}")]
153 TargetNotFound(String),
154
155 #[error("Incompatible legal systems: {0} -> {1}")]
156 IncompatibleSystems(String, String),
157
158 #[error("Cultural conflict: {0}")]
159 CulturalConflict(String),
160
161 #[error("Translation failed: {0}")]
162 TranslationFailed(String),
163
164 #[error("Adaptation required: {0}")]
165 AdaptationRequired(String),
166
167 #[error("LLM error: {0}")]
168 Llm(#[from] anyhow::Error),
169
170 #[error("Conflict detected: {0}")]
171 ConflictDetected(String),
172
173 #[error("Semantic validation failed: {0}")]
174 SemanticValidationFailed(String),
175
176 #[error("Section not found: {0}")]
177 SectionNotFound(String),
178
179 #[error("Invalid input: {0}")]
180 InvalidInput(String),
181}
182
183pub type PortingResult<T> = Result<T, PortingError>;
185
186#[async_trait]
188pub trait TextGenerator: Send + Sync {
189 async fn generate(&self, prompt: &str) -> anyhow::Result<String>;
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct PortingRequest {
196 pub statutes: Vec<Statute>,
198 pub source_jurisdiction: String,
200 pub target_jurisdiction: String,
202 pub options: PortingOptions,
204}
205
206#[derive(Debug, Clone, Default, Serialize, Deserialize)]
208pub struct PortingOptions {
209 pub translate_terms: bool,
211 pub adapt_values: bool,
213 pub apply_cultural_params: bool,
215 pub value_overrides: HashMap<String, String>,
217 pub generate_report: bool,
219 pub use_ai_suggestions: bool,
221 pub detect_conflicts: bool,
223 pub validate_semantics: bool,
225 pub section_ids: Vec<String>,
227 pub reverse_porting: bool,
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct PortingOutput {
234 pub statutes: Vec<PortedStatute>,
236 pub report: Option<CompatibilityReport>,
238 pub warnings: Vec<String>,
240 pub ai_suggestions: Vec<AdaptationSuggestion>,
242 pub conflicts: Vec<ConflictReport>,
244 pub semantic_validation: Option<SemanticValidation>,
246 pub risk_assessment: Option<RiskAssessment>,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct PortedStatute {
253 pub original_id: String,
255 pub statute: Statute,
257 pub changes: Vec<PortingChange>,
259 pub locale: Locale,
261 pub compatibility_score: f64,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct PortingChange {
268 pub change_type: ChangeType,
270 pub description: String,
272 pub original: Option<String>,
274 pub adapted: Option<String>,
276 pub reason: String,
278}
279
280#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
282pub enum ChangeType {
283 Translation,
285 ValueAdaptation,
287 CulturalAdaptation,
289 Incompatible,
291 ComplianceAddition,
293 Removal,
295}
296
297#[derive(Debug, Clone, Default, Serialize, Deserialize)]
299pub struct CompatibilityReport {
300 pub compatibility_score: f64,
302 pub adaptations_required: usize,
304 pub incompatibilities: usize,
306 pub findings: Vec<CompatibilityFinding>,
308 pub recommendations: Vec<String>,
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct CompatibilityFinding {
315 pub severity: Severity,
317 pub category: String,
319 pub description: String,
321 pub statute_id: Option<String>,
323}
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
327pub enum Severity {
328 Info,
329 Warning,
330 Error,
331 Critical,
332}
333
334#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
336pub enum RiskCategory {
337 Legal,
339 Cultural,
341 Political,
343 Economic,
345 Implementation,
347 Technical,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct AdaptationSuggestion {
354 pub statute_id: String,
356 pub suggestion: String,
358 pub rationale: String,
360 pub confidence: f64,
362 pub category: String,
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct ConflictReport {
369 pub statute_id: String,
371 pub conflict_type: ConflictType,
373 pub description: String,
375 pub severity: Severity,
377 pub resolutions: Vec<String>,
379}
380
381#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
383pub enum ConflictType {
384 Contradiction,
386 Overlap,
388 CulturalIncompatibility,
390 SystemMismatch,
392 Procedural,
394}
395
396#[derive(Debug, Clone, Serialize, Deserialize)]
398pub struct SemanticValidation {
399 pub preservation_score: f64,
401 pub findings: Vec<SemanticFinding>,
403 pub is_valid: bool,
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize)]
409pub struct SemanticFinding {
410 pub statute_id: String,
412 pub description: String,
414 pub severity: Severity,
416 pub impact: String,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct RiskAssessment {
423 pub risk_score: f64,
425 pub risk_level: RiskLevel,
427 pub risks: Vec<Risk>,
429 pub mitigations: Vec<String>,
431}
432
433#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
435pub enum RiskLevel {
436 Low,
437 Medium,
438 High,
439 Critical,
440 Negligible,
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct Risk {
446 pub id: String,
448 pub category: RiskCategory,
450 pub description: String,
452 pub likelihood: RiskLevel,
454 pub impact: f64,
456 pub severity: RiskLevel,
458}
459
460#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct BilateralAgreement {
463 pub id: String,
465 pub source_jurisdiction: String,
467 pub target_jurisdiction: String,
469 pub agreement_type: AgreementType,
471 pub mutual_recognition: Vec<String>,
473 pub adaptation_protocols: Vec<AdaptationProtocol>,
475 pub dispute_resolution: Option<String>,
477}
478
479#[derive(Debug, Clone, Serialize, Deserialize)]
481pub enum AgreementType {
482 MutualRecognition,
484 Harmonization,
486 Equivalence,
488 Cooperation,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct AdaptationProtocol {
495 pub name: String,
497 pub description: String,
499 pub statute_types: Vec<String>,
501 pub rules: Vec<String>,
503}
504
505#[derive(Debug, Clone, Serialize, Deserialize)]
507pub struct EquivalenceMapping {
508 pub source_regulation: String,
510 pub target_regulation: String,
512 pub equivalence_score: f64,
514 pub differences: Vec<String>,
516 pub notes: String,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize)]
522pub struct TermReplacement {
523 pub source_term: String,
525 pub target_term: String,
527 pub context: Option<String>,
529 pub confidence: f64,
531}
532
533#[derive(Debug, Clone, Serialize, Deserialize)]
535pub struct ContextualAdjustment {
536 pub parameter: String,
538 pub original_value: String,
540 pub adjusted_value: String,
542 pub context: String,
544 pub rationale: String,
546}
547
548#[derive(Debug, Clone, Serialize, Deserialize)]
550pub struct PortingWorkflow {
551 pub id: String,
553 pub state: WorkflowState,
555 pub statute_id: String,
557 pub source_jurisdiction: String,
559 pub target_jurisdiction: String,
561 pub completed_steps: Vec<WorkflowStep>,
563 pub pending_steps: Vec<WorkflowStep>,
565 pub approvals: Vec<Approval>,
567}
568
569#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
571pub enum WorkflowState {
572 Initiated,
574 InProgress,
576 PendingReview,
578 Approved,
580 Rejected,
582 Completed,
584}
585
586#[derive(Debug, Clone, Serialize, Deserialize)]
588pub struct WorkflowStep {
589 pub name: String,
591 pub description: String,
593 pub status: StepStatus,
595 pub completed_at: Option<String>,
597}
598
599#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
601pub enum StepStatus {
602 Pending,
603 InProgress,
604 Completed,
605 Failed,
606}
607
608#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct Approval {
611 pub approver_role: String,
613 pub status: ApprovalStatus,
615 pub comments: Option<String>,
617}
618
619#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
621pub enum ApprovalStatus {
622 Pending,
623 Approved,
624 Rejected,
625}
626
627#[derive(Debug, Clone, Serialize, Deserialize)]
629pub struct VersionedPortedStatute {
630 pub statute: PortedStatute,
632 pub version: u32,
634 pub previous_hash: Option<String>,
636 pub hash: String,
638 pub created_at: String,
640 pub created_by: String,
642 pub change_notes: String,
644}
645
646#[derive(Debug, Clone, Serialize, Deserialize)]
648pub struct ReviewRequest {
649 pub id: String,
651 pub statute: PortedStatute,
653 pub source_jurisdiction: String,
655 pub target_jurisdiction: String,
657 pub status: ReviewStatus,
659 pub assigned_expert: Option<String>,
661 pub submitted_at: String,
663 pub reviews: Vec<ExpertReview>,
665}
666
667#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
669pub enum ReviewStatus {
670 Pending,
672 Assigned,
674 InReview,
676 Completed,
678 Approved,
680 Rejected,
682 RequiresRevision,
684}
685
686#[derive(Debug, Clone, Serialize, Deserialize)]
688pub struct ExpertReview {
689 pub id: String,
691 pub expert_id: String,
693 pub expert_name: String,
695 pub qualifications: Vec<String>,
697 pub reviewed_at: String,
699 pub recommendation: ReviewRecommendation,
701 pub comments: Vec<ReviewComment>,
703 pub confidence: f64,
705 pub concerns: Vec<String>,
707 pub suggested_modifications: Vec<String>,
709}
710
711#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
713pub enum ReviewRecommendation {
714 Approve,
716 ApproveWithChanges,
718 Reject,
720 RequestInformation,
722}
723
724#[derive(Debug, Clone, Serialize, Deserialize)]
726pub struct ReviewComment {
727 pub id: String,
729 pub section: Option<String>,
731 pub text: String,
733 pub severity: Severity,
735 pub category: String,
737}
738
739#[derive(Debug, Clone, Serialize, Deserialize)]
741pub struct ComplianceCheckResult {
742 pub id: String,
744 pub statute_id: String,
746 pub checked_at: String,
748 pub status: ComplianceStatus,
750 pub compliance_score: f64,
752 pub checks: Vec<ComplianceCheck>,
754 pub violations: Vec<ComplianceViolation>,
756 pub recommendations: Vec<String>,
758}
759
760#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
762pub enum ComplianceStatus {
763 Compliant,
765 CompliantWithIssues,
767 NonCompliant,
769 RequiresReview,
771}
772
773#[derive(Debug, Clone, Serialize, Deserialize)]
775pub struct ComplianceCheck {
776 pub name: String,
778 pub description: String,
780 pub passed: bool,
782 pub details: Option<String>,
784 pub severity: Severity,
786}
787
788#[derive(Debug, Clone, Serialize, Deserialize)]
790pub struct ComplianceViolation {
791 pub violation_type: String,
793 pub description: String,
795 pub severity: Severity,
797 pub regulation: String,
799 pub remediation: Vec<String>,
801}
802
803#[async_trait]
805pub trait PortingAdapter: Send + Sync {
806 async fn port(&self, request: &PortingRequest) -> PortingResult<PortingOutput>;
808
809 async fn analyze_compatibility(
811 &self,
812 source: &Jurisdiction,
813 target: &Jurisdiction,
814 ) -> PortingResult<CompatibilityReport>;
815}
816
817#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
823pub enum LegalSystemType {
824 CommonLaw,
826 CivilLaw,
828 ReligiousLaw,
830 CustomaryLaw,
832 Mixed,
834 SocialistLaw,
836}
837
838#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
840pub enum CourtLevel {
841 Local = 1,
843 District = 2,
845 Appellate = 3,
847 Supreme = 4,
849 International = 5,
851}
852
853#[derive(Debug, Clone, Serialize, Deserialize)]
855pub struct Court {
856 pub name: String,
858 pub level: CourtLevel,
860 pub jurisdiction: String,
862 pub precedent_setting: bool,
864 pub judges: Option<u32>,
866 pub url: Option<String>,
868}
869
870#[derive(Debug, Clone, Serialize, Deserialize)]
872pub struct CourtHierarchy {
873 pub courts: Vec<Court>,
875 pub appeal_path: String,
877 pub has_jury_trials: bool,
879 pub constitutional_court: Option<String>,
881}
882
883impl CourtHierarchy {
884 pub fn new() -> Self {
886 Self {
887 courts: Vec::new(),
888 appeal_path: String::new(),
889 has_jury_trials: false,
890 constitutional_court: None,
891 }
892 }
893
894 pub fn add_court(&mut self, court: Court) {
896 self.courts.push(court);
897 }
898
899 pub fn courts_by_level(&self, level: CourtLevel) -> Vec<&Court> {
901 self.courts.iter().filter(|c| c.level == level).collect()
902 }
903}
904
905impl Default for CourtHierarchy {
906 fn default() -> Self {
907 Self::new()
908 }
909}
910
911#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
913pub enum LegislativeStage {
914 Drafting = 1,
916 Committee = 2,
918 FirstReading = 3,
920 SecondReading = 4,
922 ThirdReading = 5,
924 UpperHouse = 6,
926 Executive = 7,
928 Publication = 8,
930}
931
932#[derive(Debug, Clone, Serialize, Deserialize)]
934pub struct LegislativeProcess {
935 pub legislature_name: String,
937 pub is_bicameral: bool,
939 pub lower_house: String,
941 pub upper_house: Option<String>,
943 pub stages: Vec<LegislativeStage>,
945 pub typical_duration_days: Option<u32>,
947 pub has_direct_democracy: bool,
949 pub session_frequency: String,
951}
952
953impl LegislativeProcess {
954 pub fn new(legislature_name: String, lower_house: String) -> Self {
956 Self {
957 legislature_name,
958 is_bicameral: false,
959 lower_house,
960 upper_house: None,
961 stages: vec![
962 LegislativeStage::Drafting,
963 LegislativeStage::Committee,
964 LegislativeStage::FirstReading,
965 LegislativeStage::SecondReading,
966 LegislativeStage::ThirdReading,
967 LegislativeStage::Executive,
968 LegislativeStage::Publication,
969 ],
970 typical_duration_days: None,
971 has_direct_democracy: false,
972 session_frequency: String::from("Annual"),
973 }
974 }
975
976 pub fn with_upper_house(mut self, upper_house: String) -> Self {
978 self.is_bicameral = true;
979 self.upper_house = Some(upper_house);
980 if !self.stages.contains(&LegislativeStage::UpperHouse) {
981 self.stages.insert(5, LegislativeStage::UpperHouse);
982 }
983 self
984 }
985}
986
987#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
989pub enum ConstitutionalFeature {
990 WrittenConstitution,
992 BillOfRights,
994 SeparationOfPowers,
996 Federalism,
998 JudicialReview,
1000 ParliamentarySovereignty,
1002 PresidentialSystem,
1004 ParliamentarySystem,
1006 SemiPresidentialSystem,
1008 ConstitutionalMonarchy,
1010}
1011
1012#[derive(Debug, Clone, Serialize, Deserialize)]
1014pub struct ConstitutionalFramework {
1015 pub has_written_constitution: bool,
1017 pub constitution_name: Option<String>,
1019 pub constitution_year: Option<u32>,
1021 pub features: Vec<ConstitutionalFeature>,
1023 pub amendment_difficulty: u8,
1025 pub fundamental_rights: Vec<String>,
1027 pub government_structure: String,
1029}
1030
1031impl ConstitutionalFramework {
1032 pub fn new() -> Self {
1034 Self {
1035 has_written_constitution: true,
1036 constitution_name: None,
1037 constitution_year: None,
1038 features: Vec::new(),
1039 amendment_difficulty: 5,
1040 fundamental_rights: Vec::new(),
1041 government_structure: String::new(),
1042 }
1043 }
1044
1045 pub fn add_feature(&mut self, feature: ConstitutionalFeature) {
1047 if !self.features.contains(&feature) {
1048 self.features.push(feature);
1049 }
1050 }
1051
1052 pub fn has_feature(&self, feature: ConstitutionalFeature) -> bool {
1054 self.features.contains(&feature)
1055 }
1056}
1057
1058impl Default for ConstitutionalFramework {
1059 fn default() -> Self {
1060 Self::new()
1061 }
1062}
1063
1064#[derive(Debug, Clone, Serialize, Deserialize)]
1066pub struct JurisdictionProfile {
1067 pub code: String,
1069 pub name: String,
1071 pub legal_system: LegalSystemType,
1073 pub court_hierarchy: CourtHierarchy,
1075 pub legislative_process: LegislativeProcess,
1077 pub constitutional_framework: ConstitutionalFramework,
1079 pub official_languages: Vec<String>,
1081 pub population: Option<u64>,
1083 pub gdp_per_capita: Option<f64>,
1085 pub hdi: Option<f64>,
1087 pub legal_influences: Vec<String>,
1089 pub characteristics: Vec<String>,
1091}
1092
1093impl JurisdictionProfile {
1094 pub fn new(code: String, name: String, legal_system: LegalSystemType) -> Self {
1096 Self {
1097 code,
1098 name,
1099 legal_system,
1100 court_hierarchy: CourtHierarchy::new(),
1101 legislative_process: LegislativeProcess::new(
1102 String::from("Legislature"),
1103 String::from("Chamber"),
1104 ),
1105 constitutional_framework: ConstitutionalFramework::new(),
1106 official_languages: Vec::new(),
1107 population: None,
1108 gdp_per_capita: None,
1109 hdi: None,
1110 legal_influences: Vec::new(),
1111 characteristics: Vec::new(),
1112 }
1113 }
1114
1115 pub fn compatibility_score(&self, other: &JurisdictionProfile) -> f64 {
1117 let mut score = 0.0;
1118 let mut factors = 0.0;
1119
1120 if self.legal_system == other.legal_system {
1122 score += 3.0;
1123 } else if matches!(
1124 (self.legal_system, other.legal_system),
1125 (LegalSystemType::Mixed, _) | (_, LegalSystemType::Mixed)
1126 ) {
1127 score += 1.5;
1128 }
1129 factors += 3.0;
1130
1131 let self_features: std::collections::HashSet<_> =
1133 self.constitutional_framework.features.iter().collect();
1134 let other_features: std::collections::HashSet<_> =
1135 other.constitutional_framework.features.iter().collect();
1136 let overlap = self_features.intersection(&other_features).count();
1137 let total = self_features.union(&other_features).count();
1138 if total > 0 {
1139 score += 2.0 * (overlap as f64 / total as f64);
1140 }
1141 factors += 2.0;
1142
1143 if self.legislative_process.is_bicameral == other.legislative_process.is_bicameral {
1145 score += 1.0;
1146 } else {
1147 score += 0.5;
1148 }
1149 factors += 1.0;
1150
1151 if let (Some(self_gdp), Some(other_gdp)) = (self.gdp_per_capita, other.gdp_per_capita) {
1153 let ratio = self_gdp.min(other_gdp) / self_gdp.max(other_gdp);
1154 score += ratio;
1155 }
1156 factors += 1.0;
1157
1158 score / factors
1160 }
1161}
1162
1163#[derive(Debug, Clone, Serialize, Deserialize)]
1165pub struct JurisdictionDatabase {
1166 profiles: HashMap<String, JurisdictionProfile>,
1168}
1169
1170impl JurisdictionDatabase {
1171 pub fn new() -> Self {
1173 Self {
1174 profiles: HashMap::new(),
1175 }
1176 }
1177
1178 pub fn add_profile(&mut self, profile: JurisdictionProfile) {
1180 self.profiles.insert(profile.code.clone(), profile);
1181 }
1182
1183 pub fn get_profile(&self, code: &str) -> Option<&JurisdictionProfile> {
1185 self.profiles.get(code)
1186 }
1187
1188 pub fn get_profile_mut(&mut self, code: &str) -> Option<&mut JurisdictionProfile> {
1190 self.profiles.get_mut(code)
1191 }
1192
1193 pub fn list_codes(&self) -> Vec<&String> {
1195 self.profiles.keys().collect()
1196 }
1197
1198 pub fn find_by_legal_system(&self, system: LegalSystemType) -> Vec<&JurisdictionProfile> {
1200 self.profiles
1201 .values()
1202 .filter(|p| p.legal_system == system)
1203 .collect()
1204 }
1205
1206 pub fn find_compatible(&self, code: &str, min_score: f64) -> Vec<(&JurisdictionProfile, f64)> {
1208 if let Some(source) = self.get_profile(code) {
1209 let mut compatible: Vec<_> = self
1210 .profiles
1211 .values()
1212 .filter(|p| p.code != code)
1213 .map(|p| {
1214 let score = source.compatibility_score(p);
1215 (p, score)
1216 })
1217 .filter(|(_, score)| *score >= min_score)
1218 .collect();
1219 compatible.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
1220 compatible
1221 } else {
1222 Vec::new()
1223 }
1224 }
1225
1226 pub fn with_major_jurisdictions() -> Self {
1228 let mut db = Self::new();
1229
1230 let mut us = JurisdictionProfile::new(
1232 String::from("US"),
1233 String::from("United States"),
1234 LegalSystemType::CommonLaw,
1235 );
1236 us.official_languages = vec![String::from("en")];
1237 us.population = Some(331_000_000);
1238 us.gdp_per_capita = Some(69_287.0);
1239 us.hdi = Some(0.921);
1240 us.legal_influences = vec![String::from("English common law")];
1241 us.constitutional_framework = {
1242 let mut cf = ConstitutionalFramework::new();
1243 cf.has_written_constitution = true;
1244 cf.constitution_name = Some(String::from("Constitution of the United States"));
1245 cf.constitution_year = Some(1789);
1246 cf.add_feature(ConstitutionalFeature::WrittenConstitution);
1247 cf.add_feature(ConstitutionalFeature::BillOfRights);
1248 cf.add_feature(ConstitutionalFeature::SeparationOfPowers);
1249 cf.add_feature(ConstitutionalFeature::Federalism);
1250 cf.add_feature(ConstitutionalFeature::JudicialReview);
1251 cf.add_feature(ConstitutionalFeature::PresidentialSystem);
1252 cf.amendment_difficulty = 9;
1253 cf.government_structure = String::from("Federal presidential constitutional republic");
1254 cf.fundamental_rights = vec![
1255 String::from("Freedom of speech"),
1256 String::from("Freedom of religion"),
1257 String::from("Right to bear arms"),
1258 String::from("Due process"),
1259 String::from("Equal protection"),
1260 ];
1261 cf
1262 };
1263 us.legislative_process = LegislativeProcess::new(
1264 String::from("United States Congress"),
1265 String::from("House of Representatives"),
1266 )
1267 .with_upper_house(String::from("Senate"));
1268 us.court_hierarchy = {
1269 let mut ch = CourtHierarchy::new();
1270 ch.add_court(Court {
1271 name: String::from("Supreme Court of the United States"),
1272 level: CourtLevel::Supreme,
1273 jurisdiction: String::from("Federal"),
1274 precedent_setting: true,
1275 judges: Some(9),
1276 url: Some(String::from("https://www.supremecourt.gov")),
1277 });
1278 ch.add_court(Court {
1279 name: String::from("U.S. Courts of Appeals"),
1280 level: CourtLevel::Appellate,
1281 jurisdiction: String::from("Federal circuits"),
1282 precedent_setting: true,
1283 judges: Some(179),
1284 url: None,
1285 });
1286 ch.add_court(Court {
1287 name: String::from("U.S. District Courts"),
1288 level: CourtLevel::District,
1289 jurisdiction: String::from("Federal districts"),
1290 precedent_setting: false,
1291 judges: Some(677),
1292 url: None,
1293 });
1294 ch.has_jury_trials = true;
1295 ch.appeal_path = String::from("District → Appeals → Supreme Court");
1296 ch
1297 };
1298 db.add_profile(us);
1299
1300 let mut jp = JurisdictionProfile::new(
1302 String::from("JP"),
1303 String::from("Japan"),
1304 LegalSystemType::CivilLaw,
1305 );
1306 jp.official_languages = vec![String::from("ja")];
1307 jp.population = Some(125_000_000);
1308 jp.gdp_per_capita = Some(39_285.0);
1309 jp.hdi = Some(0.919);
1310 jp.legal_influences = vec![
1311 String::from("German civil law"),
1312 String::from("French civil law"),
1313 String::from("Anglo-American law (post-WWII)"),
1314 ];
1315 jp.constitutional_framework = {
1316 let mut cf = ConstitutionalFramework::new();
1317 cf.has_written_constitution = true;
1318 cf.constitution_name = Some(String::from("Constitution of Japan"));
1319 cf.constitution_year = Some(1947);
1320 cf.add_feature(ConstitutionalFeature::WrittenConstitution);
1321 cf.add_feature(ConstitutionalFeature::BillOfRights);
1322 cf.add_feature(ConstitutionalFeature::SeparationOfPowers);
1323 cf.add_feature(ConstitutionalFeature::JudicialReview);
1324 cf.add_feature(ConstitutionalFeature::ParliamentarySystem);
1325 cf.add_feature(ConstitutionalFeature::ConstitutionalMonarchy);
1326 cf.amendment_difficulty = 10;
1327 cf.government_structure = String::from("Unitary parliamentary constitutional monarchy");
1328 cf.fundamental_rights = vec![
1329 String::from("Equality under the law"),
1330 String::from("Freedom of thought and conscience"),
1331 String::from("Academic freedom"),
1332 String::from("Right to life, liberty, and pursuit of happiness"),
1333 String::from("Pacifism (Article 9)"),
1334 ];
1335 cf
1336 };
1337 jp.legislative_process = LegislativeProcess::new(
1338 String::from("National Diet"),
1339 String::from("House of Representatives"),
1340 )
1341 .with_upper_house(String::from("House of Councillors"));
1342 jp.court_hierarchy = {
1343 let mut ch = CourtHierarchy::new();
1344 ch.add_court(Court {
1345 name: String::from("Supreme Court of Japan"),
1346 level: CourtLevel::Supreme,
1347 jurisdiction: String::from("National"),
1348 precedent_setting: true,
1349 judges: Some(15),
1350 url: Some(String::from("https://www.courts.go.jp")),
1351 });
1352 ch.add_court(Court {
1353 name: String::from("High Courts"),
1354 level: CourtLevel::Appellate,
1355 jurisdiction: String::from("Regional"),
1356 precedent_setting: false,
1357 judges: Some(350),
1358 url: None,
1359 });
1360 ch.add_court(Court {
1361 name: String::from("District Courts"),
1362 level: CourtLevel::District,
1363 jurisdiction: String::from("Prefectural"),
1364 precedent_setting: false,
1365 judges: Some(900),
1366 url: None,
1367 });
1368 ch.has_jury_trials = false;
1369 ch.appeal_path = String::from("District → High → Supreme Court");
1370 ch
1371 };
1372 db.add_profile(jp);
1373
1374 let mut gb = JurisdictionProfile::new(
1376 String::from("GB"),
1377 String::from("United Kingdom"),
1378 LegalSystemType::CommonLaw,
1379 );
1380 gb.official_languages = vec![String::from("en")];
1381 gb.population = Some(67_000_000);
1382 gb.gdp_per_capita = Some(46_510.0);
1383 gb.hdi = Some(0.929);
1384 gb.legal_influences = vec![String::from("English common law tradition")];
1385 gb.constitutional_framework = {
1386 let mut cf = ConstitutionalFramework::new();
1387 cf.has_written_constitution = false;
1388 cf.constitution_name = None;
1389 cf.add_feature(ConstitutionalFeature::ParliamentarySovereignty);
1390 cf.add_feature(ConstitutionalFeature::ParliamentarySystem);
1391 cf.add_feature(ConstitutionalFeature::ConstitutionalMonarchy);
1392 cf.amendment_difficulty = 3;
1393 cf.government_structure = String::from("Unitary parliamentary constitutional monarchy");
1394 cf.fundamental_rights = vec![
1395 String::from("Rights under common law"),
1396 String::from("Human Rights Act 1998"),
1397 String::from("Magna Carta principles"),
1398 ];
1399 cf
1400 };
1401 gb.legislative_process = LegislativeProcess::new(
1402 String::from("Parliament of the United Kingdom"),
1403 String::from("House of Commons"),
1404 )
1405 .with_upper_house(String::from("House of Lords"));
1406 gb.court_hierarchy = {
1407 let mut ch = CourtHierarchy::new();
1408 ch.add_court(Court {
1409 name: String::from("Supreme Court of the United Kingdom"),
1410 level: CourtLevel::Supreme,
1411 jurisdiction: String::from("National"),
1412 precedent_setting: true,
1413 judges: Some(12),
1414 url: Some(String::from("https://www.supremecourt.uk")),
1415 });
1416 ch.add_court(Court {
1417 name: String::from("Court of Appeal"),
1418 level: CourtLevel::Appellate,
1419 jurisdiction: String::from("England and Wales"),
1420 precedent_setting: true,
1421 judges: Some(39),
1422 url: None,
1423 });
1424 ch.add_court(Court {
1425 name: String::from("High Court"),
1426 level: CourtLevel::District,
1427 jurisdiction: String::from("England and Wales"),
1428 precedent_setting: true,
1429 judges: Some(108),
1430 url: None,
1431 });
1432 ch.has_jury_trials = true;
1433 ch.appeal_path = String::from("High Court → Court of Appeal → Supreme Court");
1434 ch
1435 };
1436 db.add_profile(gb);
1437
1438 let mut de = JurisdictionProfile::new(
1440 String::from("DE"),
1441 String::from("Germany"),
1442 LegalSystemType::CivilLaw,
1443 );
1444 de.official_languages = vec![String::from("de")];
1445 de.population = Some(83_000_000);
1446 de.gdp_per_capita = Some(50_795.0);
1447 de.hdi = Some(0.942);
1448 de.legal_influences = vec![String::from("Roman law"), String::from("Germanic law")];
1449 de.constitutional_framework = {
1450 let mut cf = ConstitutionalFramework::new();
1451 cf.has_written_constitution = true;
1452 cf.constitution_name = Some(String::from("Basic Law (Grundgesetz)"));
1453 cf.constitution_year = Some(1949);
1454 cf.add_feature(ConstitutionalFeature::WrittenConstitution);
1455 cf.add_feature(ConstitutionalFeature::BillOfRights);
1456 cf.add_feature(ConstitutionalFeature::SeparationOfPowers);
1457 cf.add_feature(ConstitutionalFeature::Federalism);
1458 cf.add_feature(ConstitutionalFeature::JudicialReview);
1459 cf.add_feature(ConstitutionalFeature::ParliamentarySystem);
1460 cf.amendment_difficulty = 8;
1461 cf.government_structure = String::from("Federal parliamentary republic");
1462 cf.fundamental_rights = vec![
1463 String::from("Human dignity"),
1464 String::from("Right to life and physical integrity"),
1465 String::from("Equality before the law"),
1466 String::from("Freedom of faith and conscience"),
1467 String::from("Freedom of expression"),
1468 ];
1469 cf
1470 };
1471 de.legislative_process =
1472 LegislativeProcess::new(String::from("German Parliament"), String::from("Bundestag"))
1473 .with_upper_house(String::from("Bundesrat"));
1474 de.court_hierarchy = {
1475 let mut ch = CourtHierarchy::new();
1476 ch.add_court(Court {
1477 name: String::from("Federal Constitutional Court"),
1478 level: CourtLevel::Supreme,
1479 jurisdiction: String::from("Constitutional"),
1480 precedent_setting: true,
1481 judges: Some(16),
1482 url: Some(String::from("https://www.bundesverfassungsgericht.de")),
1483 });
1484 ch.add_court(Court {
1485 name: String::from("Federal Court of Justice"),
1486 level: CourtLevel::Supreme,
1487 jurisdiction: String::from("Civil and Criminal"),
1488 precedent_setting: true,
1489 judges: Some(127),
1490 url: None,
1491 });
1492 ch.constitutional_court = Some(String::from("Federal Constitutional Court"));
1493 ch.has_jury_trials = false;
1494 ch.appeal_path = String::from("Regional → Higher Regional → Federal");
1495 ch
1496 };
1497 db.add_profile(de);
1498
1499 let mut fr = JurisdictionProfile::new(
1501 String::from("FR"),
1502 String::from("France"),
1503 LegalSystemType::CivilLaw,
1504 );
1505 fr.official_languages = vec![String::from("fr")];
1506 fr.population = Some(67_000_000);
1507 fr.gdp_per_capita = Some(44_408.0);
1508 fr.hdi = Some(0.903);
1509 fr.legal_influences = vec![String::from("Napoleonic Code"), String::from("Roman law")];
1510 fr.constitutional_framework = {
1511 let mut cf = ConstitutionalFramework::new();
1512 cf.has_written_constitution = true;
1513 cf.constitution_name = Some(String::from("Constitution of the Fifth Republic"));
1514 cf.constitution_year = Some(1958);
1515 cf.add_feature(ConstitutionalFeature::WrittenConstitution);
1516 cf.add_feature(ConstitutionalFeature::BillOfRights);
1517 cf.add_feature(ConstitutionalFeature::SeparationOfPowers);
1518 cf.add_feature(ConstitutionalFeature::JudicialReview);
1519 cf.add_feature(ConstitutionalFeature::SemiPresidentialSystem);
1520 cf.amendment_difficulty = 7;
1521 cf.government_structure = String::from("Unitary semi-presidential republic");
1522 cf.fundamental_rights = vec![
1523 String::from("Liberty"),
1524 String::from("Equality"),
1525 String::from("Fraternity"),
1526 String::from("Secularism (laïcité)"),
1527 String::from("Rights of Man and Citizen"),
1528 ];
1529 cf
1530 };
1531 fr.legislative_process = LegislativeProcess::new(
1532 String::from("French Parliament"),
1533 String::from("National Assembly"),
1534 )
1535 .with_upper_house(String::from("Senate"));
1536 fr.court_hierarchy = {
1537 let mut ch = CourtHierarchy::new();
1538 ch.add_court(Court {
1539 name: String::from("Constitutional Council"),
1540 level: CourtLevel::Supreme,
1541 jurisdiction: String::from("Constitutional"),
1542 precedent_setting: true,
1543 judges: Some(9),
1544 url: Some(String::from("https://www.conseil-constitutionnel.fr")),
1545 });
1546 ch.add_court(Court {
1547 name: String::from("Court of Cassation"),
1548 level: CourtLevel::Supreme,
1549 jurisdiction: String::from("Civil and Criminal"),
1550 precedent_setting: true,
1551 judges: Some(150),
1552 url: None,
1553 });
1554 ch.constitutional_court = Some(String::from("Constitutional Council"));
1555 ch.has_jury_trials = true;
1556 ch.appeal_path = String::from("First Instance → Appeal → Cassation");
1557 ch
1558 };
1559 db.add_profile(fr);
1560
1561 db
1562 }
1563}
1564
1565impl Default for JurisdictionDatabase {
1566 fn default() -> Self {
1567 Self::new()
1568 }
1569}
1570
1571#[derive(Debug, Clone, Serialize, Deserialize)]
1577pub struct ConceptEquivalence {
1578 pub source_concept: String,
1580 pub target_concept: String,
1582 pub equivalence_score: f64,
1584 pub semantic_distance: f64,
1586 pub context: Vec<String>,
1588 pub notes: Option<String>,
1590}
1591
1592impl ConceptEquivalence {
1593 pub fn new(source_concept: String, target_concept: String, equivalence_score: f64) -> Self {
1595 Self {
1596 source_concept,
1597 target_concept,
1598 equivalence_score,
1599 semantic_distance: 1.0 - equivalence_score,
1600 context: Vec::new(),
1601 notes: None,
1602 }
1603 }
1604
1605 pub fn with_context(mut self, context: String) -> Self {
1607 self.context.push(context);
1608 self
1609 }
1610
1611 pub fn with_notes(mut self, notes: String) -> Self {
1613 self.notes = Some(notes);
1614 self
1615 }
1616}
1617
1618#[derive(Debug, Clone, Serialize, Deserialize)]
1620pub struct ConceptEquivalenceDatabase {
1621 equivalences: HashMap<String, Vec<ConceptEquivalence>>,
1623}
1624
1625impl ConceptEquivalenceDatabase {
1626 pub fn new() -> Self {
1628 Self {
1629 equivalences: HashMap::new(),
1630 }
1631 }
1632
1633 pub fn add_equivalence(&mut self, jurisdiction_pair: String, equivalence: ConceptEquivalence) {
1635 self.equivalences
1636 .entry(jurisdiction_pair)
1637 .or_default()
1638 .push(equivalence);
1639 }
1640
1641 pub fn find_equivalences(
1643 &self,
1644 source_jurisdiction: &str,
1645 target_jurisdiction: &str,
1646 concept: &str,
1647 ) -> Vec<&ConceptEquivalence> {
1648 let key = format!("{}->{}", source_jurisdiction, target_jurisdiction);
1649 self.equivalences
1650 .get(&key)
1651 .map(|equivs| {
1652 equivs
1653 .iter()
1654 .filter(|e| {
1655 e.source_concept.eq_ignore_ascii_case(concept)
1656 || e.source_concept.contains(concept)
1657 })
1658 .collect()
1659 })
1660 .unwrap_or_default()
1661 }
1662
1663 pub fn best_match(
1665 &self,
1666 source_jurisdiction: &str,
1667 target_jurisdiction: &str,
1668 concept: &str,
1669 ) -> Option<&ConceptEquivalence> {
1670 let matches = self.find_equivalences(source_jurisdiction, target_jurisdiction, concept);
1671 matches.into_iter().max_by(|a, b| {
1672 a.equivalence_score
1673 .partial_cmp(&b.equivalence_score)
1674 .unwrap()
1675 })
1676 }
1677}
1678
1679impl Default for ConceptEquivalenceDatabase {
1680 fn default() -> Self {
1681 Self::new()
1682 }
1683}
1684
1685#[derive(Debug, Clone, Serialize, Deserialize)]
1687pub struct LegalTerm {
1688 pub term: String,
1690 pub definition: String,
1692 pub jurisdiction: String,
1694 pub domain: String,
1696 pub related_terms: Vec<String>,
1698}
1699
1700impl LegalTerm {
1701 pub fn new(term: String, definition: String, jurisdiction: String, domain: String) -> Self {
1703 Self {
1704 term,
1705 definition,
1706 jurisdiction,
1707 domain,
1708 related_terms: Vec::new(),
1709 }
1710 }
1711}
1712
1713#[derive(Debug, Clone, Serialize, Deserialize)]
1715pub struct TermTranslation {
1716 pub source_term: String,
1718 pub source_jurisdiction: String,
1720 pub target_term: String,
1722 pub target_jurisdiction: String,
1724 pub accuracy: f64,
1726 pub is_direct: bool,
1728 pub valid_contexts: Vec<String>,
1730 pub notes: Option<String>,
1732}
1733
1734impl TermTranslation {
1735 pub fn new(
1737 source_term: String,
1738 source_jurisdiction: String,
1739 target_term: String,
1740 target_jurisdiction: String,
1741 accuracy: f64,
1742 is_direct: bool,
1743 ) -> Self {
1744 Self {
1745 source_term,
1746 source_jurisdiction,
1747 target_term,
1748 target_jurisdiction,
1749 accuracy,
1750 is_direct,
1751 valid_contexts: Vec::new(),
1752 notes: None,
1753 }
1754 }
1755}
1756
1757#[derive(Debug, Clone, Serialize, Deserialize)]
1759pub struct TermTranslationMatrix {
1760 translations: HashMap<String, Vec<TermTranslation>>,
1762 terms: HashMap<String, Vec<LegalTerm>>,
1764}
1765
1766impl TermTranslationMatrix {
1767 pub fn new() -> Self {
1769 Self {
1770 translations: HashMap::new(),
1771 terms: HashMap::new(),
1772 }
1773 }
1774
1775 pub fn add_term(&mut self, term: LegalTerm) {
1777 self.terms
1778 .entry(term.jurisdiction.clone())
1779 .or_default()
1780 .push(term);
1781 }
1782
1783 pub fn add_translation(&mut self, translation: TermTranslation) {
1785 let key = format!(
1786 "{}->{}",
1787 translation.source_jurisdiction, translation.target_jurisdiction
1788 );
1789 self.translations.entry(key).or_default().push(translation);
1790 }
1791
1792 pub fn find_translations(
1794 &self,
1795 source_jurisdiction: &str,
1796 target_jurisdiction: &str,
1797 term: &str,
1798 ) -> Vec<&TermTranslation> {
1799 let key = format!("{}->{}", source_jurisdiction, target_jurisdiction);
1800 self.translations
1801 .get(&key)
1802 .map(|trans| {
1803 trans
1804 .iter()
1805 .filter(|t| t.source_term.eq_ignore_ascii_case(term))
1806 .collect()
1807 })
1808 .unwrap_or_default()
1809 }
1810
1811 pub fn best_translation(
1813 &self,
1814 source_jurisdiction: &str,
1815 target_jurisdiction: &str,
1816 term: &str,
1817 context: Option<&str>,
1818 ) -> Option<&TermTranslation> {
1819 let translations = self.find_translations(source_jurisdiction, target_jurisdiction, term);
1820
1821 if let Some(ctx) = context {
1822 if let Some(trans) = translations.iter().find(|t| {
1824 t.valid_contexts.is_empty() || t.valid_contexts.iter().any(|c| c.contains(ctx))
1825 }) {
1826 return Some(trans);
1827 }
1828 }
1829
1830 translations
1832 .into_iter()
1833 .max_by(|a, b| a.accuracy.partial_cmp(&b.accuracy).unwrap())
1834 }
1835
1836 pub fn get_terms(&self, jurisdiction: &str) -> Vec<&LegalTerm> {
1838 self.terms
1839 .get(jurisdiction)
1840 .map(|terms| terms.iter().collect())
1841 .unwrap_or_default()
1842 }
1843
1844 pub fn get_terms_by_domain(&self, jurisdiction: &str, domain: &str) -> Vec<&LegalTerm> {
1846 self.get_terms(jurisdiction)
1847 .into_iter()
1848 .filter(|t| t.domain.eq_ignore_ascii_case(domain))
1849 .collect()
1850 }
1851
1852 pub fn with_common_translations() -> Self {
1854 let mut matrix = Self::new();
1855
1856 matrix.add_translation(TermTranslation::new(
1858 String::from("felony"),
1859 String::from("US"),
1860 String::from("重罪"),
1861 String::from("JP"),
1862 0.9,
1863 true,
1864 ));
1865
1866 matrix.add_translation(TermTranslation::new(
1867 String::from("misdemeanor"),
1868 String::from("US"),
1869 String::from("軽罪"),
1870 String::from("JP"),
1871 0.9,
1872 true,
1873 ));
1874
1875 matrix.add_translation(TermTranslation::new(
1876 String::from("indictment"),
1877 String::from("US"),
1878 String::from("起訴"),
1879 String::from("JP"),
1880 0.85,
1881 true,
1882 ));
1883
1884 matrix.add_translation(TermTranslation::new(
1886 String::from("起訴"),
1887 String::from("JP"),
1888 String::from("indictment"),
1889 String::from("US"),
1890 0.85,
1891 true,
1892 ));
1893
1894 matrix.add_translation(TermTranslation::new(
1895 String::from("判決"),
1896 String::from("JP"),
1897 String::from("judgment"),
1898 String::from("US"),
1899 0.9,
1900 true,
1901 ));
1902
1903 matrix.add_translation(TermTranslation::new(
1905 String::from("precedent"),
1906 String::from("GB"),
1907 String::from("jurisprudence"),
1908 String::from("FR"),
1909 0.7,
1910 false,
1911 ));
1912
1913 matrix.add_translation(TermTranslation::new(
1914 String::from("case law"),
1915 String::from("US"),
1916 String::from("判例法"),
1917 String::from("JP"),
1918 0.85,
1919 true,
1920 ));
1921
1922 matrix
1923 }
1924}
1925
1926impl Default for TermTranslationMatrix {
1927 fn default() -> Self {
1928 Self::new()
1929 }
1930}
1931
1932#[derive(Debug, Clone)]
1934pub struct SemanticDistanceCalculator {
1935 concept_db: ConceptEquivalenceDatabase,
1937}
1938
1939impl SemanticDistanceCalculator {
1940 pub fn new(concept_db: ConceptEquivalenceDatabase) -> Self {
1942 Self { concept_db }
1943 }
1944
1945 pub fn calculate_distance(
1947 &self,
1948 source_jurisdiction: &str,
1949 target_jurisdiction: &str,
1950 source_concept: &str,
1951 target_concept: &str,
1952 ) -> f64 {
1953 if let Some(equiv) =
1955 self.concept_db
1956 .best_match(source_jurisdiction, target_jurisdiction, source_concept)
1957 && equiv.target_concept.eq_ignore_ascii_case(target_concept)
1958 {
1959 return equiv.semantic_distance;
1960 }
1961
1962 self.string_similarity_distance(source_concept, target_concept)
1964 }
1965
1966 fn string_similarity_distance(&self, a: &str, b: &str) -> f64 {
1968 if a.eq_ignore_ascii_case(b) {
1969 return 0.0;
1970 }
1971
1972 let max_len = a.len().max(b.len());
1974 if max_len == 0 {
1975 return 0.0;
1976 }
1977
1978 let edit_distance = self.levenshtein_distance(a, b);
1979 (edit_distance as f64) / (max_len as f64)
1980 }
1981
1982 #[allow(clippy::needless_range_loop)]
1984 fn levenshtein_distance(&self, a: &str, b: &str) -> usize {
1985 let a_chars: Vec<char> = a.chars().collect();
1986 let b_chars: Vec<char> = b.chars().collect();
1987 let a_len = a_chars.len();
1988 let b_len = b_chars.len();
1989
1990 if a_len == 0 {
1991 return b_len;
1992 }
1993 if b_len == 0 {
1994 return a_len;
1995 }
1996
1997 let mut matrix = vec![vec![0; b_len + 1]; a_len + 1];
1998
1999 for i in 0..=a_len {
2001 matrix[i][0] = i;
2002 }
2003 for j in 0..=b_len {
2004 matrix[0][j] = j;
2005 }
2006
2007 for i in 1..=a_len {
2008 for j in 1..=b_len {
2009 let cost = if a_chars[i - 1] == b_chars[j - 1] {
2010 0
2011 } else {
2012 1
2013 };
2014 matrix[i][j] = (matrix[i - 1][j] + 1)
2015 .min(matrix[i][j - 1] + 1)
2016 .min(matrix[i - 1][j - 1] + cost);
2017 }
2018 }
2019
2020 matrix[a_len][b_len]
2021 }
2022}
2023
2024#[derive(Debug, Clone)]
2026pub struct ContextAwareTermMapper {
2027 translation_matrix: TermTranslationMatrix,
2029 context_rules: HashMap<String, Vec<String>>,
2031}
2032
2033impl ContextAwareTermMapper {
2034 pub fn new(translation_matrix: TermTranslationMatrix) -> Self {
2036 Self {
2037 translation_matrix,
2038 context_rules: HashMap::new(),
2039 }
2040 }
2041
2042 pub fn add_context_rule(&mut self, context: String, keywords: Vec<String>) {
2044 self.context_rules.insert(context, keywords);
2045 }
2046
2047 pub fn map_term(
2049 &self,
2050 source_jurisdiction: &str,
2051 target_jurisdiction: &str,
2052 term: &str,
2053 context_text: &str,
2054 ) -> Option<String> {
2055 let context = self.detect_context(context_text);
2057
2058 if let Some(translation) = self.translation_matrix.best_translation(
2060 source_jurisdiction,
2061 target_jurisdiction,
2062 term,
2063 context.as_deref(),
2064 ) {
2065 return Some(translation.target_term.clone());
2066 }
2067
2068 None
2069 }
2070
2071 fn detect_context(&self, text: &str) -> Option<String> {
2073 let text_lower = text.to_lowercase();
2074
2075 for (context, keywords) in &self.context_rules {
2076 if keywords.iter().any(|kw| text_lower.contains(kw)) {
2077 return Some(context.clone());
2078 }
2079 }
2080
2081 None
2082 }
2083}
2084
2085#[derive(Debug, Clone, Serialize, Deserialize)]
2087pub struct LegalDictionary {
2088 pub jurisdiction: String,
2090 pub terms: Vec<LegalTerm>,
2092 pub metadata: HashMap<String, String>,
2094}
2095
2096impl LegalDictionary {
2097 pub fn new(jurisdiction: String) -> Self {
2099 Self {
2100 jurisdiction,
2101 terms: Vec::new(),
2102 metadata: HashMap::new(),
2103 }
2104 }
2105
2106 pub fn add_term(&mut self, term: LegalTerm) {
2108 self.terms.push(term);
2109 }
2110
2111 pub fn find_term(&self, term_name: &str) -> Option<&LegalTerm> {
2113 self.terms
2114 .iter()
2115 .find(|t| t.term.eq_ignore_ascii_case(term_name))
2116 }
2117
2118 pub fn get_by_domain(&self, domain: &str) -> Vec<&LegalTerm> {
2120 self.terms
2121 .iter()
2122 .filter(|t| t.domain.eq_ignore_ascii_case(domain))
2123 .collect()
2124 }
2125
2126 pub fn us_dictionary() -> Self {
2128 let mut dict = Self::new(String::from("US"));
2129
2130 dict.add_term(LegalTerm::new(
2131 String::from("felony"),
2132 String::from("A serious crime punishable by imprisonment for more than one year"),
2133 String::from("US"),
2134 String::from("criminal"),
2135 ));
2136
2137 dict.add_term(LegalTerm::new(
2138 String::from("misdemeanor"),
2139 String::from("A less serious crime punishable by up to one year in jail"),
2140 String::from("US"),
2141 String::from("criminal"),
2142 ));
2143
2144 dict.add_term(LegalTerm::new(
2145 String::from("tort"),
2146 String::from("A civil wrong that causes harm or loss"),
2147 String::from("US"),
2148 String::from("civil"),
2149 ));
2150
2151 dict.add_term(LegalTerm::new(
2152 String::from("precedent"),
2153 String::from("A legal decision that serves as an authoritative rule in future cases"),
2154 String::from("US"),
2155 String::from("common law"),
2156 ));
2157
2158 dict
2159 }
2160
2161 pub fn japan_dictionary() -> Self {
2163 let mut dict = Self::new(String::from("JP"));
2164
2165 dict.add_term(LegalTerm::new(
2166 String::from("重罪"),
2167 String::from("重大な犯罪"),
2168 String::from("JP"),
2169 String::from("criminal"),
2170 ));
2171
2172 dict.add_term(LegalTerm::new(
2173 String::from("軽罪"),
2174 String::from("比較的軽微な犯罪"),
2175 String::from("JP"),
2176 String::from("criminal"),
2177 ));
2178
2179 dict.add_term(LegalTerm::new(
2180 String::from("不法行為"),
2181 String::from("他人の権利を侵害する行為"),
2182 String::from("JP"),
2183 String::from("civil"),
2184 ));
2185
2186 dict.add_term(LegalTerm::new(
2187 String::from("判例"),
2188 String::from("裁判所の判断の先例"),
2189 String::from("JP"),
2190 String::from("civil law"),
2191 ));
2192
2193 dict
2194 }
2195}
2196
2197#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
2203pub enum CulturalExceptionType {
2204 Religious,
2206 Cultural,
2208 Traditional,
2210 Ethical,
2212 Dietary,
2214 DressCode,
2216 GenderSpecific,
2218 FamilyStructure,
2220}
2221
2222#[derive(Debug, Clone, Serialize, Deserialize)]
2224pub struct CulturalException {
2225 pub exception_type: CulturalExceptionType,
2227 pub jurisdiction: String,
2229 pub description: String,
2231 pub legal_basis: Option<String>,
2233 pub applicable_domains: Vec<String>,
2235 pub resolution_strategy: String,
2237}
2238
2239impl CulturalException {
2240 pub fn new(
2242 exception_type: CulturalExceptionType,
2243 jurisdiction: String,
2244 description: String,
2245 ) -> Self {
2246 Self {
2247 exception_type,
2248 jurisdiction,
2249 description,
2250 legal_basis: None,
2251 applicable_domains: Vec::new(),
2252 resolution_strategy: String::from("Defer to local law"),
2253 }
2254 }
2255
2256 pub fn with_legal_basis(mut self, legal_basis: String) -> Self {
2258 self.legal_basis = Some(legal_basis);
2259 self
2260 }
2261
2262 pub fn with_domain(mut self, domain: String) -> Self {
2264 self.applicable_domains.push(domain);
2265 self
2266 }
2267}
2268
2269#[derive(Debug, Clone, Serialize, Deserialize)]
2271pub struct CulturalExceptionRegistry {
2272 exceptions: HashMap<String, Vec<CulturalException>>,
2274}
2275
2276impl CulturalExceptionRegistry {
2277 pub fn new() -> Self {
2279 Self {
2280 exceptions: HashMap::new(),
2281 }
2282 }
2283
2284 pub fn add_exception(&mut self, exception: CulturalException) {
2286 self.exceptions
2287 .entry(exception.jurisdiction.clone())
2288 .or_default()
2289 .push(exception);
2290 }
2291
2292 pub fn get_exceptions(&self, jurisdiction: &str) -> Vec<&CulturalException> {
2294 self.exceptions
2295 .get(jurisdiction)
2296 .map(|excs| excs.iter().collect())
2297 .unwrap_or_default()
2298 }
2299
2300 pub fn get_by_type(
2302 &self,
2303 jurisdiction: &str,
2304 exception_type: CulturalExceptionType,
2305 ) -> Vec<&CulturalException> {
2306 self.get_exceptions(jurisdiction)
2307 .into_iter()
2308 .filter(|e| e.exception_type == exception_type)
2309 .collect()
2310 }
2311
2312 pub fn with_common_exceptions() -> Self {
2314 let mut registry = Self::new();
2315
2316 registry.add_exception(
2318 CulturalException::new(
2319 CulturalExceptionType::Religious,
2320 String::from("JP"),
2321 String::from("Shinto shrine visits and ceremonies"),
2322 )
2323 .with_legal_basis(String::from(
2324 "Freedom of religion - Constitution Article 20",
2325 ))
2326 .with_domain(String::from("labor"))
2327 .with_domain(String::from("education")),
2328 );
2329
2330 registry.add_exception(
2332 CulturalException::new(
2333 CulturalExceptionType::Religious,
2334 String::from("US"),
2335 String::from("Religious accommodation in workplace"),
2336 )
2337 .with_legal_basis(String::from("Title VII of Civil Rights Act"))
2338 .with_domain(String::from("employment")),
2339 );
2340
2341 registry.add_exception(
2343 CulturalException::new(
2344 CulturalExceptionType::Religious,
2345 String::from("FR"),
2346 String::from("Laïcité - strict separation of religion and state"),
2347 )
2348 .with_legal_basis(String::from("French Constitution Article 1"))
2349 .with_domain(String::from("public service"))
2350 .with_domain(String::from("education")),
2351 );
2352
2353 registry
2354 }
2355}
2356
2357impl Default for CulturalExceptionRegistry {
2358 fn default() -> Self {
2359 Self::new()
2360 }
2361}
2362
2363#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2365pub enum CalendarSystem {
2366 Gregorian,
2368 Japanese,
2370 Islamic,
2372 Hebrew,
2374 Chinese,
2376 Buddhist,
2378}
2379
2380#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2382pub enum HolidayType {
2383 National,
2385 Religious,
2387 Cultural,
2389 Regional,
2391}
2392
2393#[derive(Debug, Clone, Serialize, Deserialize)]
2395pub struct Holiday {
2396 pub name: String,
2398 pub holiday_type: HolidayType,
2400 pub jurisdiction: String,
2402 pub fixed_date: Option<(u8, u8)>,
2404 pub is_legal_holiday: bool,
2406 pub legal_implications: Vec<String>,
2408}
2409
2410impl Holiday {
2411 pub fn new(name: String, holiday_type: HolidayType, jurisdiction: String) -> Self {
2413 Self {
2414 name,
2415 holiday_type,
2416 jurisdiction,
2417 fixed_date: None,
2418 is_legal_holiday: false,
2419 legal_implications: Vec::new(),
2420 }
2421 }
2422
2423 pub fn with_fixed_date(mut self, month: u8, day: u8) -> Self {
2425 self.fixed_date = Some((month, day));
2426 self
2427 }
2428
2429 pub fn as_legal_holiday(mut self) -> Self {
2431 self.is_legal_holiday = true;
2432 self
2433 }
2434}
2435
2436#[derive(Debug, Clone, Serialize, Deserialize)]
2438pub struct HolidayCalendar {
2439 pub jurisdiction: String,
2441 pub calendar_system: CalendarSystem,
2443 pub holidays: Vec<Holiday>,
2445}
2446
2447impl HolidayCalendar {
2448 pub fn new(jurisdiction: String, calendar_system: CalendarSystem) -> Self {
2450 Self {
2451 jurisdiction,
2452 calendar_system,
2453 holidays: Vec::new(),
2454 }
2455 }
2456
2457 pub fn add_holiday(&mut self, holiday: Holiday) {
2459 self.holidays.push(holiday);
2460 }
2461
2462 pub fn get_by_type(&self, holiday_type: HolidayType) -> Vec<&Holiday> {
2464 self.holidays
2465 .iter()
2466 .filter(|h| h.holiday_type == holiday_type)
2467 .collect()
2468 }
2469
2470 pub fn us_calendar() -> Self {
2472 let mut calendar = Self::new(String::from("US"), CalendarSystem::Gregorian);
2473
2474 let mut new_year = Holiday::new(
2475 String::from("New Year's Day"),
2476 HolidayType::National,
2477 String::from("US"),
2478 )
2479 .with_fixed_date(1, 1)
2480 .as_legal_holiday();
2481 new_year
2482 .legal_implications
2483 .push(String::from("Federal holiday - offices closed"));
2484 calendar.add_holiday(new_year);
2485
2486 let mut independence = Holiday::new(
2487 String::from("Independence Day"),
2488 HolidayType::National,
2489 String::from("US"),
2490 )
2491 .with_fixed_date(7, 4)
2492 .as_legal_holiday();
2493 independence
2494 .legal_implications
2495 .push(String::from("Federal holiday - offices closed"));
2496 calendar.add_holiday(independence);
2497
2498 calendar
2499 }
2500
2501 pub fn japan_calendar() -> Self {
2503 let mut calendar = Self::new(String::from("JP"), CalendarSystem::Japanese);
2504
2505 let mut new_year = Holiday::new(
2506 String::from("元日 (New Year's Day)"),
2507 HolidayType::National,
2508 String::from("JP"),
2509 )
2510 .with_fixed_date(1, 1)
2511 .as_legal_holiday();
2512 new_year
2513 .legal_implications
2514 .push(String::from("National holiday - banks closed"));
2515 calendar.add_holiday(new_year);
2516
2517 let mut constitution = Holiday::new(
2518 String::from("憲法記念日 (Constitution Day)"),
2519 HolidayType::National,
2520 String::from("JP"),
2521 )
2522 .with_fixed_date(5, 3)
2523 .as_legal_holiday();
2524 constitution
2525 .legal_implications
2526 .push(String::from("National holiday - government offices closed"));
2527 calendar.add_holiday(constitution);
2528
2529 calendar
2530 }
2531}
2532
2533#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
2535pub enum Currency {
2536 USD,
2537 JPY,
2538 EUR,
2539 GBP,
2540 CNY,
2541}
2542
2543impl Currency {
2544 pub fn code(&self) -> &str {
2546 match self {
2547 Currency::USD => "USD",
2548 Currency::JPY => "JPY",
2549 Currency::EUR => "EUR",
2550 Currency::GBP => "GBP",
2551 Currency::CNY => "CNY",
2552 }
2553 }
2554
2555 pub fn symbol(&self) -> &str {
2557 match self {
2558 Currency::USD => "$",
2559 Currency::JPY => "¥",
2560 Currency::EUR => "€",
2561 Currency::GBP => "£",
2562 Currency::CNY => "¥",
2563 }
2564 }
2565}
2566
2567#[derive(Debug, Clone, Serialize, Deserialize)]
2569pub struct MonetaryConversion {
2570 pub source_amount: f64,
2572 pub source_currency: Currency,
2574 pub target_amount: f64,
2576 pub target_currency: Currency,
2578 pub exchange_rate: f64,
2580 pub conversion_date: Option<String>,
2582 pub legal_significance: Option<String>,
2584}
2585
2586impl MonetaryConversion {
2587 pub fn new(
2589 source_amount: f64,
2590 source_currency: Currency,
2591 target_currency: Currency,
2592 exchange_rate: f64,
2593 ) -> Self {
2594 Self {
2595 source_amount,
2596 source_currency,
2597 target_amount: source_amount * exchange_rate,
2598 target_currency,
2599 exchange_rate,
2600 conversion_date: None,
2601 legal_significance: None,
2602 }
2603 }
2604
2605 pub fn exceeds_threshold(&self, threshold: f64) -> bool {
2607 self.target_amount >= threshold
2608 }
2609}
2610
2611#[derive(Debug, Clone, Serialize, Deserialize)]
2613pub struct MonetaryAdapter {
2614 exchange_rates: HashMap<String, f64>,
2616 legal_thresholds: HashMap<String, Vec<(String, f64)>>,
2618}
2619
2620impl MonetaryAdapter {
2621 pub fn new() -> Self {
2623 Self {
2624 exchange_rates: HashMap::new(),
2625 legal_thresholds: HashMap::new(),
2626 }
2627 }
2628
2629 pub fn add_rate(&mut self, from: Currency, to: Currency, rate: f64) {
2631 let key = format!("{}->{}", from.code(), to.code());
2632 self.exchange_rates.insert(key, rate);
2633 }
2634
2635 pub fn add_threshold(&mut self, jurisdiction: String, description: String, amount: f64) {
2637 self.legal_thresholds
2638 .entry(jurisdiction)
2639 .or_default()
2640 .push((description, amount));
2641 }
2642
2643 pub fn convert(&self, amount: f64, from: Currency, to: Currency) -> Option<MonetaryConversion> {
2645 let key = format!("{}->{}", from.code(), to.code());
2646 self.exchange_rates
2647 .get(&key)
2648 .map(|rate| MonetaryConversion::new(amount, from, to, *rate))
2649 }
2650
2651 pub fn with_common_rates() -> Self {
2653 let mut adapter = Self::new();
2654
2655 adapter.add_rate(Currency::USD, Currency::JPY, 150.0);
2657 adapter.add_rate(Currency::JPY, Currency::USD, 0.0067);
2658 adapter.add_rate(Currency::USD, Currency::EUR, 0.92);
2659 adapter.add_rate(Currency::EUR, Currency::USD, 1.09);
2660 adapter.add_rate(Currency::GBP, Currency::USD, 1.27);
2661 adapter.add_rate(Currency::USD, Currency::GBP, 0.79);
2662
2663 adapter.add_threshold(
2665 String::from("US"),
2666 String::from("Felony theft threshold"),
2667 1000.0,
2668 );
2669 adapter.add_threshold(
2670 String::from("JP"),
2671 String::from("Major theft threshold (重罪窃盗)"),
2672 150_000.0,
2673 );
2674 adapter.add_threshold(
2675 String::from("US"),
2676 String::from("Federal reporting requirement"),
2677 10_000.0,
2678 );
2679
2680 adapter
2681 }
2682}
2683
2684impl Default for MonetaryAdapter {
2685 fn default() -> Self {
2686 Self::new()
2687 }
2688}
2689
2690#[derive(Debug, Clone, Serialize, Deserialize)]
2692pub struct AgeOfMajority {
2693 pub jurisdiction: String,
2695 pub age: u8,
2697 pub exceptions: Vec<String>,
2699 pub legal_implications: Vec<String>,
2701}
2702
2703impl AgeOfMajority {
2704 pub fn new(jurisdiction: String, age: u8) -> Self {
2706 Self {
2707 jurisdiction,
2708 age,
2709 exceptions: Vec::new(),
2710 legal_implications: Vec::new(),
2711 }
2712 }
2713}
2714
2715#[derive(Debug, Clone, Serialize, Deserialize)]
2717pub struct AgeOfMajorityMapper {
2718 ages: HashMap<String, AgeOfMajority>,
2720}
2721
2722impl AgeOfMajorityMapper {
2723 pub fn new() -> Self {
2725 Self {
2726 ages: HashMap::new(),
2727 }
2728 }
2729
2730 pub fn add_age(&mut self, age: AgeOfMajority) {
2732 self.ages.insert(age.jurisdiction.clone(), age);
2733 }
2734
2735 pub fn get_age(&self, jurisdiction: &str) -> Option<&AgeOfMajority> {
2737 self.ages.get(jurisdiction)
2738 }
2739
2740 pub fn map_age_reference(
2742 &self,
2743 source_jurisdiction: &str,
2744 target_jurisdiction: &str,
2745 ) -> Option<String> {
2746 if let (Some(source), Some(target)) = (
2747 self.get_age(source_jurisdiction),
2748 self.get_age(target_jurisdiction),
2749 ) && source.age != target.age
2750 {
2751 return Some(format!(
2752 "Age adjusted from {} to {} for {}",
2753 source.age, target.age, target_jurisdiction
2754 ));
2755 }
2756 None
2757 }
2758
2759 pub fn with_common_jurisdictions() -> Self {
2761 let mut mapper = Self::new();
2762
2763 let mut us = AgeOfMajority::new(String::from("US"), 18);
2764 us.legal_implications.push(String::from("Voting rights"));
2765 us.legal_implications
2766 .push(String::from("Contract capacity"));
2767 us.exceptions.push(String::from("Alcohol: 21 years"));
2768 mapper.add_age(us);
2769
2770 let mut jp = AgeOfMajority::new(String::from("JP"), 18);
2771 jp.legal_implications
2772 .push(String::from("Full legal capacity"));
2773 jp.legal_implications
2774 .push(String::from("Marriage without parental consent"));
2775 jp.exceptions
2776 .push(String::from("Alcohol and tobacco: 20 years (until 2022)"));
2777 mapper.add_age(jp);
2778
2779 let mut gb = AgeOfMajority::new(String::from("GB"), 18);
2780 gb.legal_implications
2781 .push(String::from("Full contractual capacity"));
2782 gb.legal_implications.push(String::from("Voting rights"));
2783 mapper.add_age(gb);
2784
2785 mapper
2786 }
2787}
2788
2789impl Default for AgeOfMajorityMapper {
2790 fn default() -> Self {
2791 Self::new()
2792 }
2793}
2794
2795#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
2797pub enum LegalCapacityType {
2798 Contractual,
2800 Testamentary,
2802 CriminalResponsibility,
2804 Voting,
2806 Marriage,
2808 Employment,
2810}
2811
2812#[derive(Debug, Clone, Serialize, Deserialize)]
2814pub struct LegalCapacityRule {
2815 pub capacity_type: LegalCapacityType,
2817 pub jurisdiction: String,
2819 pub minimum_age: u8,
2821 pub conditions: Vec<String>,
2823 pub exceptions: Vec<String>,
2825}
2826
2827impl LegalCapacityRule {
2828 pub fn new(capacity_type: LegalCapacityType, jurisdiction: String, minimum_age: u8) -> Self {
2830 Self {
2831 capacity_type,
2832 jurisdiction,
2833 minimum_age,
2834 conditions: Vec::new(),
2835 exceptions: Vec::new(),
2836 }
2837 }
2838}
2839
2840#[derive(Debug, Clone, Serialize, Deserialize)]
2842pub struct LegalCapacityAdapter {
2843 rules: HashMap<String, Vec<LegalCapacityRule>>,
2845}
2846
2847impl LegalCapacityAdapter {
2848 pub fn new() -> Self {
2850 Self {
2851 rules: HashMap::new(),
2852 }
2853 }
2854
2855 pub fn add_rule(&mut self, rule: LegalCapacityRule) {
2857 self.rules
2858 .entry(rule.jurisdiction.clone())
2859 .or_default()
2860 .push(rule);
2861 }
2862
2863 pub fn get_rules(&self, jurisdiction: &str) -> Vec<&LegalCapacityRule> {
2865 self.rules
2866 .get(jurisdiction)
2867 .map(|rules| rules.iter().collect())
2868 .unwrap_or_default()
2869 }
2870
2871 pub fn get_rule(
2873 &self,
2874 jurisdiction: &str,
2875 capacity_type: LegalCapacityType,
2876 ) -> Option<&LegalCapacityRule> {
2877 self.get_rules(jurisdiction)
2878 .into_iter()
2879 .find(|r| r.capacity_type == capacity_type)
2880 }
2881
2882 pub fn with_common_rules() -> Self {
2884 let mut adapter = Self::new();
2885
2886 let mut us_contract =
2888 LegalCapacityRule::new(LegalCapacityType::Contractual, String::from("US"), 18);
2889 us_contract
2890 .exceptions
2891 .push(String::from("Necessaries doctrine for minors"));
2892 adapter.add_rule(us_contract);
2893
2894 adapter.add_rule(LegalCapacityRule::new(
2895 LegalCapacityType::Voting,
2896 String::from("US"),
2897 18,
2898 ));
2899
2900 adapter.add_rule(LegalCapacityRule::new(
2901 LegalCapacityType::CriminalResponsibility,
2902 String::from("US"),
2903 18,
2904 ));
2905
2906 adapter.add_rule(LegalCapacityRule::new(
2908 LegalCapacityType::Contractual,
2909 String::from("JP"),
2910 18,
2911 ));
2912
2913 let mut jp_marriage =
2914 LegalCapacityRule::new(LegalCapacityType::Marriage, String::from("JP"), 18);
2915 jp_marriage.conditions.push(String::from(
2916 "Parental consent required until age 20 (pre-2022)",
2917 ));
2918 adapter.add_rule(jp_marriage);
2919
2920 adapter.add_rule(LegalCapacityRule::new(
2921 LegalCapacityType::CriminalResponsibility,
2922 String::from("JP"),
2923 14,
2924 ));
2925
2926 adapter
2927 }
2928}
2929
2930impl Default for LegalCapacityAdapter {
2931 fn default() -> Self {
2932 Self::new()
2933 }
2934}
2935
2936pub struct PortingEngine {
2938 source: Jurisdiction,
2940 target: Jurisdiction,
2942 text_generator: Option<Box<dyn TextGenerator>>,
2944 term_replacements: Vec<TermReplacement>,
2946 equivalence_mappings: Vec<EquivalenceMapping>,
2948}
2949
2950impl PortingEngine {
2951 pub fn new(source: Jurisdiction, target: Jurisdiction) -> Self {
2953 Self {
2954 source,
2955 target,
2956 text_generator: None,
2957 term_replacements: Vec::new(),
2958 equivalence_mappings: Vec::new(),
2959 }
2960 }
2961
2962 pub fn with_text_generator(mut self, generator: Box<dyn TextGenerator>) -> Self {
2964 self.text_generator = Some(generator);
2965 self
2966 }
2967
2968 pub fn with_term_replacements(mut self, replacements: Vec<TermReplacement>) -> Self {
2970 self.term_replacements = replacements;
2971 self
2972 }
2973
2974 pub fn with_equivalence_mappings(mut self, mappings: Vec<EquivalenceMapping>) -> Self {
2976 self.equivalence_mappings = mappings;
2977 self
2978 }
2979
2980 pub fn port_statute(
2982 &self,
2983 statute: &Statute,
2984 options: &PortingOptions,
2985 ) -> PortingResult<PortedStatute> {
2986 let mut changes = Vec::new();
2987 let mut adapted = statute.clone();
2988
2989 if options.apply_cultural_params {
2991 self.apply_cultural_adaptations(&mut adapted, &mut changes)?;
2992 }
2993
2994 adapted.id = format!("{}-{}", self.target.id.to_lowercase(), statute.id);
2996
2997 let compatibility_score = if changes.is_empty() {
2999 1.0
3000 } else {
3001 let incompatible_count = changes
3002 .iter()
3003 .filter(|c| matches!(c.change_type, ChangeType::Incompatible))
3004 .count();
3005 let major_count = changes
3006 .iter()
3007 .filter(|c| {
3008 matches!(
3009 c.change_type,
3010 ChangeType::CulturalAdaptation | ChangeType::Translation
3011 )
3012 })
3013 .count();
3014
3015 1.0 - (incompatible_count as f64 * 0.3 + major_count as f64 * 0.1).min(0.9)
3017 };
3018
3019 Ok(PortedStatute {
3020 original_id: statute.id.clone(),
3021 statute: adapted,
3022 changes,
3023 locale: self.target.locale.clone(),
3024 compatibility_score,
3025 })
3026 }
3027
3028 fn apply_cultural_adaptations(
3029 &self,
3030 _statute: &mut Statute,
3031 changes: &mut Vec<PortingChange>,
3032 ) -> PortingResult<()> {
3033 let source_params = &self.source.cultural_params;
3034 let target_params = &self.target.cultural_params;
3035
3036 if source_params.age_of_majority != target_params.age_of_majority
3038 && let (Some(source_age), Some(target_age)) =
3039 (source_params.age_of_majority, target_params.age_of_majority)
3040 {
3041 changes.push(PortingChange {
3043 change_type: ChangeType::ValueAdaptation,
3044 description: "Age of majority adjusted".to_string(),
3045 original: Some(source_age.to_string()),
3046 adapted: Some(target_age.to_string()),
3047 reason: format!(
3048 "Target jurisdiction ({}) has different age of majority",
3049 self.target.id
3050 ),
3051 });
3052 }
3053
3054 for prohibition in &target_params.prohibitions {
3056 changes.push(PortingChange {
3057 change_type: ChangeType::CulturalAdaptation,
3058 description: format!("Checked against prohibition: {}", prohibition),
3059 original: None,
3060 adapted: None,
3061 reason: "Target jurisdiction has cultural prohibition".to_string(),
3062 });
3063 }
3064
3065 Ok(())
3066 }
3067
3068 pub fn generate_report(&self, statutes: &[Statute]) -> CompatibilityReport {
3070 let mut report = CompatibilityReport::default();
3071 let mut findings = Vec::new();
3072
3073 if self.source.legal_system != self.target.legal_system {
3075 findings.push(CompatibilityFinding {
3076 severity: Severity::Warning,
3077 category: "Legal System".to_string(),
3078 description: format!(
3079 "Different legal systems: {:?} -> {:?}",
3080 self.source.legal_system, self.target.legal_system
3081 ),
3082 statute_id: None,
3083 });
3084 report.adaptations_required += 1;
3085 }
3086
3087 for statute in statutes {
3089 if statute.discretion_logic.is_some() {
3090 findings.push(CompatibilityFinding {
3091 severity: Severity::Info,
3092 category: "Discretion".to_string(),
3093 description: "Statute contains discretionary elements requiring local review"
3094 .to_string(),
3095 statute_id: Some(statute.id.clone()),
3096 });
3097 }
3098 }
3099
3100 report.findings = findings;
3101 report.compatibility_score = self.calculate_compatibility_score(&report);
3102 report.recommendations = self.generate_recommendations(&report);
3103
3104 report
3105 }
3106
3107 fn calculate_compatibility_score(&self, report: &CompatibilityReport) -> f64 {
3108 let base_score = 1.0;
3109 let deductions =
3110 (report.adaptations_required as f64 * 0.1) + (report.incompatibilities as f64 * 0.2);
3111 (base_score - deductions).max(0.0)
3112 }
3113
3114 fn generate_recommendations(&self, report: &CompatibilityReport) -> Vec<String> {
3115 let mut recommendations = Vec::new();
3116
3117 if report.compatibility_score < 0.5 {
3118 recommendations.push(
3119 "Low compatibility score. Consider a full legal review before adoption."
3120 .to_string(),
3121 );
3122 }
3123
3124 if self.source.legal_system != self.target.legal_system {
3125 recommendations.push(
3126 "Legal systems differ. Case law adaptation may be required for common law targets."
3127 .to_string(),
3128 );
3129 }
3130
3131 recommendations
3132 }
3133
3134 pub async fn generate_ai_suggestions(
3136 &self,
3137 statute: &Statute,
3138 ) -> PortingResult<Vec<AdaptationSuggestion>> {
3139 let generator = self.text_generator.as_ref().ok_or_else(|| {
3140 PortingError::AdaptationRequired("Text generator not configured".to_string())
3141 })?;
3142
3143 let prompt = format!(
3144 "Analyze the following statute for cultural adaptation from {} to {}:\n\
3145 Statute ID: {}\n\
3146 Title: {}\n\
3147 Source Legal System: {:?}\n\
3148 Target Legal System: {:?}\n\
3149 Source Cultural Parameters: Age of Majority = {:?}, Prohibitions = {:?}\n\
3150 Target Cultural Parameters: Age of Majority = {:?}, Prohibitions = {:?}\n\n\
3151 Please provide specific adaptation suggestions with rationale.",
3152 self.source.id,
3153 self.target.id,
3154 statute.id,
3155 statute.title,
3156 self.source.legal_system,
3157 self.target.legal_system,
3158 self.source.cultural_params.age_of_majority,
3159 self.source.cultural_params.prohibitions,
3160 self.target.cultural_params.age_of_majority,
3161 self.target.cultural_params.prohibitions
3162 );
3163
3164 let response = generator.generate(&prompt).await?;
3165
3166 let suggestions = vec![AdaptationSuggestion {
3168 statute_id: statute.id.clone(),
3169 suggestion: response,
3170 rationale: "AI-generated based on cultural parameter analysis".to_string(),
3171 confidence: 0.8,
3172 category: "Cultural Adaptation".to_string(),
3173 }];
3174
3175 Ok(suggestions)
3176 }
3177
3178 pub fn port_sections(
3180 &self,
3181 statute: &Statute,
3182 section_ids: &[String],
3183 options: &PortingOptions,
3184 ) -> PortingResult<PortedStatute> {
3185 let mut ported = self.port_statute(statute, options)?;
3188
3189 ported.changes.push(PortingChange {
3191 change_type: ChangeType::ComplianceAddition,
3192 description: format!("Partial porting of sections: {:?}", section_ids),
3193 original: None,
3194 adapted: Some(format!("{} sections ported", section_ids.len())),
3195 reason: "Selective section porting requested".to_string(),
3196 });
3197
3198 Ok(ported)
3199 }
3200
3201 pub fn reverse_port_analysis(
3203 &self,
3204 _target_statute: &Statute,
3205 ) -> PortingResult<Vec<PortingChange>> {
3206 let mut changes = Vec::new();
3207
3208 if let (Some(target_age), Some(source_age)) = (
3210 self.target.cultural_params.age_of_majority,
3211 self.source.cultural_params.age_of_majority,
3212 ) && target_age != source_age
3213 {
3214 changes.push(PortingChange {
3215 change_type: ChangeType::ValueAdaptation,
3216 description: "Reverse age of majority adjustment".to_string(),
3217 original: Some(target_age.to_string()),
3218 adapted: Some(source_age.to_string()),
3219 reason: format!(
3220 "Reverting to source jurisdiction ({}) age of majority",
3221 self.source.id
3222 ),
3223 });
3224 }
3225
3226 for prohibition in &self.target.cultural_params.prohibitions {
3228 if !self
3229 .source
3230 .cultural_params
3231 .prohibitions
3232 .contains(prohibition)
3233 {
3234 changes.push(PortingChange {
3235 change_type: ChangeType::Removal,
3236 description: format!("Remove prohibition: {}", prohibition),
3237 original: Some(prohibition.clone()),
3238 adapted: None,
3239 reason: "Source jurisdiction does not have this prohibition".to_string(),
3240 });
3241 }
3242 }
3243
3244 Ok(changes)
3245 }
3246
3247 pub fn detect_conflicts(&self, statute: &Statute) -> Vec<ConflictReport> {
3249 let mut conflicts = Vec::new();
3250
3251 if self.source.legal_system != self.target.legal_system {
3253 conflicts.push(ConflictReport {
3254 statute_id: statute.id.clone(),
3255 conflict_type: ConflictType::SystemMismatch,
3256 description: format!(
3257 "Legal system mismatch: {:?} vs {:?}",
3258 self.source.legal_system, self.target.legal_system
3259 ),
3260 severity: Severity::Warning,
3261 resolutions: vec![
3262 "Adapt procedural elements to target legal system".to_string(),
3263 "Consult legal expert for system-specific modifications".to_string(),
3264 ],
3265 });
3266 }
3267
3268 for prohibition in &self.target.cultural_params.prohibitions {
3270 conflicts.push(ConflictReport {
3272 statute_id: statute.id.clone(),
3273 conflict_type: ConflictType::CulturalIncompatibility,
3274 description: format!("Check compatibility with prohibition: {}", prohibition),
3275 severity: Severity::Info,
3276 resolutions: vec![
3277 format!("Review statute for compliance with: {}", prohibition),
3278 "Consider alternative formulations".to_string(),
3279 ],
3280 });
3281 }
3282
3283 conflicts
3284 }
3285
3286 pub fn validate_semantics(
3288 &self,
3289 original: &Statute,
3290 ported: &PortedStatute,
3291 ) -> SemanticValidation {
3292 let mut findings = Vec::new();
3293
3294 if original.title != ported.statute.title {
3296 findings.push(SemanticFinding {
3297 statute_id: original.id.clone(),
3298 description: "Title modified during porting".to_string(),
3299 severity: Severity::Info,
3300 impact: "May affect legal citation and reference".to_string(),
3301 });
3302 }
3303
3304 for change in &ported.changes {
3306 match change.change_type {
3307 ChangeType::Translation => {
3308 findings.push(SemanticFinding {
3309 statute_id: original.id.clone(),
3310 description: format!("Translation: {}", change.description),
3311 severity: Severity::Info,
3312 impact: "Semantic drift possible in translation".to_string(),
3313 });
3314 }
3315 ChangeType::Incompatible => {
3316 findings.push(SemanticFinding {
3317 statute_id: original.id.clone(),
3318 description: format!("Incompatibility: {}", change.description),
3319 severity: Severity::Error,
3320 impact: "Significant semantic change required".to_string(),
3321 });
3322 }
3323 _ => {}
3324 }
3325 }
3326
3327 let error_count = findings
3329 .iter()
3330 .filter(|f| f.severity == Severity::Error)
3331 .count();
3332 let warning_count = findings
3333 .iter()
3334 .filter(|f| f.severity == Severity::Warning)
3335 .count();
3336
3337 let preservation_score = 1.0 - (error_count as f64 * 0.3) - (warning_count as f64 * 0.1);
3338 let preservation_score = preservation_score.clamp(0.0, 1.0);
3339
3340 SemanticValidation {
3341 preservation_score,
3342 is_valid: preservation_score >= 0.7,
3343 findings,
3344 }
3345 }
3346
3347 pub fn assess_risks(&self, ported: &PortedStatute) -> RiskAssessment {
3349 let mut risks = Vec::new();
3350
3351 if self.source.legal_system != self.target.legal_system {
3353 risks.push(Risk {
3354 id: uuid::Uuid::new_v4().to_string(),
3355 category: RiskCategory::Legal,
3356 description: "Different legal systems may cause interpretation issues".to_string(),
3357 likelihood: RiskLevel::Medium,
3358 impact: 0.6,
3359 severity: RiskLevel::Medium,
3360 });
3361 }
3362
3363 let cultural_changes = ported
3365 .changes
3366 .iter()
3367 .filter(|c| matches!(c.change_type, ChangeType::CulturalAdaptation))
3368 .count();
3369
3370 if cultural_changes > 0 {
3371 risks.push(Risk {
3372 id: uuid::Uuid::new_v4().to_string(),
3373 category: RiskCategory::Cultural,
3374 description: format!(
3375 "{} cultural adaptations may affect statute applicability",
3376 cultural_changes
3377 ),
3378 likelihood: RiskLevel::Medium,
3379 impact: 0.5,
3380 severity: RiskLevel::Low,
3381 });
3382 }
3383
3384 let incompatibilities = ported
3386 .changes
3387 .iter()
3388 .filter(|c| matches!(c.change_type, ChangeType::Incompatible))
3389 .count();
3390
3391 if incompatibilities > 0 {
3392 risks.push(Risk {
3393 id: uuid::Uuid::new_v4().to_string(),
3394 category: RiskCategory::Legal,
3395 description: format!("{} incompatibilities detected", incompatibilities),
3396 likelihood: RiskLevel::High,
3397 impact: 0.8,
3398 severity: RiskLevel::High,
3399 });
3400 }
3401
3402 let risk_score = if risks.is_empty() {
3404 0.1
3405 } else {
3406 let risk_level_to_f64 = |level: RiskLevel| match level {
3408 RiskLevel::Negligible => 0.1,
3409 RiskLevel::Low => 0.25,
3410 RiskLevel::Medium => 0.5,
3411 RiskLevel::High => 0.75,
3412 RiskLevel::Critical => 1.0,
3413 };
3414 risks
3415 .iter()
3416 .map(|r| risk_level_to_f64(r.likelihood) * r.impact)
3417 .sum::<f64>()
3418 / risks.len() as f64
3419 };
3420
3421 let risk_level = match risk_score {
3422 s if s < 0.25 => RiskLevel::Low,
3423 s if s < 0.5 => RiskLevel::Medium,
3424 s if s < 0.75 => RiskLevel::High,
3425 _ => RiskLevel::Critical,
3426 };
3427
3428 let mitigations = vec![
3429 "Conduct legal expert review".to_string(),
3430 "Pilot test in limited scope".to_string(),
3431 "Monitor implementation closely".to_string(),
3432 "Establish feedback mechanism".to_string(),
3433 ];
3434
3435 RiskAssessment {
3436 risk_score,
3437 risk_level,
3438 risks,
3439 mitigations,
3440 }
3441 }
3442
3443 pub async fn batch_port(
3445 &self,
3446 statutes: &[Statute],
3447 options: &PortingOptions,
3448 ) -> PortingResult<PortingOutput> {
3449 let mut ported_statutes = Vec::new();
3450 let mut all_warnings = Vec::new();
3451 let mut all_ai_suggestions = Vec::new();
3452 let mut all_conflicts = Vec::new();
3453
3454 for statute in statutes {
3455 let ported = if !options.section_ids.is_empty() {
3457 self.port_sections(statute, &options.section_ids, options)?
3458 } else {
3459 self.port_statute(statute, options)?
3460 };
3461
3462 if options.use_ai_suggestions && self.text_generator.is_some() {
3464 match self.generate_ai_suggestions(statute).await {
3465 Ok(suggestions) => all_ai_suggestions.extend(suggestions),
3466 Err(e) => {
3467 all_warnings.push(format!("AI suggestion failed for {}: {}", statute.id, e))
3468 }
3469 }
3470 }
3471
3472 if options.detect_conflicts {
3474 all_conflicts.extend(self.detect_conflicts(statute));
3475 }
3476
3477 ported_statutes.push(ported);
3478 }
3479
3480 let report = if options.generate_report {
3482 Some(self.generate_report(statutes))
3483 } else {
3484 None
3485 };
3486
3487 let semantic_validation = if options.validate_semantics && !ported_statutes.is_empty() {
3489 Some(self.validate_semantics(&statutes[0], &ported_statutes[0]))
3490 } else {
3491 None
3492 };
3493
3494 let risk_assessment = if !ported_statutes.is_empty() {
3496 Some(self.assess_risks(&ported_statutes[0]))
3497 } else {
3498 None
3499 };
3500
3501 Ok(PortingOutput {
3502 statutes: ported_statutes,
3503 report,
3504 warnings: all_warnings,
3505 ai_suggestions: all_ai_suggestions,
3506 conflicts: all_conflicts,
3507 semantic_validation,
3508 risk_assessment,
3509 })
3510 }
3511
3512 pub fn create_bilateral_agreement(&self, agreement_type: AgreementType) -> BilateralAgreement {
3514 BilateralAgreement {
3515 id: format!(
3516 "{}-{}-agreement",
3517 self.source.id.to_lowercase(),
3518 self.target.id.to_lowercase()
3519 ),
3520 source_jurisdiction: self.source.id.clone(),
3521 target_jurisdiction: self.target.id.clone(),
3522 agreement_type,
3523 mutual_recognition: vec![
3524 "Both parties recognize each other's legal frameworks".to_string(),
3525 "Statutes ported under this agreement maintain legal validity".to_string(),
3526 ],
3527 adaptation_protocols: vec![AdaptationProtocol {
3528 name: "Standard Adaptation Protocol".to_string(),
3529 description: "Default protocol for statute adaptation".to_string(),
3530 statute_types: vec!["civil".to_string(), "commercial".to_string()],
3531 rules: vec![
3532 "Preserve legal intent and semantic meaning".to_string(),
3533 "Adapt numerical thresholds to local standards".to_string(),
3534 "Replace legal terms with local equivalents".to_string(),
3535 ],
3536 }],
3537 dispute_resolution: Some(
3538 "Disputes resolved through bilateral consultation".to_string(),
3539 ),
3540 }
3541 }
3542
3543 pub fn find_regulatory_equivalence(&self, statute: &Statute) -> Vec<EquivalenceMapping> {
3545 self.equivalence_mappings
3547 .iter()
3548 .filter(|m| m.source_regulation == statute.id)
3549 .cloned()
3550 .collect()
3551 }
3552
3553 pub async fn find_similar_statutes(
3555 &self,
3556 statute: &Statute,
3557 candidate_statutes: &[Statute],
3558 ) -> Vec<(Statute, f64)> {
3559 let mut similarities = Vec::new();
3560
3561 for candidate in candidate_statutes {
3562 let similarity = self.calculate_similarity(&statute.title, &candidate.title);
3564 if similarity > 0.3 {
3565 similarities.push((candidate.clone(), similarity));
3566 }
3567 }
3568
3569 similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
3571 similarities
3572 }
3573
3574 fn calculate_similarity(&self, text1: &str, text2: &str) -> f64 {
3575 let lower1 = text1.to_lowercase();
3577 let lower2 = text2.to_lowercase();
3578
3579 let words1: std::collections::HashSet<_> = lower1.split_whitespace().collect();
3580 let words2: std::collections::HashSet<_> = lower2.split_whitespace().collect();
3581
3582 let intersection = words1.intersection(&words2).count();
3583 let union = words1.union(&words2).count();
3584
3585 if union == 0 {
3586 0.0
3587 } else {
3588 intersection as f64 / union as f64
3589 }
3590 }
3591
3592 pub fn apply_term_replacement(&self, statute: &mut Statute) -> Vec<TermReplacement> {
3594 let mut applied_replacements = Vec::new();
3595
3596 for replacement in &self.term_replacements {
3597 if statute.title.contains(&replacement.source_term) {
3599 statute.title = statute
3600 .title
3601 .replace(&replacement.source_term, &replacement.target_term);
3602 applied_replacements.push(replacement.clone());
3603 }
3604 }
3605
3606 applied_replacements
3607 }
3608
3609 pub fn adjust_parameters_contextually(&self, statute: &Statute) -> Vec<ContextualAdjustment> {
3611 let mut adjustments = Vec::new();
3612
3613 if let (Some(source_age), Some(target_age)) = (
3615 self.source.cultural_params.age_of_majority,
3616 self.target.cultural_params.age_of_majority,
3617 ) && source_age != target_age
3618 {
3619 adjustments.push(ContextualAdjustment {
3620 parameter: "age_of_majority".to_string(),
3621 original_value: source_age.to_string(),
3622 adjusted_value: target_age.to_string(),
3623 context: format!("Statute: {}", statute.id),
3624 rationale: "Age of majority differs between jurisdictions".to_string(),
3625 });
3626 }
3627
3628 if statute.title.to_lowercase().contains("fine")
3630 || statute.title.to_lowercase().contains("payment")
3631 {
3632 adjustments.push(ContextualAdjustment {
3633 parameter: "currency".to_string(),
3634 original_value: self.source.locale.language.clone(),
3635 adjusted_value: self.target.locale.language.clone(),
3636 context: "Monetary statute".to_string(),
3637 rationale: "Currency and amounts need localization".to_string(),
3638 });
3639 }
3640
3641 adjustments
3642 }
3643
3644 pub fn create_workflow(&self, statute_id: String) -> PortingWorkflow {
3646 PortingWorkflow {
3647 id: format!("workflow-{}", statute_id),
3648 state: WorkflowState::Initiated,
3649 statute_id: statute_id.clone(),
3650 source_jurisdiction: self.source.id.clone(),
3651 target_jurisdiction: self.target.id.clone(),
3652 completed_steps: Vec::new(),
3653 pending_steps: vec![
3654 WorkflowStep {
3655 name: "Initial Analysis".to_string(),
3656 description: "Analyze statute for porting compatibility".to_string(),
3657 status: StepStatus::Pending,
3658 completed_at: None,
3659 },
3660 WorkflowStep {
3661 name: "Cultural Adaptation".to_string(),
3662 description: "Apply cultural parameter adaptations".to_string(),
3663 status: StepStatus::Pending,
3664 completed_at: None,
3665 },
3666 WorkflowStep {
3667 name: "Legal Review".to_string(),
3668 description: "Review by legal expert".to_string(),
3669 status: StepStatus::Pending,
3670 completed_at: None,
3671 },
3672 WorkflowStep {
3673 name: "Final Approval".to_string(),
3674 description: "Final approval by authority".to_string(),
3675 status: StepStatus::Pending,
3676 completed_at: None,
3677 },
3678 ],
3679 approvals: vec![
3680 Approval {
3681 approver_role: "Legal Expert".to_string(),
3682 status: ApprovalStatus::Pending,
3683 comments: None,
3684 },
3685 Approval {
3686 approver_role: "Jurisdictional Authority".to_string(),
3687 status: ApprovalStatus::Pending,
3688 comments: None,
3689 },
3690 ],
3691 }
3692 }
3693
3694 pub fn advance_workflow(&self, workflow: &mut PortingWorkflow) -> PortingResult<()> {
3696 if let Some(mut step) = workflow.pending_steps.first().cloned() {
3697 step.status = StepStatus::Completed;
3698 step.completed_at = Some(chrono::Utc::now().to_rfc3339());
3699 workflow.completed_steps.push(step);
3700 workflow.pending_steps.remove(0);
3701
3702 if workflow.pending_steps.is_empty() {
3704 workflow.state = WorkflowState::PendingReview;
3705 } else {
3706 workflow.state = WorkflowState::InProgress;
3707 }
3708
3709 Ok(())
3710 } else {
3711 Err(PortingError::AdaptationRequired(
3712 "No pending steps to advance".to_string(),
3713 ))
3714 }
3715 }
3716
3717 pub fn create_versioned_statute(
3719 &self,
3720 statute: PortedStatute,
3721 version: u32,
3722 created_by: String,
3723 change_notes: String,
3724 ) -> VersionedPortedStatute {
3725 use std::collections::hash_map::DefaultHasher;
3726 use std::hash::{Hash, Hasher};
3727
3728 let mut hasher = DefaultHasher::new();
3730 statute.statute.id.hash(&mut hasher);
3731 statute.statute.title.hash(&mut hasher);
3732 version.hash(&mut hasher);
3733 let hash = format!("{:x}", hasher.finish());
3734
3735 VersionedPortedStatute {
3736 statute,
3737 version,
3738 previous_hash: if version > 1 {
3739 Some("previous_hash_placeholder".to_string())
3740 } else {
3741 None
3742 },
3743 hash,
3744 created_at: chrono::Utc::now().to_rfc3339(),
3745 created_by,
3746 change_notes,
3747 }
3748 }
3749
3750 pub fn compare_versions(
3752 &self,
3753 v1: &VersionedPortedStatute,
3754 v2: &VersionedPortedStatute,
3755 ) -> Vec<String> {
3756 let mut differences = Vec::new();
3757
3758 if v1.statute.statute.title != v2.statute.statute.title {
3759 differences.push(format!(
3760 "Title changed from '{}' to '{}'",
3761 v1.statute.statute.title, v2.statute.statute.title
3762 ));
3763 }
3764
3765 if v1.statute.changes.len() != v2.statute.changes.len() {
3766 differences.push(format!(
3767 "Number of changes: {} -> {}",
3768 v1.statute.changes.len(),
3769 v2.statute.changes.len()
3770 ));
3771 }
3772
3773 differences
3774 }
3775
3776 pub fn submit_for_review(&self, statute: PortedStatute) -> ReviewRequest {
3778 ReviewRequest {
3779 id: format!("review-{}", statute.statute.id),
3780 statute,
3781 source_jurisdiction: self.source.id.clone(),
3782 target_jurisdiction: self.target.id.clone(),
3783 status: ReviewStatus::Pending,
3784 assigned_expert: None,
3785 submitted_at: chrono::Utc::now().to_rfc3339(),
3786 reviews: Vec::new(),
3787 }
3788 }
3789
3790 pub fn assign_expert(&self, request: &mut ReviewRequest, expert_id: String) {
3792 request.assigned_expert = Some(expert_id);
3793 request.status = ReviewStatus::Assigned;
3794 }
3795
3796 pub fn add_expert_review(
3798 &self,
3799 request: &mut ReviewRequest,
3800 review: ExpertReview,
3801 ) -> PortingResult<()> {
3802 request.reviews.push(review.clone());
3803 request.status = ReviewStatus::InReview;
3804
3805 match review.recommendation {
3807 ReviewRecommendation::Approve => {
3808 request.status = ReviewStatus::Approved;
3809 }
3810 ReviewRecommendation::ApproveWithChanges => {
3811 request.status = ReviewStatus::RequiresRevision;
3812 }
3813 ReviewRecommendation::Reject => {
3814 request.status = ReviewStatus::Rejected;
3815 }
3816 ReviewRecommendation::RequestInformation => {
3817 request.status = ReviewStatus::InReview;
3818 }
3819 }
3820
3821 Ok(())
3822 }
3823
3824 pub fn create_review_comment(
3826 &self,
3827 section: Option<String>,
3828 text: String,
3829 severity: Severity,
3830 category: String,
3831 ) -> ReviewComment {
3832 ReviewComment {
3833 id: format!("comment-{}", chrono::Utc::now().timestamp()),
3834 section,
3835 text,
3836 severity,
3837 category,
3838 }
3839 }
3840
3841 pub fn check_compliance(&self, statute: &PortedStatute) -> ComplianceCheckResult {
3843 let mut checks = Vec::new();
3844 let mut violations = Vec::new();
3845
3846 let legal_system_check = ComplianceCheck {
3848 name: "Legal System Compatibility".to_string(),
3849 description: "Verify statute is compatible with target legal system".to_string(),
3850 passed: self.source.legal_system == self.target.legal_system,
3851 details: Some(format!(
3852 "Source: {:?}, Target: {:?}",
3853 self.source.legal_system, self.target.legal_system
3854 )),
3855 severity: if self.source.legal_system != self.target.legal_system {
3856 Severity::Warning
3857 } else {
3858 Severity::Info
3859 },
3860 };
3861 checks.push(legal_system_check.clone());
3862
3863 if !legal_system_check.passed {
3864 violations.push(ComplianceViolation {
3865 violation_type: "Legal System Mismatch".to_string(),
3866 description: "Source and target legal systems differ".to_string(),
3867 severity: Severity::Error,
3868 regulation: "Legal System Compatibility Requirements".to_string(),
3869 remediation: vec![
3870 "Review statute for procedural adaptations".to_string(),
3871 "Consult legal expert for system-specific modifications".to_string(),
3872 ],
3873 });
3874 }
3875
3876 let cultural_check = ComplianceCheck {
3878 name: "Cultural Parameter Compliance".to_string(),
3879 description: "Verify cultural parameters are properly adapted".to_string(),
3880 passed: !statute.changes.is_empty(),
3881 details: Some(format!(
3882 "{} cultural adaptations made",
3883 statute.changes.len()
3884 )),
3885 severity: Severity::Info,
3886 };
3887 checks.push(cultural_check);
3888
3889 let mut has_prohibited_content = false;
3891 for prohibition in &self.target.cultural_params.prohibitions {
3892 if statute
3893 .statute
3894 .title
3895 .to_lowercase()
3896 .contains(&prohibition.to_lowercase())
3897 {
3898 has_prohibited_content = true;
3899 violations.push(ComplianceViolation {
3900 violation_type: "Prohibited Content".to_string(),
3901 description: format!("Statute may conflict with prohibition: {}", prohibition),
3902 severity: Severity::Error,
3903 regulation: format!("Cultural Prohibition: {}", prohibition),
3904 remediation: vec![
3905 "Review statute content for compliance".to_string(),
3906 "Consider alternative formulations".to_string(),
3907 "Seek legal expert review".to_string(),
3908 ],
3909 });
3910 }
3911 }
3912
3913 checks.push(ComplianceCheck {
3914 name: "Prohibited Content Check".to_string(),
3915 description: "Verify statute does not violate cultural prohibitions".to_string(),
3916 passed: !has_prohibited_content,
3917 details: Some(format!(
3918 "Checked {} prohibitions",
3919 self.target.cultural_params.prohibitions.len()
3920 )),
3921 severity: if has_prohibited_content {
3922 Severity::Error
3923 } else {
3924 Severity::Info
3925 },
3926 });
3927
3928 checks.push(ComplianceCheck {
3930 name: "Title Preservation".to_string(),
3931 description: "Verify title maintains semantic meaning".to_string(),
3932 passed: true,
3933 details: Some("Title checked for semantic preservation".to_string()),
3934 severity: Severity::Info,
3935 });
3936
3937 checks.push(ComplianceCheck {
3939 name: "Change Tracking".to_string(),
3940 description: "Verify all changes are documented".to_string(),
3941 passed: !statute.changes.is_empty(),
3942 details: Some(format!("{} changes tracked", statute.changes.len())),
3943 severity: Severity::Info,
3944 });
3945
3946 let passed_count = checks.iter().filter(|c| c.passed).count();
3948 let compliance_score = passed_count as f64 / checks.len() as f64;
3949
3950 let status = if violations.iter().any(|v| v.severity == Severity::Critical) {
3952 ComplianceStatus::NonCompliant
3953 } else if violations.iter().any(|v| v.severity == Severity::Error) {
3954 ComplianceStatus::RequiresReview
3955 } else if !violations.is_empty() {
3956 ComplianceStatus::CompliantWithIssues
3957 } else {
3958 ComplianceStatus::Compliant
3959 };
3960
3961 let mut recommendations = Vec::new();
3963 if compliance_score < 0.8 {
3964 recommendations.push("Consider additional review before adoption".to_string());
3965 }
3966 if !violations.is_empty() {
3967 recommendations.push("Address identified violations before implementation".to_string());
3968 }
3969 if self.source.legal_system != self.target.legal_system {
3970 recommendations
3971 .push("Engage legal expert familiar with target legal system".to_string());
3972 }
3973
3974 ComplianceCheckResult {
3975 id: format!("compliance-{}", statute.statute.id),
3976 statute_id: statute.statute.id.clone(),
3977 checked_at: chrono::Utc::now().to_rfc3339(),
3978 status,
3979 compliance_score,
3980 checks,
3981 violations,
3982 recommendations,
3983 }
3984 }
3985
3986 pub fn batch_check_compliance(&self, statutes: &[PortedStatute]) -> Vec<ComplianceCheckResult> {
3988 statutes.iter().map(|s| self.check_compliance(s)).collect()
3989 }
3990
3991 pub fn generate_compliance_summary(
3993 &self,
3994 results: &[ComplianceCheckResult],
3995 ) -> ComplianceSummary {
3996 let total = results.len();
3997 let compliant = results
3998 .iter()
3999 .filter(|r| r.status == ComplianceStatus::Compliant)
4000 .count();
4001 let compliant_with_issues = results
4002 .iter()
4003 .filter(|r| r.status == ComplianceStatus::CompliantWithIssues)
4004 .count();
4005 let non_compliant = results
4006 .iter()
4007 .filter(|r| r.status == ComplianceStatus::NonCompliant)
4008 .count();
4009 let requires_review = results
4010 .iter()
4011 .filter(|r| r.status == ComplianceStatus::RequiresReview)
4012 .count();
4013
4014 let avg_score = if !results.is_empty() {
4015 results.iter().map(|r| r.compliance_score).sum::<f64>() / results.len() as f64
4016 } else {
4017 0.0
4018 };
4019
4020 let total_violations: usize = results.iter().map(|r| r.violations.len()).sum();
4021
4022 ComplianceSummary {
4023 total_statutes: total,
4024 compliant,
4025 compliant_with_issues,
4026 non_compliant,
4027 requires_review,
4028 average_compliance_score: avg_score,
4029 total_violations,
4030 critical_violations: results
4031 .iter()
4032 .flat_map(|r| &r.violations)
4033 .filter(|v| v.severity == Severity::Critical)
4034 .count(),
4035 }
4036 }
4037
4038 pub fn export_compatibility_report(
4040 &self,
4041 report: &CompatibilityReport,
4042 format: ExportFormat,
4043 ) -> PortingResult<String> {
4044 match format {
4045 ExportFormat::Json => serde_json::to_string_pretty(report).map_err(|e| {
4046 PortingError::AdaptationRequired(format!("JSON serialization failed: {}", e))
4047 }),
4048 ExportFormat::Markdown => Ok(self.format_report_as_markdown(report)),
4049 }
4050 }
4051
4052 fn format_report_as_markdown(&self, report: &CompatibilityReport) -> String {
4053 let mut md = String::new();
4054 md.push_str("# Compatibility Report\n\n");
4055 md.push_str(&format!(
4056 "**Compatibility Score:** {:.1}%\n\n",
4057 report.compatibility_score * 100.0
4058 ));
4059 md.push_str(&format!(
4060 "**Adaptations Required:** {}\n\n",
4061 report.adaptations_required
4062 ));
4063 md.push_str(&format!(
4064 "**Incompatibilities:** {}\n\n",
4065 report.incompatibilities
4066 ));
4067
4068 if !report.findings.is_empty() {
4069 md.push_str("## Findings\n\n");
4070 for finding in &report.findings {
4071 md.push_str(&format!(
4072 "- **[{:?}]** {}: {}\n",
4073 finding.severity, finding.category, finding.description
4074 ));
4075 }
4076 md.push('\n');
4077 }
4078
4079 if !report.recommendations.is_empty() {
4080 md.push_str("## Recommendations\n\n");
4081 for rec in &report.recommendations {
4082 md.push_str(&format!("- {}\n", rec));
4083 }
4084 }
4085
4086 md
4087 }
4088
4089 pub fn export_porting_output(
4091 &self,
4092 output: &PortingOutput,
4093 format: ExportFormat,
4094 ) -> PortingResult<String> {
4095 match format {
4096 ExportFormat::Json => serde_json::to_string_pretty(output).map_err(|e| {
4097 PortingError::AdaptationRequired(format!("JSON serialization failed: {}", e))
4098 }),
4099 ExportFormat::Markdown => Ok(self.format_output_as_markdown(output)),
4100 }
4101 }
4102
4103 fn format_output_as_markdown(&self, output: &PortingOutput) -> String {
4104 let mut md = String::new();
4105 md.push_str("# Porting Output\n\n");
4106 md.push_str(&format!(
4107 "**Statutes Ported:** {}\n\n",
4108 output.statutes.len()
4109 ));
4110
4111 for (i, statute) in output.statutes.iter().enumerate() {
4112 md.push_str(&format!(
4113 "## Statute {} of {}\n\n",
4114 i + 1,
4115 output.statutes.len()
4116 ));
4117 md.push_str(&format!("**Original ID:** {}\n\n", statute.original_id));
4118 md.push_str(&format!("**New ID:** {}\n\n", statute.statute.id));
4119 md.push_str(&format!("**Title:** {}\n\n", statute.statute.title));
4120 md.push_str(&format!("**Changes:** {}\n\n", statute.changes.len()));
4121 }
4122
4123 if let Some(report) = &output.report {
4124 md.push_str(&self.format_report_as_markdown(report));
4125 }
4126
4127 md
4128 }
4129
4130 pub fn calculate_tfidf_similarity(&self, statute1: &Statute, statute2: &Statute) -> f64 {
4132 let text1 = format!("{} {}", statute1.title, statute1.id);
4134 let text2 = format!("{} {}", statute2.title, statute2.id);
4135
4136 let words1: Vec<&str> = text1.split_whitespace().collect();
4138 let words2: Vec<&str> = text2.split_whitespace().collect();
4139
4140 let mut tf1 = std::collections::HashMap::new();
4142 let mut tf2 = std::collections::HashMap::new();
4143
4144 for word in &words1 {
4145 *tf1.entry(word.to_lowercase()).or_insert(0) += 1;
4146 }
4147 for word in &words2 {
4148 *tf2.entry(word.to_lowercase()).or_insert(0) += 1;
4149 }
4150
4151 let mut dot_product = 0.0;
4153 let mut norm1 = 0.0;
4154 let mut norm2 = 0.0;
4155
4156 let all_terms: std::collections::HashSet<_> =
4157 tf1.keys().chain(tf2.keys()).map(|s| s.as_str()).collect();
4158
4159 for term in all_terms {
4160 let v1 = *tf1.get(term).unwrap_or(&0) as f64;
4161 let v2 = *tf2.get(term).unwrap_or(&0) as f64;
4162 dot_product += v1 * v2;
4163 norm1 += v1 * v1;
4164 norm2 += v2 * v2;
4165 }
4166
4167 if norm1 == 0.0 || norm2 == 0.0 {
4168 0.0
4169 } else {
4170 dot_product / (norm1.sqrt() * norm2.sqrt())
4171 }
4172 }
4173
4174 pub fn create_template(
4176 &self,
4177 name: String,
4178 description: String,
4179 statute_types: Vec<String>,
4180 ) -> PortingTemplate {
4181 PortingTemplate {
4182 id: format!("template-{}-{}", self.source.id, self.target.id),
4183 name,
4184 description,
4185 statute_types,
4186 term_replacements: self.term_replacements.clone(),
4187 contextual_rules: vec![
4188 "Adjust age thresholds based on cultural parameters".to_string(),
4189 "Replace currency references with local currency".to_string(),
4190 "Adapt procedural elements to target legal system".to_string(),
4191 ],
4192 target_legal_systems: vec![self.target.legal_system],
4193 }
4194 }
4195
4196 pub fn apply_template(
4198 &self,
4199 statute: &Statute,
4200 template: &PortingTemplate,
4201 ) -> PortingResult<PortedStatute> {
4202 let options = PortingOptions {
4203 apply_cultural_params: true,
4204 translate_terms: true,
4205 ..Default::default()
4206 };
4207
4208 let engine_with_template = PortingEngine::new(self.source.clone(), self.target.clone())
4210 .with_term_replacements(template.term_replacements.clone());
4211
4212 engine_with_template.port_statute(statute, &options)
4213 }
4214
4215 pub fn generate_conflict_resolutions(
4217 &self,
4218 conflicts: &[ConflictReport],
4219 ) -> Vec<ConflictResolution> {
4220 let mut resolutions = Vec::new();
4221
4222 for (i, conflict) in conflicts.iter().enumerate() {
4223 let (priority, effort) = match conflict.severity {
4224 Severity::Critical => (10, EffortLevel::VeryHigh),
4225 Severity::Error => (8, EffortLevel::High),
4226 Severity::Warning => (5, EffortLevel::Medium),
4227 Severity::Info => (2, EffortLevel::Low),
4228 };
4229
4230 resolutions.push(ConflictResolution {
4231 conflict_id: format!("conflict-{}", i),
4232 strategy: conflict
4233 .resolutions
4234 .first()
4235 .cloned()
4236 .unwrap_or_else(|| "Consult legal expert for resolution strategy".to_string()),
4237 priority,
4238 effort,
4239 steps: conflict.resolutions.clone(),
4240 expected_outcome: format!(
4241 "Resolve {:?} conflict for statute {}",
4242 conflict.conflict_type, conflict.statute_id
4243 ),
4244 });
4245 }
4246
4247 resolutions.sort_by(|a, b| b.priority.cmp(&a.priority));
4249 resolutions
4250 }
4251
4252 pub async fn multi_hop_port(
4254 &self,
4255 statute: &Statute,
4256 intermediate_jurisdictions: &[Jurisdiction],
4257 options: &PortingOptions,
4258 ) -> PortingResult<PortingChain> {
4259 let mut hop_results = Vec::new();
4260 let mut cumulative_changes = Vec::new();
4261 let mut current_statute = statute.clone();
4262
4263 for intermediate in intermediate_jurisdictions {
4265 let hop_engine = PortingEngine::new(self.source.clone(), intermediate.clone());
4266 let ported = hop_engine.port_statute(¤t_statute, options)?;
4267
4268 cumulative_changes.extend(ported.changes.clone());
4269 current_statute = ported.statute.clone();
4270 hop_results.push(ported);
4271 }
4272
4273 let final_ported = self.port_statute(¤t_statute, options)?;
4275 cumulative_changes.extend(final_ported.changes.clone());
4276 hop_results.push(final_ported);
4277
4278 let chain_score = 1.0 - (cumulative_changes.len() as f64 * 0.05).min(1.0);
4280
4281 Ok(PortingChain {
4282 id: format!("chain-{}", statute.id),
4283 source_jurisdiction: self.source.id.clone(),
4284 target_jurisdiction: self.target.id.clone(),
4285 intermediate_hops: intermediate_jurisdictions
4286 .iter()
4287 .map(|j| j.id.clone())
4288 .collect(),
4289 hop_results,
4290 cumulative_changes,
4291 chain_score,
4292 })
4293 }
4294
4295 pub fn record_history(
4297 &self,
4298 statute_id: String,
4299 user: String,
4300 options: &PortingOptions,
4301 success: bool,
4302 error: Option<String>,
4303 ) -> PortingHistoryEntry {
4304 PortingHistoryEntry {
4305 id: format!("history-{}", chrono::Utc::now().timestamp()),
4306 timestamp: chrono::Utc::now().to_rfc3339(),
4307 source_jurisdiction: self.source.id.clone(),
4308 target_jurisdiction: self.target.id.clone(),
4309 statute_id,
4310 user,
4311 options: options.clone(),
4312 success,
4313 error,
4314 }
4315 }
4316
4317 pub fn build_lineage(
4319 &self,
4320 original_id: String,
4321 original_jurisdiction: String,
4322 porting_history: &[PortingHistoryEntry],
4323 ) -> StatuteLineage {
4324 let mut derived_versions = Vec::new();
4325
4326 for entry in porting_history.iter().filter(|e| e.success) {
4328 if entry.source_jurisdiction == original_jurisdiction {
4329 derived_versions.push(LineageNode {
4330 jurisdiction: entry.target_jurisdiction.clone(),
4331 statute_id: entry.statute_id.clone(),
4332 parent_jurisdiction: Some(entry.source_jurisdiction.clone()),
4333 ported_at: entry.timestamp.clone(),
4334 children: Vec::new(),
4335 });
4336 }
4337 }
4338
4339 StatuteLineage {
4340 original_id,
4341 original_jurisdiction,
4342 total_ports: derived_versions.len(),
4343 derived_versions,
4344 }
4345 }
4346
4347 pub fn generate_diff(&self, original: &Statute, ported: &PortedStatute) -> StatuteDiff {
4349 let mut differences = Vec::new();
4350
4351 if original.id != ported.statute.id {
4353 differences.push(FieldDiff {
4354 field: "id".to_string(),
4355 original: original.id.clone(),
4356 new: ported.statute.id.clone(),
4357 change_type: DiffChangeType::Modified,
4358 });
4359 }
4360
4361 if original.title != ported.statute.title {
4363 differences.push(FieldDiff {
4364 field: "title".to_string(),
4365 original: original.title.clone(),
4366 new: ported.statute.title.clone(),
4367 change_type: DiffChangeType::Modified,
4368 });
4369 }
4370
4371 let similarity_score = if differences.is_empty() {
4373 1.0
4374 } else {
4375 1.0 - (differences.len() as f64 * 0.1).min(0.9)
4376 };
4377
4378 StatuteDiff {
4379 original_id: original.id.clone(),
4380 ported_id: ported.statute.id.clone(),
4381 differences,
4382 similarity_score,
4383 }
4384 }
4385
4386 pub fn export_diff_markdown(&self, diff: &StatuteDiff) -> String {
4388 let mut md = String::new();
4389 md.push_str("# Statute Diff\n\n");
4390 md.push_str(&format!("**Original ID:** {}\n\n", diff.original_id));
4391 md.push_str(&format!("**Ported ID:** {}\n\n", diff.ported_id));
4392 md.push_str(&format!(
4393 "**Similarity Score:** {:.1}%\n\n",
4394 diff.similarity_score * 100.0
4395 ));
4396
4397 if !diff.differences.is_empty() {
4398 md.push_str("## Changes\n\n");
4399 for field_diff in &diff.differences {
4400 md.push_str(&format!("### {}\n\n", field_diff.field));
4401 md.push_str(&format!("**Type:** {:?}\n\n", field_diff.change_type));
4402 md.push_str(&format!(
4403 "```diff\n- {}\n+ {}\n```\n\n",
4404 field_diff.original, field_diff.new
4405 ));
4406 }
4407 }
4408
4409 md
4410 }
4411}
4412
4413#[derive(Debug, Clone, Serialize, Deserialize)]
4415pub struct ComplianceSummary {
4416 pub total_statutes: usize,
4418 pub compliant: usize,
4420 pub compliant_with_issues: usize,
4422 pub non_compliant: usize,
4424 pub requires_review: usize,
4426 pub average_compliance_score: f64,
4428 pub total_violations: usize,
4430 pub critical_violations: usize,
4432}
4433
4434#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4436pub enum ExportFormat {
4437 Json,
4439 Markdown,
4441}
4442
4443#[derive(Debug, Clone, Serialize, Deserialize)]
4445pub struct PortingTemplate {
4446 pub id: String,
4448 pub name: String,
4450 pub description: String,
4452 pub statute_types: Vec<String>,
4454 pub term_replacements: Vec<TermReplacement>,
4456 pub contextual_rules: Vec<String>,
4458 pub target_legal_systems: Vec<LegalSystem>,
4460}
4461
4462#[derive(Debug, Clone, Serialize, Deserialize)]
4464pub struct ConflictResolution {
4465 pub conflict_id: String,
4467 pub strategy: String,
4469 pub priority: u8,
4471 pub effort: EffortLevel,
4473 pub steps: Vec<String>,
4475 pub expected_outcome: String,
4477}
4478
4479#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4481pub enum EffortLevel {
4482 Low,
4483 Medium,
4484 High,
4485 VeryHigh,
4486}
4487
4488#[derive(Debug, Clone, Serialize, Deserialize)]
4490pub struct PortingChain {
4491 pub id: String,
4493 pub source_jurisdiction: String,
4495 pub target_jurisdiction: String,
4497 pub intermediate_hops: Vec<String>,
4499 pub hop_results: Vec<PortedStatute>,
4501 pub cumulative_changes: Vec<PortingChange>,
4503 pub chain_score: f64,
4505}
4506
4507#[derive(Debug, Clone, Serialize, Deserialize)]
4509pub struct PortingHistoryEntry {
4510 pub id: String,
4512 pub timestamp: String,
4514 pub source_jurisdiction: String,
4516 pub target_jurisdiction: String,
4518 pub statute_id: String,
4520 pub user: String,
4522 pub options: PortingOptions,
4524 pub success: bool,
4526 pub error: Option<String>,
4528}
4529
4530#[derive(Debug, Clone, Serialize, Deserialize)]
4532pub struct StatuteLineage {
4533 pub original_id: String,
4535 pub original_jurisdiction: String,
4537 pub derived_versions: Vec<LineageNode>,
4539 pub total_ports: usize,
4541}
4542
4543#[derive(Debug, Clone, Serialize, Deserialize)]
4545pub struct LineageNode {
4546 pub jurisdiction: String,
4548 pub statute_id: String,
4550 pub parent_jurisdiction: Option<String>,
4552 pub ported_at: String,
4554 pub children: Vec<LineageNode>,
4556}
4557
4558#[derive(Debug, Clone, Serialize, Deserialize)]
4560pub struct StatuteDiff {
4561 pub original_id: String,
4563 pub ported_id: String,
4565 pub differences: Vec<FieldDiff>,
4567 pub similarity_score: f64,
4569}
4570
4571#[derive(Debug, Clone, Serialize, Deserialize)]
4573pub struct FieldDiff {
4574 pub field: String,
4576 pub original: String,
4578 pub new: String,
4580 pub change_type: DiffChangeType,
4582}
4583
4584#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4586pub enum DiffChangeType {
4587 Modified,
4588 Added,
4589 Removed,
4590}
4591
4592#[derive(Debug, Clone, Serialize, Deserialize)]
4598pub struct CulturalContextAnalysis {
4599 pub id: String,
4601 pub jurisdiction: String,
4603 pub social_norms: Vec<SocialNorm>,
4605 pub historical_context: Vec<HistoricalFactor>,
4607 pub cultural_trends: Vec<CulturalTrend>,
4609 pub power_distance: f64,
4611 pub individualism_score: f64,
4613 pub uncertainty_avoidance: f64,
4615 pub time_orientation: f64,
4617}
4618
4619#[derive(Debug, Clone, Serialize, Deserialize)]
4621pub struct SocialNorm {
4622 pub description: String,
4624 pub category: NormCategory,
4626 pub strength: f64,
4628 pub legally_recognized: bool,
4630}
4631
4632#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4634pub enum NormCategory {
4635 Family,
4637 Gender,
4639 Age,
4641 Economic,
4643 Public,
4645 Private,
4647}
4648
4649#[derive(Debug, Clone, Serialize, Deserialize)]
4651pub struct HistoricalFactor {
4652 pub description: String,
4654 pub period: String,
4656 pub impact: f64,
4658 pub legal_principles: Vec<String>,
4660}
4661
4662#[derive(Debug, Clone, Serialize, Deserialize)]
4664pub struct CulturalTrend {
4665 pub description: String,
4667 pub direction: f64,
4669 pub velocity: f64,
4671 pub legal_status: TrendLegalStatus,
4673}
4674
4675#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4677pub enum TrendLegalStatus {
4678 Codified,
4680 UnderConsideration,
4682 Unaddressed,
4684 Resisted,
4686}
4687
4688impl CulturalContextAnalysis {
4689 pub fn new(jurisdiction: String) -> Self {
4691 Self {
4692 id: uuid::Uuid::new_v4().to_string(),
4693 jurisdiction,
4694 social_norms: Vec::new(),
4695 historical_context: Vec::new(),
4696 cultural_trends: Vec::new(),
4697 power_distance: 0.5,
4698 individualism_score: 0.0,
4699 uncertainty_avoidance: 0.5,
4700 time_orientation: 0.0,
4701 }
4702 }
4703
4704 pub fn add_norm(&mut self, norm: SocialNorm) {
4706 self.social_norms.push(norm);
4707 }
4708
4709 pub fn add_historical_factor(&mut self, factor: HistoricalFactor) {
4711 self.historical_context.push(factor);
4712 }
4713
4714 pub fn add_trend(&mut self, trend: CulturalTrend) {
4716 self.cultural_trends.push(trend);
4717 }
4718
4719 pub fn assess_compatibility(&self, other: &CulturalContextAnalysis) -> f64 {
4721 let mut score = 0.0;
4722 let mut factors = 0.0;
4723
4724 score += 1.0 - (self.power_distance - other.power_distance).abs();
4726 score += 1.0 - ((self.individualism_score - other.individualism_score).abs() / 2.0);
4727 score += 1.0 - (self.uncertainty_avoidance - other.uncertainty_avoidance).abs();
4728 score += 1.0 - ((self.time_orientation - other.time_orientation).abs() / 2.0);
4729 factors += 4.0;
4730
4731 if factors > 0.0 { score / factors } else { 0.5 }
4732 }
4733}
4734
4735#[derive(Debug, Clone, Serialize, Deserialize)]
4737pub struct LocalPracticeIntegration {
4738 pub id: String,
4740 pub jurisdiction: String,
4742 pub practices: Vec<LocalPractice>,
4744 pub recommendations: Vec<IntegrationRecommendation>,
4746}
4747
4748#[derive(Debug, Clone, Serialize, Deserialize)]
4750pub struct LocalPractice {
4751 pub name: String,
4753 pub description: String,
4755 pub practice_type: PracticeType,
4757 pub geographic_scope: GeographicScope,
4759 pub prevalence: f64,
4761 pub legal_status: PracticeLegalStatus,
4763 pub conflicts_with_law: bool,
4765 pub related_statutes: Vec<String>,
4767}
4768
4769#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4771pub enum PracticeType {
4772 Business,
4774 DisputeResolution,
4776 Contract,
4778 Property,
4780 Family,
4782 Inheritance,
4784 Governance,
4786}
4787
4788#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4790pub enum GeographicScope {
4791 National,
4793 Regional(String),
4795 Local(String),
4797 Community(String),
4799}
4800
4801#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4803pub enum PracticeLegalStatus {
4804 Recognized,
4806 Permitted,
4808 Tolerated,
4810 Ambiguous,
4812 Prohibited,
4814}
4815
4816#[derive(Debug, Clone, Serialize, Deserialize)]
4818pub struct IntegrationRecommendation {
4819 pub practice_name: String,
4821 pub recommendation_type: RecommendationType,
4823 pub justification: String,
4825 pub implementation_steps: Vec<String>,
4827 pub priority: f64,
4829}
4830
4831#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4833pub enum RecommendationType {
4834 Codify,
4836 Reference,
4838 Exception,
4840 Harmonize,
4842 Prohibit,
4844}
4845
4846impl LocalPracticeIntegration {
4847 pub fn new(jurisdiction: String) -> Self {
4849 Self {
4850 id: uuid::Uuid::new_v4().to_string(),
4851 jurisdiction,
4852 practices: Vec::new(),
4853 recommendations: Vec::new(),
4854 }
4855 }
4856
4857 pub fn add_practice(&mut self, practice: LocalPractice) {
4859 self.practices.push(practice);
4860 }
4861
4862 pub fn generate_recommendations(&mut self, _statute: &Statute) {
4864 for practice in &self.practices {
4865 if practice.prevalence > 0.7 && practice.legal_status == PracticeLegalStatus::Tolerated
4866 {
4867 self.recommendations.push(IntegrationRecommendation {
4868 practice_name: practice.name.clone(),
4869 recommendation_type: RecommendationType::Codify,
4870 justification: format!(
4871 "High prevalence ({:.1}%) warrants formal recognition",
4872 practice.prevalence * 100.0
4873 ),
4874 implementation_steps: vec![
4875 "Draft codification language".to_string(),
4876 "Stakeholder consultation".to_string(),
4877 "Legislative proposal".to_string(),
4878 ],
4879 priority: practice.prevalence,
4880 });
4881 }
4882 }
4883 }
4884}
4885
4886#[derive(Debug, Clone, Serialize, Deserialize)]
4888pub struct CustomaryLawConsideration {
4889 pub id: String,
4891 pub jurisdiction: String,
4893 pub customary_laws: Vec<CustomaryLaw>,
4895 pub interactions: Vec<CustomaryStatutoryInteraction>,
4897}
4898
4899#[derive(Debug, Clone, Serialize, Deserialize)]
4901pub struct CustomaryLaw {
4902 pub name: String,
4904 pub description: String,
4906 pub subject: CustomarySubject,
4908 pub age_years: usize,
4910 pub geographic_scope: GeographicScope,
4912 pub recognition: CustomaryRecognition,
4914 pub binding_force: f64,
4916 pub modern_compatibility: f64,
4918}
4919
4920#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4922pub enum CustomarySubject {
4923 Land,
4925 Water,
4927 Fishing,
4929 Marriage,
4931 Inheritance,
4933 Dispute,
4935 Criminal,
4937 Commercial,
4939}
4940
4941#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4943pub enum CustomaryRecognition {
4944 Incorporated,
4946 Supplementary,
4948 Acknowledged,
4950 Informal,
4952 Unrecognized,
4954}
4955
4956#[derive(Debug, Clone, Serialize, Deserialize)]
4958pub struct CustomaryStatutoryInteraction {
4959 pub customary_law: String,
4961 pub statutory_law: String,
4963 pub interaction_type: InteractionType,
4965 pub resolution: String,
4967 pub precedents: Vec<String>,
4969}
4970
4971#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4973pub enum InteractionType {
4974 Harmonious,
4976 StatutoryDefers,
4978 CustomaryDefers,
4980 Conflict,
4982 Parallel,
4984}
4985
4986impl CustomaryLawConsideration {
4987 pub fn new(jurisdiction: String) -> Self {
4989 Self {
4990 id: uuid::Uuid::new_v4().to_string(),
4991 jurisdiction,
4992 customary_laws: Vec::new(),
4993 interactions: Vec::new(),
4994 }
4995 }
4996
4997 pub fn add_customary_law(&mut self, law: CustomaryLaw) {
4999 self.customary_laws.push(law);
5000 }
5001
5002 pub fn analyze_interaction(
5004 &mut self,
5005 statute: &Statute,
5006 customary_law: &CustomaryLaw,
5007 ) -> InteractionType {
5008 let interaction_type = if customary_law.modern_compatibility > 0.8 {
5010 InteractionType::Harmonious
5011 } else if customary_law.recognition == CustomaryRecognition::Incorporated {
5012 InteractionType::StatutoryDefers
5013 } else if customary_law.recognition == CustomaryRecognition::Unrecognized {
5014 InteractionType::CustomaryDefers
5015 } else {
5016 InteractionType::Parallel
5017 };
5018
5019 self.interactions.push(CustomaryStatutoryInteraction {
5020 customary_law: customary_law.name.clone(),
5021 statutory_law: statute.id.clone(),
5022 interaction_type,
5023 resolution: "To be determined through consultation".to_string(),
5024 precedents: Vec::new(),
5025 });
5026
5027 interaction_type
5028 }
5029}
5030
5031#[derive(Debug, Clone, Serialize, Deserialize)]
5033pub struct ReligiousLawCompatibility {
5034 pub id: String,
5036 pub jurisdiction: String,
5038 pub religious_systems: Vec<ReligiousLawSystem>,
5040 pub assessments: Vec<CompatibilityAssessment>,
5042}
5043
5044#[derive(Debug, Clone, Serialize, Deserialize)]
5046pub struct ReligiousLawSystem {
5047 pub name: String,
5049 pub religion: Religion,
5051 pub legal_status: ReligiousLegalStatus,
5053 pub population_percentage: f64,
5055 pub subject_matters: Vec<ReligiousSubject>,
5057 pub civil_interaction: CivilReligiousInteraction,
5059}
5060
5061#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5063pub enum Religion {
5064 Islam,
5066 Judaism,
5068 Hinduism,
5070 Catholicism,
5072 Buddhism,
5074 Other,
5076}
5077
5078#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5080pub enum ReligiousLegalStatus {
5081 StateReligion,
5083 ParallelSystem,
5085 PersonalStatus,
5087 Voluntary,
5089 Unrecognized,
5091}
5092
5093#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5095pub enum ReligiousSubject {
5096 Marriage,
5098 Divorce,
5100 Inheritance,
5102 Dietary,
5104 HolyDays,
5106 Finance,
5108 Criminal,
5110 Comprehensive,
5112}
5113
5114#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5116pub enum CivilReligiousInteraction {
5117 ReligiousPrecedence,
5119 CivilPrecedence,
5121 DualSystem,
5123 OptIn,
5125 Separated,
5127}
5128
5129#[derive(Debug, Clone, Serialize, Deserialize)]
5131pub struct CompatibilityAssessment {
5132 pub id: String,
5134 pub religious_system: String,
5136 pub statute_id: String,
5138 pub compatibility_score: f64,
5140 pub conflicts: Vec<ReligiousConflict>,
5142 pub accommodations: Vec<String>,
5144}
5145
5146#[derive(Debug, Clone, Serialize, Deserialize)]
5148pub struct ReligiousConflict {
5149 pub description: String,
5151 pub severity: f64,
5153 pub affected_population: f64,
5155 pub resolution_option: String,
5157}
5158
5159impl ReligiousLawCompatibility {
5160 pub fn new(jurisdiction: String) -> Self {
5162 Self {
5163 id: uuid::Uuid::new_v4().to_string(),
5164 jurisdiction,
5165 religious_systems: Vec::new(),
5166 assessments: Vec::new(),
5167 }
5168 }
5169
5170 pub fn add_religious_system(&mut self, system: ReligiousLawSystem) {
5172 self.religious_systems.push(system);
5173 }
5174
5175 pub fn assess_compatibility(&mut self, statute: &Statute) {
5177 for system in &self.religious_systems {
5178 let conflicts = Vec::new();
5179
5180 let compatibility_score = match system.civil_interaction {
5182 CivilReligiousInteraction::Separated => 1.0,
5183 CivilReligiousInteraction::OptIn => 0.9,
5184 CivilReligiousInteraction::DualSystem => 0.7,
5185 CivilReligiousInteraction::CivilPrecedence => 0.8,
5186 CivilReligiousInteraction::ReligiousPrecedence => 0.5,
5187 };
5188
5189 self.assessments.push(CompatibilityAssessment {
5190 id: uuid::Uuid::new_v4().to_string(),
5191 religious_system: system.name.clone(),
5192 statute_id: statute.id.clone(),
5193 compatibility_score,
5194 conflicts,
5195 accommodations: vec![
5196 "Provide religious exemption clause".to_string(),
5197 "Create alternative compliance pathway".to_string(),
5198 ],
5199 });
5200 }
5201 }
5202}
5203
5204#[derive(Debug, Clone, Serialize, Deserialize)]
5206pub struct IndigenousRightsAssessment {
5207 pub id: String,
5209 pub jurisdiction: String,
5211 pub indigenous_peoples: Vec<IndigenousPeople>,
5213 pub recognized_rights: Vec<IndigenousRight>,
5215 pub impact_assessments: Vec<IndigenousImpact>,
5217}
5218
5219#[derive(Debug, Clone, Serialize, Deserialize)]
5221pub struct IndigenousPeople {
5222 pub name: String,
5224 pub population: usize,
5226 pub territories: Vec<String>,
5228 pub recognition_status: IndigenousRecognition,
5230 pub self_governance: GovernanceLevel,
5232}
5233
5234#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5236pub enum IndigenousRecognition {
5237 TreatyRecognized,
5239 ConstitutionallyRecognized,
5241 StatutoryRecognized,
5243 AdministrativeRecognition,
5245 Unrecognized,
5247}
5248
5249#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5251pub enum GovernanceLevel {
5252 Sovereign,
5254 Autonomous,
5256 Limited,
5258 Consultation,
5260 None,
5262}
5263
5264#[derive(Debug, Clone, Serialize, Deserialize)]
5266pub struct IndigenousRight {
5267 pub description: String,
5269 pub category: IndigenousRightCategory,
5271 pub legal_basis: Vec<String>,
5273 pub geographic_scope: Option<Vec<String>>,
5275 pub limitations: Vec<String>,
5277}
5278
5279#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5281pub enum IndigenousRightCategory {
5282 Land,
5284 SelfDetermination,
5286 Culture,
5288 Language,
5290 Resources,
5292 Consultation,
5294 Traditional,
5296}
5297
5298#[derive(Debug, Clone, Serialize, Deserialize)]
5300pub struct IndigenousImpact {
5301 pub id: String,
5303 pub statute_id: String,
5305 pub affected_people: Vec<String>,
5307 pub impact_areas: Vec<ImpactArea>,
5309 pub impact_score: f64,
5311 pub consultation_conducted: bool,
5313 pub fpic_obtained: bool,
5315 pub mitigation_measures: Vec<String>,
5317}
5318
5319#[derive(Debug, Clone, Serialize, Deserialize)]
5321pub struct ImpactArea {
5322 pub description: String,
5324 pub impact_type: ImpactType,
5326 pub severity: f64,
5328 pub affected_rights: Vec<IndigenousRightCategory>,
5330}
5331
5332#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5334pub enum ImpactType {
5335 Positive,
5337 Neutral,
5339 Negative,
5341 Mixed,
5343}
5344
5345impl IndigenousRightsAssessment {
5346 pub fn new(jurisdiction: String) -> Self {
5348 Self {
5349 id: uuid::Uuid::new_v4().to_string(),
5350 jurisdiction,
5351 indigenous_peoples: Vec::new(),
5352 recognized_rights: Vec::new(),
5353 impact_assessments: Vec::new(),
5354 }
5355 }
5356
5357 pub fn add_people(&mut self, people: IndigenousPeople) {
5359 self.indigenous_peoples.push(people);
5360 }
5361
5362 pub fn add_right(&mut self, right: IndigenousRight) {
5364 self.recognized_rights.push(right);
5365 }
5366
5367 pub fn assess_impact(&mut self, statute: &Statute) -> f64 {
5369 let mut total_impact = 0.0;
5370 let mut count = 0;
5371
5372 for people in &self.indigenous_peoples {
5373 let impact = IndigenousImpact {
5374 id: uuid::Uuid::new_v4().to_string(),
5375 statute_id: statute.id.clone(),
5376 affected_people: vec![people.name.clone()],
5377 impact_areas: vec![],
5378 impact_score: 0.0,
5379 consultation_conducted: false,
5380 fpic_obtained: false,
5381 mitigation_measures: vec![
5382 "Conduct consultation with affected communities".to_string(),
5383 "Obtain free, prior, and informed consent".to_string(),
5384 "Include cultural exception provisions".to_string(),
5385 ],
5386 };
5387 total_impact += impact.impact_score;
5388 count += 1;
5389 self.impact_assessments.push(impact);
5390 }
5391
5392 if count > 0 {
5393 total_impact / count as f64
5394 } else {
5395 0.0
5396 }
5397 }
5398
5399 pub fn check_consultation_requirements(&self) -> bool {
5401 self.impact_assessments
5402 .iter()
5403 .all(|impact| impact.consultation_conducted && impact.fpic_obtained)
5404 }
5405}
5406
5407#[derive(Debug, Clone, Serialize, Deserialize)]
5413pub struct CostBenefitProjection {
5414 pub id: String,
5416 pub statute_id: String,
5418 pub source_jurisdiction: String,
5420 pub target_jurisdiction: String,
5422 pub costs: Vec<PortingCost>,
5424 pub benefits: Vec<PortingBenefit>,
5426 pub total_cost: f64,
5428 pub total_benefit: f64,
5430 pub net_benefit: f64,
5432 pub benefit_cost_ratio: f64,
5434 pub payback_period: Option<f64>,
5436 pub risk_adjustment: RiskAdjustment,
5438}
5439
5440#[derive(Debug, Clone, Serialize, Deserialize)]
5442pub struct PortingCost {
5443 pub category: CostCategory,
5445 pub description: String,
5447 pub amount: f64,
5449 pub timeframe: CostTimeframe,
5451 pub certainty: f64,
5453}
5454
5455#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5457pub enum CostCategory {
5458 Legal,
5460 Translation,
5462 Consultation,
5464 Legislative,
5466 Implementation,
5468 Training,
5470 Technology,
5472 Monitoring,
5474}
5475
5476#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5478pub enum CostTimeframe {
5479 OneTime,
5481 Annual,
5483 MultiYear(u32),
5485}
5486
5487#[derive(Debug, Clone, Serialize, Deserialize)]
5489pub struct PortingBenefit {
5490 pub category: BenefitCategory,
5492 pub description: String,
5494 pub monetary_value: Option<f64>,
5496 pub qualitative_value: String,
5498 pub timeframe: CostTimeframe,
5500 pub certainty: f64,
5502}
5503
5504#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5506pub enum BenefitCategory {
5507 Economic,
5509 Social,
5511 Legal,
5513 Trade,
5515 Administrative,
5517 HumanRights,
5519 Environmental,
5521}
5522
5523#[derive(Debug, Clone, Serialize, Deserialize)]
5525pub struct RiskAdjustment {
5526 pub discount_factor: f64,
5528 pub risks: Vec<String>,
5530 pub scenarios: Vec<Scenario>,
5532}
5533
5534#[derive(Debug, Clone, Serialize, Deserialize)]
5536pub struct Scenario {
5537 pub name: String,
5539 pub probability: f64,
5541 pub net_benefit: f64,
5543}
5544
5545impl CostBenefitProjection {
5546 pub fn new(
5548 statute_id: String,
5549 source_jurisdiction: String,
5550 target_jurisdiction: String,
5551 ) -> Self {
5552 Self {
5553 id: uuid::Uuid::new_v4().to_string(),
5554 statute_id,
5555 source_jurisdiction,
5556 target_jurisdiction,
5557 costs: Vec::new(),
5558 benefits: Vec::new(),
5559 total_cost: 0.0,
5560 total_benefit: 0.0,
5561 net_benefit: 0.0,
5562 benefit_cost_ratio: 0.0,
5563 payback_period: None,
5564 risk_adjustment: RiskAdjustment {
5565 discount_factor: 1.0,
5566 risks: Vec::new(),
5567 scenarios: Vec::new(),
5568 },
5569 }
5570 }
5571
5572 pub fn add_cost(&mut self, cost: PortingCost) {
5574 self.costs.push(cost);
5575 self.recalculate();
5576 }
5577
5578 pub fn add_benefit(&mut self, benefit: PortingBenefit) {
5580 self.benefits.push(benefit);
5581 self.recalculate();
5582 }
5583
5584 fn recalculate(&mut self) {
5586 self.total_cost = self.costs.iter().map(|c| c.amount).sum();
5587 self.total_benefit = self.benefits.iter().filter_map(|b| b.monetary_value).sum();
5588 self.net_benefit = self.total_benefit - self.total_cost;
5589 self.benefit_cost_ratio = if self.total_cost > 0.0 {
5590 self.total_benefit / self.total_cost
5591 } else {
5592 0.0
5593 };
5594
5595 if self.total_benefit > self.total_cost && self.total_benefit > 0.0 {
5597 let annual_benefit: f64 = self
5598 .benefits
5599 .iter()
5600 .filter(|b| matches!(b.timeframe, CostTimeframe::Annual))
5601 .filter_map(|b| b.monetary_value)
5602 .sum();
5603 if annual_benefit > 0.0 {
5604 self.payback_period = Some(self.total_cost / annual_benefit);
5605 }
5606 }
5607 }
5608}
5609
5610#[derive(Debug, Clone, Serialize, Deserialize)]
5612pub struct MarketImpactAssessment {
5613 pub id: String,
5615 pub statute_id: String,
5617 pub jurisdiction: String,
5619 pub affected_sectors: Vec<MarketSector>,
5621 pub competitiveness_impact: CompetitivenessImpact,
5623 pub entry_barriers: Vec<EntryBarrier>,
5625 pub market_changes: Vec<MarketChange>,
5627 pub impact_score: f64,
5629}
5630
5631#[derive(Debug, Clone, Serialize, Deserialize)]
5633pub struct MarketSector {
5634 pub name: String,
5636 pub size_percentage: f64,
5638 pub businesses_affected: usize,
5640 pub impact_type: ImpactType,
5642 pub impact_magnitude: f64,
5644}
5645
5646#[derive(Debug, Clone, Serialize, Deserialize)]
5648pub struct CompetitivenessImpact {
5649 pub domestic_change: f64,
5651 pub international_change: f64,
5653 pub drivers: Vec<String>,
5655 pub advantages: Vec<String>,
5657}
5658
5659#[derive(Debug, Clone, Serialize, Deserialize)]
5661pub struct EntryBarrier {
5662 pub barrier_type: BarrierType,
5664 pub description: String,
5666 pub severity: f64,
5668 pub affected_parties: Vec<String>,
5670}
5671
5672#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5674pub enum BarrierType {
5675 Regulatory,
5677 Cost,
5679 Technical,
5681 Information,
5683 Cultural,
5685}
5686
5687#[derive(Debug, Clone, Serialize, Deserialize)]
5689pub struct MarketChange {
5690 pub description: String,
5692 pub timeframe: String,
5694 pub probability: f64,
5696 pub structural_impact: bool,
5698}
5699
5700impl MarketImpactAssessment {
5701 pub fn new(statute_id: String, jurisdiction: String) -> Self {
5703 Self {
5704 id: uuid::Uuid::new_v4().to_string(),
5705 statute_id,
5706 jurisdiction,
5707 affected_sectors: Vec::new(),
5708 competitiveness_impact: CompetitivenessImpact {
5709 domestic_change: 0.0,
5710 international_change: 0.0,
5711 drivers: Vec::new(),
5712 advantages: Vec::new(),
5713 },
5714 entry_barriers: Vec::new(),
5715 market_changes: Vec::new(),
5716 impact_score: 0.0,
5717 }
5718 }
5719
5720 pub fn add_sector(&mut self, sector: MarketSector) {
5722 self.affected_sectors.push(sector);
5723 self.recalculate_impact();
5724 }
5725
5726 fn recalculate_impact(&mut self) {
5728 if self.affected_sectors.is_empty() {
5729 self.impact_score = 0.0;
5730 return;
5731 }
5732
5733 let weighted_impact: f64 = self
5734 .affected_sectors
5735 .iter()
5736 .map(|s| {
5737 let magnitude = s.impact_magnitude;
5738 let sign = match s.impact_type {
5739 ImpactType::Positive => 1.0,
5740 ImpactType::Negative => -1.0,
5741 ImpactType::Neutral => 0.0,
5742 ImpactType::Mixed => 0.0,
5743 };
5744 s.size_percentage * magnitude * sign
5745 })
5746 .sum();
5747
5748 self.impact_score = weighted_impact.clamp(-1.0, 1.0);
5749 }
5750}
5751
5752#[derive(Debug, Clone, Serialize, Deserialize)]
5754pub struct ComplianceCostEstimation {
5755 pub id: String,
5757 pub statute_id: String,
5759 pub jurisdiction: String,
5761 pub direct_costs: Vec<ComplianceCost>,
5763 pub indirect_costs: Vec<ComplianceCost>,
5765 pub affected_entities: Vec<AffectedEntity>,
5767 pub total_burden: f64,
5769 pub average_cost_per_entity: f64,
5771}
5772
5773#[derive(Debug, Clone, Serialize, Deserialize)]
5775pub struct ComplianceCost {
5776 pub cost_type: ComplianceCostType,
5778 pub description: String,
5780 pub amount: f64,
5782 pub frequency: CostTimeframe,
5784 pub certainty: f64,
5786}
5787
5788#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5790pub enum ComplianceCostType {
5791 Administrative,
5793 Reporting,
5795 Audit,
5797 Systems,
5799 Training,
5801 Professional,
5803 Opportunity,
5805}
5806
5807#[derive(Debug, Clone, Serialize, Deserialize)]
5809pub struct AffectedEntity {
5810 pub entity_type: EntityType,
5812 pub count: usize,
5814 pub average_cost: f64,
5816 pub capacity: ComplianceCapacity,
5818}
5819
5820#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5822pub enum EntityType {
5823 LargeBusiness,
5825 SME,
5827 Individual,
5829 Government,
5831 NonProfit,
5833}
5834
5835#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5837pub enum ComplianceCapacity {
5838 High,
5840 Moderate,
5842 Low,
5844 Insufficient,
5846}
5847
5848impl ComplianceCostEstimation {
5849 pub fn new(statute_id: String, jurisdiction: String) -> Self {
5851 Self {
5852 id: uuid::Uuid::new_v4().to_string(),
5853 statute_id,
5854 jurisdiction,
5855 direct_costs: Vec::new(),
5856 indirect_costs: Vec::new(),
5857 affected_entities: Vec::new(),
5858 total_burden: 0.0,
5859 average_cost_per_entity: 0.0,
5860 }
5861 }
5862
5863 pub fn add_direct_cost(&mut self, cost: ComplianceCost) {
5865 self.direct_costs.push(cost);
5866 self.recalculate();
5867 }
5868
5869 pub fn add_indirect_cost(&mut self, cost: ComplianceCost) {
5871 self.indirect_costs.push(cost);
5872 self.recalculate();
5873 }
5874
5875 pub fn add_affected_entity(&mut self, entity: AffectedEntity) {
5877 self.affected_entities.push(entity);
5878 self.recalculate();
5879 }
5880
5881 fn recalculate(&mut self) {
5883 let direct_total: f64 = self.direct_costs.iter().map(|c| c.amount).sum();
5884 let indirect_total: f64 = self.indirect_costs.iter().map(|c| c.amount).sum();
5885 self.total_burden = direct_total + indirect_total;
5886
5887 let total_entities: usize = self.affected_entities.iter().map(|e| e.count).sum();
5888 self.average_cost_per_entity = if total_entities > 0 {
5889 self.total_burden / total_entities as f64
5890 } else {
5891 0.0
5892 };
5893 }
5894}
5895
5896#[derive(Debug, Clone, Serialize, Deserialize)]
5898pub struct BusinessImpactReport {
5899 pub id: String,
5901 pub statute_id: String,
5903 pub jurisdiction: String,
5905 pub generated_at: String,
5907 pub executive_summary: String,
5909 pub sector_impacts: Vec<SectorImpact>,
5911 pub size_impacts: Vec<SizeImpact>,
5913 pub regional_impacts: Vec<RegionalImpact>,
5915 pub recommendations: Vec<String>,
5917 pub business_climate_score: f64,
5919}
5920
5921#[derive(Debug, Clone, Serialize, Deserialize)]
5923pub struct SectorImpact {
5924 pub sector: String,
5926 pub description: String,
5928 pub jobs_impact: i32,
5930 pub revenue_impact_percent: f64,
5932 pub investment_impact: String,
5934}
5935
5936#[derive(Debug, Clone, Serialize, Deserialize)]
5938pub struct SizeImpact {
5939 pub size_category: EntityType,
5941 pub burden_ratio: f64,
5943 pub competitive_impact: String,
5945 pub survival_risk: RiskLevel,
5947}
5948
5949#[derive(Debug, Clone, Serialize, Deserialize)]
5951pub struct RegionalImpact {
5952 pub region: String,
5954 pub description: String,
5956 pub gdp_impact_percent: f64,
5958 pub employment_impact: i32,
5960}
5961
5962impl BusinessImpactReport {
5963 pub fn new(statute_id: String, jurisdiction: String) -> Self {
5965 Self {
5966 id: uuid::Uuid::new_v4().to_string(),
5967 statute_id,
5968 jurisdiction,
5969 generated_at: chrono::Utc::now().to_rfc3339(),
5970 executive_summary: String::new(),
5971 sector_impacts: Vec::new(),
5972 size_impacts: Vec::new(),
5973 regional_impacts: Vec::new(),
5974 recommendations: Vec::new(),
5975 business_climate_score: 0.0,
5976 }
5977 }
5978
5979 pub fn generate_summary(&mut self) {
5981 let sector_count = self.sector_impacts.len();
5982 let avg_revenue_impact: f64 = if !self.sector_impacts.is_empty() {
5983 self.sector_impacts
5984 .iter()
5985 .map(|s| s.revenue_impact_percent)
5986 .sum::<f64>()
5987 / sector_count as f64
5988 } else {
5989 0.0
5990 };
5991
5992 self.executive_summary = format!(
5993 "Business Impact Analysis for statute {}: {} sectors analyzed, average revenue impact {:.1}%, overall business climate score {:.2}",
5994 self.statute_id, sector_count, avg_revenue_impact, self.business_climate_score
5995 );
5996 }
5997}
5998
5999#[derive(Debug, Clone, Serialize, Deserialize)]
6001pub struct IndustryConsultation {
6002 pub id: String,
6004 pub statute_id: String,
6006 pub jurisdiction: String,
6008 pub associations: Vec<IndustryAssociation>,
6010 pub responses: Vec<ConsultationResponse>,
6012 pub hearing_ids: Vec<String>,
6014 pub feedback_analysis: FeedbackAnalysis,
6016}
6017
6018#[derive(Debug, Clone, Serialize, Deserialize)]
6020pub struct IndustryAssociation {
6021 pub name: String,
6023 pub sector: String,
6025 pub member_count: usize,
6027 pub contact: String,
6029 pub status: ConsultationStatus,
6031}
6032
6033#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6035pub enum ConsultationStatus {
6036 NotContacted,
6038 Invited,
6040 Responded,
6042 Declined,
6044 FollowUpNeeded,
6046}
6047
6048#[derive(Debug, Clone, Serialize, Deserialize)]
6050pub struct ConsultationResponse {
6051 pub organization: String,
6053 pub date: String,
6055 pub support_level: f64,
6057 pub concerns: Vec<String>,
6059 pub suggestions: Vec<String>,
6061 pub claimed_impacts: Vec<String>,
6063}
6064
6065#[derive(Debug, Clone, Serialize, Deserialize)]
6067pub struct FeedbackAnalysis {
6068 pub response_count: usize,
6070 pub average_support: f64,
6072 pub common_concerns: Vec<String>,
6074 pub consensus_recommendations: Vec<String>,
6076 pub divided_issues: Vec<String>,
6078}
6079
6080impl IndustryConsultation {
6081 pub fn new(statute_id: String, jurisdiction: String) -> Self {
6083 Self {
6084 id: uuid::Uuid::new_v4().to_string(),
6085 statute_id,
6086 jurisdiction,
6087 associations: Vec::new(),
6088 responses: Vec::new(),
6089 hearing_ids: Vec::new(),
6090 feedback_analysis: FeedbackAnalysis {
6091 response_count: 0,
6092 average_support: 0.0,
6093 common_concerns: Vec::new(),
6094 consensus_recommendations: Vec::new(),
6095 divided_issues: Vec::new(),
6096 },
6097 }
6098 }
6099
6100 pub fn add_association(&mut self, association: IndustryAssociation) {
6102 self.associations.push(association);
6103 }
6104
6105 pub fn add_response(&mut self, response: ConsultationResponse) {
6107 self.responses.push(response);
6108 self.analyze_feedback();
6109 }
6110
6111 fn analyze_feedback(&mut self) {
6113 self.feedback_analysis.response_count = self.responses.len();
6114
6115 if !self.responses.is_empty() {
6116 self.feedback_analysis.average_support =
6117 self.responses.iter().map(|r| r.support_level).sum::<f64>()
6118 / self.responses.len() as f64;
6119
6120 let mut concern_map: HashMap<String, usize> = HashMap::new();
6122 for response in &self.responses {
6123 for concern in &response.concerns {
6124 *concern_map.entry(concern.clone()).or_insert(0) += 1;
6125 }
6126 }
6127
6128 self.feedback_analysis.common_concerns = concern_map
6129 .into_iter()
6130 .filter(|(_, count)| *count >= 2)
6131 .map(|(concern, _)| concern)
6132 .collect();
6133 }
6134 }
6135}
6136
6137#[derive(Debug, Clone, Serialize, Deserialize)]
6143pub struct PortedStatuteSimulation {
6144 pub id: String,
6146 pub statute_id: String,
6148 pub jurisdiction: String,
6150 pub parameters: SimulationParameters,
6152 pub outcomes: Vec<SimulationOutcome>,
6154 pub compliance_rate: f64,
6156 pub effectiveness: f64,
6158 pub unintended_consequences: Vec<UnintendedConsequence>,
6160 pub resource_requirements: SimulationResourceRequirements,
6162 pub simulated_at: String,
6164}
6165
6166#[derive(Debug, Clone, Serialize, Deserialize)]
6168pub struct SimulationResourceRequirements {
6169 pub financial_cost: f64,
6171 pub currency: String,
6173 pub personnel_count: usize,
6175 pub training_hours: f64,
6177 pub infrastructure: Vec<String>,
6179 pub technology: Vec<String>,
6181}
6182
6183#[derive(Debug, Clone, Serialize, Deserialize)]
6185pub struct SimulationParameters {
6186 pub population_size: usize,
6188 pub time_horizon_years: u32,
6190 pub simulation_runs: usize,
6192 pub confidence_level: f64,
6194 pub enforcement_intensity: f64,
6196 pub compliance_culture: f64,
6198}
6199
6200#[derive(Debug, Clone, Serialize, Deserialize)]
6202pub struct SimulationOutcome {
6203 pub category: OutcomeCategory,
6205 pub description: String,
6207 pub probability: f64,
6209 pub magnitude: f64,
6211 pub affected_population_pct: f64,
6213 pub timeframe: String,
6215}
6216
6217#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6219pub enum OutcomeCategory {
6220 PositiveIntended,
6222 NegativeIntended,
6224 PositiveUnintended,
6226 NegativeUnintended,
6228 Neutral,
6230}
6231
6232#[derive(Debug, Clone, Serialize, Deserialize)]
6234pub struct UnintendedConsequence {
6235 pub description: String,
6237 pub severity: f64,
6239 pub likelihood: f64,
6241 pub affected_groups: Vec<String>,
6243 pub mitigation_strategies: Vec<String>,
6245}
6246
6247impl PortedStatuteSimulation {
6248 pub fn new(statute_id: String, jurisdiction: String, parameters: SimulationParameters) -> Self {
6250 Self {
6251 id: uuid::Uuid::new_v4().to_string(),
6252 statute_id,
6253 jurisdiction,
6254 parameters,
6255 outcomes: Vec::new(),
6256 compliance_rate: 0.0,
6257 effectiveness: 0.0,
6258 unintended_consequences: Vec::new(),
6259 resource_requirements: SimulationResourceRequirements {
6260 financial_cost: 0.0,
6261 currency: "USD".to_string(),
6262 personnel_count: 0,
6263 training_hours: 0.0,
6264 infrastructure: Vec::new(),
6265 technology: Vec::new(),
6266 },
6267 simulated_at: chrono::Utc::now().to_rfc3339(),
6268 }
6269 }
6270
6271 pub fn add_outcome(&mut self, outcome: SimulationOutcome) {
6273 self.outcomes.push(outcome);
6274 }
6275
6276 pub fn add_unintended_consequence(&mut self, consequence: UnintendedConsequence) {
6278 self.unintended_consequences.push(consequence);
6279 }
6280
6281 pub fn high_severity_consequences(&self) -> Vec<&UnintendedConsequence> {
6283 self.unintended_consequences
6284 .iter()
6285 .filter(|c| c.severity >= 0.7)
6286 .collect()
6287 }
6288
6289 pub fn likely_negative_outcomes(&self) -> Vec<&SimulationOutcome> {
6291 self.outcomes
6292 .iter()
6293 .filter(|o| {
6294 matches!(
6295 o.category,
6296 OutcomeCategory::NegativeIntended | OutcomeCategory::NegativeUnintended
6297 ) && o.probability >= 0.5
6298 })
6299 .collect()
6300 }
6301}
6302
6303#[derive(Debug, Clone, Serialize, Deserialize)]
6305pub struct ComparativeOutcomeAnalysis {
6306 pub id: String,
6308 pub source_jurisdiction: String,
6310 pub target_jurisdiction: String,
6312 pub statute_id: String,
6314 pub comparisons: Vec<OutcomeComparison>,
6316 pub similarity_score: f64,
6318 pub key_differences: Vec<KeyDifference>,
6320 pub recommendations: Vec<String>,
6322 pub created_at: String,
6324}
6325
6326#[derive(Debug, Clone, Serialize, Deserialize)]
6328pub struct OutcomeComparison {
6329 pub outcome: String,
6331 pub source_value: f64,
6333 pub target_value: f64,
6335 pub difference_pct: f64,
6337 pub significance: f64,
6339 pub explanation: String,
6341}
6342
6343#[derive(Debug, Clone, Serialize, Deserialize)]
6345pub struct KeyDifference {
6346 pub category: DifferenceCategory,
6348 pub description: String,
6350 pub impact: f64,
6352 pub requires_adaptation: bool,
6354}
6355
6356#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6358pub enum DifferenceCategory {
6359 Cultural,
6361 LegalSystem,
6363 Economic,
6365 Social,
6367 Political,
6369 Infrastructure,
6371}
6372
6373impl ComparativeOutcomeAnalysis {
6374 pub fn new(
6376 source_jurisdiction: String,
6377 target_jurisdiction: String,
6378 statute_id: String,
6379 ) -> Self {
6380 Self {
6381 id: uuid::Uuid::new_v4().to_string(),
6382 source_jurisdiction,
6383 target_jurisdiction,
6384 statute_id,
6385 comparisons: Vec::new(),
6386 similarity_score: 0.0,
6387 key_differences: Vec::new(),
6388 recommendations: Vec::new(),
6389 created_at: chrono::Utc::now().to_rfc3339(),
6390 }
6391 }
6392
6393 pub fn add_comparison(&mut self, comparison: OutcomeComparison) {
6395 self.comparisons.push(comparison);
6396 self.calculate_similarity();
6397 }
6398
6399 pub fn add_key_difference(&mut self, difference: KeyDifference) {
6401 self.key_differences.push(difference);
6402 }
6403
6404 fn calculate_similarity(&mut self) {
6406 if self.comparisons.is_empty() {
6407 self.similarity_score = 0.0;
6408 return;
6409 }
6410
6411 let total_similarity: f64 = self
6412 .comparisons
6413 .iter()
6414 .map(|c| {
6415 1.0 - (c.difference_pct.abs() / 100.0).min(1.0)
6417 })
6418 .sum();
6419
6420 self.similarity_score = total_similarity / self.comparisons.len() as f64;
6421 }
6422
6423 pub fn significant_differences(&self) -> Vec<&OutcomeComparison> {
6425 self.comparisons
6426 .iter()
6427 .filter(|c| c.difference_pct.abs() >= 20.0)
6428 .collect()
6429 }
6430}
6431
6432#[derive(Debug, Clone, Serialize, Deserialize)]
6434pub struct PopulationImpactModeling {
6435 pub id: String,
6437 pub statute_id: String,
6439 pub jurisdiction: String,
6441 pub segments: Vec<PopulationSegment>,
6443 pub overall_impact: f64,
6445 pub equity_assessment: EquityAssessment,
6447 pub projections: Vec<DemographicProjection>,
6449 pub created_at: String,
6451}
6452
6453#[derive(Debug, Clone, Serialize, Deserialize)]
6455pub struct PopulationSegment {
6456 pub name: String,
6458 pub size: usize,
6460 pub percentage: f64,
6462 pub impact_level: f64,
6464 pub impact_type: PopulationImpactType,
6466 pub effects: Vec<String>,
6468 pub vulnerability_factors: Vec<String>,
6470}
6471
6472#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6474pub enum PopulationImpactType {
6475 HighlyBeneficial,
6477 ModeratelyBeneficial,
6479 Neutral,
6481 ModeratelyHarmful,
6483 HighlyHarmful,
6485}
6486
6487#[derive(Debug, Clone, Serialize, Deserialize)]
6489pub struct EquityAssessment {
6490 pub gini_coefficient: f64,
6492 pub disparate_impact: bool,
6494 pub vulnerable_groups_affected: Vec<String>,
6496 pub equity_score: f64,
6498 pub equity_recommendations: Vec<String>,
6500}
6501
6502#[derive(Debug, Clone, Serialize, Deserialize)]
6504pub struct DemographicProjection {
6505 pub year: u32,
6507 pub segment: String,
6509 pub compliance_rate: f64,
6511 pub net_benefit: f64,
6513 pub confidence_interval: (f64, f64),
6515}
6516
6517impl PopulationImpactModeling {
6518 pub fn new(statute_id: String, jurisdiction: String) -> Self {
6520 Self {
6521 id: uuid::Uuid::new_v4().to_string(),
6522 statute_id,
6523 jurisdiction,
6524 segments: Vec::new(),
6525 overall_impact: 0.0,
6526 equity_assessment: EquityAssessment {
6527 gini_coefficient: 0.0,
6528 disparate_impact: false,
6529 vulnerable_groups_affected: Vec::new(),
6530 equity_score: 1.0,
6531 equity_recommendations: Vec::new(),
6532 },
6533 projections: Vec::new(),
6534 created_at: chrono::Utc::now().to_rfc3339(),
6535 }
6536 }
6537
6538 pub fn add_segment(&mut self, segment: PopulationSegment) {
6540 self.segments.push(segment);
6541 self.calculate_overall_impact();
6542 self.assess_equity();
6543 }
6544
6545 fn calculate_overall_impact(&mut self) {
6547 if self.segments.is_empty() {
6548 self.overall_impact = 0.0;
6549 return;
6550 }
6551
6552 let weighted_impact: f64 = self
6553 .segments
6554 .iter()
6555 .map(|s| {
6556 let impact_value = match s.impact_type {
6557 PopulationImpactType::HighlyBeneficial => s.impact_level,
6558 PopulationImpactType::ModeratelyBeneficial => s.impact_level * 0.5,
6559 PopulationImpactType::Neutral => 0.0,
6560 PopulationImpactType::ModeratelyHarmful => -s.impact_level * 0.5,
6561 PopulationImpactType::HighlyHarmful => -s.impact_level,
6562 };
6563 impact_value * (s.percentage / 100.0)
6564 })
6565 .sum();
6566
6567 self.overall_impact = weighted_impact;
6568 }
6569
6570 fn assess_equity(&mut self) {
6572 if self.segments.is_empty() {
6573 return;
6574 }
6575
6576 let mut impacts: Vec<f64> = self.segments.iter().map(|s| s.impact_level).collect();
6578 impacts.sort_by(|a, b| a.partial_cmp(b).unwrap());
6579
6580 let n = impacts.len() as f64;
6581 let mut gini_sum = 0.0;
6582 for (i, impact) in impacts.iter().enumerate() {
6583 gini_sum += (2.0 * (i + 1) as f64 - n - 1.0) * impact;
6584 }
6585 let mean_impact = impacts.iter().sum::<f64>() / n;
6586 if mean_impact > 0.0 {
6587 self.equity_assessment.gini_coefficient = gini_sum / (n * n * mean_impact);
6588 }
6589
6590 let max_impact = impacts.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
6592 let min_impact = impacts.iter().cloned().fold(f64::INFINITY, f64::min);
6593 if max_impact > 0.0 {
6594 self.equity_assessment.disparate_impact = (min_impact / max_impact) < 0.8;
6595 }
6596
6597 self.equity_assessment.equity_score = 1.0 - self.equity_assessment.gini_coefficient;
6599 }
6600
6601 pub fn negatively_impacted_segments(&self) -> Vec<&PopulationSegment> {
6603 self.segments
6604 .iter()
6605 .filter(|s| {
6606 matches!(
6607 s.impact_type,
6608 PopulationImpactType::ModeratelyHarmful | PopulationImpactType::HighlyHarmful
6609 )
6610 })
6611 .collect()
6612 }
6613}
6614
6615#[derive(Debug, Clone, Serialize, Deserialize)]
6617pub struct EnforcementSimulation {
6618 pub id: String,
6620 pub statute_id: String,
6622 pub jurisdiction: String,
6624 pub scenarios: Vec<EnforcementScenario>,
6626 pub optimal_strategy: Option<EnforcementStrategy>,
6628 pub efficiency_score: f64,
6630 pub created_at: String,
6632}
6633
6634#[derive(Debug, Clone, Serialize, Deserialize)]
6636pub struct EnforcementScenario {
6637 pub name: String,
6639 pub strategy: EnforcementStrategy,
6641 pub compliance_rate: f64,
6643 pub cost: f64,
6645 pub currency: String,
6647 pub effectiveness: f64,
6649 pub public_acceptance: f64,
6651 pub risks: Vec<String>,
6653}
6654
6655#[derive(Debug, Clone, Serialize, Deserialize)]
6657pub struct EnforcementStrategy {
6658 pub name: String,
6660 pub mechanisms: Vec<EnforcementMechanism>,
6662 pub penalties: Vec<Penalty>,
6664 pub monitoring: MonitoringApproach,
6666 pub resources: ResourceAllocation,
6668}
6669
6670#[derive(Debug, Clone, Serialize, Deserialize)]
6672pub struct EnforcementMechanism {
6673 pub mechanism_type: MechanismType,
6675 pub description: String,
6677 pub frequency: String,
6679 pub effectiveness: f64,
6681}
6682
6683#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6685pub enum MechanismType {
6686 Inspection,
6688 Audit,
6690 Reporting,
6692 AutomatedMonitoring,
6694 PublicDisclosure,
6696 Certification,
6698}
6699
6700#[derive(Debug, Clone, Serialize, Deserialize)]
6702pub struct Penalty {
6703 pub violation_type: String,
6705 pub amount: f64,
6707 pub currency: String,
6709 pub additional_sanctions: Vec<String>,
6711 pub deterrence: f64,
6713}
6714
6715#[derive(Debug, Clone, Serialize, Deserialize)]
6717pub struct MonitoringApproach {
6718 pub approach_type: MonitoringType,
6720 pub coverage: f64,
6722 pub frequency: String,
6724 pub technology: Vec<String>,
6726}
6727
6728#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6730pub enum MonitoringType {
6731 Continuous,
6733 Periodic,
6735 RandomSampling,
6737 RiskBased,
6739 ComplaintDriven,
6741}
6742
6743#[derive(Debug, Clone, Serialize, Deserialize)]
6745pub struct ResourceAllocation {
6746 pub personnel: usize,
6748 pub budget: f64,
6750 pub currency: String,
6752 pub equipment: Vec<String>,
6754 pub training_hours: f64,
6756}
6757
6758impl EnforcementSimulation {
6759 pub fn new(statute_id: String, jurisdiction: String) -> Self {
6761 Self {
6762 id: uuid::Uuid::new_v4().to_string(),
6763 statute_id,
6764 jurisdiction,
6765 scenarios: Vec::new(),
6766 optimal_strategy: None,
6767 efficiency_score: 0.0,
6768 created_at: chrono::Utc::now().to_rfc3339(),
6769 }
6770 }
6771
6772 pub fn add_scenario(&mut self, scenario: EnforcementScenario) {
6774 self.scenarios.push(scenario);
6775 self.find_optimal_strategy();
6776 }
6777
6778 fn find_optimal_strategy(&mut self) {
6780 if self.scenarios.is_empty() {
6781 self.optimal_strategy = None;
6782 self.efficiency_score = 0.0;
6783 return;
6784 }
6785
6786 let best_scenario = self.scenarios.iter().max_by(|a, b| {
6788 let a_ratio = if a.cost > 0.0 {
6789 a.effectiveness / a.cost
6790 } else {
6791 a.effectiveness
6792 };
6793 let b_ratio = if b.cost > 0.0 {
6794 b.effectiveness / b.cost
6795 } else {
6796 b.effectiveness
6797 };
6798 a_ratio.partial_cmp(&b_ratio).unwrap()
6799 });
6800
6801 if let Some(best) = best_scenario {
6802 self.optimal_strategy = Some(best.strategy.clone());
6803 self.efficiency_score = if best.cost > 0.0 {
6804 best.effectiveness / best.cost
6805 } else {
6806 best.effectiveness
6807 };
6808 }
6809 }
6810
6811 pub fn high_effectiveness_scenarios(&self) -> Vec<&EnforcementScenario> {
6813 self.scenarios
6814 .iter()
6815 .filter(|s| s.effectiveness >= 0.7)
6816 .collect()
6817 }
6818}
6819
6820#[derive(Debug, Clone, Serialize, Deserialize)]
6822pub struct ABTestingFramework {
6823 pub id: String,
6825 pub statute_id: String,
6827 pub jurisdiction: String,
6829 pub variants: Vec<PortingVariant>,
6831 pub config: TestConfiguration,
6833 pub results: Option<ABTestResults>,
6835 pub status: ABTestStatus,
6837 pub created_at: String,
6839}
6840
6841#[derive(Debug, Clone, Serialize, Deserialize)]
6843pub struct PortingVariant {
6844 pub id: String,
6846 pub name: String,
6848 pub ported_statute_id: String,
6850 pub differences: Vec<String>,
6852 pub hypothesis: String,
6854 pub traffic_allocation: f64,
6856}
6857
6858#[derive(Debug, Clone, Serialize, Deserialize)]
6860pub struct TestConfiguration {
6861 pub sample_size: usize,
6863 pub duration_days: u32,
6865 pub significance_threshold: f64,
6867 pub minimum_effect: f64,
6869 pub primary_metric: String,
6871 pub secondary_metrics: Vec<String>,
6873}
6874
6875#[derive(Debug, Clone, Serialize, Deserialize)]
6877pub struct ABTestResults {
6878 pub performances: Vec<VariantPerformance>,
6880 pub winner_id: Option<String>,
6882 pub statistically_significant: bool,
6884 pub confidence_level: f64,
6886 pub recommendations: Vec<String>,
6888 pub completed_at: String,
6890}
6891
6892#[derive(Debug, Clone, Serialize, Deserialize)]
6894pub struct VariantPerformance {
6895 pub variant_id: String,
6897 pub primary_metric_value: f64,
6899 pub secondary_metric_values: HashMap<String, f64>,
6901 pub sample_size: usize,
6903 pub compliance_rate: f64,
6905 pub user_satisfaction: f64,
6907 pub confidence_interval: (f64, f64),
6909}
6910
6911#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6913pub enum ABTestStatus {
6914 Setup,
6916 Running,
6918 Paused,
6920 Completed,
6922 Cancelled,
6924}
6925
6926impl ABTestingFramework {
6927 pub fn new(statute_id: String, jurisdiction: String, config: TestConfiguration) -> Self {
6929 Self {
6930 id: uuid::Uuid::new_v4().to_string(),
6931 statute_id,
6932 jurisdiction,
6933 variants: Vec::new(),
6934 config,
6935 results: None,
6936 status: ABTestStatus::Setup,
6937 created_at: chrono::Utc::now().to_rfc3339(),
6938 }
6939 }
6940
6941 pub fn add_variant(&mut self, variant: PortingVariant) {
6943 self.variants.push(variant);
6944 }
6945
6946 pub fn start_test(&mut self) -> Result<(), PortingError> {
6948 if self.variants.len() < 2 {
6949 return Err(PortingError::InvalidInput(
6950 "Need at least 2 variants for A/B testing".to_string(),
6951 ));
6952 }
6953
6954 let total_allocation: f64 = self.variants.iter().map(|v| v.traffic_allocation).sum();
6955 if (total_allocation - 1.0).abs() > 0.01 {
6956 return Err(PortingError::InvalidInput(
6957 "Traffic allocation must sum to 1.0".to_string(),
6958 ));
6959 }
6960
6961 self.status = ABTestStatus::Running;
6962 Ok(())
6963 }
6964
6965 pub fn record_results(&mut self, results: ABTestResults) {
6967 self.results = Some(results);
6968 self.status = ABTestStatus::Completed;
6969 }
6970
6971 pub fn get_winner(&self) -> Option<&PortingVariant> {
6973 if let Some(results) = &self.results
6974 && let Some(winner_id) = &results.winner_id
6975 {
6976 return self.variants.iter().find(|v| &v.id == winner_id);
6977 }
6978 None
6979 }
6980}
6981
6982#[derive(Debug, Clone, Serialize, Deserialize)]
6988pub struct PortingAgent {
6989 pub id: String,
6991 pub name: String,
6993 pub specialization: AgentSpecialization,
6995 pub model: LearningModel,
6997 pub performance: AgentPerformance,
6999 pub capabilities: Vec<AgentCapability>,
7001 pub state: AgentState,
7003 pub created_at: String,
7005}
7006
7007#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7009pub enum AgentSpecialization {
7010 CulturalAdaptation,
7012 LegalSystemCompatibility,
7014 SemanticPreservation,
7016 ConflictResolution,
7018 RiskAssessment,
7020 ComplianceChecking,
7022 GeneralAnalysis,
7024}
7025
7026#[derive(Debug, Clone, Serialize, Deserialize)]
7028pub struct LearningModel {
7029 pub version: String,
7031 pub model_type: ModelType,
7033 pub training_data_size: usize,
7035 pub accuracy: f64,
7037 pub last_trained: String,
7039 pub parameters: ModelParameters,
7041}
7042
7043#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7045pub enum ModelType {
7046 Supervised,
7048 Reinforcement,
7050 Transfer,
7052 Ensemble,
7054 NeuralNetwork,
7056}
7057
7058#[derive(Debug, Clone, Serialize, Deserialize)]
7060pub struct ModelParameters {
7061 pub learning_rate: f64,
7063 pub batch_size: usize,
7065 pub layers: usize,
7067 pub hidden_units: usize,
7069 pub dropout_rate: f64,
7071}
7072
7073#[derive(Debug, Clone, Serialize, Deserialize)]
7075pub struct AgentPerformance {
7076 pub total_analyses: usize,
7078 pub successful_analyses: usize,
7080 pub average_accuracy: f64,
7082 pub average_time_seconds: f64,
7084 pub user_satisfaction: f64,
7086 pub improvement_rate: f64,
7088}
7089
7090#[derive(Debug, Clone, Serialize, Deserialize)]
7092pub struct AgentCapability {
7093 pub name: String,
7095 pub description: String,
7097 pub proficiency: f64,
7099 pub confidence: f64,
7101}
7102
7103#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7105pub enum AgentState {
7106 Idle,
7108 Analyzing,
7110 Learning,
7112 WaitingForFeedback,
7114 Suspended,
7116}
7117
7118impl PortingAgent {
7119 pub fn new(name: String, specialization: AgentSpecialization) -> Self {
7121 Self {
7122 id: uuid::Uuid::new_v4().to_string(),
7123 name,
7124 specialization,
7125 model: LearningModel {
7126 version: "1.0.0".to_string(),
7127 model_type: ModelType::Supervised,
7128 training_data_size: 0,
7129 accuracy: 0.5,
7130 last_trained: chrono::Utc::now().to_rfc3339(),
7131 parameters: ModelParameters {
7132 learning_rate: 0.001,
7133 batch_size: 32,
7134 layers: 3,
7135 hidden_units: 128,
7136 dropout_rate: 0.2,
7137 },
7138 },
7139 performance: AgentPerformance {
7140 total_analyses: 0,
7141 successful_analyses: 0,
7142 average_accuracy: 0.0,
7143 average_time_seconds: 0.0,
7144 user_satisfaction: 0.0,
7145 improvement_rate: 0.0,
7146 },
7147 capabilities: Vec::new(),
7148 state: AgentState::Idle,
7149 created_at: chrono::Utc::now().to_rfc3339(),
7150 }
7151 }
7152
7153 pub fn add_capability(&mut self, capability: AgentCapability) {
7155 self.capabilities.push(capability);
7156 }
7157
7158 pub fn set_state(&mut self, state: AgentState) {
7160 self.state = state;
7161 }
7162
7163 pub fn record_analysis(&mut self, success: bool, accuracy: f64, time_seconds: f64) {
7165 self.performance.total_analyses += 1;
7166 if success {
7167 self.performance.successful_analyses += 1;
7168 }
7169
7170 let n = self.performance.total_analyses as f64;
7172 self.performance.average_accuracy =
7173 (self.performance.average_accuracy * (n - 1.0) + accuracy) / n;
7174
7175 self.performance.average_time_seconds =
7177 (self.performance.average_time_seconds * (n - 1.0) + time_seconds) / n;
7178 }
7179
7180 pub fn success_rate(&self) -> f64 {
7182 if self.performance.total_analyses == 0 {
7183 0.0
7184 } else {
7185 self.performance.successful_analyses as f64 / self.performance.total_analyses as f64
7186 }
7187 }
7188}
7189
7190#[derive(Debug, Clone, Serialize, Deserialize)]
7192pub struct AutomatedAdaptationProposal {
7193 pub id: String,
7195 pub agent_id: String,
7197 pub statute_id: String,
7199 pub source_jurisdiction: String,
7201 pub target_jurisdiction: String,
7203 pub adaptations: Vec<ProposedAdaptation>,
7205 pub confidence: f64,
7207 pub reasoning: String,
7209 pub alternatives: Vec<AlternativeProposal>,
7211 pub generated_at: String,
7213}
7214
7215#[derive(Debug, Clone, Serialize, Deserialize)]
7217pub struct ProposedAdaptation {
7218 pub adaptation_type: AdaptationType,
7220 pub original: String,
7222 pub proposed: String,
7224 pub justification: String,
7226 pub confidence: f64,
7228 pub impact: AdaptationImpact,
7230}
7231
7232#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7234pub enum AdaptationType {
7235 CulturalParameter,
7237 LegalTerm,
7239 Structural,
7241 Procedural,
7243 Penalty,
7245 Temporal,
7247}
7248
7249#[derive(Debug, Clone, Serialize, Deserialize)]
7251pub struct AdaptationImpact {
7252 pub semantic_preservation: f64,
7254 pub legal_validity: f64,
7256 pub cultural_appropriateness: f64,
7258 pub implementation_complexity: f64,
7260}
7261
7262#[derive(Debug, Clone, Serialize, Deserialize)]
7264pub struct AlternativeProposal {
7265 pub id: String,
7267 pub description: String,
7269 pub proposed_value: String,
7271 pub confidence: f64,
7273 pub tradeoffs: Vec<String>,
7275}
7276
7277impl AutomatedAdaptationProposal {
7278 pub fn new(
7280 agent_id: String,
7281 statute_id: String,
7282 source_jurisdiction: String,
7283 target_jurisdiction: String,
7284 ) -> Self {
7285 Self {
7286 id: uuid::Uuid::new_v4().to_string(),
7287 agent_id,
7288 statute_id,
7289 source_jurisdiction,
7290 target_jurisdiction,
7291 adaptations: Vec::new(),
7292 confidence: 0.0,
7293 reasoning: String::new(),
7294 alternatives: Vec::new(),
7295 generated_at: chrono::Utc::now().to_rfc3339(),
7296 }
7297 }
7298
7299 pub fn add_adaptation(&mut self, adaptation: ProposedAdaptation) {
7301 self.adaptations.push(adaptation);
7302 self.recalculate_confidence();
7303 }
7304
7305 pub fn add_alternative(&mut self, alternative: AlternativeProposal) {
7307 self.alternatives.push(alternative);
7308 }
7309
7310 fn recalculate_confidence(&mut self) {
7312 if self.adaptations.is_empty() {
7313 self.confidence = 0.0;
7314 return;
7315 }
7316
7317 let total_confidence: f64 = self.adaptations.iter().map(|a| a.confidence).sum();
7318 self.confidence = total_confidence / self.adaptations.len() as f64;
7319 }
7320
7321 pub fn high_confidence_adaptations(&self) -> Vec<&ProposedAdaptation> {
7323 self.adaptations
7324 .iter()
7325 .filter(|a| a.confidence >= 0.8)
7326 .collect()
7327 }
7328}
7329
7330#[derive(Debug, Clone, Serialize, Deserialize)]
7332pub struct SelfImprovingModel {
7333 pub id: String,
7335 pub name: String,
7337 pub version: String,
7339 pub training_data: TrainingDataset,
7341 pub metrics: ModelMetrics,
7343 pub improvement_history: Vec<ImprovementRecord>,
7345 pub learning_strategy: LearningStrategy,
7347}
7348
7349#[derive(Debug, Clone, Serialize, Deserialize)]
7351pub struct TrainingDataset {
7352 pub sample_count: usize,
7354 pub positive_examples: usize,
7356 pub negative_examples: usize,
7358 pub last_updated: String,
7360 pub quality_score: f64,
7362}
7363
7364#[derive(Debug, Clone, Serialize, Deserialize)]
7366pub struct ModelMetrics {
7367 pub precision: f64,
7369 pub recall: f64,
7371 pub f1_score: f64,
7373 pub accuracy: f64,
7375 pub roc_auc: f64,
7377}
7378
7379#[derive(Debug, Clone, Serialize, Deserialize)]
7381pub struct ImprovementRecord {
7382 pub previous_version: String,
7384 pub new_version: String,
7386 pub accuracy_delta: f64,
7388 pub f1_delta: f64,
7390 pub samples_added: usize,
7392 pub improved_at: String,
7394 pub notes: String,
7396}
7397
7398#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7400pub enum LearningStrategy {
7401 ActiveLearning,
7403 ContinuousLearning,
7405 ReinforcementLearning,
7407 TransferLearning,
7409}
7410
7411impl SelfImprovingModel {
7412 pub fn new(name: String) -> Self {
7414 Self {
7415 id: uuid::Uuid::new_v4().to_string(),
7416 name,
7417 version: "1.0.0".to_string(),
7418 training_data: TrainingDataset {
7419 sample_count: 0,
7420 positive_examples: 0,
7421 negative_examples: 0,
7422 last_updated: chrono::Utc::now().to_rfc3339(),
7423 quality_score: 0.0,
7424 },
7425 metrics: ModelMetrics {
7426 precision: 0.0,
7427 recall: 0.0,
7428 f1_score: 0.0,
7429 accuracy: 0.0,
7430 roc_auc: 0.0,
7431 },
7432 improvement_history: Vec::new(),
7433 learning_strategy: LearningStrategy::ContinuousLearning,
7434 }
7435 }
7436
7437 pub fn add_training_data(&mut self, positive: usize, negative: usize) {
7439 self.training_data.sample_count += positive + negative;
7440 self.training_data.positive_examples += positive;
7441 self.training_data.negative_examples += negative;
7442 self.training_data.last_updated = chrono::Utc::now().to_rfc3339();
7443
7444 let balance = if self.training_data.sample_count > 0 {
7446 let ratio = self.training_data.positive_examples as f64
7447 / self.training_data.sample_count as f64;
7448 1.0 - (ratio - 0.5).abs() * 2.0 } else {
7450 0.0
7451 };
7452 self.training_data.quality_score = balance;
7453 }
7454
7455 pub fn record_improvement(
7457 &mut self,
7458 new_accuracy: f64,
7459 new_f1: f64,
7460 samples_added: usize,
7461 notes: String,
7462 ) {
7463 let accuracy_delta = new_accuracy - self.metrics.accuracy;
7464 let f1_delta = new_f1 - self.metrics.f1_score;
7465
7466 let record = ImprovementRecord {
7467 previous_version: self.version.clone(),
7468 new_version: self.increment_version(),
7469 accuracy_delta,
7470 f1_delta,
7471 samples_added,
7472 improved_at: chrono::Utc::now().to_rfc3339(),
7473 notes,
7474 };
7475
7476 self.improvement_history.push(record);
7477 self.metrics.accuracy = new_accuracy;
7478 self.metrics.f1_score = new_f1;
7479 }
7480
7481 fn increment_version(&mut self) -> String {
7483 let parts: Vec<&str> = self.version.split('.').collect();
7484 if parts.len() == 3
7485 && let Ok(patch) = parts[2].parse::<u32>()
7486 {
7487 let new_version = format!("{}.{}.{}", parts[0], parts[1], patch + 1);
7488 self.version = new_version.clone();
7489 return new_version;
7490 }
7491 self.version.clone()
7492 }
7493
7494 pub fn total_improvement(&self) -> f64 {
7496 self.improvement_history
7497 .iter()
7498 .map(|r| r.accuracy_delta)
7499 .sum()
7500 }
7501}
7502
7503#[derive(Debug, Clone, Serialize, Deserialize)]
7505pub struct ContinuousLearningSystem {
7506 pub id: String,
7508 pub outcomes: Vec<PortingOutcome>,
7510 pub feedback: Vec<UserFeedback>,
7512 pub insights: Vec<LearningInsight>,
7514 pub metrics: LearningSystemMetrics,
7516}
7517
7518#[derive(Debug, Clone, Serialize, Deserialize)]
7520pub struct PortingOutcome {
7521 pub id: String,
7523 pub porting_id: String,
7525 pub statute_id: String,
7527 pub success: bool,
7529 pub quality_score: f64,
7531 pub adaptations_made: Vec<String>,
7533 pub issues: Vec<String>,
7535 pub recorded_at: String,
7537}
7538
7539#[derive(Debug, Clone, Serialize, Deserialize)]
7541pub struct UserFeedback {
7542 pub id: String,
7544 pub outcome_id: String,
7546 pub rating: u8,
7548 pub feedback_text: String,
7550 pub issues_noted: Vec<String>,
7552 pub suggestions: Vec<String>,
7554 pub submitted_at: String,
7556}
7557
7558#[derive(Debug, Clone, Serialize, Deserialize)]
7560pub struct LearningInsight {
7561 pub id: String,
7563 pub insight_type: InsightType,
7565 pub description: String,
7567 pub confidence: f64,
7569 pub evidence_count: usize,
7571 pub recommendation: String,
7573 pub discovered_at: String,
7575}
7576
7577#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7579pub enum InsightType {
7580 Pattern,
7582 FailureMode,
7584 BestPractice,
7586 Correlation,
7588 EdgeCase,
7590}
7591
7592#[derive(Debug, Clone, Serialize, Deserialize)]
7594pub struct LearningSystemMetrics {
7595 pub total_outcomes: usize,
7597 pub success_rate: f64,
7599 pub average_quality: f64,
7601 pub insights_count: usize,
7603 pub feedback_count: usize,
7605 pub average_rating: f64,
7607}
7608
7609impl ContinuousLearningSystem {
7610 pub fn new() -> Self {
7612 Self {
7613 id: uuid::Uuid::new_v4().to_string(),
7614 outcomes: Vec::new(),
7615 feedback: Vec::new(),
7616 insights: Vec::new(),
7617 metrics: LearningSystemMetrics {
7618 total_outcomes: 0,
7619 success_rate: 0.0,
7620 average_quality: 0.0,
7621 insights_count: 0,
7622 feedback_count: 0,
7623 average_rating: 0.0,
7624 },
7625 }
7626 }
7627
7628 pub fn record_outcome(&mut self, outcome: PortingOutcome) {
7630 self.outcomes.push(outcome);
7631 self.update_metrics();
7632 }
7633
7634 pub fn add_feedback(&mut self, feedback: UserFeedback) {
7636 self.feedback.push(feedback);
7637 self.update_metrics();
7638 }
7639
7640 pub fn add_insight(&mut self, insight: LearningInsight) {
7642 self.insights.push(insight);
7643 self.metrics.insights_count = self.insights.len();
7644 }
7645
7646 fn update_metrics(&mut self) {
7648 self.metrics.total_outcomes = self.outcomes.len();
7649
7650 if !self.outcomes.is_empty() {
7651 let successes = self.outcomes.iter().filter(|o| o.success).count();
7652 self.metrics.success_rate = successes as f64 / self.outcomes.len() as f64;
7653
7654 let total_quality: f64 = self.outcomes.iter().map(|o| o.quality_score).sum();
7655 self.metrics.average_quality = total_quality / self.outcomes.len() as f64;
7656 }
7657
7658 self.metrics.feedback_count = self.feedback.len();
7659 if !self.feedback.is_empty() {
7660 let total_rating: u32 = self.feedback.iter().map(|f| f.rating as u32).sum();
7661 self.metrics.average_rating = total_rating as f64 / self.feedback.len() as f64;
7662 }
7663 }
7664
7665 pub fn high_confidence_insights(&self) -> Vec<&LearningInsight> {
7667 self.insights
7668 .iter()
7669 .filter(|i| i.confidence >= 0.8)
7670 .collect()
7671 }
7672}
7673
7674impl Default for ContinuousLearningSystem {
7675 fn default() -> Self {
7676 Self::new()
7677 }
7678}
7679
7680#[derive(Debug, Clone, Serialize, Deserialize)]
7682pub struct HumanInTheLoopSystem {
7683 pub id: String,
7685 pub pending_reviews: Vec<PendingReview>,
7687 pub completed_reviews: Vec<CompletedReview>,
7689 pub reviewers: Vec<ExpertReviewer>,
7691 pub config: HitlConfiguration,
7693}
7694
7695#[derive(Debug, Clone, Serialize, Deserialize)]
7697pub struct PendingReview {
7698 pub id: String,
7700 pub proposal_id: String,
7702 pub agent_id: String,
7704 pub priority: u8,
7706 pub review_reason: ReviewReason,
7708 pub context: String,
7710 pub questions: Vec<String>,
7712 pub created_at: String,
7714}
7715
7716#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7718pub enum ReviewReason {
7719 LowConfidence,
7721 HighStakes,
7723 NovelSituation,
7725 ConflictingRecommendations,
7727 LegalComplexity,
7729 UserRequested,
7731}
7732
7733#[derive(Debug, Clone, Serialize, Deserialize)]
7735pub struct CompletedReview {
7736 pub id: String,
7738 pub pending_review_id: String,
7740 pub reviewer_id: String,
7742 pub decision: AgentReviewDecision,
7744 pub comments: String,
7746 pub corrections: Vec<AgentReviewCorrection>,
7748 pub confidence: f64,
7750 pub review_time_seconds: f64,
7752 pub completed_at: String,
7754}
7755
7756#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7758pub enum AgentReviewDecision {
7759 Approve,
7761 ApproveWithModifications,
7763 Reject,
7765 RequestMoreInfo,
7767 Escalate,
7769}
7770
7771#[derive(Debug, Clone, Serialize, Deserialize)]
7773pub struct AgentReviewCorrection {
7774 pub field: String,
7776 pub original_value: String,
7778 pub corrected_value: String,
7780 pub explanation: String,
7782}
7783
7784#[derive(Debug, Clone, Serialize, Deserialize)]
7786pub struct ExpertReviewer {
7787 pub id: String,
7789 pub name: String,
7791 pub expertise: Vec<String>,
7793 pub reviews_completed: usize,
7795 pub average_review_time: f64,
7797 pub accuracy: f64,
7799}
7800
7801#[derive(Debug, Clone, Serialize, Deserialize)]
7803pub struct HitlConfiguration {
7804 pub confidence_threshold: f64,
7806 pub require_review_for_high_stakes: bool,
7808 pub max_review_time: f64,
7810 pub escalation_threshold: usize,
7812}
7813
7814impl HumanInTheLoopSystem {
7815 pub fn new(config: HitlConfiguration) -> Self {
7817 Self {
7818 id: uuid::Uuid::new_v4().to_string(),
7819 pending_reviews: Vec::new(),
7820 completed_reviews: Vec::new(),
7821 reviewers: Vec::new(),
7822 config,
7823 }
7824 }
7825
7826 pub fn submit_for_review(&mut self, review: PendingReview) {
7828 self.pending_reviews.push(review);
7829 }
7830
7831 pub fn complete_review(&mut self, review: CompletedReview) {
7833 self.pending_reviews
7835 .retain(|r| r.id != review.pending_review_id);
7836 self.completed_reviews.push(review);
7838 }
7839
7840 pub fn add_reviewer(&mut self, reviewer: ExpertReviewer) {
7842 self.reviewers.push(reviewer);
7843 }
7844
7845 pub fn high_priority_reviews(&self) -> Vec<&PendingReview> {
7847 self.pending_reviews
7848 .iter()
7849 .filter(|r| r.priority >= 4)
7850 .collect()
7851 }
7852
7853 pub fn approval_rate(&self) -> f64 {
7855 if self.completed_reviews.is_empty() {
7856 return 0.0;
7857 }
7858
7859 let approved = self
7860 .completed_reviews
7861 .iter()
7862 .filter(|r| {
7863 matches!(
7864 r.decision,
7865 AgentReviewDecision::Approve | AgentReviewDecision::ApproveWithModifications
7866 )
7867 })
7868 .count();
7869
7870 approved as f64 / self.completed_reviews.len() as f64
7871 }
7872}
7873
7874#[derive(Debug, Clone, Serialize, Deserialize)]
7880pub struct ConflictPrecedent {
7881 pub id: String,
7883 pub source_jurisdiction: String,
7885 pub target_jurisdiction: String,
7887 pub conflict_type: ConflictType,
7889 pub description: String,
7891 pub resolution_used: String,
7893 pub effectiveness: f64,
7895 pub resolved_by: Option<String>,
7897 pub resolved_at: String,
7899 pub lessons_learned: Vec<String>,
7901 pub applicable_statute_types: Vec<String>,
7903 pub tags: Vec<String>,
7905}
7906
7907#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7909pub struct ConflictPrecedentDatabase {
7910 precedents: Vec<ConflictPrecedent>,
7912 jurisdiction_index: HashMap<(String, String), Vec<usize>>,
7914}
7915
7916impl ConflictPrecedentDatabase {
7917 pub fn new() -> Self {
7919 Self {
7920 precedents: Vec::new(),
7921 jurisdiction_index: HashMap::new(),
7922 }
7923 }
7924
7925 pub fn add_precedent(&mut self, precedent: ConflictPrecedent) {
7927 let idx = self.precedents.len();
7928 let key = (
7929 precedent.source_jurisdiction.clone(),
7930 precedent.target_jurisdiction.clone(),
7931 );
7932
7933 self.jurisdiction_index.entry(key).or_default().push(idx);
7934
7935 self.precedents.push(precedent);
7936 }
7937
7938 pub fn find_relevant_precedents(
7940 &self,
7941 source_jurisdiction: &str,
7942 target_jurisdiction: &str,
7943 conflict_type: &ConflictType,
7944 ) -> Vec<&ConflictPrecedent> {
7945 let key = (
7946 source_jurisdiction.to_string(),
7947 target_jurisdiction.to_string(),
7948 );
7949
7950 if let Some(indices) = self.jurisdiction_index.get(&key) {
7951 indices
7952 .iter()
7953 .filter_map(|&idx| self.precedents.get(idx))
7954 .filter(|p| {
7955 std::mem::discriminant(&p.conflict_type)
7957 == std::mem::discriminant(conflict_type)
7958 })
7959 .collect()
7960 } else {
7961 Vec::new()
7962 }
7963 }
7964
7965 pub fn get_effective_precedents(&self) -> Vec<&ConflictPrecedent> {
7967 self.precedents
7968 .iter()
7969 .filter(|p| p.effectiveness >= 0.7)
7970 .collect()
7971 }
7972
7973 pub fn all_precedents(&self) -> &[ConflictPrecedent] {
7975 &self.precedents
7976 }
7977}
7978
7979#[derive(Debug, Clone, Serialize, Deserialize)]
7981pub struct NegotiatedResolutionTemplate {
7982 pub id: String,
7984 pub name: String,
7986 pub conflict_types: Vec<ConflictType>,
7988 pub source_patterns: Vec<String>,
7990 pub target_patterns: Vec<String>,
7992 pub approach: String,
7994 pub negotiation_steps: Vec<NegotiationStep>,
7996 pub fallback_strategies: Vec<String>,
7998 pub success_rate: f64,
8000 pub stakeholders: Vec<String>,
8002 pub required_approvals: Vec<String>,
8004}
8005
8006#[derive(Debug, Clone, Serialize, Deserialize)]
8008pub struct NegotiationStep {
8009 pub step_number: usize,
8011 pub description: String,
8013 pub involved_parties: Vec<String>,
8015 pub expected_outcome: String,
8017 pub estimated_days: u32,
8019}
8020
8021#[derive(Debug, Clone, Serialize, Deserialize)]
8023pub struct ConflictResolutionWorkflow {
8024 pub id: String,
8026 pub conflict: ConflictReport,
8028 pub state: ResolutionWorkflowState,
8030 pub proposed_resolution: Option<String>,
8032 pub stakeholder_reviews: Vec<StakeholderReview>,
8034 pub expert_consultations: Vec<ExpertConsultation>,
8036 pub final_decision: Option<ResolutionDecision>,
8038 pub created_at: String,
8040 pub updated_at: String,
8042 pub escalation_level: EscalationLevel,
8044}
8045
8046#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8048pub enum ResolutionWorkflowState {
8049 InitialAssessment,
8051 AwaitingExpert,
8053 StakeholderReview,
8055 NegotiationInProgress,
8057 DecisionPending,
8059 Resolved,
8061 Escalated,
8063 Abandoned,
8065}
8066
8067#[derive(Debug, Clone, Serialize, Deserialize)]
8069pub struct StakeholderReview {
8070 pub reviewer_id: String,
8072 pub reviewer_name: String,
8074 pub role: String,
8076 pub reviewed_at: String,
8078 pub recommendation: StakeholderRecommendation,
8080 pub comments: String,
8082 pub concerns: Vec<String>,
8084 pub modifications: Vec<String>,
8086}
8087
8088#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8090pub enum StakeholderRecommendation {
8091 Approve,
8093 ApproveWithModifications,
8095 RequestAlternative,
8097 Reject,
8099}
8100
8101#[derive(Debug, Clone, Serialize, Deserialize)]
8103pub struct ExpertConsultation {
8104 pub id: String,
8106 pub expert_id: String,
8108 pub expert_name: String,
8110 pub expertise_area: String,
8112 pub consulted_at: String,
8114 pub opinion: String,
8116 pub recommended_approach: String,
8118 pub confidence: f64,
8120 pub legal_references: Vec<String>,
8122}
8123
8124#[derive(Debug, Clone, Serialize, Deserialize)]
8126pub struct ResolutionDecision {
8127 pub id: String,
8129 pub decision_maker_id: String,
8131 pub decision_maker_role: String,
8133 pub decided_at: String,
8135 pub chosen_strategy: String,
8137 pub rationale: String,
8139 pub implementation_plan: Vec<String>,
8141 pub monitoring_requirements: Vec<String>,
8143 pub accepted_risks: Vec<String>,
8145}
8146
8147#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
8149pub enum EscalationLevel {
8150 Routine,
8152 Elevated,
8154 High,
8156 Critical,
8158}
8159
8160#[derive(Debug, Clone)]
8162pub struct ConflictDetector {
8163 pub precedent_db: ConflictPrecedentDatabase,
8165 pub templates: Vec<NegotiatedResolutionTemplate>,
8167}
8168
8169impl ConflictDetector {
8170 pub fn new() -> Self {
8172 Self {
8173 precedent_db: ConflictPrecedentDatabase::new(),
8174 templates: Vec::new(),
8175 }
8176 }
8177
8178 pub fn with_precedents(precedent_db: ConflictPrecedentDatabase) -> Self {
8180 Self {
8181 precedent_db,
8182 templates: Vec::new(),
8183 }
8184 }
8185
8186 pub fn analyze_severity(
8188 &self,
8189 conflict: &ConflictReport,
8190 source_jurisdiction: &Jurisdiction,
8191 target_jurisdiction: &Jurisdiction,
8192 ) -> Severity {
8193 let mut severity_score = 0;
8194
8195 severity_score += match conflict.conflict_type {
8197 ConflictType::Contradiction => 3,
8198 ConflictType::CulturalIncompatibility => 2,
8199 ConflictType::SystemMismatch => 2,
8200 ConflictType::Overlap => 1,
8201 ConflictType::Procedural => 1,
8202 };
8203
8204 if source_jurisdiction.legal_system != target_jurisdiction.legal_system {
8206 severity_score += 1;
8207 }
8208
8209 let precedents = self.precedent_db.find_relevant_precedents(
8211 &source_jurisdiction.id,
8212 &target_jurisdiction.id,
8213 &conflict.conflict_type,
8214 );
8215
8216 if precedents.is_empty() {
8217 severity_score += 1;
8219 } else {
8220 let avg_effectiveness: f64 =
8222 precedents.iter().map(|p| p.effectiveness).sum::<f64>() / precedents.len() as f64;
8223 if avg_effectiveness < 0.5 {
8224 severity_score += 1;
8225 }
8226 }
8227
8228 match severity_score {
8230 0..=2 => Severity::Info,
8231 3..=4 => Severity::Warning,
8232 5..=6 => Severity::Error,
8233 _ => Severity::Critical,
8234 }
8235 }
8236
8237 pub fn recommend_strategies(
8239 &self,
8240 conflict: &ConflictReport,
8241 source_jurisdiction: &Jurisdiction,
8242 target_jurisdiction: &Jurisdiction,
8243 ) -> Vec<String> {
8244 let mut strategies = Vec::new();
8245
8246 let precedents = self.precedent_db.find_relevant_precedents(
8248 &source_jurisdiction.id,
8249 &target_jurisdiction.id,
8250 &conflict.conflict_type,
8251 );
8252
8253 for precedent in precedents.iter().take(3) {
8254 if precedent.effectiveness >= 0.7 {
8255 strategies.push(format!(
8256 "{} (proven effective: {:.0}%)",
8257 precedent.resolution_used,
8258 precedent.effectiveness * 100.0
8259 ));
8260 }
8261 }
8262
8263 for template in &self.templates {
8265 if template.conflict_types.contains(&conflict.conflict_type) {
8266 strategies.push(format!(
8267 "{} (template: {}, success rate: {:.0}%)",
8268 template.approach,
8269 template.name,
8270 template.success_rate * 100.0
8271 ));
8272 }
8273 }
8274
8275 if strategies.is_empty() {
8277 strategies.extend(conflict.resolutions.clone());
8278 }
8279
8280 strategies
8281 }
8282
8283 pub fn create_resolution_workflow(
8285 &self,
8286 conflict: ConflictReport,
8287 ) -> ConflictResolutionWorkflow {
8288 let severity = conflict.severity;
8289 let escalation_level = match severity {
8290 Severity::Info => EscalationLevel::Routine,
8291 Severity::Warning => EscalationLevel::Elevated,
8292 Severity::Error => EscalationLevel::High,
8293 Severity::Critical => EscalationLevel::Critical,
8294 };
8295
8296 let now = chrono::Utc::now().to_rfc3339();
8297
8298 ConflictResolutionWorkflow {
8299 id: format!("workflow-{}", uuid::Uuid::new_v4()),
8300 conflict,
8301 state: ResolutionWorkflowState::InitialAssessment,
8302 proposed_resolution: None,
8303 stakeholder_reviews: Vec::new(),
8304 expert_consultations: Vec::new(),
8305 final_decision: None,
8306 created_at: now.clone(),
8307 updated_at: now,
8308 escalation_level,
8309 }
8310 }
8311
8312 pub fn add_template(&mut self, template: NegotiatedResolutionTemplate) {
8314 self.templates.push(template);
8315 }
8316}
8317
8318impl Default for ConflictDetector {
8319 fn default() -> Self {
8320 Self::new()
8321 }
8322}
8323
8324#[derive(Debug, Clone, Serialize, Deserialize)]
8330pub struct LlmAdaptationSuggestion {
8331 pub id: String,
8333 pub statute_id: String,
8335 pub section: Option<String>,
8337 pub suggestion: String,
8339 pub rationale: String,
8341 pub confidence: f64,
8343 pub category: AdaptationCategory,
8345 pub source_context: Vec<String>,
8347 pub target_context: Vec<String>,
8349 pub alternatives: Vec<String>,
8351 pub risks: Vec<String>,
8353 pub legal_references: Vec<String>,
8355}
8356
8357#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8359pub enum AdaptationCategory {
8360 Terminology,
8362 Procedural,
8364 Cultural,
8366 Numerical,
8368 Structural,
8370 LegalPrinciple,
8372 Compliance,
8374}
8375
8376#[derive(Debug, Clone, Serialize, Deserialize)]
8378pub struct SimilarStatute {
8379 pub statute: Statute,
8381 pub jurisdiction: String,
8383 pub similarity_score: f64,
8385 pub matching_features: Vec<MatchingFeature>,
8387 pub differences: Vec<String>,
8389 pub relevance: String,
8391}
8392
8393#[derive(Debug, Clone, Serialize, Deserialize)]
8395pub struct MatchingFeature {
8396 pub feature_type: FeatureType,
8398 pub description: String,
8400 pub strength: f64,
8402}
8403
8404#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8406pub enum FeatureType {
8407 LegalEffect,
8409 Structure,
8411 Terminology,
8413 Scope,
8415 Conditions,
8417 Remedies,
8419}
8420
8421#[derive(Debug, Clone, Serialize, Deserialize)]
8423pub struct GapAnalysis {
8424 pub id: String,
8426 pub source_statute_id: String,
8428 pub target_jurisdiction: String,
8430 pub gaps: Vec<Gap>,
8432 pub coverage_score: f64,
8434 pub assessment: String,
8436 pub recommendations: Vec<String>,
8438}
8439
8440#[derive(Debug, Clone, Serialize, Deserialize)]
8442pub struct Gap {
8443 pub gap_type: GapType,
8445 pub description: String,
8447 pub severity: Severity,
8449 pub missing_element: String,
8451 pub importance: String,
8453 pub solutions: Vec<String>,
8455}
8456
8457#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8459pub enum GapType {
8460 MissingConcept,
8462 MissingProcedure,
8464 MissingEnforcement,
8466 MissingSafeguard,
8468 InsufficientSpecificity,
8470 MissingCulturalElement,
8472}
8473
8474#[derive(Debug, Clone, Serialize, Deserialize)]
8476pub struct CulturalSensitivityAnalysis {
8477 pub id: String,
8479 pub statute_id: String,
8481 pub sensitivity_score: f64,
8483 pub issues: Vec<CulturalIssue>,
8485 pub safe_aspects: Vec<String>,
8487 pub assessment: String,
8489 pub required_consultations: Vec<String>,
8491}
8492
8493#[derive(Debug, Clone, Serialize, Deserialize)]
8495pub struct CulturalIssue {
8496 pub issue_type: CulturalIssueType,
8498 pub description: String,
8500 pub severity: Severity,
8502 pub affected_section: String,
8504 pub explanation: String,
8506 pub adaptations: Vec<String>,
8508 pub stakeholders_to_consult: Vec<String>,
8510}
8511
8512#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8514pub enum CulturalIssueType {
8515 Religious,
8517 Traditional,
8519 SocialNorm,
8521 Gender,
8523 Family,
8525 Language,
8527 Historical,
8529}
8530
8531#[derive(Debug, Clone, Serialize, Deserialize)]
8533pub struct PlainLanguageExplanation {
8534 pub id: String,
8536 pub statute_id: String,
8538 pub audience_level: AudienceLevel,
8540 pub summary: String,
8542 pub explanation: String,
8544 pub key_points: Vec<String>,
8546 pub examples: Vec<String>,
8548 pub faqs: Vec<FrequentlyAskedQuestion>,
8550 pub readability_score: f64,
8552}
8553
8554#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8556pub enum AudienceLevel {
8557 GeneralPublic,
8559 Business,
8561 Government,
8563 Legal,
8565 Academic,
8567}
8568
8569#[derive(Debug, Clone, Serialize, Deserialize)]
8571pub struct FrequentlyAskedQuestion {
8572 pub question: String,
8574 pub answer: String,
8576 pub related_topics: Vec<String>,
8578}
8579
8580#[derive(Clone)]
8582pub struct AiPortingAssistant {
8583 generator: Option<std::sync::Arc<dyn TextGenerator>>,
8585}
8586
8587impl AiPortingAssistant {
8588 pub fn new() -> Self {
8590 Self { generator: None }
8591 }
8592
8593 pub fn with_generator(generator: std::sync::Arc<dyn TextGenerator>) -> Self {
8595 Self {
8596 generator: Some(generator),
8597 }
8598 }
8599
8600 pub async fn generate_adaptation_suggestions(
8602 &self,
8603 statute: &Statute,
8604 source_jurisdiction: &Jurisdiction,
8605 target_jurisdiction: &Jurisdiction,
8606 ) -> PortingResult<Vec<LlmAdaptationSuggestion>> {
8607 let mut suggestions = Vec::new();
8608
8609 if let Some(generator) = &self.generator {
8611 let prompt = format!(
8612 "Analyze porting statute '{}' from {} to {}. \
8613 Source legal system: {:?}, Target legal system: {:?}. \
8614 Provide detailed adaptation suggestions considering legal, cultural, and procedural differences.",
8615 statute.title,
8616 source_jurisdiction.name,
8617 target_jurisdiction.name,
8618 source_jurisdiction.legal_system,
8619 target_jurisdiction.legal_system
8620 );
8621
8622 let response = generator
8623 .generate(&prompt)
8624 .await
8625 .map_err(PortingError::Llm)?;
8626
8627 suggestions.push(LlmAdaptationSuggestion {
8630 id: format!("llm-sugg-{}", uuid::Uuid::new_v4()),
8631 statute_id: statute.id.clone(),
8632 section: None,
8633 suggestion: response.clone(),
8634 rationale: "AI-generated analysis based on jurisdiction differences".to_string(),
8635 confidence: 0.75,
8636 category: AdaptationCategory::Cultural,
8637 source_context: vec![format!(
8638 "{:?} legal system",
8639 source_jurisdiction.legal_system
8640 )],
8641 target_context: vec![format!(
8642 "{:?} legal system",
8643 target_jurisdiction.legal_system
8644 )],
8645 alternatives: vec![],
8646 risks: vec![],
8647 legal_references: vec![],
8648 });
8649 } else {
8650 if source_jurisdiction.legal_system != target_jurisdiction.legal_system {
8652 suggestions.push(LlmAdaptationSuggestion {
8653 id: format!("rule-sugg-{}", uuid::Uuid::new_v4()),
8654 statute_id: statute.id.clone(),
8655 section: None,
8656 suggestion: format!(
8657 "Adapt procedural elements from {:?} to {:?} legal system",
8658 source_jurisdiction.legal_system, target_jurisdiction.legal_system
8659 ),
8660 rationale: "Legal system differences require procedural adaptation".to_string(),
8661 confidence: 0.8,
8662 category: AdaptationCategory::Procedural,
8663 source_context: vec![],
8664 target_context: vec![],
8665 alternatives: vec![],
8666 risks: vec!["May require expert legal review".to_string()],
8667 legal_references: vec![],
8668 });
8669 }
8670 }
8671
8672 Ok(suggestions)
8673 }
8674
8675 pub async fn discover_similar_statutes(
8677 &self,
8678 statute: &Statute,
8679 jurisdictions: &[Jurisdiction],
8680 ) -> PortingResult<Vec<SimilarStatute>> {
8681 let mut similar = Vec::new();
8682
8683 for jurisdiction in jurisdictions {
8686 let similarity_score = self.calculate_similarity(statute, jurisdiction);
8687
8688 if similarity_score > 0.3 {
8689 similar.push(SimilarStatute {
8690 statute: statute.clone(),
8691 jurisdiction: jurisdiction.id.clone(),
8692 similarity_score,
8693 matching_features: vec![MatchingFeature {
8694 feature_type: FeatureType::Terminology,
8695 description: "Similar legal terminology".to_string(),
8696 strength: similarity_score,
8697 }],
8698 differences: vec![],
8699 relevance: format!(
8700 "Found in {} legal system",
8701 match jurisdiction.legal_system {
8702 LegalSystem::CommonLaw => "common law",
8703 LegalSystem::CivilLaw => "civil law",
8704 _ => "other",
8705 }
8706 ),
8707 });
8708 }
8709 }
8710
8711 similar.sort_by(|a, b| b.similarity_score.partial_cmp(&a.similarity_score).unwrap());
8713
8714 Ok(similar)
8715 }
8716
8717 pub async fn analyze_gaps(
8719 &self,
8720 statute: &Statute,
8721 source_jurisdiction: &Jurisdiction,
8722 target_jurisdiction: &Jurisdiction,
8723 ) -> PortingResult<GapAnalysis> {
8724 let mut gaps = Vec::new();
8725
8726 gaps.push(Gap {
8728 gap_type: GapType::MissingEnforcement,
8729 description: "Verify enforcement mechanisms exist in target jurisdiction".to_string(),
8730 severity: Severity::Warning,
8731 missing_element: "Enforcement authority".to_string(),
8732 importance: "Required for effective statute implementation".to_string(),
8733 solutions: vec![
8734 "Identify equivalent enforcement body in target jurisdiction".to_string(),
8735 "Establish new enforcement mechanism if needed".to_string(),
8736 ],
8737 });
8738
8739 if source_jurisdiction.cultural_params.prohibitions
8741 != target_jurisdiction.cultural_params.prohibitions
8742 {
8743 gaps.push(Gap {
8744 gap_type: GapType::MissingCulturalElement,
8745 description: "Cultural prohibition differences detected".to_string(),
8746 severity: Severity::Info,
8747 missing_element: "Cultural context alignment".to_string(),
8748 importance: "Ensures cultural appropriateness".to_string(),
8749 solutions: vec![
8750 "Consult with cultural advisors".to_string(),
8751 "Adapt language and examples".to_string(),
8752 ],
8753 });
8754 }
8755
8756 let coverage_score = 1.0 - (gaps.len() as f64 * 0.1).min(0.7);
8757
8758 Ok(GapAnalysis {
8759 id: format!("gap-{}", uuid::Uuid::new_v4()),
8760 source_statute_id: statute.id.clone(),
8761 target_jurisdiction: target_jurisdiction.id.clone(),
8762 gaps,
8763 coverage_score,
8764 assessment: if coverage_score > 0.7 {
8765 "Good coverage with minor gaps".to_string()
8766 } else {
8767 "Significant gaps require attention".to_string()
8768 },
8769 recommendations: vec![
8770 "Address identified gaps before implementation".to_string(),
8771 "Conduct stakeholder review".to_string(),
8772 ],
8773 })
8774 }
8775
8776 pub async fn check_cultural_sensitivity(
8778 &self,
8779 statute: &Statute,
8780 target_jurisdiction: &Jurisdiction,
8781 ) -> PortingResult<CulturalSensitivityAnalysis> {
8782 let mut issues = Vec::new();
8783
8784 for prohibition in &target_jurisdiction.cultural_params.prohibitions {
8786 issues.push(CulturalIssue {
8787 issue_type: CulturalIssueType::Religious,
8788 description: format!("Review for compliance with: {}", prohibition),
8789 severity: Severity::Warning,
8790 affected_section: "General".to_string(),
8791 explanation: "Cultural/religious prohibition may affect statute applicability"
8792 .to_string(),
8793 adaptations: vec![
8794 "Add exception clause if appropriate".to_string(),
8795 "Adjust language to respect cultural norms".to_string(),
8796 ],
8797 stakeholders_to_consult: vec![
8798 "Cultural affairs ministry".to_string(),
8799 "Religious leaders".to_string(),
8800 ],
8801 });
8802 }
8803
8804 let sensitivity_score = if issues.is_empty() {
8805 0.1 } else {
8807 0.5 + (issues.len() as f64 * 0.1).min(0.4)
8808 };
8809
8810 Ok(CulturalSensitivityAnalysis {
8811 id: format!("cultural-{}", uuid::Uuid::new_v4()),
8812 statute_id: statute.id.clone(),
8813 sensitivity_score,
8814 issues,
8815 safe_aspects: vec!["Legal framework structure".to_string()],
8816 assessment: if sensitivity_score < 0.3 {
8817 "Low cultural sensitivity concerns".to_string()
8818 } else if sensitivity_score < 0.7 {
8819 "Moderate cultural considerations needed".to_string()
8820 } else {
8821 "High cultural sensitivity - extensive consultation required".to_string()
8822 },
8823 required_consultations: vec!["Cultural advisors".to_string()],
8824 })
8825 }
8826
8827 pub async fn generate_plain_explanation(
8829 &self,
8830 statute: &Statute,
8831 audience_level: AudienceLevel,
8832 ) -> PortingResult<PlainLanguageExplanation> {
8833 let summary = match audience_level {
8834 AudienceLevel::GeneralPublic => {
8835 format!(
8836 "This law '{}' provides certain legal rights and responsibilities.",
8837 statute.title
8838 )
8839 }
8840 AudienceLevel::Business => {
8841 format!(
8842 "'{}' establishes legal framework affecting business operations.",
8843 statute.title
8844 )
8845 }
8846 AudienceLevel::Government => {
8847 format!(
8848 "'{}' defines statutory requirements for government implementation.",
8849 statute.title
8850 )
8851 }
8852 AudienceLevel::Legal => {
8853 format!(
8854 "Statute '{}' with effect: {:?}",
8855 statute.title, statute.effect.effect_type
8856 )
8857 }
8858 AudienceLevel::Academic => {
8859 format!(
8860 "Legal statute '{}' for academic analysis and research.",
8861 statute.title
8862 )
8863 }
8864 };
8865
8866 let explanation = format!(
8867 "The statute titled '{}' establishes legal provisions in its jurisdiction. \
8868 It has been analyzed for potential porting to other legal systems.",
8869 statute.title
8870 );
8871
8872 Ok(PlainLanguageExplanation {
8873 id: format!("explain-{}", uuid::Uuid::new_v4()),
8874 statute_id: statute.id.clone(),
8875 audience_level,
8876 summary,
8877 explanation,
8878 key_points: vec![
8879 "Defines legal rights and obligations".to_string(),
8880 "Subject to jurisdictional requirements".to_string(),
8881 "May require adaptation for different legal systems".to_string(),
8882 ],
8883 examples: vec!["Example: Implementation in similar jurisdictions".to_string()],
8884 faqs: vec![FrequentlyAskedQuestion {
8885 question: "What does this statute cover?".to_string(),
8886 answer: "It establishes legal framework for specific matters.".to_string(),
8887 related_topics: vec!["Legal compliance".to_string()],
8888 }],
8889 readability_score: 0.8,
8890 })
8891 }
8892
8893 fn calculate_similarity(&self, _statute: &Statute, _jurisdiction: &Jurisdiction) -> f64 {
8895 0.5
8898 }
8899}
8900
8901impl Default for AiPortingAssistant {
8902 fn default() -> Self {
8903 Self::new()
8904 }
8905}
8906
8907#[derive(Debug, Clone, Serialize, Deserialize)]
8913pub struct TargetJurisdictionComplianceCheck {
8914 pub id: String,
8916 pub is_compliant: bool,
8918 pub compliance_score: f64,
8920 pub issues: Vec<ValidationComplianceIssue>,
8922 pub recommendations: Vec<String>,
8924 pub checked_regulations: Vec<String>,
8926}
8927
8928#[derive(Debug, Clone, Serialize, Deserialize)]
8930pub struct ValidationComplianceIssue {
8931 pub id: String,
8933 pub severity: ComplianceSeverity,
8935 pub category: ComplianceCategory,
8937 pub description: String,
8939 pub conflicting_regulation: String,
8941 pub suggested_resolution: Option<String>,
8943}
8944
8945#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8947pub enum ComplianceSeverity {
8948 Critical,
8950 High,
8952 Medium,
8954 Low,
8956 Info,
8958}
8959
8960#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8962pub enum ComplianceCategory {
8963 Constitutional,
8965 Regulatory,
8967 Procedural,
8969 Cultural,
8971 Technical,
8973 Administrative,
8975}
8976
8977#[derive(Debug, Clone)]
8979pub struct TargetJurisdictionChecker {
8980 #[allow(dead_code)]
8982 target_jurisdiction: Jurisdiction,
8983 regulations: HashMap<String, RegulationEntry>,
8985}
8986
8987#[derive(Debug, Clone, Serialize, Deserialize)]
8989pub struct RegulationEntry {
8990 pub id: String,
8992 pub title: String,
8994 pub authority: String,
8996 pub scope: Vec<String>,
8998 pub requirements: Vec<String>,
9000}
9001
9002impl TargetJurisdictionChecker {
9003 pub fn new(target_jurisdiction: Jurisdiction) -> Self {
9005 let mut regulations = HashMap::new();
9006
9007 match target_jurisdiction.id.as_str() {
9009 "US" => {
9010 regulations.insert(
9011 "cfr-title-5".to_string(),
9012 RegulationEntry {
9013 id: "cfr-title-5".to_string(),
9014 title: "Code of Federal Regulations - Administrative Procedures"
9015 .to_string(),
9016 authority: "Federal Government".to_string(),
9017 scope: vec!["administrative".to_string(), "procedural".to_string()],
9018 requirements: vec![
9019 "Public comment period".to_string(),
9020 "Notice of rulemaking".to_string(),
9021 ],
9022 },
9023 );
9024 }
9025 "JP" => {
9026 regulations.insert(
9027 "gyosei-tetsuzuki".to_string(),
9028 RegulationEntry {
9029 id: "gyosei-tetsuzuki".to_string(),
9030 title: "行政手続法 (Administrative Procedure Act)".to_string(),
9031 authority: "国会 (Diet)".to_string(),
9032 scope: vec!["administrative".to_string(), "procedural".to_string()],
9033 requirements: vec![
9034 "意見公募 (Public comment)".to_string(),
9035 "理由の提示 (Reason disclosure)".to_string(),
9036 ],
9037 },
9038 );
9039 }
9040 _ => {}
9041 }
9042
9043 Self {
9044 target_jurisdiction,
9045 regulations,
9046 }
9047 }
9048
9049 pub fn check_compliance(&self, statute: &Statute) -> TargetJurisdictionComplianceCheck {
9051 let mut issues = Vec::new();
9052 let mut checked_regulations = Vec::new();
9053
9054 for (reg_id, regulation) in &self.regulations {
9056 checked_regulations.push(regulation.title.clone());
9057
9058 if self.has_scope_overlap(statute, regulation) {
9060 for requirement in ®ulation.requirements {
9062 if !self.meets_requirement(statute, requirement) {
9063 issues.push(ValidationComplianceIssue {
9064 id: uuid::Uuid::new_v4().to_string(),
9065 severity: ComplianceSeverity::Medium,
9066 category: ComplianceCategory::Regulatory,
9067 description: format!("Does not meet requirement: {}", requirement),
9068 conflicting_regulation: reg_id.clone(),
9069 suggested_resolution: Some(format!(
9070 "Add provisions for {}",
9071 requirement
9072 )),
9073 });
9074 }
9075 }
9076 }
9077 }
9078
9079 let compliance_score = if issues.is_empty() {
9080 1.0
9081 } else {
9082 let critical_count = issues
9083 .iter()
9084 .filter(|i| i.severity == ComplianceSeverity::Critical)
9085 .count();
9086 let high_count = issues
9087 .iter()
9088 .filter(|i| i.severity == ComplianceSeverity::High)
9089 .count();
9090
9091 if critical_count > 0 {
9092 0.0
9093 } else if high_count > 0 {
9094 0.5
9095 } else {
9096 0.8
9097 }
9098 };
9099
9100 TargetJurisdictionComplianceCheck {
9101 id: uuid::Uuid::new_v4().to_string(),
9102 is_compliant: issues
9103 .iter()
9104 .all(|i| i.severity != ComplianceSeverity::Critical),
9105 compliance_score,
9106 issues,
9107 recommendations: vec![
9108 "Review all identified compliance issues".to_string(),
9109 "Consult with local legal experts".to_string(),
9110 ],
9111 checked_regulations,
9112 }
9113 }
9114
9115 fn has_scope_overlap(&self, _statute: &Statute, _regulation: &RegulationEntry) -> bool {
9116 true
9118 }
9119
9120 fn meets_requirement(&self, _statute: &Statute, _requirement: &str) -> bool {
9121 false
9123 }
9124}
9125
9126#[derive(Debug, Clone, Serialize, Deserialize)]
9128pub struct ConstitutionalAnalysis {
9129 pub id: String,
9131 pub is_compatible: bool,
9133 pub compatibility_score: f64,
9135 pub issues: Vec<ConstitutionalIssue>,
9137 pub relevant_provisions: Vec<String>,
9139 pub recommended_amendments: Vec<String>,
9141}
9142
9143#[derive(Debug, Clone, Serialize, Deserialize)]
9145pub struct ConstitutionalIssue {
9146 pub id: String,
9148 pub issue_type: ConstitutionalIssueType,
9150 pub description: String,
9152 pub conflicting_provision: String,
9154 pub severity: ComplianceSeverity,
9156 pub suggested_remedy: Option<String>,
9158}
9159
9160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9162pub enum ConstitutionalIssueType {
9163 FundamentalRights,
9165 LegislativeAuthority,
9167 SeparationOfPowers,
9169 Federalism,
9171 DueProcess,
9173 EqualProtection,
9175}
9176
9177#[derive(Debug, Clone)]
9179pub struct ConstitutionalAnalyzer {
9180 #[allow(dead_code)]
9182 target_jurisdiction: Jurisdiction,
9183 provisions: HashMap<String, ConstitutionalProvision>,
9185}
9186
9187#[derive(Debug, Clone, Serialize, Deserialize)]
9189pub struct ConstitutionalProvision {
9190 pub reference: String,
9192 pub text: String,
9194 pub category: ConstitutionalIssueType,
9196}
9197
9198impl ConstitutionalAnalyzer {
9199 pub fn new(target_jurisdiction: Jurisdiction) -> Self {
9201 let mut provisions = HashMap::new();
9202
9203 match target_jurisdiction.id.as_str() {
9205 "US" => {
9206 provisions.insert(
9207 "amend-1".to_string(),
9208 ConstitutionalProvision {
9209 reference: "First Amendment".to_string(),
9210 text: "Freedom of speech, religion, press, assembly".to_string(),
9211 category: ConstitutionalIssueType::FundamentalRights,
9212 },
9213 );
9214 provisions.insert(
9215 "amend-14".to_string(),
9216 ConstitutionalProvision {
9217 reference: "Fourteenth Amendment".to_string(),
9218 text: "Equal protection and due process".to_string(),
9219 category: ConstitutionalIssueType::EqualProtection,
9220 },
9221 );
9222 }
9223 "JP" => {
9224 provisions.insert(
9225 "art-14".to_string(),
9226 ConstitutionalProvision {
9227 reference: "憲法第14条 (Article 14)".to_string(),
9228 text: "法の下の平等 (Equality under the law)".to_string(),
9229 category: ConstitutionalIssueType::EqualProtection,
9230 },
9231 );
9232 provisions.insert(
9233 "art-21".to_string(),
9234 ConstitutionalProvision {
9235 reference: "憲法第21条 (Article 21)".to_string(),
9236 text: "表現の自由 (Freedom of expression)".to_string(),
9237 category: ConstitutionalIssueType::FundamentalRights,
9238 },
9239 );
9240 }
9241 _ => {}
9242 }
9243
9244 Self {
9245 target_jurisdiction,
9246 provisions,
9247 }
9248 }
9249
9250 pub fn analyze(&self, statute: &Statute) -> ConstitutionalAnalysis {
9252 let mut issues = Vec::new();
9253 let mut relevant_provisions = Vec::new();
9254
9255 for provision in self.provisions.values() {
9257 relevant_provisions.push(provision.reference.clone());
9258
9259 if self.may_conflict(statute, provision) {
9261 issues.push(ConstitutionalIssue {
9262 id: uuid::Uuid::new_v4().to_string(),
9263 issue_type: provision.category,
9264 description: format!("Potential conflict with {}", provision.reference),
9265 conflicting_provision: provision.reference.clone(),
9266 severity: ComplianceSeverity::High,
9267 suggested_remedy: Some(
9268 "Review and modify to ensure constitutional compliance".to_string(),
9269 ),
9270 });
9271 }
9272 }
9273
9274 let compatibility_score = if issues.is_empty() {
9275 1.0
9276 } else {
9277 let critical_count = issues
9278 .iter()
9279 .filter(|i| i.severity == ComplianceSeverity::Critical)
9280 .count();
9281 if critical_count > 0 { 0.0 } else { 0.6 }
9282 };
9283
9284 ConstitutionalAnalysis {
9285 id: uuid::Uuid::new_v4().to_string(),
9286 is_compatible: issues
9287 .iter()
9288 .all(|i| i.severity != ComplianceSeverity::Critical),
9289 compatibility_score,
9290 issues,
9291 relevant_provisions,
9292 recommended_amendments: vec![
9293 "Consult constitutional law experts".to_string(),
9294 "Consider judicial review".to_string(),
9295 ],
9296 }
9297 }
9298
9299 fn may_conflict(&self, _statute: &Statute, _provision: &ConstitutionalProvision) -> bool {
9300 false
9302 }
9303}
9304
9305#[derive(Debug, Clone, Serialize, Deserialize)]
9307pub struct TreatyComplianceResult {
9308 pub id: String,
9310 pub is_compliant: bool,
9312 pub compliance_score: f64,
9314 pub conflicts: Vec<TreatyConflict>,
9316 pub checked_treaties: Vec<String>,
9318 pub recommendations: Vec<String>,
9320}
9321
9322#[derive(Debug, Clone, Serialize, Deserialize)]
9324pub struct TreatyConflict {
9325 pub id: String,
9327 pub treaty_name: String,
9329 pub provision: String,
9331 pub description: String,
9333 pub severity: ComplianceSeverity,
9335 pub suggested_resolution: Option<String>,
9337}
9338
9339#[derive(Debug, Clone)]
9341pub struct TreatyTargetJurisdictionChecker {
9342 #[allow(dead_code)]
9344 target_jurisdiction: Jurisdiction,
9345 treaties: HashMap<String, TreatyEntry>,
9347}
9348
9349#[derive(Debug, Clone, Serialize, Deserialize)]
9351pub struct TreatyEntry {
9352 pub id: String,
9354 pub name: String,
9356 pub ratified: bool,
9358 pub obligations: Vec<String>,
9360 pub prohibitions: Vec<String>,
9362}
9363
9364impl TreatyTargetJurisdictionChecker {
9365 pub fn new(target_jurisdiction: Jurisdiction) -> Self {
9367 let mut treaties = HashMap::new();
9368
9369 treaties.insert(
9371 "iccpr".to_string(),
9372 TreatyEntry {
9373 id: "iccpr".to_string(),
9374 name: "International Covenant on Civil and Political Rights".to_string(),
9375 ratified: true,
9376 obligations: vec![
9377 "Protect right to life".to_string(),
9378 "Ensure fair trial".to_string(),
9379 "Freedom of expression".to_string(),
9380 ],
9381 prohibitions: vec!["Torture".to_string(), "Arbitrary detention".to_string()],
9382 },
9383 );
9384
9385 treaties.insert(
9386 "icescr".to_string(),
9387 TreatyEntry {
9388 id: "icescr".to_string(),
9389 name: "International Covenant on Economic, Social and Cultural Rights".to_string(),
9390 ratified: true,
9391 obligations: vec![
9392 "Right to work".to_string(),
9393 "Right to education".to_string(),
9394 "Right to health".to_string(),
9395 ],
9396 prohibitions: vec![],
9397 },
9398 );
9399
9400 Self {
9401 target_jurisdiction,
9402 treaties,
9403 }
9404 }
9405
9406 pub fn check_compliance(&self, statute: &Statute) -> TreatyComplianceResult {
9408 let mut conflicts = Vec::new();
9409 let mut checked_treaties = Vec::new();
9410
9411 for treaty in self.treaties.values() {
9412 if !treaty.ratified {
9413 continue;
9414 }
9415
9416 checked_treaties.push(treaty.name.clone());
9417
9418 for prohibition in &treaty.prohibitions {
9420 if self.may_violate_prohibition(statute, prohibition) {
9421 conflicts.push(TreatyConflict {
9422 id: uuid::Uuid::new_v4().to_string(),
9423 treaty_name: treaty.name.clone(),
9424 provision: prohibition.clone(),
9425 description: format!("May violate prohibition on {}", prohibition),
9426 severity: ComplianceSeverity::Critical,
9427 suggested_resolution: Some(
9428 "Remove provisions that violate treaty prohibition".to_string(),
9429 ),
9430 });
9431 }
9432 }
9433 }
9434
9435 let compliance_score = if conflicts.is_empty() {
9436 1.0
9437 } else {
9438 let critical_count = conflicts
9439 .iter()
9440 .filter(|c| c.severity == ComplianceSeverity::Critical)
9441 .count();
9442 if critical_count > 0 { 0.0 } else { 0.7 }
9443 };
9444
9445 TreatyComplianceResult {
9446 id: uuid::Uuid::new_v4().to_string(),
9447 is_compliant: conflicts
9448 .iter()
9449 .all(|c| c.severity != ComplianceSeverity::Critical),
9450 compliance_score,
9451 conflicts,
9452 checked_treaties,
9453 recommendations: vec![
9454 "Review all applicable international treaties".to_string(),
9455 "Ensure compliance with treaty obligations".to_string(),
9456 ],
9457 }
9458 }
9459
9460 fn may_violate_prohibition(&self, _statute: &Statute, _prohibition: &str) -> bool {
9461 false
9463 }
9464}
9465
9466#[derive(Debug, Clone, Serialize, Deserialize)]
9468pub struct HumanRightsAssessment {
9469 pub id: String,
9471 pub impact_score: f64,
9473 pub affected_rights: Vec<AffectedRight>,
9475 pub vulnerable_groups: Vec<VulnerableGroupImpact>,
9477 pub mitigation_measures: Vec<String>,
9479 pub summary: String,
9481}
9482
9483#[derive(Debug, Clone, Serialize, Deserialize)]
9485pub struct AffectedRight {
9486 pub right: String,
9488 pub impact: RightImpactType,
9490 pub severity: ImpactSeverity,
9492 pub description: String,
9494}
9495
9496#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9498pub enum RightImpactType {
9499 Enhancement,
9501 Neutral,
9503 Restriction,
9505 Violation,
9507}
9508
9509#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9511pub enum ImpactSeverity {
9512 Severe,
9514 Moderate,
9516 Minor,
9518 Negligible,
9520}
9521
9522#[derive(Debug, Clone, Serialize, Deserialize)]
9524pub struct VulnerableGroupImpact {
9525 pub group: String,
9527 pub impact: String,
9529 pub severity: ImpactSeverity,
9531 pub recommended_protections: Vec<String>,
9533}
9534
9535#[derive(Debug, Clone)]
9537pub struct HumanRightsAssessor {
9538 #[allow(dead_code)]
9540 target_jurisdiction: Jurisdiction,
9541}
9542
9543impl HumanRightsAssessor {
9544 pub fn new(target_jurisdiction: Jurisdiction) -> Self {
9546 Self {
9547 target_jurisdiction,
9548 }
9549 }
9550
9551 pub fn assess(&self, statute: &Statute) -> HumanRightsAssessment {
9553 let mut affected_rights = Vec::new();
9554 let mut vulnerable_groups = Vec::new();
9555
9556 let rights_to_check = vec![
9558 "Right to equality",
9559 "Right to privacy",
9560 "Freedom of expression",
9561 "Right to fair trial",
9562 ];
9563
9564 for right in rights_to_check {
9565 let impact = self.assess_right_impact(statute, right);
9566 if impact.impact != RightImpactType::Neutral {
9567 affected_rights.push(impact);
9568 }
9569 }
9570
9571 let groups_to_check = vec![
9573 "Children",
9574 "Elderly",
9575 "Persons with disabilities",
9576 "Minorities",
9577 ];
9578
9579 for group in groups_to_check {
9580 if let Some(impact) = self.assess_group_impact(statute, group) {
9581 vulnerable_groups.push(impact);
9582 }
9583 }
9584
9585 let impact_score = self.calculate_impact_score(&affected_rights);
9587
9588 HumanRightsAssessment {
9589 id: uuid::Uuid::new_v4().to_string(),
9590 impact_score,
9591 affected_rights,
9592 vulnerable_groups,
9593 mitigation_measures: vec![
9594 "Include non-discrimination clauses".to_string(),
9595 "Add safeguards for vulnerable groups".to_string(),
9596 "Ensure proportionality of restrictions".to_string(),
9597 ],
9598 summary: if impact_score >= 0.0 {
9599 "Statute has positive or neutral human rights impact".to_string()
9600 } else {
9601 "Statute may negatively impact human rights - review recommended".to_string()
9602 },
9603 }
9604 }
9605
9606 fn assess_right_impact(&self, _statute: &Statute, right: &str) -> AffectedRight {
9607 AffectedRight {
9609 right: right.to_string(),
9610 impact: RightImpactType::Neutral,
9611 severity: ImpactSeverity::Negligible,
9612 description: format!("No significant impact on {}", right),
9613 }
9614 }
9615
9616 fn assess_group_impact(
9617 &self,
9618 _statute: &Statute,
9619 _group: &str,
9620 ) -> Option<VulnerableGroupImpact> {
9621 None
9623 }
9624
9625 fn calculate_impact_score(&self, affected_rights: &[AffectedRight]) -> f64 {
9626 if affected_rights.is_empty() {
9627 return 0.0;
9628 }
9629
9630 let mut total_score = 0.0;
9631 for right in affected_rights {
9632 let score = match right.impact {
9633 RightImpactType::Enhancement => 1.0,
9634 RightImpactType::Neutral => 0.0,
9635 RightImpactType::Restriction => -0.5,
9636 RightImpactType::Violation => -1.0,
9637 };
9638 total_score += score;
9639 }
9640
9641 total_score / affected_rights.len() as f64
9642 }
9643}
9644
9645#[derive(Debug, Clone, Serialize, Deserialize)]
9647pub struct EnforceabilityPrediction {
9648 pub id: String,
9650 pub is_enforceable: bool,
9652 pub enforceability_score: f64,
9654 pub challenges: Vec<EnforcementChallenge>,
9656 pub required_mechanisms: Vec<String>,
9658 pub estimated_cost: Option<f64>,
9660 pub recommendations: Vec<String>,
9662}
9663
9664#[derive(Debug, Clone, Serialize, Deserialize)]
9666pub struct EnforcementChallenge {
9667 pub id: String,
9669 pub challenge_type: EnforcementChallengeType,
9671 pub description: String,
9673 pub severity: ImpactSeverity,
9675 pub suggested_solution: Option<String>,
9677}
9678
9679#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9681pub enum EnforcementChallengeType {
9682 Authority,
9684 Resources,
9686 Technical,
9688 Cultural,
9690 Administrative,
9692 Monitoring,
9694}
9695
9696#[derive(Debug, Clone)]
9698pub struct EnforceabilityPredictor {
9699 #[allow(dead_code)]
9701 target_jurisdiction: Jurisdiction,
9702}
9703
9704impl EnforceabilityPredictor {
9705 pub fn new(target_jurisdiction: Jurisdiction) -> Self {
9707 Self {
9708 target_jurisdiction,
9709 }
9710 }
9711
9712 pub fn predict(&self, statute: &Statute) -> EnforceabilityPrediction {
9714 let mut challenges = Vec::new();
9715 let mut required_mechanisms = Vec::new();
9716
9717 if self.lacks_enforcement_authority(statute) {
9719 challenges.push(EnforcementChallenge {
9720 id: uuid::Uuid::new_v4().to_string(),
9721 challenge_type: EnforcementChallengeType::Authority,
9722 description: "Lacks clear enforcement authority".to_string(),
9723 severity: ImpactSeverity::Severe,
9724 suggested_solution: Some(
9725 "Designate enforcement agency and grant necessary authority".to_string(),
9726 ),
9727 });
9728 }
9729
9730 if self.requires_significant_resources(statute) {
9731 challenges.push(EnforcementChallenge {
9732 id: uuid::Uuid::new_v4().to_string(),
9733 challenge_type: EnforcementChallengeType::Resources,
9734 description: "Requires significant enforcement resources".to_string(),
9735 severity: ImpactSeverity::Moderate,
9736 suggested_solution: Some(
9737 "Allocate budget for enforcement infrastructure".to_string(),
9738 ),
9739 });
9740 }
9741
9742 required_mechanisms.extend(vec![
9744 "Enforcement agency designation".to_string(),
9745 "Penalty structure".to_string(),
9746 "Monitoring system".to_string(),
9747 "Reporting requirements".to_string(),
9748 ]);
9749
9750 let enforceability_score = if challenges.is_empty() {
9751 0.9
9752 } else {
9753 let severe_count = challenges
9754 .iter()
9755 .filter(|c| c.severity == ImpactSeverity::Severe)
9756 .count();
9757 if severe_count > 0 { 0.3 } else { 0.6 }
9758 };
9759
9760 EnforceabilityPrediction {
9761 id: uuid::Uuid::new_v4().to_string(),
9762 is_enforceable: enforceability_score >= 0.5,
9763 enforceability_score,
9764 challenges,
9765 required_mechanisms,
9766 estimated_cost: Some(100000.0), recommendations: vec![
9768 "Establish clear enforcement procedures".to_string(),
9769 "Allocate adequate resources".to_string(),
9770 "Train enforcement personnel".to_string(),
9771 ],
9772 }
9773 }
9774
9775 fn lacks_enforcement_authority(&self, _statute: &Statute) -> bool {
9776 false
9778 }
9779
9780 fn requires_significant_resources(&self, _statute: &Statute) -> bool {
9781 true
9783 }
9784}
9785
9786#[derive(Debug, Clone)]
9788pub struct ValidationFramework {
9789 compliance_checker: TargetJurisdictionChecker,
9790 constitutional_analyzer: ConstitutionalAnalyzer,
9791 treaty_checker: TreatyTargetJurisdictionChecker,
9792 human_rights_assessor: HumanRightsAssessor,
9793 enforceability_predictor: EnforceabilityPredictor,
9794}
9795
9796#[derive(Debug, Clone, Serialize, Deserialize)]
9798pub struct ValidationResult {
9799 pub id: String,
9801 pub passed: bool,
9803 pub overall_score: f64,
9805 pub compliance: TargetJurisdictionComplianceCheck,
9807 pub constitutional: ConstitutionalAnalysis,
9809 pub treaty_compliance: TreatyComplianceResult,
9811 pub human_rights: HumanRightsAssessment,
9813 pub enforceability: EnforceabilityPrediction,
9815 pub summary: String,
9817}
9818
9819impl ValidationFramework {
9820 pub fn new(target_jurisdiction: Jurisdiction) -> Self {
9822 Self {
9823 compliance_checker: TargetJurisdictionChecker::new(target_jurisdiction.clone()),
9824 constitutional_analyzer: ConstitutionalAnalyzer::new(target_jurisdiction.clone()),
9825 treaty_checker: TreatyTargetJurisdictionChecker::new(target_jurisdiction.clone()),
9826 human_rights_assessor: HumanRightsAssessor::new(target_jurisdiction.clone()),
9827 enforceability_predictor: EnforceabilityPredictor::new(target_jurisdiction),
9828 }
9829 }
9830
9831 pub fn validate(&self, statute: &Statute) -> ValidationResult {
9833 let compliance = self.compliance_checker.check_compliance(statute);
9834 let constitutional = self.constitutional_analyzer.analyze(statute);
9835 let treaty_compliance = self.treaty_checker.check_compliance(statute);
9836 let human_rights = self.human_rights_assessor.assess(statute);
9837 let enforceability = self.enforceability_predictor.predict(statute);
9838
9839 let overall_score = (
9841 compliance.compliance_score
9842 + constitutional.compatibility_score
9843 + treaty_compliance.compliance_score
9844 + enforceability.enforceability_score
9845 + (human_rights.impact_score + 1.0) / 2.0
9846 ) / 5.0;
9848
9849 let passed = compliance.is_compliant
9850 && constitutional.is_compatible
9851 && treaty_compliance.is_compliant
9852 && human_rights.impact_score >= 0.0
9853 && enforceability.is_enforceable;
9854
9855 let summary = if passed {
9856 format!("Validation passed with overall score {:.2}", overall_score)
9857 } else {
9858 format!(
9859 "Validation failed - review required (score: {:.2})",
9860 overall_score
9861 )
9862 };
9863
9864 ValidationResult {
9865 id: uuid::Uuid::new_v4().to_string(),
9866 passed,
9867 overall_score,
9868 compliance,
9869 constitutional,
9870 treaty_compliance,
9871 human_rights,
9872 enforceability,
9873 summary,
9874 }
9875 }
9876}
9877
9878#[derive(Debug, Clone, Serialize, Deserialize)]
9884pub struct FeasibilityAnalysis {
9885 pub id: String,
9887 pub is_feasible: bool,
9889 pub feasibility_score: f64,
9891 pub technical_feasibility: f64,
9893 pub legal_feasibility: f64,
9895 pub cultural_feasibility: f64,
9897 pub economic_feasibility: f64,
9899 pub political_feasibility: f64,
9901 pub factors: Vec<FeasibilityFactor>,
9903 pub risks: Vec<String>,
9905 pub prerequisites: Vec<String>,
9907 pub estimated_time_days: u32,
9909 pub estimated_cost_usd: f64,
9911 pub recommended_approach: String,
9913 pub alternatives: Vec<String>,
9915 pub recommendation: FeasibilityRecommendation,
9917 pub notes: Vec<String>,
9919}
9920
9921#[derive(Debug, Clone, Serialize, Deserialize)]
9923pub struct FeasibilityFactor {
9924 pub id: String,
9926 pub category: FeasibilityCategory,
9928 pub name: String,
9930 pub impact: f64,
9932 pub severity: FeasibilitySeverity,
9934 pub description: String,
9936 pub mitigation_strategies: Vec<String>,
9938}
9939
9940#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9942pub enum FeasibilityCategory {
9943 Technical,
9945 Legal,
9947 Cultural,
9949 Economic,
9951 Political,
9953 Administrative,
9955 Stakeholder,
9957 Resources,
9959}
9960
9961#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9963pub enum FeasibilitySeverity {
9964 Critical,
9966 Major,
9968 Moderate,
9970 Minor,
9972 Negligible,
9974}
9975
9976#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9978pub enum FeasibilityRecommendation {
9979 StronglyRecommended,
9981 Recommended,
9983 Conditional,
9985 NotRecommended,
9987 StronglyNotRecommended,
9989}
9990
9991#[derive(Debug, Clone)]
9993pub struct PrePortingFeasibilityAnalyzer {
9994 source_jurisdiction: Jurisdiction,
9996 target_jurisdiction: Jurisdiction,
9998 validation_framework: ValidationFramework,
10000}
10001
10002impl PrePortingFeasibilityAnalyzer {
10003 pub fn new(source_jurisdiction: Jurisdiction, target_jurisdiction: Jurisdiction) -> Self {
10005 Self {
10006 source_jurisdiction: source_jurisdiction.clone(),
10007 target_jurisdiction: target_jurisdiction.clone(),
10008 validation_framework: ValidationFramework::new(target_jurisdiction),
10009 }
10010 }
10011
10012 pub fn analyze(&self, statute: &Statute) -> FeasibilityAnalysis {
10014 let mut factors = Vec::new();
10015 let mut risks = Vec::new();
10016 let mut prerequisites = Vec::new();
10017 let mut notes = Vec::new();
10018
10019 let technical_feasibility =
10021 self.analyze_technical_feasibility(statute, &mut factors, &mut notes);
10022
10023 let validation_result = self.validation_framework.validate(statute);
10025 let legal_feasibility = validation_result.overall_score;
10026
10027 if !validation_result.passed {
10028 factors.push(FeasibilityFactor {
10029 id: uuid::Uuid::new_v4().to_string(),
10030 category: FeasibilityCategory::Legal,
10031 name: "Legal Validation Issues".to_string(),
10032 impact: -0.5,
10033 severity: FeasibilitySeverity::Major,
10034 description: validation_result.summary.clone(),
10035 mitigation_strategies: vec![
10036 "Address compliance issues before porting".to_string(),
10037 "Consult with legal experts".to_string(),
10038 ],
10039 });
10040 risks.push("Legal validation failed".to_string());
10041 }
10042
10043 let cultural_feasibility =
10045 self.analyze_cultural_feasibility(statute, &mut factors, &mut notes);
10046
10047 let economic_feasibility =
10049 self.analyze_economic_feasibility(statute, &mut factors, &mut notes);
10050
10051 let political_feasibility =
10053 self.analyze_political_feasibility(statute, &mut factors, &mut notes);
10054
10055 let feasibility_score = technical_feasibility * 0.2
10057 + legal_feasibility * 0.3
10058 + cultural_feasibility * 0.2
10059 + economic_feasibility * 0.15
10060 + political_feasibility * 0.15;
10061
10062 let is_feasible = feasibility_score >= 0.6 && legal_feasibility >= 0.5;
10064
10065 let recommendation = if feasibility_score >= 0.85 {
10067 FeasibilityRecommendation::StronglyRecommended
10068 } else if feasibility_score >= 0.7 {
10069 FeasibilityRecommendation::Recommended
10070 } else if feasibility_score >= 0.5 {
10071 FeasibilityRecommendation::Conditional
10072 } else if feasibility_score >= 0.3 {
10073 FeasibilityRecommendation::NotRecommended
10074 } else {
10075 FeasibilityRecommendation::StronglyNotRecommended
10076 };
10077
10078 prerequisites.extend(vec![
10080 "Secure stakeholder buy-in".to_string(),
10081 "Allocate necessary resources".to_string(),
10082 "Complete legal review".to_string(),
10083 ]);
10084
10085 if cultural_feasibility < 0.7 {
10086 prerequisites.push("Conduct cultural impact assessment".to_string());
10087 }
10088
10089 let complexity_factor = 1.0 + (1.0 - feasibility_score);
10091 let estimated_time_days = (30.0 * complexity_factor) as u32;
10092 let estimated_cost_usd = 50000.0 * complexity_factor;
10093
10094 let recommended_approach = if is_feasible {
10096 "Proceed with phased approach: (1) Legal review, (2) Cultural adaptation, (3) Stakeholder engagement, (4) Pilot implementation".to_string()
10097 } else {
10098 format!(
10099 "Address critical issues before proceeding: focus on improving {} feasibility",
10100 self.identify_weakest_area(
10101 technical_feasibility,
10102 legal_feasibility,
10103 cultural_feasibility,
10104 economic_feasibility,
10105 political_feasibility
10106 )
10107 )
10108 };
10109
10110 let alternatives = vec![
10112 "Partial porting of compatible sections only".to_string(),
10113 "Phased implementation with pilot programs".to_string(),
10114 "Create hybrid approach combining elements from both jurisdictions".to_string(),
10115 ];
10116
10117 FeasibilityAnalysis {
10118 id: uuid::Uuid::new_v4().to_string(),
10119 is_feasible,
10120 feasibility_score,
10121 technical_feasibility,
10122 legal_feasibility,
10123 cultural_feasibility,
10124 economic_feasibility,
10125 political_feasibility,
10126 factors,
10127 risks,
10128 prerequisites,
10129 estimated_time_days,
10130 estimated_cost_usd,
10131 recommended_approach,
10132 alternatives,
10133 recommendation,
10134 notes,
10135 }
10136 }
10137
10138 fn analyze_technical_feasibility(
10139 &self,
10140 _statute: &Statute,
10141 factors: &mut Vec<FeasibilityFactor>,
10142 notes: &mut Vec<String>,
10143 ) -> f64 {
10144 let mut score: f64 = 0.8; if self.source_jurisdiction.legal_system == self.target_jurisdiction.legal_system {
10148 factors.push(FeasibilityFactor {
10149 id: uuid::Uuid::new_v4().to_string(),
10150 category: FeasibilityCategory::Technical,
10151 name: "Legal System Compatibility".to_string(),
10152 impact: 0.3,
10153 severity: FeasibilitySeverity::Minor,
10154 description: "Same legal system family facilitates porting".to_string(),
10155 mitigation_strategies: vec![],
10156 });
10157 score += 0.1;
10158 notes.push("Legal systems are compatible".to_string());
10159 } else {
10160 factors.push(FeasibilityFactor {
10161 id: uuid::Uuid::new_v4().to_string(),
10162 category: FeasibilityCategory::Technical,
10163 name: "Legal System Incompatibility".to_string(),
10164 impact: -0.2,
10165 severity: FeasibilitySeverity::Moderate,
10166 description: "Different legal systems require adaptation".to_string(),
10167 mitigation_strategies: vec![
10168 "Engage experts in both legal systems".to_string(),
10169 "Identify structural differences early".to_string(),
10170 ],
10171 });
10172 score -= 0.1;
10173 notes.push("Legal systems differ - requires careful adaptation".to_string());
10174 }
10175
10176 score.clamp(0.0, 1.0)
10177 }
10178
10179 fn analyze_cultural_feasibility(
10180 &self,
10181 _statute: &Statute,
10182 factors: &mut Vec<FeasibilityFactor>,
10183 notes: &mut Vec<String>,
10184 ) -> f64 {
10185 let mut score: f64 = 0.7;
10186
10187 if self.source_jurisdiction.id == self.target_jurisdiction.id {
10189 return 1.0;
10190 }
10191
10192 let source_params = &self.source_jurisdiction.cultural_params;
10194 let target_params = &self.target_jurisdiction.cultural_params;
10195
10196 if source_params.age_of_majority != target_params.age_of_majority {
10198 factors.push(FeasibilityFactor {
10199 id: uuid::Uuid::new_v4().to_string(),
10200 category: FeasibilityCategory::Cultural,
10201 name: "Age of Majority Difference".to_string(),
10202 impact: -0.1,
10203 severity: FeasibilitySeverity::Minor,
10204 description: format!(
10205 "Age of majority differs: {:?} vs {:?}",
10206 source_params.age_of_majority, target_params.age_of_majority
10207 ),
10208 mitigation_strategies: vec!["Adjust age-related provisions".to_string()],
10209 });
10210 score -= 0.05;
10211 notes.push("Age-related provisions need adjustment".to_string());
10212 }
10213
10214 if source_params.prohibitions != target_params.prohibitions {
10216 factors.push(FeasibilityFactor {
10217 id: uuid::Uuid::new_v4().to_string(),
10218 category: FeasibilityCategory::Cultural,
10219 name: "Prohibitions Difference".to_string(),
10220 impact: -0.15,
10221 severity: FeasibilitySeverity::Moderate,
10222 description: "Prohibitions lists differ between jurisdictions".to_string(),
10223 mitigation_strategies: vec![
10224 "Review prohibition-related provisions".to_string(),
10225 "Align with target jurisdiction prohibitions".to_string(),
10226 ],
10227 });
10228 score -= 0.1;
10229 }
10230
10231 score.clamp(0.0, 1.0)
10232 }
10233
10234 fn analyze_economic_feasibility(
10235 &self,
10236 _statute: &Statute,
10237 factors: &mut Vec<FeasibilityFactor>,
10238 _notes: &mut Vec<String>,
10239 ) -> f64 {
10240 let score = 0.75; factors.push(FeasibilityFactor {
10243 id: uuid::Uuid::new_v4().to_string(),
10244 category: FeasibilityCategory::Economic,
10245 name: "Implementation Cost".to_string(),
10246 impact: -0.2,
10247 severity: FeasibilitySeverity::Moderate,
10248 description: "Porting requires investment in legal review and adaptation".to_string(),
10249 mitigation_strategies: vec![
10250 "Secure budget allocation early".to_string(),
10251 "Consider phased implementation to spread costs".to_string(),
10252 ],
10253 });
10254
10255 score
10256 }
10257
10258 fn analyze_political_feasibility(
10259 &self,
10260 _statute: &Statute,
10261 factors: &mut Vec<FeasibilityFactor>,
10262 _notes: &mut Vec<String>,
10263 ) -> f64 {
10264 let score = 0.6; factors.push(FeasibilityFactor {
10267 id: uuid::Uuid::new_v4().to_string(),
10268 category: FeasibilityCategory::Political,
10269 name: "Stakeholder Engagement Required".to_string(),
10270 impact: -0.15,
10271 severity: FeasibilitySeverity::Moderate,
10272 description: "Requires engagement with multiple stakeholders and political support"
10273 .to_string(),
10274 mitigation_strategies: vec![
10275 "Early stakeholder consultation".to_string(),
10276 "Build coalition of supporters".to_string(),
10277 "Address concerns proactively".to_string(),
10278 ],
10279 });
10280
10281 score
10282 }
10283
10284 fn identify_weakest_area(
10285 &self,
10286 technical: f64,
10287 legal: f64,
10288 cultural: f64,
10289 economic: f64,
10290 political: f64,
10291 ) -> &'static str {
10292 let scores = [
10293 (technical, "technical"),
10294 (legal, "legal"),
10295 (cultural, "cultural"),
10296 (economic, "economic"),
10297 (political, "political"),
10298 ];
10299
10300 scores
10301 .iter()
10302 .min_by(|a, b| a.0.partial_cmp(&b.0).unwrap())
10303 .map(|(_, name)| *name)
10304 .unwrap_or("overall")
10305 }
10306}
10307
10308#[derive(Debug, Clone, Serialize, Deserialize)]
10314pub struct PortingProject {
10315 pub id: String,
10317 pub name: String,
10319 pub description: String,
10321 pub source_jurisdiction: String,
10323 pub target_jurisdiction: String,
10325 pub status: ProjectStatus,
10327 pub statute_ids: Vec<String>,
10329 pub stakeholders: Vec<Stakeholder>,
10331 pub timeline: ProjectTimeline,
10333 pub created_at: String,
10335 pub updated_at: String,
10337 pub metadata: HashMap<String, String>,
10339}
10340
10341#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10343pub enum ProjectStatus {
10344 Planning,
10346 InProgress,
10348 UnderReview,
10350 Approved,
10352 Rejected,
10354 OnHold,
10356 Completed,
10358 Cancelled,
10360}
10361
10362#[derive(Debug, Clone, Serialize, Deserialize)]
10364pub struct Stakeholder {
10365 pub id: String,
10367 pub name: String,
10369 pub email: String,
10371 pub role: StakeholderRole,
10373 pub notification_preferences: NotificationPreferences,
10375}
10376
10377#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10379pub enum StakeholderRole {
10380 ProjectManager,
10382 LegalExpert,
10384 TechnicalReviewer,
10386 Approver,
10388 Observer,
10390 Contributor,
10392}
10393
10394#[derive(Debug, Clone, Serialize, Deserialize)]
10396pub struct NotificationPreferences {
10397 pub on_status_change: bool,
10399 pub on_deadline_approaching: bool,
10401 pub on_assignment: bool,
10403 pub on_review_request: bool,
10405 pub channels: Vec<NotificationChannel>,
10407}
10408
10409#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10411pub enum NotificationChannel {
10412 Email,
10414 InApp,
10416 Sms,
10418 Webhook,
10420 Website,
10422 PublicNotice,
10424}
10425
10426#[derive(Debug, Clone, Serialize, Deserialize)]
10428pub struct ProjectTimeline {
10429 pub start_date: String,
10431 pub end_date: String,
10433 pub milestones: Vec<Milestone>,
10435 pub current_phase: String,
10437}
10438
10439#[derive(Debug, Clone, Serialize, Deserialize)]
10441pub struct Milestone {
10442 pub id: String,
10444 pub name: String,
10446 pub description: String,
10448 pub target_date: String,
10450 pub completed: bool,
10452 pub completed_date: Option<String>,
10454 pub dependencies: Vec<String>,
10456}
10457
10458#[derive(Debug)]
10460pub struct PortingProjectManager {
10461 projects: HashMap<String, PortingProject>,
10462}
10463
10464impl PortingProjectManager {
10465 pub fn new() -> Self {
10467 Self {
10468 projects: HashMap::new(),
10469 }
10470 }
10471
10472 pub fn create_project(
10474 &mut self,
10475 name: String,
10476 description: String,
10477 source_jurisdiction: String,
10478 target_jurisdiction: String,
10479 ) -> PortingProject {
10480 let id = uuid::Uuid::new_v4().to_string();
10481 let now = chrono::Utc::now().to_rfc3339();
10482
10483 let project = PortingProject {
10484 id: id.clone(),
10485 name,
10486 description,
10487 source_jurisdiction,
10488 target_jurisdiction,
10489 status: ProjectStatus::Planning,
10490 statute_ids: Vec::new(),
10491 stakeholders: Vec::new(),
10492 timeline: ProjectTimeline {
10493 start_date: now.clone(),
10494 end_date: now.clone(),
10495 milestones: Vec::new(),
10496 current_phase: "Planning".to_string(),
10497 },
10498 created_at: now.clone(),
10499 updated_at: now,
10500 metadata: HashMap::new(),
10501 };
10502
10503 self.projects.insert(id, project.clone());
10504 project
10505 }
10506
10507 pub fn get_project(&self, id: &str) -> Option<&PortingProject> {
10509 self.projects.get(id)
10510 }
10511
10512 pub fn update_status(&mut self, project_id: &str, status: ProjectStatus) -> Option<()> {
10514 let project = self.projects.get_mut(project_id)?;
10515 project.status = status;
10516 project.updated_at = chrono::Utc::now().to_rfc3339();
10517 Some(())
10518 }
10519
10520 pub fn add_statute(&mut self, project_id: &str, statute_id: String) -> Option<()> {
10522 let project = self.projects.get_mut(project_id)?;
10523 project.statute_ids.push(statute_id);
10524 project.updated_at = chrono::Utc::now().to_rfc3339();
10525 Some(())
10526 }
10527
10528 pub fn add_stakeholder(&mut self, project_id: &str, stakeholder: Stakeholder) -> Option<()> {
10530 let project = self.projects.get_mut(project_id)?;
10531 project.stakeholders.push(stakeholder);
10532 project.updated_at = chrono::Utc::now().to_rfc3339();
10533 Some(())
10534 }
10535
10536 pub fn add_milestone(&mut self, project_id: &str, milestone: Milestone) -> Option<()> {
10538 let project = self.projects.get_mut(project_id)?;
10539 project.timeline.milestones.push(milestone);
10540 project.updated_at = chrono::Utc::now().to_rfc3339();
10541 Some(())
10542 }
10543
10544 pub fn complete_milestone(&mut self, project_id: &str, milestone_id: &str) -> Option<()> {
10546 let project = self.projects.get_mut(project_id)?;
10547 let milestone = project
10548 .timeline
10549 .milestones
10550 .iter_mut()
10551 .find(|m| m.id == milestone_id)?;
10552 milestone.completed = true;
10553 milestone.completed_date = Some(chrono::Utc::now().to_rfc3339());
10554 project.updated_at = chrono::Utc::now().to_rfc3339();
10555 Some(())
10556 }
10557
10558 pub fn list_projects(&self) -> Vec<&PortingProject> {
10560 self.projects.values().collect()
10561 }
10562
10563 pub fn list_projects_by_status(&self, status: ProjectStatus) -> Vec<&PortingProject> {
10565 self.projects
10566 .values()
10567 .filter(|p| p.status == status)
10568 .collect()
10569 }
10570}
10571
10572impl Default for PortingProjectManager {
10573 fn default() -> Self {
10574 Self::new()
10575 }
10576}
10577
10578#[derive(Debug, Clone, Serialize, Deserialize)]
10580pub struct ReviewWorkflowStep {
10581 pub id: String,
10583 pub name: String,
10585 pub order: u32,
10587 pub required_reviewers: Vec<String>,
10589 pub optional_reviewers: Vec<String>,
10591 pub min_approvals: u32,
10593 pub status: ReviewStepStatus,
10595 pub reviews: Vec<WorkflowReview>,
10597}
10598
10599#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10601pub enum ReviewStepStatus {
10602 Pending,
10604 InProgress,
10606 Approved,
10608 Rejected,
10610 Skipped,
10612}
10613
10614#[derive(Debug, Clone, Serialize, Deserialize)]
10616pub struct WorkflowReview {
10617 pub id: String,
10619 pub reviewer_id: String,
10621 pub decision: ReviewDecision,
10623 pub comments: String,
10625 pub reviewed_at: String,
10627 pub recommended_changes: Vec<String>,
10629}
10630
10631#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10633pub enum ReviewDecision {
10634 Approve,
10636 ApproveWithConditions,
10638 RequestChanges,
10640 Reject,
10642}
10643
10644#[derive(Debug)]
10646pub struct StakeholderReviewWorkflow {
10647 workflows: HashMap<String, Vec<ReviewWorkflowStep>>,
10648}
10649
10650impl StakeholderReviewWorkflow {
10651 pub fn new() -> Self {
10653 Self {
10654 workflows: HashMap::new(),
10655 }
10656 }
10657
10658 pub fn create_workflow(&mut self, project_id: String, steps: Vec<ReviewWorkflowStep>) {
10660 self.workflows.insert(project_id, steps);
10661 }
10662
10663 pub fn submit_review(
10665 &mut self,
10666 project_id: &str,
10667 step_id: &str,
10668 review: WorkflowReview,
10669 ) -> Option<()> {
10670 let steps = self.workflows.get_mut(project_id)?;
10671 let step = steps.iter_mut().find(|s| s.id == step_id)?;
10672 step.reviews.push(review);
10673
10674 let approvals = step
10676 .reviews
10677 .iter()
10678 .filter(|r| {
10679 matches!(
10680 r.decision,
10681 ReviewDecision::Approve | ReviewDecision::ApproveWithConditions
10682 )
10683 })
10684 .count() as u32;
10685
10686 if approvals >= step.min_approvals {
10687 step.status = ReviewStepStatus::Approved;
10688 }
10689
10690 Some(())
10691 }
10692
10693 pub fn get_workflow_status(&self, project_id: &str) -> Option<&Vec<ReviewWorkflowStep>> {
10695 self.workflows.get(project_id)
10696 }
10697
10698 pub fn advance_workflow(&mut self, project_id: &str) -> Option<usize> {
10700 let steps = self.workflows.get_mut(project_id)?;
10701
10702 let current_step = steps
10703 .iter()
10704 .position(|s| s.status == ReviewStepStatus::InProgress)?;
10705
10706 if steps[current_step].status == ReviewStepStatus::Approved
10707 && current_step + 1 < steps.len()
10708 {
10709 steps[current_step + 1].status = ReviewStepStatus::InProgress;
10710 return Some(current_step + 1);
10711 }
10712
10713 None
10714 }
10715}
10716
10717impl Default for StakeholderReviewWorkflow {
10718 fn default() -> Self {
10719 Self::new()
10720 }
10721}
10722
10723#[derive(Debug, Clone, Serialize, Deserialize)]
10725pub struct PortingIteration {
10726 pub id: String,
10728 pub project_id: String,
10730 pub iteration_number: u32,
10732 pub branch: Option<String>,
10734 pub parent_iteration_id: Option<String>,
10736 pub statute_snapshot: String,
10738 pub changes: Vec<IterationChange>,
10740 pub created_at: String,
10742 pub created_by: String,
10744 pub notes: String,
10746 pub tags: Vec<String>,
10748}
10749
10750#[derive(Debug, Clone, Serialize, Deserialize)]
10752pub struct IterationChange {
10753 pub id: String,
10755 pub change_type: IterationChangeType,
10757 pub field: String,
10759 pub previous_value: String,
10761 pub new_value: String,
10763 pub reason: String,
10765}
10766
10767#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10769pub enum IterationChangeType {
10770 Addition,
10772 Modification,
10774 Deletion,
10776 Restructure,
10778}
10779
10780#[derive(Debug)]
10782pub struct PortingVersionControl {
10783 iterations: HashMap<String, Vec<PortingIteration>>,
10784 branches: HashMap<String, Vec<String>>, }
10786
10787impl PortingVersionControl {
10788 pub fn new() -> Self {
10790 Self {
10791 iterations: HashMap::new(),
10792 branches: HashMap::new(),
10793 }
10794 }
10795
10796 pub fn create_iteration(
10798 &mut self,
10799 project_id: String,
10800 statute_snapshot: String,
10801 created_by: String,
10802 notes: String,
10803 ) -> PortingIteration {
10804 let iterations = self.iterations.entry(project_id.clone()).or_default();
10805 let iteration_number = (iterations.len() + 1) as u32;
10806
10807 let iteration = PortingIteration {
10808 id: uuid::Uuid::new_v4().to_string(),
10809 project_id,
10810 iteration_number,
10811 branch: None, parent_iteration_id: iterations.last().map(|i| i.id.clone()),
10813 statute_snapshot,
10814 changes: Vec::new(),
10815 created_at: chrono::Utc::now().to_rfc3339(),
10816 created_by,
10817 notes,
10818 tags: Vec::new(),
10819 };
10820
10821 iterations.push(iteration.clone());
10822 iteration
10823 }
10824
10825 pub fn get_iterations(&self, project_id: &str) -> Option<&Vec<PortingIteration>> {
10827 self.iterations.get(project_id)
10828 }
10829
10830 pub fn get_iteration(
10832 &self,
10833 project_id: &str,
10834 iteration_number: u32,
10835 ) -> Option<&PortingIteration> {
10836 self.iterations
10837 .get(project_id)?
10838 .iter()
10839 .find(|i| i.iteration_number == iteration_number)
10840 }
10841
10842 pub fn compare_iterations(
10844 &self,
10845 project_id: &str,
10846 from_iteration: u32,
10847 to_iteration: u32,
10848 ) -> Option<Vec<IterationChange>> {
10849 let iterations = self.iterations.get(project_id)?;
10850 let _from = iterations
10851 .iter()
10852 .find(|i| i.iteration_number == from_iteration)?;
10853 let to = iterations
10854 .iter()
10855 .find(|i| i.iteration_number == to_iteration)?;
10856
10857 Some(to.changes.clone())
10859 }
10860
10861 pub fn create_branch(
10863 &mut self,
10864 project_id: String,
10865 branch_name: String,
10866 from_iteration_number: u32,
10867 created_by: String,
10868 notes: String,
10869 ) -> Option<PortingIteration> {
10870 let iterations = self.iterations.get(&project_id)?;
10871 let from_iteration = iterations
10872 .iter()
10873 .find(|i| i.iteration_number == from_iteration_number)?
10874 .clone();
10875
10876 self.branches
10878 .entry(project_id.clone())
10879 .or_default()
10880 .push(branch_name.clone());
10881
10882 let all_iterations = self.iterations.entry(project_id.clone()).or_default();
10884 let iteration_number = (all_iterations.len() + 1) as u32;
10885
10886 let iteration = PortingIteration {
10887 id: uuid::Uuid::new_v4().to_string(),
10888 project_id,
10889 iteration_number,
10890 branch: Some(branch_name),
10891 parent_iteration_id: Some(from_iteration.id.clone()),
10892 statute_snapshot: from_iteration.statute_snapshot.clone(),
10893 changes: Vec::new(),
10894 created_at: chrono::Utc::now().to_rfc3339(),
10895 created_by,
10896 notes,
10897 tags: vec!["branch".to_string()],
10898 };
10899
10900 all_iterations.push(iteration.clone());
10901 Some(iteration)
10902 }
10903
10904 pub fn get_branches(&self, project_id: &str) -> Vec<String> {
10906 self.branches.get(project_id).cloned().unwrap_or_default()
10907 }
10908
10909 pub fn get_branch_iterations(
10911 &self,
10912 project_id: &str,
10913 branch_name: &str,
10914 ) -> Vec<PortingIteration> {
10915 self.iterations
10916 .get(project_id)
10917 .map(|iterations| {
10918 iterations
10919 .iter()
10920 .filter(|i| i.branch.as_deref() == Some(branch_name))
10921 .cloned()
10922 .collect()
10923 })
10924 .unwrap_or_default()
10925 }
10926
10927 pub fn merge_branch(
10929 &mut self,
10930 project_id: String,
10931 source_branch: String,
10932 target_branch: Option<String>,
10933 created_by: String,
10934 notes: String,
10935 ) -> Option<PortingIteration> {
10936 let iterations = self.iterations.get(&project_id)?;
10937
10938 let source_iteration = iterations
10940 .iter()
10941 .filter(|i| i.branch.as_deref() == Some(&source_branch))
10942 .max_by_key(|i| i.iteration_number)?
10943 .clone();
10944
10945 let all_iterations = self.iterations.entry(project_id.clone()).or_default();
10947 let iteration_number = (all_iterations.len() + 1) as u32;
10948
10949 let iteration = PortingIteration {
10950 id: uuid::Uuid::new_v4().to_string(),
10951 project_id,
10952 iteration_number,
10953 branch: target_branch,
10954 parent_iteration_id: Some(source_iteration.id.clone()),
10955 statute_snapshot: source_iteration.statute_snapshot.clone(),
10956 changes: source_iteration.changes.clone(),
10957 created_at: chrono::Utc::now().to_rfc3339(),
10958 created_by,
10959 notes: format!("Merged {} - {}", source_branch, notes),
10960 tags: vec!["merge".to_string()],
10961 };
10962
10963 all_iterations.push(iteration.clone());
10964 Some(iteration)
10965 }
10966
10967 pub fn generate_changelog(&self, project_id: &str) -> Option<PortingChangelog> {
10969 let iterations = self.iterations.get(project_id)?;
10970
10971 if iterations.is_empty() {
10972 return None;
10973 }
10974
10975 let mut entries = Vec::new();
10976
10977 for iteration in iterations {
10978 let mut change_summary = Vec::new();
10979
10980 for change in &iteration.changes {
10981 change_summary.push(format!(
10982 "{:?}: {} ({})",
10983 change.change_type, change.field, change.reason
10984 ));
10985 }
10986
10987 entries.push(ChangelogEntry {
10988 id: uuid::Uuid::new_v4().to_string(),
10989 iteration_number: iteration.iteration_number,
10990 iteration_id: iteration.id.clone(),
10991 branch: iteration.branch.clone(),
10992 timestamp: iteration.created_at.clone(),
10993 author: iteration.created_by.clone(),
10994 summary: iteration.notes.clone(),
10995 changes: change_summary,
10996 tags: iteration.tags.clone(),
10997 });
10998 }
10999
11000 Some(PortingChangelog {
11001 id: uuid::Uuid::new_v4().to_string(),
11002 project_id: project_id.to_string(),
11003 generated_at: chrono::Utc::now().to_rfc3339(),
11004 entries,
11005 total_iterations: iterations.len(),
11006 branches: self.get_branches(project_id),
11007 })
11008 }
11009
11010 pub fn revert_to_iteration(
11012 &mut self,
11013 project_id: &str,
11014 iteration_number: u32,
11015 created_by: String,
11016 ) -> Option<PortingIteration> {
11017 let iteration = self.get_iteration(project_id, iteration_number)?.clone();
11018
11019 Some(self.create_iteration(
11020 project_id.to_string(),
11021 iteration.statute_snapshot.clone(),
11022 created_by,
11023 format!("Reverted to iteration {}", iteration_number),
11024 ))
11025 }
11026}
11027
11028impl Default for PortingVersionControl {
11029 fn default() -> Self {
11030 Self::new()
11031 }
11032}
11033
11034#[derive(Debug, Clone, Serialize, Deserialize)]
11036pub struct PortingChangelog {
11037 pub id: String,
11039 pub project_id: String,
11041 pub generated_at: String,
11043 pub entries: Vec<ChangelogEntry>,
11045 pub total_iterations: usize,
11047 pub branches: Vec<String>,
11049}
11050
11051#[derive(Debug, Clone, Serialize, Deserialize)]
11053pub struct ChangelogEntry {
11054 pub id: String,
11056 pub iteration_number: u32,
11058 pub iteration_id: String,
11060 pub branch: Option<String>,
11062 pub timestamp: String,
11064 pub author: String,
11066 pub summary: String,
11068 pub changes: Vec<String>,
11070 pub tags: Vec<String>,
11072}
11073
11074impl PortingChangelog {
11075 pub fn to_markdown(&self) -> String {
11077 let mut output = String::new();
11078
11079 output.push_str("# Porting Changelog\n\n");
11080 output.push_str(&format!("**Project ID:** {}\n", self.project_id));
11081 output.push_str(&format!("**Generated:** {}\n", self.generated_at));
11082 output.push_str(&format!(
11083 "**Total Iterations:** {}\n",
11084 self.total_iterations
11085 ));
11086
11087 if !self.branches.is_empty() {
11088 output.push_str(&format!("**Branches:** {}\n", self.branches.join(", ")));
11089 }
11090
11091 output.push_str("\n---\n\n");
11092
11093 for entry in &self.entries {
11094 let branch_info = entry
11095 .branch
11096 .as_ref()
11097 .map(|b| format!(" [{}]", b))
11098 .unwrap_or_default();
11099
11100 output.push_str(&format!(
11101 "## Iteration {}{}\n\n",
11102 entry.iteration_number, branch_info
11103 ));
11104 output.push_str(&format!("**Date:** {}\n", entry.timestamp));
11105 output.push_str(&format!("**Author:** {}\n", entry.author));
11106 output.push_str(&format!("**Summary:** {}\n\n", entry.summary));
11107
11108 if !entry.changes.is_empty() {
11109 output.push_str("**Changes:**\n\n");
11110 for change in &entry.changes {
11111 output.push_str(&format!("- {}\n", change));
11112 }
11113 output.push('\n');
11114 }
11115
11116 if !entry.tags.is_empty() {
11117 output.push_str(&format!("**Tags:** {}\n\n", entry.tags.join(", ")));
11118 }
11119
11120 output.push_str("---\n\n");
11121 }
11122
11123 output
11124 }
11125
11126 pub fn to_json(&self) -> Result<String, serde_json::Error> {
11128 serde_json::to_string_pretty(self)
11129 }
11130}
11131
11132#[derive(Debug, Clone, Serialize, Deserialize)]
11134pub struct ApprovalChain {
11135 pub id: String,
11137 pub name: String,
11139 pub steps: Vec<ApprovalStep>,
11141 pub status: ApprovalChainStatus,
11143}
11144
11145#[derive(Debug, Clone, Serialize, Deserialize)]
11147pub struct ApprovalStep {
11148 pub id: String,
11150 pub name: String,
11152 pub order: u32,
11154 pub approvers: Vec<String>,
11156 pub approval_mode: ApprovalMode,
11158 pub status: ApprovalStepStatus,
11160 pub approvals: Vec<ApprovalRecord>,
11162 pub auto_approve_after: Option<u64>,
11164}
11165
11166#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11168pub enum ApprovalMode {
11169 Any,
11171 All,
11173 Majority,
11175 Threshold(u32),
11177}
11178
11179#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11181pub enum ApprovalStepStatus {
11182 Pending,
11184 Approved,
11186 Rejected,
11188 TimedOut,
11190}
11191
11192#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11194pub enum ApprovalChainStatus {
11195 NotStarted,
11197 InProgress,
11199 Completed,
11201 Failed,
11203}
11204
11205#[derive(Debug, Clone, Serialize, Deserialize)]
11207pub struct ApprovalRecord {
11208 pub id: String,
11210 pub approver_id: String,
11212 pub approved: bool,
11214 pub comments: String,
11216 pub approved_at: String,
11218}
11219
11220#[derive(Debug)]
11222pub struct ApprovalChainManager {
11223 chains: HashMap<String, ApprovalChain>,
11224}
11225
11226impl ApprovalChainManager {
11227 pub fn new() -> Self {
11229 Self {
11230 chains: HashMap::new(),
11231 }
11232 }
11233
11234 pub fn create_chain(&mut self, name: String, steps: Vec<ApprovalStep>) -> ApprovalChain {
11236 let chain = ApprovalChain {
11237 id: uuid::Uuid::new_v4().to_string(),
11238 name,
11239 steps,
11240 status: ApprovalChainStatus::NotStarted,
11241 };
11242
11243 self.chains.insert(chain.id.clone(), chain.clone());
11244 chain
11245 }
11246
11247 pub fn submit_approval(
11249 &mut self,
11250 chain_id: &str,
11251 step_id: &str,
11252 approval: ApprovalRecord,
11253 ) -> Option<()> {
11254 let chain = self.chains.get_mut(chain_id)?;
11255 let step = chain.steps.iter_mut().find(|s| s.id == step_id)?;
11256 step.approvals.push(approval);
11257
11258 let approved_count = step.approvals.iter().filter(|a| a.approved).count();
11260 let total_approvers = step.approvers.len();
11261
11262 let step_approved = match step.approval_mode {
11263 ApprovalMode::Any => approved_count >= 1,
11264 ApprovalMode::All => approved_count == total_approvers,
11265 ApprovalMode::Majority => approved_count > total_approvers / 2,
11266 ApprovalMode::Threshold(n) => approved_count >= n as usize,
11267 };
11268
11269 if step_approved {
11270 step.status = ApprovalStepStatus::Approved;
11271 }
11272
11273 Some(())
11274 }
11275
11276 pub fn get_chain(&self, chain_id: &str) -> Option<&ApprovalChain> {
11278 self.chains.get(chain_id)
11279 }
11280
11281 pub fn advance_chain(&mut self, chain_id: &str) -> Option<usize> {
11283 let chain = self.chains.get_mut(chain_id)?;
11284
11285 let current_step = chain
11286 .steps
11287 .iter()
11288 .position(|s| s.status == ApprovalStepStatus::Pending)?;
11289
11290 if chain.steps[current_step].status == ApprovalStepStatus::Approved {
11291 if current_step + 1 < chain.steps.len() {
11292 return Some(current_step + 1);
11293 } else {
11294 chain.status = ApprovalChainStatus::Completed;
11295 }
11296 }
11297
11298 None
11299 }
11300}
11301
11302impl Default for ApprovalChainManager {
11303 fn default() -> Self {
11304 Self::new()
11305 }
11306}
11307
11308#[derive(Debug, Clone, Serialize, Deserialize)]
11310pub struct Notification {
11311 pub id: String,
11313 pub recipient_id: String,
11315 pub notification_type: NotificationType,
11317 pub title: String,
11319 pub message: String,
11321 pub project_id: Option<String>,
11323 pub priority: NotificationPriority,
11325 pub created_at: String,
11327 pub read: bool,
11329 pub channels: Vec<NotificationChannel>,
11331}
11332
11333#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11335pub enum NotificationType {
11336 StatusChange,
11338 DeadlineApproaching,
11340 Assignment,
11342 ReviewRequest,
11344 ApprovalRequest,
11346 MilestoneCompleted,
11348 ProjectCompleted,
11350}
11351
11352#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11354pub enum NotificationPriority {
11355 Low,
11357 Normal,
11359 High,
11361 Urgent,
11363}
11364
11365#[derive(Debug, Clone, Serialize, Deserialize)]
11367pub struct DeadlineTracker {
11368 pub id: String,
11370 pub project_id: String,
11372 pub name: String,
11374 pub deadline: String,
11376 pub warning_days: u32,
11378 pub status: DeadlineStatus,
11380 pub assigned_to: Vec<String>,
11382}
11383
11384#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11386pub enum DeadlineStatus {
11387 OnTrack,
11389 Approaching,
11391 Overdue,
11393 Completed,
11395}
11396
11397#[derive(Debug)]
11399pub struct NotificationManager {
11400 notifications: HashMap<String, Vec<Notification>>,
11401 deadlines: HashMap<String, Vec<DeadlineTracker>>,
11402}
11403
11404impl NotificationManager {
11405 pub fn new() -> Self {
11407 Self {
11408 notifications: HashMap::new(),
11409 deadlines: HashMap::new(),
11410 }
11411 }
11412
11413 pub fn send_notification(&mut self, notification: Notification) {
11415 let recipient_id = notification.recipient_id.clone();
11416 self.notifications
11417 .entry(recipient_id)
11418 .or_default()
11419 .push(notification);
11420 }
11421
11422 pub fn get_notifications(&self, stakeholder_id: &str) -> Vec<&Notification> {
11424 self.notifications
11425 .get(stakeholder_id)
11426 .map(|n| n.iter().collect())
11427 .unwrap_or_default()
11428 }
11429
11430 pub fn mark_as_read(&mut self, stakeholder_id: &str, notification_id: &str) -> Option<()> {
11432 let notifications = self.notifications.get_mut(stakeholder_id)?;
11433 let notification = notifications.iter_mut().find(|n| n.id == notification_id)?;
11434 notification.read = true;
11435 Some(())
11436 }
11437
11438 pub fn add_deadline(&mut self, deadline: DeadlineTracker) {
11440 let project_id = deadline.project_id.clone();
11441 self.deadlines.entry(project_id).or_default().push(deadline);
11442 }
11443
11444 pub fn get_deadlines(&self, project_id: &str) -> Vec<&DeadlineTracker> {
11446 self.deadlines
11447 .get(project_id)
11448 .map(|d| d.iter().collect())
11449 .unwrap_or_default()
11450 }
11451
11452 pub fn check_deadlines(&mut self) -> Vec<Notification> {
11454 let mut notifications = Vec::new();
11455 let now = chrono::Utc::now();
11456
11457 for (project_id, deadlines) in &self.deadlines {
11458 for deadline in deadlines {
11459 if let Ok(deadline_date) = chrono::DateTime::parse_from_rfc3339(&deadline.deadline)
11460 {
11461 let days_until = (deadline_date.signed_duration_since(now)).num_days();
11462
11463 if days_until >= 0 && days_until <= deadline.warning_days as i64 {
11464 for stakeholder_id in &deadline.assigned_to {
11465 let notification = Notification {
11466 id: uuid::Uuid::new_v4().to_string(),
11467 recipient_id: stakeholder_id.clone(),
11468 notification_type: NotificationType::DeadlineApproaching,
11469 title: format!("Deadline Approaching: {}", deadline.name),
11470 message: format!(
11471 "Deadline '{}' is approaching in {} days",
11472 deadline.name, days_until
11473 ),
11474 project_id: Some(project_id.clone()),
11475 priority: if days_until <= 3 {
11476 NotificationPriority::Urgent
11477 } else {
11478 NotificationPriority::High
11479 },
11480 created_at: now.to_rfc3339(),
11481 read: false,
11482 channels: vec![
11483 NotificationChannel::Email,
11484 NotificationChannel::InApp,
11485 ],
11486 };
11487 notifications.push(notification);
11488 }
11489 }
11490 }
11491 }
11492 }
11493
11494 notifications
11495 }
11496}
11497
11498impl Default for NotificationManager {
11499 fn default() -> Self {
11500 Self::new()
11501 }
11502}
11503
11504#[derive(Debug, Clone, Serialize, Deserialize)]
11510pub struct ExecutiveSummary {
11511 pub project_id: String,
11513 pub title: String,
11515 pub source_jurisdiction: String,
11517 pub target_jurisdiction: String,
11519 pub statutes_count: usize,
11521 pub compatibility_score: f64,
11523 pub risk_level: RiskLevel,
11525 pub key_findings: Vec<String>,
11527 pub recommendations: Vec<String>,
11529 pub timeline_summary: String,
11531 pub stakeholders: Vec<String>,
11533 pub generated_at: String,
11535}
11536
11537#[derive(Debug, Clone)]
11539pub struct ExecutiveSummaryGenerator;
11540
11541impl ExecutiveSummaryGenerator {
11542 pub fn new() -> Self {
11544 Self
11545 }
11546
11547 pub fn generate(
11549 &self,
11550 project: &PortingProject,
11551 ported_statutes: &[PortedStatute],
11552 ) -> ExecutiveSummary {
11553 let compatibility_score = if !ported_statutes.is_empty() {
11554 ported_statutes
11555 .iter()
11556 .map(|s| s.compatibility_score)
11557 .sum::<f64>()
11558 / ported_statutes.len() as f64
11559 } else {
11560 0.0
11561 };
11562
11563 let risk_level = if compatibility_score >= 0.8 {
11564 RiskLevel::Low
11565 } else if compatibility_score >= 0.5 {
11566 RiskLevel::Medium
11567 } else {
11568 RiskLevel::High
11569 };
11570
11571 let key_findings = self.extract_key_findings(ported_statutes);
11572 let recommendations = self.generate_recommendations(ported_statutes, compatibility_score);
11573
11574 ExecutiveSummary {
11575 project_id: project.id.clone(),
11576 title: project.name.clone(),
11577 source_jurisdiction: project.source_jurisdiction.clone(),
11578 target_jurisdiction: project.target_jurisdiction.clone(),
11579 statutes_count: ported_statutes.len(),
11580 compatibility_score,
11581 risk_level,
11582 key_findings,
11583 recommendations,
11584 timeline_summary: format!(
11585 "Created: {}, Last updated: {}",
11586 project.created_at, project.updated_at
11587 ),
11588 stakeholders: project
11589 .stakeholders
11590 .iter()
11591 .map(|s| s.name.clone())
11592 .collect(),
11593 generated_at: chrono::Utc::now().to_rfc3339(),
11594 }
11595 }
11596
11597 fn extract_key_findings(&self, ported_statutes: &[PortedStatute]) -> Vec<String> {
11598 let mut findings = Vec::new();
11599
11600 let total_changes: usize = ported_statutes.iter().map(|s| s.changes.len()).sum();
11601 if total_changes > 0 {
11602 findings.push(format!(
11603 "Total of {} adaptations made across {} statutes",
11604 total_changes,
11605 ported_statutes.len()
11606 ));
11607 }
11608
11609 let cultural_changes = ported_statutes
11610 .iter()
11611 .flat_map(|s| &s.changes)
11612 .filter(|c| matches!(c.change_type, ChangeType::CulturalAdaptation))
11613 .count();
11614 if cultural_changes > 0 {
11615 findings.push(format!(
11616 "{} cultural adaptations required",
11617 cultural_changes
11618 ));
11619 }
11620
11621 let high_risk_count = ported_statutes
11622 .iter()
11623 .filter(|s| s.compatibility_score < 0.5)
11624 .count();
11625 if high_risk_count > 0 {
11626 findings.push(format!(
11627 "{} statutes require significant adaptation (compatibility < 50%)",
11628 high_risk_count
11629 ));
11630 }
11631
11632 if findings.is_empty() {
11633 findings.push("All statutes ported successfully with minimal adaptations".to_string());
11634 }
11635
11636 findings
11637 }
11638
11639 fn generate_recommendations(
11640 &self,
11641 ported_statutes: &[PortedStatute],
11642 compatibility_score: f64,
11643 ) -> Vec<String> {
11644 let mut recommendations = Vec::new();
11645
11646 if compatibility_score < 0.5 {
11647 recommendations
11648 .push("Comprehensive legal review recommended before implementation".to_string());
11649 recommendations.push(
11650 "Consider pilot program in limited jurisdiction before full rollout".to_string(),
11651 );
11652 } else if compatibility_score < 0.8 {
11653 recommendations.push("Expert review recommended for adapted sections".to_string());
11654 }
11655
11656 let needs_review = ported_statutes
11657 .iter()
11658 .filter(|s| !s.changes.is_empty())
11659 .count();
11660 if needs_review > 0 {
11661 recommendations.push(format!(
11662 "Review {} statutes with cultural adaptations",
11663 needs_review
11664 ));
11665 }
11666
11667 if recommendations.is_empty() {
11668 recommendations.push("Proceed with standard implementation process".to_string());
11669 }
11670
11671 recommendations
11672 }
11673}
11674
11675impl Default for ExecutiveSummaryGenerator {
11676 fn default() -> Self {
11677 Self::new()
11678 }
11679}
11680
11681#[derive(Debug, Clone, Serialize, Deserialize)]
11683pub struct RiskAssessmentReport {
11684 pub project_id: String,
11686 pub title: String,
11688 pub overall_risk_score: f64,
11690 pub overall_risk_level: RiskLevel,
11692 pub risks_by_category: HashMap<RiskCategory, Vec<Risk>>,
11694 pub mitigation_strategies: Vec<MitigationStrategy>,
11696 pub risk_matrix: RiskMatrix,
11698 pub generated_at: String,
11700}
11701
11702#[derive(Debug, Clone, Serialize, Deserialize)]
11704pub struct RiskMatrix {
11705 pub critical: Vec<String>,
11707 pub moderate_high_prob: Vec<String>,
11709 pub moderate_high_impact: Vec<String>,
11711 pub low: Vec<String>,
11713}
11714
11715#[derive(Debug, Clone, Serialize, Deserialize)]
11717pub struct MitigationStrategy {
11718 pub risk_id: String,
11720 pub strategy: String,
11722 pub effectiveness: f64,
11724 pub cost: MitigationCost,
11726 pub priority: Priority,
11728}
11729
11730#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11732pub enum MitigationCost {
11733 Low,
11735 Medium,
11737 High,
11739}
11740
11741#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11743pub enum Priority {
11744 Low,
11746 Medium,
11748 High,
11750 Critical,
11752}
11753
11754#[derive(Debug, Clone)]
11756pub struct RiskAssessmentReportGenerator;
11757
11758impl RiskAssessmentReportGenerator {
11759 pub fn new() -> Self {
11761 Self
11762 }
11763
11764 pub fn generate(
11766 &self,
11767 project: &PortingProject,
11768 risk_assessments: &[RiskAssessment],
11769 ) -> RiskAssessmentReport {
11770 let overall_risk_score = if !risk_assessments.is_empty() {
11771 risk_assessments.iter().map(|r| r.risk_score).sum::<f64>()
11772 / risk_assessments.len() as f64
11773 } else {
11774 0.0
11775 };
11776
11777 let overall_risk_level = if overall_risk_score >= 0.7 {
11778 RiskLevel::High
11779 } else if overall_risk_score >= 0.4 {
11780 RiskLevel::Medium
11781 } else {
11782 RiskLevel::Low
11783 };
11784
11785 let mut risks_by_category: HashMap<RiskCategory, Vec<Risk>> = HashMap::new();
11786 for assessment in risk_assessments {
11787 for risk in &assessment.risks {
11788 risks_by_category
11789 .entry(risk.category)
11790 .or_default()
11791 .push(risk.clone());
11792 }
11793 }
11794
11795 let mitigation_strategies = self.generate_mitigation_strategies(&risks_by_category);
11796 let risk_matrix = self.build_risk_matrix(&risks_by_category);
11797
11798 RiskAssessmentReport {
11799 project_id: project.id.clone(),
11800 title: format!("Risk Assessment: {}", project.name),
11801 overall_risk_score,
11802 overall_risk_level,
11803 risks_by_category,
11804 mitigation_strategies,
11805 risk_matrix,
11806 generated_at: chrono::Utc::now().to_rfc3339(),
11807 }
11808 }
11809
11810 #[allow(dead_code)]
11811 fn generate_mitigation_strategies(
11812 &self,
11813 risks_by_category: &HashMap<RiskCategory, Vec<Risk>>,
11814 ) -> Vec<MitigationStrategy> {
11815 let mut strategies = Vec::new();
11816
11817 for (category, risks) in risks_by_category {
11818 for risk in risks {
11819 let strategy = match (category, risk.severity) {
11820 (RiskCategory::Legal, RiskLevel::High) => MitigationStrategy {
11821 risk_id: risk.id.clone(),
11822 strategy: "Engage constitutional law experts for comprehensive review"
11823 .to_string(),
11824 effectiveness: 0.9,
11825 cost: MitigationCost::High,
11826 priority: Priority::Critical,
11827 },
11828 (RiskCategory::Cultural, RiskLevel::High) => MitigationStrategy {
11829 risk_id: risk.id.clone(),
11830 strategy: "Conduct cultural sensitivity review with local experts"
11831 .to_string(),
11832 effectiveness: 0.85,
11833 cost: MitigationCost::Medium,
11834 priority: Priority::High,
11835 },
11836 (RiskCategory::Political, RiskLevel::High) => MitigationStrategy {
11837 risk_id: risk.id.clone(),
11838 strategy: "Establish stakeholder consultation process".to_string(),
11839 effectiveness: 0.75,
11840 cost: MitigationCost::Medium,
11841 priority: Priority::High,
11842 },
11843 (RiskCategory::Economic, RiskLevel::High) => MitigationStrategy {
11844 risk_id: risk.id.clone(),
11845 strategy: "Perform detailed cost-benefit analysis".to_string(),
11846 effectiveness: 0.8,
11847 cost: MitigationCost::Medium,
11848 priority: Priority::High,
11849 },
11850 (RiskCategory::Implementation, RiskLevel::High) => MitigationStrategy {
11851 risk_id: risk.id.clone(),
11852 strategy: "Develop phased implementation plan with pilot program"
11853 .to_string(),
11854 effectiveness: 0.8,
11855 cost: MitigationCost::High,
11856 priority: Priority::High,
11857 },
11858 _ => MitigationStrategy {
11859 risk_id: risk.id.clone(),
11860 strategy: format!(
11861 "Standard {} risk mitigation procedures",
11862 format!("{:?}", category).to_lowercase()
11863 ),
11864 effectiveness: 0.7,
11865 cost: MitigationCost::Low,
11866 priority: Priority::Medium,
11867 },
11868 };
11869 strategies.push(strategy);
11870 }
11871 }
11872
11873 strategies
11874 }
11875
11876 fn build_risk_matrix(
11877 &self,
11878 risks_by_category: &HashMap<RiskCategory, Vec<Risk>>,
11879 ) -> RiskMatrix {
11880 let mut critical = Vec::new();
11881 let mut moderate_high_prob = Vec::new();
11882 let mut moderate_high_impact = Vec::new();
11883 let mut low = Vec::new();
11884
11885 for risks in risks_by_category.values() {
11886 for risk in risks {
11887 let risk_desc = format!("{}: {}", risk.id, risk.description);
11888 match (risk.severity, risk.likelihood) {
11889 (RiskLevel::High, RiskLevel::High) => critical.push(risk_desc),
11890 (RiskLevel::High, _) => moderate_high_impact.push(risk_desc),
11891 (_, RiskLevel::High) => moderate_high_prob.push(risk_desc),
11892 _ => low.push(risk_desc),
11893 }
11894 }
11895 }
11896
11897 RiskMatrix {
11898 critical,
11899 moderate_high_prob,
11900 moderate_high_impact,
11901 low,
11902 }
11903 }
11904}
11905
11906impl Default for RiskAssessmentReportGenerator {
11907 fn default() -> Self {
11908 Self::new()
11909 }
11910}
11911
11912#[derive(Debug, Clone, Serialize, Deserialize)]
11914pub struct ImplementationRoadmap {
11915 pub project_id: String,
11917 pub title: String,
11919 pub phases: Vec<ImplementationPhase>,
11921 pub critical_path: Vec<String>,
11923 pub resource_requirements: ResourceRequirements,
11925 pub estimated_duration_days: u32,
11927 pub generated_at: String,
11929}
11930
11931#[derive(Debug, Clone, Serialize, Deserialize)]
11933pub struct ImplementationPhase {
11934 pub phase_number: u32,
11936 pub name: String,
11938 pub description: String,
11940 pub tasks: Vec<ImplementationTask>,
11942 pub dependencies: Vec<u32>,
11944 pub estimated_duration_days: u32,
11946 pub success_criteria: Vec<String>,
11948}
11949
11950#[derive(Debug, Clone, Serialize, Deserialize)]
11952pub struct ImplementationTask {
11953 pub id: String,
11955 pub name: String,
11957 pub description: String,
11959 pub assigned_to: String,
11961 pub estimated_effort_days: u32,
11963 pub priority: Priority,
11965 pub dependencies: Vec<String>,
11967}
11968
11969#[derive(Debug, Clone, Serialize, Deserialize)]
11971pub struct ResourceRequirements {
11972 pub personnel: Vec<PersonnelRequirement>,
11974 pub budget_estimate: BudgetEstimate,
11976 pub infrastructure: Vec<String>,
11978}
11979
11980#[derive(Debug, Clone, Serialize, Deserialize)]
11982pub struct PersonnelRequirement {
11983 pub role: String,
11985 pub count: u32,
11987 pub time_commitment_days: u32,
11989}
11990
11991#[derive(Debug, Clone, Serialize, Deserialize)]
11993pub struct BudgetEstimate {
11994 pub currency: String,
11996 pub min_amount: f64,
11998 pub max_amount: f64,
12000 pub breakdown: HashMap<String, f64>,
12002}
12003
12004#[derive(Debug, Clone)]
12006pub struct ImplementationRoadmapGenerator;
12007
12008impl ImplementationRoadmapGenerator {
12009 pub fn new() -> Self {
12011 Self
12012 }
12013
12014 pub fn generate(
12016 &self,
12017 project: &PortingProject,
12018 ported_statutes: &[PortedStatute],
12019 ) -> ImplementationRoadmap {
12020 let phases = self.generate_phases(ported_statutes);
12021 let critical_path = self.identify_critical_path(&phases);
12022 let resource_requirements = self.estimate_resources(ported_statutes, &phases);
12023 let estimated_duration_days = phases.iter().map(|p| p.estimated_duration_days).sum();
12024
12025 ImplementationRoadmap {
12026 project_id: project.id.clone(),
12027 title: format!("Implementation Roadmap: {}", project.name),
12028 phases,
12029 critical_path,
12030 resource_requirements,
12031 estimated_duration_days,
12032 generated_at: chrono::Utc::now().to_rfc3339(),
12033 }
12034 }
12035
12036 fn generate_phases(&self, ported_statutes: &[PortedStatute]) -> Vec<ImplementationPhase> {
12037 vec![
12038 ImplementationPhase {
12039 phase_number: 1,
12040 name: "Legal Review and Validation".to_string(),
12041 description: "Comprehensive legal review of ported statutes".to_string(),
12042 tasks: vec![
12043 ImplementationTask {
12044 id: "task-1-1".to_string(),
12045 name: "Constitutional compatibility review".to_string(),
12046 description: "Review all statutes for constitutional compatibility"
12047 .to_string(),
12048 assigned_to: "Constitutional Law Team".to_string(),
12049 estimated_effort_days: 10,
12050 priority: Priority::Critical,
12051 dependencies: vec![],
12052 },
12053 ImplementationTask {
12054 id: "task-1-2".to_string(),
12055 name: "Conflict detection and resolution".to_string(),
12056 description: "Identify and resolve conflicts with existing laws"
12057 .to_string(),
12058 assigned_to: "Legal Analysis Team".to_string(),
12059 estimated_effort_days: 8,
12060 priority: Priority::High,
12061 dependencies: vec!["task-1-1".to_string()],
12062 },
12063 ],
12064 dependencies: vec![],
12065 estimated_duration_days: 15,
12066 success_criteria: vec![
12067 "All constitutional issues identified and addressed".to_string(),
12068 "No unresolved conflicts with existing laws".to_string(),
12069 ],
12070 },
12071 ImplementationPhase {
12072 phase_number: 2,
12073 name: "Stakeholder Consultation".to_string(),
12074 description: "Engage stakeholders and gather feedback".to_string(),
12075 tasks: vec![
12076 ImplementationTask {
12077 id: "task-2-1".to_string(),
12078 name: "Public comment period".to_string(),
12079 description: "Open public comment period for feedback".to_string(),
12080 assigned_to: "Public Affairs Team".to_string(),
12081 estimated_effort_days: 30,
12082 priority: Priority::High,
12083 dependencies: vec!["task-1-2".to_string()],
12084 },
12085 ImplementationTask {
12086 id: "task-2-2".to_string(),
12087 name: "Expert consultations".to_string(),
12088 description: "Conduct consultations with subject matter experts"
12089 .to_string(),
12090 assigned_to: "Policy Team".to_string(),
12091 estimated_effort_days: 15,
12092 priority: Priority::High,
12093 dependencies: vec!["task-1-2".to_string()],
12094 },
12095 ],
12096 dependencies: vec![1],
12097 estimated_duration_days: 30,
12098 success_criteria: vec![
12099 "All stakeholder feedback documented".to_string(),
12100 "Major concerns addressed".to_string(),
12101 ],
12102 },
12103 ImplementationPhase {
12104 phase_number: 3,
12105 name: "Pilot Implementation".to_string(),
12106 description: "Limited pilot rollout to test implementation".to_string(),
12107 tasks: vec![ImplementationTask {
12108 id: "task-3-1".to_string(),
12109 name: format!(
12110 "Pilot program for {} statutes",
12111 ported_statutes.len().min(5)
12112 ),
12113 description: "Implement pilot program in limited jurisdiction".to_string(),
12114 assigned_to: "Implementation Team".to_string(),
12115 estimated_effort_days: 45,
12116 priority: Priority::High,
12117 dependencies: vec!["task-2-1".to_string(), "task-2-2".to_string()],
12118 }],
12119 dependencies: vec![2],
12120 estimated_duration_days: 60,
12121 success_criteria: vec![
12122 "Pilot successfully completed".to_string(),
12123 "Implementation issues identified and documented".to_string(),
12124 ],
12125 },
12126 ImplementationPhase {
12127 phase_number: 4,
12128 name: "Full Rollout".to_string(),
12129 description: "Complete implementation across jurisdiction".to_string(),
12130 tasks: vec![ImplementationTask {
12131 id: "task-4-1".to_string(),
12132 name: "Full jurisdiction rollout".to_string(),
12133 description: "Implement all ported statutes across full jurisdiction"
12134 .to_string(),
12135 assigned_to: "Implementation Team".to_string(),
12136 estimated_effort_days: 90,
12137 priority: Priority::Critical,
12138 dependencies: vec!["task-3-1".to_string()],
12139 }],
12140 dependencies: vec![3],
12141 estimated_duration_days: 120,
12142 success_criteria: vec![
12143 "All statutes successfully implemented".to_string(),
12144 "Monitoring and enforcement mechanisms in place".to_string(),
12145 ],
12146 },
12147 ]
12148 }
12149
12150 fn identify_critical_path(&self, phases: &[ImplementationPhase]) -> Vec<String> {
12151 let mut critical_path = Vec::new();
12152 for phase in phases {
12153 critical_path.push(format!(
12154 "Phase {}: {} ({} days)",
12155 phase.phase_number, phase.name, phase.estimated_duration_days
12156 ));
12157 }
12158 critical_path
12159 }
12160
12161 fn estimate_resources(
12162 &self,
12163 ported_statutes: &[PortedStatute],
12164 phases: &[ImplementationPhase],
12165 ) -> ResourceRequirements {
12166 let statute_count = ported_statutes.len();
12167 let complexity_factor = if statute_count > 20 { 1.5 } else { 1.0 };
12168
12169 let personnel = vec![
12170 PersonnelRequirement {
12171 role: "Legal Experts".to_string(),
12172 count: (statute_count / 10).max(2) as u32,
12173 time_commitment_days: (30.0 * complexity_factor) as u32,
12174 },
12175 PersonnelRequirement {
12176 role: "Policy Analysts".to_string(),
12177 count: (statute_count / 15).max(1) as u32,
12178 time_commitment_days: (25.0 * complexity_factor) as u32,
12179 },
12180 PersonnelRequirement {
12181 role: "Implementation Managers".to_string(),
12182 count: 2,
12183 time_commitment_days: phases.iter().map(|p| p.estimated_duration_days).sum(),
12184 },
12185 ];
12186
12187 let base_budget = statute_count as f64 * 50000.0;
12188 let mut breakdown = HashMap::new();
12189 breakdown.insert("Personnel".to_string(), base_budget * 0.6);
12190 breakdown.insert("Consultation and Review".to_string(), base_budget * 0.2);
12191 breakdown.insert(
12192 "Infrastructure and Training".to_string(),
12193 base_budget * 0.15,
12194 );
12195 breakdown.insert("Contingency".to_string(), base_budget * 0.05);
12196
12197 ResourceRequirements {
12198 personnel,
12199 budget_estimate: BudgetEstimate {
12200 currency: "USD".to_string(),
12201 min_amount: base_budget * 0.8,
12202 max_amount: base_budget * 1.3,
12203 breakdown,
12204 },
12205 infrastructure: vec![
12206 "Legal database access".to_string(),
12207 "Collaboration platform".to_string(),
12208 "Document management system".to_string(),
12209 ],
12210 }
12211 }
12212}
12213
12214impl Default for ImplementationRoadmapGenerator {
12215 fn default() -> Self {
12216 Self::new()
12217 }
12218}
12219
12220#[derive(Debug, Clone, Serialize, Deserialize)]
12222pub struct CostBenefitAnalysis {
12223 pub project_id: String,
12225 pub title: String,
12227 pub total_costs: CostBreakdown,
12229 pub total_benefits: BenefitAnalysis,
12231 pub net_present_value: f64,
12233 pub benefit_cost_ratio: f64,
12235 pub return_on_investment: f64,
12237 pub recommendation: CBARecommendation,
12239 pub generated_at: String,
12241}
12242
12243#[derive(Debug, Clone, Serialize, Deserialize)]
12245pub struct CostBreakdown {
12246 pub currency: String,
12248 pub direct_costs: f64,
12250 pub indirect_costs: f64,
12252 pub implementation_costs: f64,
12254 pub maintenance_costs_annual: f64,
12256 pub total_five_year: f64,
12258}
12259
12260#[derive(Debug, Clone, Serialize, Deserialize)]
12262pub struct BenefitAnalysis {
12263 pub currency: String,
12265 pub quantifiable_benefits: f64,
12267 pub qualitative_benefits: Vec<QualitativeBenefit>,
12269 pub economic_impact: f64,
12271 pub social_impact_score: f64,
12273}
12274
12275#[derive(Debug, Clone, Serialize, Deserialize)]
12277pub struct QualitativeBenefit {
12278 pub category: String,
12280 pub description: String,
12282 pub impact_level: StakeholderImpactLevel,
12284}
12285
12286#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12288pub enum ImpactLevel {
12289 Low,
12291 Medium,
12293 High,
12295 Transformative,
12297}
12298
12299#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12301pub enum CBARecommendation {
12302 StronglyRecommend,
12304 RecommendWithConditions,
12306 Neutral,
12308 DoNotRecommend,
12310}
12311
12312#[derive(Debug, Clone)]
12314pub struct CostBenefitAnalyzer;
12315
12316impl CostBenefitAnalyzer {
12317 pub fn new() -> Self {
12319 Self
12320 }
12321
12322 pub fn analyze(
12324 &self,
12325 project: &PortingProject,
12326 roadmap: &ImplementationRoadmap,
12327 ported_statutes: &[PortedStatute],
12328 ) -> CostBenefitAnalysis {
12329 let total_costs = self.calculate_costs(
12330 &roadmap.resource_requirements,
12331 roadmap.estimated_duration_days,
12332 );
12333 let total_benefits = self.estimate_benefits(ported_statutes);
12334
12335 let net_present_value = total_benefits.quantifiable_benefits - total_costs.total_five_year;
12336 let benefit_cost_ratio = if total_costs.total_five_year > 0.0 {
12337 total_benefits.quantifiable_benefits / total_costs.total_five_year
12338 } else {
12339 0.0
12340 };
12341 let return_on_investment = if total_costs.total_five_year > 0.0 {
12342 ((total_benefits.quantifiable_benefits - total_costs.total_five_year)
12343 / total_costs.total_five_year)
12344 * 100.0
12345 } else {
12346 0.0
12347 };
12348
12349 let recommendation = if benefit_cost_ratio >= 2.0 && net_present_value > 1_000_000.0 {
12350 CBARecommendation::StronglyRecommend
12351 } else if benefit_cost_ratio >= 1.0 {
12352 CBARecommendation::RecommendWithConditions
12353 } else if benefit_cost_ratio >= 0.7 {
12354 CBARecommendation::Neutral
12355 } else {
12356 CBARecommendation::DoNotRecommend
12357 };
12358
12359 CostBenefitAnalysis {
12360 project_id: project.id.clone(),
12361 title: format!("Cost-Benefit Analysis: {}", project.name),
12362 total_costs,
12363 total_benefits,
12364 net_present_value,
12365 benefit_cost_ratio,
12366 return_on_investment,
12367 recommendation,
12368 generated_at: chrono::Utc::now().to_rfc3339(),
12369 }
12370 }
12371
12372 fn calculate_costs(
12373 &self,
12374 resources: &ResourceRequirements,
12375 duration_days: u32,
12376 ) -> CostBreakdown {
12377 let direct_costs = resources.budget_estimate.min_amount;
12378 let indirect_costs = direct_costs * 0.25;
12379 let implementation_costs = (duration_days as f64 / 30.0) * 100_000.0;
12380 let maintenance_costs_annual = direct_costs * 0.15;
12381 let total_five_year =
12382 direct_costs + indirect_costs + implementation_costs + (maintenance_costs_annual * 5.0);
12383
12384 CostBreakdown {
12385 currency: resources.budget_estimate.currency.clone(),
12386 direct_costs,
12387 indirect_costs,
12388 implementation_costs,
12389 maintenance_costs_annual,
12390 total_five_year,
12391 }
12392 }
12393
12394 fn estimate_benefits(&self, ported_statutes: &[PortedStatute]) -> BenefitAnalysis {
12395 let statute_count = ported_statutes.len();
12396 let avg_compatibility = if !ported_statutes.is_empty() {
12397 ported_statutes
12398 .iter()
12399 .map(|s| s.compatibility_score)
12400 .sum::<f64>()
12401 / ported_statutes.len() as f64
12402 } else {
12403 0.0
12404 };
12405
12406 let base_benefit_per_statute = 200_000.0;
12407 let quantifiable_benefits =
12408 statute_count as f64 * base_benefit_per_statute * avg_compatibility * 5.0;
12409
12410 let economic_impact = quantifiable_benefits * 1.5;
12411 let social_impact_score = avg_compatibility * 0.9;
12412
12413 let qualitative_benefits = vec![
12414 QualitativeBenefit {
12415 category: "Legal Harmonization".to_string(),
12416 description: "Improved legal compatibility between jurisdictions".to_string(),
12417 impact_level: if avg_compatibility >= 0.8 {
12418 StakeholderImpactLevel::High
12419 } else {
12420 StakeholderImpactLevel::Medium
12421 },
12422 },
12423 QualitativeBenefit {
12424 category: "Governance".to_string(),
12425 description: "Enhanced legal framework and governance quality".to_string(),
12426 impact_level: StakeholderImpactLevel::High,
12427 },
12428 QualitativeBenefit {
12429 category: "International Cooperation".to_string(),
12430 description: "Strengthened bilateral legal cooperation".to_string(),
12431 impact_level: StakeholderImpactLevel::Medium,
12432 },
12433 ];
12434
12435 BenefitAnalysis {
12436 currency: "USD".to_string(),
12437 quantifiable_benefits,
12438 qualitative_benefits,
12439 economic_impact,
12440 social_impact_score,
12441 }
12442 }
12443}
12444
12445impl Default for CostBenefitAnalyzer {
12446 fn default() -> Self {
12447 Self::new()
12448 }
12449}
12450
12451#[derive(Debug, Clone, Serialize, Deserialize)]
12453pub struct ComplianceCertification {
12454 pub id: String,
12456 pub project_id: String,
12458 pub title: String,
12460 pub certification_level: CertificationLevel,
12462 pub status: CertificationStatus,
12464 pub certified_statutes: Vec<String>,
12466 pub validation_results: Vec<ValidationResult>,
12468 pub certifier: CertifierInfo,
12470 pub certification_date: String,
12472 pub expiration_date: Option<String>,
12474 pub conditions: Vec<String>,
12476 pub signature: Option<String>,
12478}
12479
12480#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12482pub enum CertificationLevel {
12483 Provisional,
12485 Standard,
12487 Enhanced,
12489 Full,
12491}
12492
12493#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12495pub enum CertificationStatus {
12496 Pending,
12498 UnderReview,
12500 Certified,
12502 Conditional,
12504 Revoked,
12506}
12507
12508#[derive(Debug, Clone, Serialize, Deserialize)]
12510pub struct CertifierInfo {
12511 pub name: String,
12513 pub organization: String,
12515 pub credentials: Vec<String>,
12517 pub contact: String,
12519}
12520
12521#[derive(Debug, Clone)]
12523pub struct ComplianceCertificationManager {
12524 certifications: HashMap<String, ComplianceCertification>,
12525}
12526
12527impl ComplianceCertificationManager {
12528 pub fn new() -> Self {
12530 Self {
12531 certifications: HashMap::new(),
12532 }
12533 }
12534
12535 pub fn issue_certification(
12537 &mut self,
12538 project_id: String,
12539 validation_results: Vec<ValidationResult>,
12540 certifier: CertifierInfo,
12541 ) -> ComplianceCertification {
12542 let id = uuid::Uuid::new_v4().to_string();
12543
12544 let overall_score = if !validation_results.is_empty() {
12545 validation_results
12546 .iter()
12547 .map(|r| r.overall_score)
12548 .sum::<f64>()
12549 / validation_results.len() as f64
12550 } else {
12551 0.0
12552 };
12553
12554 let certification_level = if overall_score >= 0.95 {
12555 CertificationLevel::Full
12556 } else if overall_score >= 0.85 {
12557 CertificationLevel::Enhanced
12558 } else if overall_score >= 0.75 {
12559 CertificationLevel::Standard
12560 } else {
12561 CertificationLevel::Provisional
12562 };
12563
12564 let status = if overall_score >= 0.75 {
12565 CertificationStatus::Certified
12566 } else if overall_score >= 0.6 {
12567 CertificationStatus::Conditional
12568 } else {
12569 CertificationStatus::Pending
12570 };
12571
12572 let certified_statutes: Vec<String> = validation_results
12573 .iter()
12574 .filter(|r| r.overall_score >= 0.75)
12575 .map(|r| r.id.clone())
12576 .collect();
12577
12578 let mut conditions = Vec::new();
12579 if overall_score < 0.95 {
12580 conditions.push("Periodic review required every 12 months".to_string());
12581 }
12582 if overall_score < 0.85 {
12583 conditions.push("Implementation monitoring required".to_string());
12584 }
12585
12586 let now = chrono::Utc::now();
12587 let expiration = if overall_score >= 0.85 {
12588 Some((now + chrono::Duration::days(365 * 3)).to_rfc3339())
12589 } else {
12590 Some((now + chrono::Duration::days(365)).to_rfc3339())
12591 };
12592
12593 let certification = ComplianceCertification {
12594 id: id.clone(),
12595 project_id: project_id.clone(),
12596 title: format!("Compliance Certification - Project {}", project_id),
12597 certification_level,
12598 status,
12599 certified_statutes,
12600 validation_results,
12601 certifier,
12602 certification_date: now.to_rfc3339(),
12603 expiration_date: expiration,
12604 conditions,
12605 signature: Some(format!("CERT-{}", &id[..8])),
12606 };
12607
12608 self.certifications.insert(id, certification.clone());
12609 certification
12610 }
12611
12612 pub fn get_certification(&self, id: &str) -> Option<&ComplianceCertification> {
12614 self.certifications.get(id)
12615 }
12616
12617 pub fn revoke_certification(&mut self, id: &str) -> Option<()> {
12619 let cert = self.certifications.get_mut(id)?;
12620 cert.status = CertificationStatus::Revoked;
12621 Some(())
12622 }
12623}
12624
12625impl Default for ComplianceCertificationManager {
12626 fn default() -> Self {
12627 Self::new()
12628 }
12629}
12630
12631#[derive(Debug, Clone, Serialize, Deserialize)]
12637pub struct ApiPortingRequest {
12638 pub source_jurisdiction: String,
12640 pub target_jurisdiction: String,
12642 pub statute_ids: Vec<String>,
12644 pub options: PortingOptions,
12646}
12647
12648#[derive(Debug, Clone, Serialize, Deserialize)]
12650pub struct ApiPortingResponse {
12651 pub request_id: String,
12653 pub status: ApiStatus,
12655 pub results: Option<Vec<PortedStatute>>,
12657 pub error: Option<String>,
12659 pub processing_time_ms: u64,
12661}
12662
12663#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12665pub enum ApiStatus {
12666 Accepted,
12668 Processing,
12670 Completed,
12672 Failed,
12674}
12675
12676#[derive(Debug, Clone)]
12678pub struct BilateralAgreementTemplateLibrary {
12679 templates: HashMap<String, BilateralAgreementTemplate>,
12680}
12681
12682#[derive(Debug, Clone, Serialize, Deserialize)]
12684pub struct BilateralAgreementTemplate {
12685 pub id: String,
12687 pub name: String,
12689 pub description: String,
12691 pub applicable_systems: Vec<LegalSystem>,
12693 pub sections: Vec<TemplateSection>,
12695 pub required_parameters: Vec<TemplateParameter>,
12697 pub optional_parameters: Vec<TemplateParameter>,
12699}
12700
12701#[derive(Debug, Clone, Serialize, Deserialize)]
12703pub struct TemplateSection {
12704 pub section_number: u32,
12706 pub title: String,
12708 pub content_template: String,
12710 pub required: bool,
12712}
12713
12714#[derive(Debug, Clone, Serialize, Deserialize)]
12716pub struct TemplateParameter {
12717 pub name: String,
12719 pub description: String,
12721 pub parameter_type: ParameterType,
12723 pub default_value: Option<String>,
12725}
12726
12727#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12729pub enum ParameterType {
12730 String,
12732 Number,
12734 Date,
12736 Boolean,
12738 List,
12740}
12741
12742impl BilateralAgreementTemplateLibrary {
12743 pub fn new() -> Self {
12745 let mut library = Self {
12746 templates: HashMap::new(),
12747 };
12748 library.add_default_templates();
12749 library
12750 }
12751
12752 fn add_default_templates(&mut self) {
12753 self.add_template(BilateralAgreementTemplate {
12755 id: "general-bilateral".to_string(),
12756 name: "General Bilateral Legal Cooperation Agreement".to_string(),
12757 description: "Standard template for bilateral legal cooperation".to_string(),
12758 applicable_systems: vec![
12759 LegalSystem::CivilLaw,
12760 LegalSystem::CommonLaw,
12761 ],
12762 sections: vec![
12763 TemplateSection {
12764 section_number: 1,
12765 title: "Parties and Purpose".to_string(),
12766 content_template: "This agreement is entered into between {{source_jurisdiction}} and {{target_jurisdiction}} for the purpose of {{purpose}}.".to_string(),
12767 required: true,
12768 },
12769 TemplateSection {
12770 section_number: 2,
12771 title: "Scope of Cooperation".to_string(),
12772 content_template: "The parties agree to cooperate in the following areas: {{cooperation_areas}}.".to_string(),
12773 required: true,
12774 },
12775 TemplateSection {
12776 section_number: 3,
12777 title: "Legal Framework Porting".to_string(),
12778 content_template: "The parties agree to facilitate the porting of legal frameworks according to the principles set forth in {{porting_principles}}.".to_string(),
12779 required: true,
12780 },
12781 TemplateSection {
12782 section_number: 4,
12783 title: "Cultural Adaptation".to_string(),
12784 content_template: "All ported statutes shall be adapted to respect the cultural, religious, and social norms of the target jurisdiction.".to_string(),
12785 required: true,
12786 },
12787 TemplateSection {
12788 section_number: 5,
12789 title: "Review and Approval Process".to_string(),
12790 content_template: "Ported statutes shall undergo review by {{review_body}} before implementation.".to_string(),
12791 required: true,
12792 },
12793 ],
12794 required_parameters: vec![
12795 TemplateParameter {
12796 name: "source_jurisdiction".to_string(),
12797 description: "Source jurisdiction name".to_string(),
12798 parameter_type: ParameterType::String,
12799 default_value: None,
12800 },
12801 TemplateParameter {
12802 name: "target_jurisdiction".to_string(),
12803 description: "Target jurisdiction name".to_string(),
12804 parameter_type: ParameterType::String,
12805 default_value: None,
12806 },
12807 TemplateParameter {
12808 name: "purpose".to_string(),
12809 description: "Purpose of the agreement".to_string(),
12810 parameter_type: ParameterType::String,
12811 default_value: Some("legal framework cooperation and mutual development".to_string()),
12812 },
12813 ],
12814 optional_parameters: vec![
12815 TemplateParameter {
12816 name: "cooperation_areas".to_string(),
12817 description: "Areas of legal cooperation".to_string(),
12818 parameter_type: ParameterType::List,
12819 default_value: Some("civil law, commercial law, administrative law".to_string()),
12820 },
12821 ],
12822 });
12823 }
12824
12825 pub fn add_template(&mut self, template: BilateralAgreementTemplate) {
12827 self.templates.insert(template.id.clone(), template);
12828 }
12829
12830 pub fn get_template(&self, id: &str) -> Option<&BilateralAgreementTemplate> {
12832 self.templates.get(id)
12833 }
12834
12835 pub fn list_templates(&self) -> Vec<&BilateralAgreementTemplate> {
12837 self.templates.values().collect()
12838 }
12839
12840 pub fn generate_agreement(
12842 &self,
12843 template_id: &str,
12844 parameters: &HashMap<String, String>,
12845 ) -> Option<String> {
12846 let template = self.get_template(template_id)?;
12847 let mut agreement = String::new();
12848
12849 agreement.push_str(&format!("# {}\n\n", template.name));
12850
12851 for section in &template.sections {
12852 agreement.push_str(&format!(
12853 "## Section {}: {}\n\n",
12854 section.section_number, section.title
12855 ));
12856
12857 let mut content = section.content_template.clone();
12858 for (key, value) in parameters {
12859 content = content.replace(&format!("{{{{{}}}}}", key), value);
12860 }
12861
12862 agreement.push_str(&format!("{}\n\n", content));
12863 }
12864
12865 Some(agreement)
12866 }
12867}
12868
12869impl Default for BilateralAgreementTemplateLibrary {
12870 fn default() -> Self {
12871 Self::new()
12872 }
12873}
12874
12875#[derive(Debug, Clone, Serialize, Deserialize)]
12877pub struct RegulatorySandbox {
12878 pub id: String,
12880 pub name: String,
12882 pub description: String,
12884 pub status: SandboxStatus,
12886 pub test_statutes: Vec<String>,
12888 pub scenarios: Vec<TestScenario>,
12890 pub results: Vec<SandboxTestResult>,
12892 pub start_date: String,
12894 pub end_date: Option<String>,
12896}
12897
12898#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12900pub enum SandboxStatus {
12901 Planning,
12903 Active,
12905 Evaluation,
12907 Completed,
12909 Terminated,
12911}
12912
12913#[derive(Debug, Clone, Serialize, Deserialize)]
12915pub struct TestScenario {
12916 pub id: String,
12918 pub name: String,
12920 pub description: String,
12922 pub parameters: HashMap<String, String>,
12924 pub expected_outcomes: Vec<String>,
12926}
12927
12928#[derive(Debug, Clone, Serialize, Deserialize)]
12930pub struct SandboxTestResult {
12931 pub scenario_id: String,
12933 pub status: TestStatus,
12935 pub actual_outcomes: Vec<String>,
12937 pub issues: Vec<String>,
12939 pub recommendations: Vec<String>,
12941 pub test_date: String,
12943}
12944
12945#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12947pub enum TestStatus {
12948 Passed,
12950 PassedWithIssues,
12952 Failed,
12954 Inconclusive,
12956}
12957
12958#[derive(Debug, Clone)]
12960pub struct RegulatorySandboxManager {
12961 sandboxes: HashMap<String, RegulatorySandbox>,
12962}
12963
12964impl RegulatorySandboxManager {
12965 pub fn new() -> Self {
12967 Self {
12968 sandboxes: HashMap::new(),
12969 }
12970 }
12971
12972 pub fn create_sandbox(
12974 &mut self,
12975 name: String,
12976 description: String,
12977 test_statutes: Vec<String>,
12978 ) -> RegulatorySandbox {
12979 let id = uuid::Uuid::new_v4().to_string();
12980 let sandbox = RegulatorySandbox {
12981 id: id.clone(),
12982 name,
12983 description,
12984 status: SandboxStatus::Planning,
12985 test_statutes,
12986 scenarios: Vec::new(),
12987 results: Vec::new(),
12988 start_date: chrono::Utc::now().to_rfc3339(),
12989 end_date: None,
12990 };
12991 self.sandboxes.insert(id, sandbox.clone());
12992 sandbox
12993 }
12994
12995 pub fn add_scenario(&mut self, sandbox_id: &str, scenario: TestScenario) -> Option<()> {
12997 let sandbox = self.sandboxes.get_mut(sandbox_id)?;
12998 sandbox.scenarios.push(scenario);
12999 Some(())
13000 }
13001
13002 pub fn record_result(&mut self, sandbox_id: &str, result: SandboxTestResult) -> Option<()> {
13004 let sandbox = self.sandboxes.get_mut(sandbox_id)?;
13005 sandbox.results.push(result);
13006 Some(())
13007 }
13008
13009 pub fn activate_sandbox(&mut self, sandbox_id: &str) -> Option<()> {
13011 let sandbox = self.sandboxes.get_mut(sandbox_id)?;
13012 sandbox.status = SandboxStatus::Active;
13013 Some(())
13014 }
13015
13016 pub fn complete_sandbox(&mut self, sandbox_id: &str) -> Option<()> {
13018 let sandbox = self.sandboxes.get_mut(sandbox_id)?;
13019 sandbox.status = SandboxStatus::Completed;
13020 sandbox.end_date = Some(chrono::Utc::now().to_rfc3339());
13021 Some(())
13022 }
13023
13024 pub fn get_sandbox(&self, sandbox_id: &str) -> Option<&RegulatorySandbox> {
13026 self.sandboxes.get(sandbox_id)
13027 }
13028}
13029
13030impl Default for RegulatorySandboxManager {
13031 fn default() -> Self {
13032 Self::new()
13033 }
13034}
13035
13036#[derive(Debug, Clone, Serialize, Deserialize)]
13038pub struct AffectedPartyNotification {
13039 pub id: String,
13041 pub project_id: String,
13043 pub title: String,
13045 pub content: String,
13047 pub affected_categories: Vec<AffectedPartyCategory>,
13049 pub channels: Vec<NotificationChannel>,
13051 pub notification_date: String,
13053 pub response_deadline: Option<String>,
13055 pub feedback: Vec<PublicFeedback>,
13057}
13058
13059#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13061pub enum AffectedPartyCategory {
13062 GeneralPublic,
13064 Businesses,
13066 NonProfits,
13068 GovernmentAgencies,
13070 LegalProfessionals,
13072 AcademicInstitutions,
13074}
13075
13076#[derive(Debug, Clone, Serialize, Deserialize)]
13078pub struct PublicFeedback {
13079 pub id: String,
13081 pub submitter: Option<String>,
13083 pub category: FeedbackCategory,
13085 pub content: String,
13087 pub submitted_at: String,
13089}
13090
13091#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13093pub enum FeedbackCategory {
13094 Support,
13096 Concern,
13098 Question,
13100 Suggestion,
13102 Objection,
13104}
13105
13106#[derive(Debug, Clone)]
13108pub struct AffectedPartyNotificationManager {
13109 notifications: HashMap<String, AffectedPartyNotification>,
13110}
13111
13112impl AffectedPartyNotificationManager {
13113 pub fn new() -> Self {
13115 Self {
13116 notifications: HashMap::new(),
13117 }
13118 }
13119
13120 pub fn send_notification(
13122 &mut self,
13123 project_id: String,
13124 title: String,
13125 content: String,
13126 affected_categories: Vec<AffectedPartyCategory>,
13127 response_deadline_days: Option<u32>,
13128 ) -> AffectedPartyNotification {
13129 let id = uuid::Uuid::new_v4().to_string();
13130 let now = chrono::Utc::now();
13131
13132 let response_deadline = response_deadline_days
13133 .map(|days| (now + chrono::Duration::days(days as i64)).to_rfc3339());
13134
13135 let notification = AffectedPartyNotification {
13136 id: id.clone(),
13137 project_id,
13138 title,
13139 content,
13140 affected_categories,
13141 channels: vec![
13142 NotificationChannel::Email,
13143 NotificationChannel::Website,
13144 NotificationChannel::PublicNotice,
13145 ],
13146 notification_date: now.to_rfc3339(),
13147 response_deadline,
13148 feedback: Vec::new(),
13149 };
13150
13151 self.notifications.insert(id, notification.clone());
13152 notification
13153 }
13154
13155 pub fn record_feedback(
13157 &mut self,
13158 notification_id: &str,
13159 feedback: PublicFeedback,
13160 ) -> Option<()> {
13161 let notification = self.notifications.get_mut(notification_id)?;
13162 notification.feedback.push(feedback);
13163 Some(())
13164 }
13165
13166 pub fn get_notification(&self, notification_id: &str) -> Option<&AffectedPartyNotification> {
13168 self.notifications.get(notification_id)
13169 }
13170
13171 pub fn list_feedback(&self, notification_id: &str) -> Option<&[PublicFeedback]> {
13173 self.notifications
13174 .get(notification_id)
13175 .map(|n| n.feedback.as_slice())
13176 }
13177}
13178
13179impl Default for AffectedPartyNotificationManager {
13180 fn default() -> Self {
13181 Self::new()
13182 }
13183}
13184
13185#[derive(Debug, Clone, Serialize, Deserialize)]
13187pub struct PublicCommentPeriod {
13188 pub id: String,
13190 pub project_id: String,
13192 pub title: String,
13194 pub description: String,
13196 pub start_date: String,
13198 pub end_date: String,
13200 pub status: CommentPeriodStatus,
13202 pub documents: Vec<CommentDocument>,
13204 pub comments: Vec<PublicComment>,
13206 pub hearings: Vec<PublicHearing>,
13208}
13209
13210#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13212pub enum CommentPeriodStatus {
13213 Upcoming,
13215 Open,
13217 Closed,
13219 Extended,
13221}
13222
13223#[derive(Debug, Clone, Serialize, Deserialize)]
13225pub struct CommentDocument {
13226 pub id: String,
13228 pub title: String,
13230 pub document_type: DocumentType,
13232 pub description: String,
13234 pub url: String,
13236}
13237
13238#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13240pub enum DocumentType {
13241 DraftStatute,
13243 ImpactAssessment,
13245 ExplanatoryMemorandum,
13247 TechnicalReport,
13249}
13250
13251#[derive(Debug, Clone, Serialize, Deserialize)]
13253pub struct PublicComment {
13254 pub id: String,
13256 pub commenter: CommenterInfo,
13258 pub comment_text: String,
13260 pub document_id: Option<String>,
13262 pub section_reference: Option<String>,
13264 pub submitted_at: String,
13266 pub category: FeedbackCategory,
13268}
13269
13270#[derive(Debug, Clone, Serialize, Deserialize)]
13272pub struct CommenterInfo {
13273 pub name: Option<String>,
13275 pub organization: Option<String>,
13277 pub email: Option<String>,
13279 pub affiliation: AffectedPartyCategory,
13281}
13282
13283#[derive(Debug, Clone, Serialize, Deserialize)]
13285pub struct PublicHearing {
13286 pub id: String,
13288 pub title: String,
13290 pub datetime: String,
13292 pub location: String,
13294 pub virtual_link: Option<String>,
13296 pub agenda: Vec<String>,
13298 pub registration_required: bool,
13300}
13301
13302#[derive(Debug, Clone)]
13304pub struct PublicCommentPeriodManager {
13305 periods: HashMap<String, PublicCommentPeriod>,
13306}
13307
13308impl PublicCommentPeriodManager {
13309 pub fn new() -> Self {
13311 Self {
13312 periods: HashMap::new(),
13313 }
13314 }
13315
13316 pub fn open_comment_period(
13318 &mut self,
13319 project_id: String,
13320 title: String,
13321 description: String,
13322 duration_days: u32,
13323 ) -> PublicCommentPeriod {
13324 let id = uuid::Uuid::new_v4().to_string();
13325 let now = chrono::Utc::now();
13326 let end_date = now + chrono::Duration::days(duration_days as i64);
13327
13328 let period = PublicCommentPeriod {
13329 id: id.clone(),
13330 project_id,
13331 title,
13332 description,
13333 start_date: now.to_rfc3339(),
13334 end_date: end_date.to_rfc3339(),
13335 status: CommentPeriodStatus::Open,
13336 documents: Vec::new(),
13337 comments: Vec::new(),
13338 hearings: Vec::new(),
13339 };
13340
13341 self.periods.insert(id, period.clone());
13342 period
13343 }
13344
13345 pub fn add_document(&mut self, period_id: &str, document: CommentDocument) -> Option<()> {
13347 let period = self.periods.get_mut(period_id)?;
13348 period.documents.push(document);
13349 Some(())
13350 }
13351
13352 pub fn submit_comment(&mut self, period_id: &str, comment: PublicComment) -> Option<()> {
13354 let period = self.periods.get_mut(period_id)?;
13355 if period.status == CommentPeriodStatus::Open
13356 || period.status == CommentPeriodStatus::Extended
13357 {
13358 period.comments.push(comment);
13359 Some(())
13360 } else {
13361 None
13362 }
13363 }
13364
13365 pub fn schedule_hearing(&mut self, period_id: &str, hearing: PublicHearing) -> Option<()> {
13367 let period = self.periods.get_mut(period_id)?;
13368 period.hearings.push(hearing);
13369 Some(())
13370 }
13371
13372 pub fn extend_period(&mut self, period_id: &str, additional_days: u32) -> Option<()> {
13374 let period = self.periods.get_mut(period_id)?;
13375 if let Ok(current_end) = chrono::DateTime::parse_from_rfc3339(&period.end_date) {
13376 let new_end = current_end + chrono::Duration::days(additional_days as i64);
13377 period.end_date = new_end.to_rfc3339();
13378 period.status = CommentPeriodStatus::Extended;
13379 Some(())
13380 } else {
13381 None
13382 }
13383 }
13384
13385 pub fn close_period(&mut self, period_id: &str) -> Option<()> {
13387 let period = self.periods.get_mut(period_id)?;
13388 period.status = CommentPeriodStatus::Closed;
13389 Some(())
13390 }
13391
13392 pub fn get_period(&self, period_id: &str) -> Option<&PublicCommentPeriod> {
13394 self.periods.get(period_id)
13395 }
13396
13397 pub fn list_comments(&self, period_id: &str) -> Option<&[PublicComment]> {
13399 self.periods.get(period_id).map(|p| p.comments.as_slice())
13400 }
13401
13402 pub fn generate_comment_summary(&self, period_id: &str) -> Option<CommentSummary> {
13404 let period = self.periods.get(period_id)?;
13405
13406 let total_comments = period.comments.len();
13407 let mut category_counts: HashMap<FeedbackCategory, usize> = HashMap::new();
13408 let mut affiliation_counts: HashMap<AffectedPartyCategory, usize> = HashMap::new();
13409
13410 for comment in &period.comments {
13411 *category_counts.entry(comment.category).or_insert(0) += 1;
13412 *affiliation_counts
13413 .entry(comment.commenter.affiliation)
13414 .or_insert(0) += 1;
13415 }
13416
13417 Some(CommentSummary {
13418 period_id: period_id.to_string(),
13419 total_comments,
13420 category_breakdown: category_counts,
13421 affiliation_breakdown: affiliation_counts,
13422 key_themes: self.extract_key_themes(&period.comments),
13423 })
13424 }
13425
13426 fn extract_key_themes(&self, _comments: &[PublicComment]) -> Vec<String> {
13427 vec![
13429 "Constitutional compatibility concerns".to_string(),
13430 "Implementation timeline questions".to_string(),
13431 "Cultural adaptation suggestions".to_string(),
13432 ]
13433 }
13434}
13435
13436impl Default for PublicCommentPeriodManager {
13437 fn default() -> Self {
13438 Self::new()
13439 }
13440}
13441
13442#[derive(Debug, Clone, Serialize, Deserialize)]
13444pub struct CommentSummary {
13445 pub period_id: String,
13447 pub total_comments: usize,
13449 pub category_breakdown: HashMap<FeedbackCategory, usize>,
13451 pub affiliation_breakdown: HashMap<AffectedPartyCategory, usize>,
13453 pub key_themes: Vec<String>,
13455}
13456
13457#[derive(Debug, Clone, Serialize, Deserialize)]
13463pub struct DiscussionThread {
13464 pub id: String,
13466 pub project_id: String,
13468 pub title: String,
13470 pub context: String,
13472 pub status: ThreadStatus,
13474 pub comments: Vec<ThreadComment>,
13476 pub created_at: String,
13478 pub created_by: String,
13480 pub tags: Vec<String>,
13482 pub resolved_by: Option<String>,
13484 pub resolved_at: Option<String>,
13486}
13487
13488#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13490pub enum ThreadStatus {
13491 Open,
13493 UnderReview,
13495 Resolved,
13497 Archived,
13499}
13500
13501#[derive(Debug, Clone, Serialize, Deserialize)]
13503pub struct ThreadComment {
13504 pub id: String,
13506 pub parent_id: Option<String>,
13508 pub author_id: String,
13510 pub text: String,
13512 pub created_at: String,
13514 pub edited_at: Option<String>,
13516 pub replies: Vec<ThreadComment>,
13518 pub upvotes: u32,
13520 pub upvoted_by: Vec<String>,
13522 pub is_important: bool,
13524}
13525
13526#[derive(Debug)]
13528pub struct DiscussionThreadManager {
13529 threads: HashMap<String, DiscussionThread>,
13530}
13531
13532impl DiscussionThreadManager {
13533 pub fn new() -> Self {
13535 Self {
13536 threads: HashMap::new(),
13537 }
13538 }
13539
13540 pub fn create_thread(
13542 &mut self,
13543 project_id: String,
13544 title: String,
13545 context: String,
13546 created_by: String,
13547 tags: Vec<String>,
13548 ) -> DiscussionThread {
13549 let thread = DiscussionThread {
13550 id: uuid::Uuid::new_v4().to_string(),
13551 project_id,
13552 title,
13553 context,
13554 status: ThreadStatus::Open,
13555 comments: Vec::new(),
13556 created_at: chrono::Utc::now().to_rfc3339(),
13557 created_by,
13558 tags,
13559 resolved_by: None,
13560 resolved_at: None,
13561 };
13562
13563 self.threads.insert(thread.id.clone(), thread.clone());
13564 thread
13565 }
13566
13567 pub fn add_comment(
13569 &mut self,
13570 thread_id: &str,
13571 author_id: String,
13572 text: String,
13573 parent_id: Option<String>,
13574 ) -> Option<ThreadComment> {
13575 let thread = self.threads.get_mut(thread_id)?;
13576
13577 let comment = ThreadComment {
13578 id: uuid::Uuid::new_v4().to_string(),
13579 parent_id: parent_id.clone(),
13580 author_id,
13581 text,
13582 created_at: chrono::Utc::now().to_rfc3339(),
13583 edited_at: None,
13584 replies: Vec::new(),
13585 upvotes: 0,
13586 upvoted_by: Vec::new(),
13587 is_important: false,
13588 };
13589
13590 if let Some(parent) = parent_id {
13592 Self::add_reply_to_comment(&mut thread.comments, &parent, comment.clone())?;
13593 } else {
13594 thread.comments.push(comment.clone());
13595 }
13596
13597 Some(comment)
13598 }
13599
13600 fn add_reply_to_comment(
13601 comments: &mut Vec<ThreadComment>,
13602 parent_id: &str,
13603 reply: ThreadComment,
13604 ) -> Option<()> {
13605 for comment in comments {
13606 if comment.id == parent_id {
13607 comment.replies.push(reply);
13608 return Some(());
13609 }
13610 if Self::add_reply_to_comment(&mut comment.replies, parent_id, reply.clone()).is_some()
13612 {
13613 return Some(());
13614 }
13615 }
13616 None
13617 }
13618
13619 pub fn upvote_comment(
13621 &mut self,
13622 thread_id: &str,
13623 comment_id: &str,
13624 user_id: String,
13625 ) -> Option<()> {
13626 let thread = self.threads.get_mut(thread_id)?;
13627 Self::upvote_comment_recursive(&mut thread.comments, comment_id, user_id)
13628 }
13629
13630 fn upvote_comment_recursive(
13631 comments: &mut Vec<ThreadComment>,
13632 comment_id: &str,
13633 user_id: String,
13634 ) -> Option<()> {
13635 for comment in comments {
13636 if comment.id == comment_id {
13637 if !comment.upvoted_by.contains(&user_id) {
13638 comment.upvoted_by.push(user_id);
13639 comment.upvotes += 1;
13640 }
13641 return Some(());
13642 }
13643 if Self::upvote_comment_recursive(&mut comment.replies, comment_id, user_id.clone())
13644 .is_some()
13645 {
13646 return Some(());
13647 }
13648 }
13649 None
13650 }
13651
13652 pub fn resolve_thread(&mut self, thread_id: &str, resolved_by: String) -> Option<()> {
13654 let thread = self.threads.get_mut(thread_id)?;
13655 thread.status = ThreadStatus::Resolved;
13656 thread.resolved_by = Some(resolved_by);
13657 thread.resolved_at = Some(chrono::Utc::now().to_rfc3339());
13658 Some(())
13659 }
13660
13661 pub fn get_thread(&self, thread_id: &str) -> Option<&DiscussionThread> {
13663 self.threads.get(thread_id)
13664 }
13665
13666 pub fn list_threads(&self, project_id: &str) -> Vec<&DiscussionThread> {
13668 self.threads
13669 .values()
13670 .filter(|t| t.project_id == project_id)
13671 .collect()
13672 }
13673}
13674
13675impl Default for DiscussionThreadManager {
13676 fn default() -> Self {
13677 Self::new()
13678 }
13679}
13680
13681#[derive(Debug, Clone, Serialize, Deserialize)]
13687pub struct StakeholderVote {
13688 pub id: String,
13690 pub project_id: String,
13692 pub title: String,
13694 pub description: String,
13696 pub vote_type: VoteType,
13698 pub options: Vec<VoteOption>,
13700 pub eligible_voters: Vec<String>,
13702 pub votes_cast: HashMap<String, Vec<String>>, pub status: VoteStatus,
13706 pub start_time: String,
13708 pub end_time: String,
13710 pub minimum_participation: Option<f64>,
13712 pub approval_threshold: Option<f64>,
13714}
13715
13716#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13718pub enum VoteType {
13719 SingleChoice,
13721 MultipleChoice,
13723 Ranking,
13725 Approval,
13727}
13728
13729#[derive(Debug, Clone, Serialize, Deserialize)]
13731pub struct VoteOption {
13732 pub id: String,
13734 pub text: String,
13736 pub description: String,
13738 pub vote_count: u32,
13740}
13741
13742#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13744pub enum VoteStatus {
13745 Pending,
13747 Active,
13749 Closed,
13751 Passed,
13753 Failed,
13755}
13756
13757#[derive(Debug, Clone, Serialize, Deserialize)]
13759pub struct VoteResult {
13760 pub vote_id: String,
13762 pub total_eligible: usize,
13764 pub total_votes: usize,
13766 pub participation_rate: f64,
13768 pub winning_options: Vec<String>,
13770 pub results: HashMap<String, u32>,
13772 pub passed: bool,
13774}
13775
13776#[derive(Debug)]
13778pub struct VotingManager {
13779 votes: HashMap<String, StakeholderVote>,
13780}
13781
13782impl VotingManager {
13783 pub fn new() -> Self {
13785 Self {
13786 votes: HashMap::new(),
13787 }
13788 }
13789
13790 pub fn create_vote(
13792 &mut self,
13793 project_id: String,
13794 title: String,
13795 description: String,
13796 vote_type: VoteType,
13797 options: Vec<VoteOption>,
13798 eligible_voters: Vec<String>,
13799 duration_hours: u32,
13800 ) -> StakeholderVote {
13801 let now = chrono::Utc::now();
13802 let end_time = now + chrono::Duration::hours(duration_hours as i64);
13803
13804 let vote = StakeholderVote {
13805 id: uuid::Uuid::new_v4().to_string(),
13806 project_id,
13807 title,
13808 description,
13809 vote_type,
13810 options,
13811 eligible_voters,
13812 votes_cast: HashMap::new(),
13813 status: VoteStatus::Active,
13814 start_time: now.to_rfc3339(),
13815 end_time: end_time.to_rfc3339(),
13816 minimum_participation: None,
13817 approval_threshold: Some(0.5), };
13819
13820 self.votes.insert(vote.id.clone(), vote.clone());
13821 vote
13822 }
13823
13824 pub fn cast_vote(
13826 &mut self,
13827 vote_id: &str,
13828 voter_id: String,
13829 selected_options: Vec<String>,
13830 ) -> Option<()> {
13831 let vote = self.votes.get_mut(vote_id)?;
13832
13833 if !vote.eligible_voters.contains(&voter_id) {
13835 return None;
13836 }
13837
13838 if vote.status != VoteStatus::Active {
13840 return None;
13841 }
13842
13843 match vote.vote_type {
13845 VoteType::SingleChoice => {
13846 if selected_options.len() != 1 {
13847 return None;
13848 }
13849 }
13850 VoteType::MultipleChoice | VoteType::Approval | VoteType::Ranking => {
13851 }
13853 }
13854
13855 vote.votes_cast.insert(voter_id, selected_options.clone());
13857
13858 for option_id in selected_options {
13860 if let Some(option) = vote.options.iter_mut().find(|o| o.id == option_id) {
13861 option.vote_count += 1;
13862 }
13863 }
13864
13865 Some(())
13866 }
13867
13868 pub fn close_vote(&mut self, vote_id: &str) -> Option<VoteResult> {
13870 let vote = self.votes.get_mut(vote_id)?;
13871 vote.status = VoteStatus::Closed;
13872
13873 let total_eligible = vote.eligible_voters.len();
13874 let total_votes = vote.votes_cast.len();
13875 let participation_rate = total_votes as f64 / total_eligible as f64;
13876
13877 let max_votes = vote.options.iter().map(|o| o.vote_count).max().unwrap_or(0);
13879 let winning_options: Vec<String> = vote
13880 .options
13881 .iter()
13882 .filter(|o| o.vote_count == max_votes)
13883 .map(|o| o.text.clone())
13884 .collect();
13885
13886 let passed = if let Some(min_participation) = vote.minimum_participation {
13888 if participation_rate < min_participation {
13889 vote.status = VoteStatus::Failed;
13890 false
13891 } else {
13892 Self::check_approval_threshold(vote, max_votes, total_votes)
13893 }
13894 } else {
13895 Self::check_approval_threshold(vote, max_votes, total_votes)
13896 };
13897
13898 if passed {
13899 vote.status = VoteStatus::Passed;
13900 } else {
13901 vote.status = VoteStatus::Failed;
13902 }
13903
13904 let mut results = HashMap::new();
13905 for option in &vote.options {
13906 results.insert(option.text.clone(), option.vote_count);
13907 }
13908
13909 Some(VoteResult {
13910 vote_id: vote_id.to_string(),
13911 total_eligible,
13912 total_votes,
13913 participation_rate,
13914 winning_options,
13915 results,
13916 passed,
13917 })
13918 }
13919
13920 fn check_approval_threshold(
13921 vote: &StakeholderVote,
13922 max_votes: u32,
13923 total_votes: usize,
13924 ) -> bool {
13925 if let Some(threshold) = vote.approval_threshold {
13926 max_votes as f64 / total_votes as f64 >= threshold
13927 } else {
13928 true
13929 }
13930 }
13931
13932 pub fn get_vote(&self, vote_id: &str) -> Option<&StakeholderVote> {
13934 self.votes.get(vote_id)
13935 }
13936
13937 pub fn list_votes(&self, project_id: &str) -> Vec<&StakeholderVote> {
13939 self.votes
13940 .values()
13941 .filter(|v| v.project_id == project_id)
13942 .collect()
13943 }
13944}
13945
13946impl Default for VotingManager {
13947 fn default() -> Self {
13948 Self::new()
13949 }
13950}
13951
13952#[derive(Debug, Clone, Serialize, Deserialize)]
13958pub struct StakeholderImpact {
13959 pub id: String,
13961 pub project_id: String,
13963 pub stakeholder_id: String,
13965 pub impact_level: StakeholderImpactLevel,
13967 pub impact_category: StakeholderImpactCategory,
13969 pub description: String,
13971 pub magnitude: f64,
13973 pub timeframe: ImpactTimeframe,
13975 pub mitigation_strategies: Vec<String>,
13977 pub notification_sent: bool,
13979 pub notified_at: Option<String>,
13981}
13982
13983#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13985pub enum StakeholderImpactLevel {
13986 None,
13988 Low,
13990 Medium,
13992 High,
13994 Critical,
13996}
13997
13998#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14000pub enum StakeholderImpactCategory {
14001 Economic,
14003 Operational,
14005 Legal,
14007 Rights,
14009 Resources,
14011 Strategic,
14013}
14014
14015#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14017pub enum ImpactTimeframe {
14018 Immediate,
14020 ShortTerm,
14022 MediumTerm,
14024 LongTerm,
14026}
14027
14028#[derive(Debug)]
14030pub struct StakeholderImpactTracker {
14031 impacts: HashMap<String, Vec<StakeholderImpact>>,
14032}
14033
14034impl StakeholderImpactTracker {
14035 pub fn new() -> Self {
14037 Self {
14038 impacts: HashMap::new(),
14039 }
14040 }
14041
14042 #[allow(clippy::too_many_arguments)]
14044 pub fn record_impact(
14045 &mut self,
14046 project_id: String,
14047 stakeholder_id: String,
14048 impact_level: StakeholderImpactLevel,
14049 impact_category: StakeholderImpactCategory,
14050 description: String,
14051 magnitude: f64,
14052 timeframe: ImpactTimeframe,
14053 mitigation_strategies: Vec<String>,
14054 ) -> StakeholderImpact {
14055 let impact = StakeholderImpact {
14056 id: uuid::Uuid::new_v4().to_string(),
14057 project_id: project_id.clone(),
14058 stakeholder_id: stakeholder_id.clone(),
14059 impact_level,
14060 impact_category,
14061 description,
14062 magnitude,
14063 timeframe,
14064 mitigation_strategies,
14065 notification_sent: false,
14066 notified_at: None,
14067 };
14068
14069 self.impacts
14070 .entry(project_id)
14071 .or_default()
14072 .push(impact.clone());
14073
14074 impact
14075 }
14076
14077 pub fn mark_notified(&mut self, project_id: &str, impact_id: &str) -> Option<()> {
14079 let impacts = self.impacts.get_mut(project_id)?;
14080 let impact = impacts.iter_mut().find(|i| i.id == impact_id)?;
14081 impact.notification_sent = true;
14082 impact.notified_at = Some(chrono::Utc::now().to_rfc3339());
14083 Some(())
14084 }
14085
14086 pub fn get_stakeholder_impacts(
14088 &self,
14089 project_id: &str,
14090 stakeholder_id: &str,
14091 ) -> Vec<&StakeholderImpact> {
14092 self.impacts
14093 .get(project_id)
14094 .map(|impacts| {
14095 impacts
14096 .iter()
14097 .filter(|i| i.stakeholder_id == stakeholder_id)
14098 .collect()
14099 })
14100 .unwrap_or_default()
14101 }
14102
14103 pub fn get_unnotified_critical_impacts(&self, project_id: &str) -> Vec<&StakeholderImpact> {
14105 self.impacts
14106 .get(project_id)
14107 .map(|impacts| {
14108 impacts
14109 .iter()
14110 .filter(|i| {
14111 matches!(
14112 i.impact_level,
14113 StakeholderImpactLevel::High | StakeholderImpactLevel::Critical
14114 ) && !i.notification_sent
14115 })
14116 .collect()
14117 })
14118 .unwrap_or_default()
14119 }
14120
14121 pub fn get_impact_summary(&self, project_id: &str) -> HashMap<StakeholderImpactLevel, usize> {
14123 let mut summary = HashMap::new();
14124
14125 if let Some(impacts) = self.impacts.get(project_id) {
14126 for impact in impacts {
14127 *summary.entry(impact.impact_level).or_insert(0) += 1;
14128 }
14129 }
14130
14131 summary
14132 }
14133}
14134
14135impl Default for StakeholderImpactTracker {
14136 fn default() -> Self {
14137 Self::new()
14138 }
14139}
14140
14141#[derive(Debug, Clone, Serialize, Deserialize)]
14147pub struct SemanticEquivalence {
14148 pub id: String,
14150 pub source_concept: String,
14152 pub target_concept: String,
14154 pub equivalence_score: f64,
14156 pub similarity_score: f64,
14158 pub structural_score: f64,
14160 pub functional_score: f64,
14162 pub confidence: f64,
14164 pub explanation: String,
14166 pub similarities: Vec<String>,
14168 pub differences: Vec<String>,
14170 pub context_compatibility: f64,
14172}
14173
14174#[derive(Clone)]
14176pub struct SemanticEquivalenceDetector {
14177 generator: Option<std::sync::Arc<dyn TextGenerator>>,
14179}
14180
14181impl SemanticEquivalenceDetector {
14182 pub fn new() -> Self {
14184 Self { generator: None }
14185 }
14186
14187 pub fn with_generator(generator: std::sync::Arc<dyn TextGenerator>) -> Self {
14189 Self {
14190 generator: Some(generator),
14191 }
14192 }
14193
14194 pub async fn detect_equivalence(
14196 &self,
14197 source_concept: &str,
14198 target_concept: &str,
14199 source_jurisdiction: &Jurisdiction,
14200 target_jurisdiction: &Jurisdiction,
14201 ) -> PortingResult<SemanticEquivalence> {
14202 let (similarity_score, explanation, similarities, differences) =
14203 if let Some(generator) = &self.generator {
14204 let prompt = format!(
14206 "Analyze semantic equivalence between legal concepts:\n\
14207 Source: '{}' in {} ({:?} legal system)\n\
14208 Target: '{}' in {} ({:?} legal system)\n\n\
14209 Provide:\n\
14210 1. Similarity score (0.0-1.0)\n\
14211 2. Brief explanation\n\
14212 3. Key similarities (3 points)\n\
14213 4. Key differences (3 points)",
14214 source_concept,
14215 source_jurisdiction.name,
14216 source_jurisdiction.legal_system,
14217 target_concept,
14218 target_jurisdiction.name,
14219 target_jurisdiction.legal_system
14220 );
14221
14222 let response = generator
14223 .generate(&prompt)
14224 .await
14225 .map_err(PortingError::Llm)?;
14226
14227 let similarity = 0.75; let explain = format!("AI Analysis: {}", response.lines().next().unwrap_or(""));
14230 let sims = vec![
14231 "Similar legal purpose".to_string(),
14232 "Comparable scope".to_string(),
14233 "Equivalent enforcement mechanisms".to_string(),
14234 ];
14235 let diffs = vec![
14236 "Different procedural requirements".to_string(),
14237 "Varying jurisdictional scope".to_string(),
14238 ];
14239
14240 (similarity, explain, sims, diffs)
14241 } else {
14242 let similarity = self.calculate_basic_similarity(source_concept, target_concept);
14244 let explain = "Rule-based similarity analysis".to_string();
14245 let sims = vec!["Lexical similarity detected".to_string()];
14246 let diffs = vec!["Different legal systems may affect interpretation".to_string()];
14247
14248 (similarity, explain, sims, diffs)
14249 };
14250
14251 let structural_score = self.calculate_structural_similarity(
14253 source_concept,
14254 target_concept,
14255 &source_jurisdiction.legal_system,
14256 &target_jurisdiction.legal_system,
14257 );
14258
14259 let functional_score = self.calculate_functional_equivalence(
14260 source_concept,
14261 target_concept,
14262 source_jurisdiction,
14263 target_jurisdiction,
14264 );
14265
14266 let equivalence_score =
14268 (similarity_score * 0.4) + (structural_score * 0.3) + (functional_score * 0.3);
14269
14270 let context_compatibility =
14272 if source_jurisdiction.legal_system == target_jurisdiction.legal_system {
14273 0.9
14274 } else {
14275 0.6
14276 };
14277
14278 Ok(SemanticEquivalence {
14279 id: format!("sem-eq-{}", uuid::Uuid::new_v4()),
14280 source_concept: source_concept.to_string(),
14281 target_concept: target_concept.to_string(),
14282 equivalence_score,
14283 similarity_score,
14284 structural_score,
14285 functional_score,
14286 confidence: similarity_score * context_compatibility,
14287 explanation,
14288 similarities,
14289 differences,
14290 context_compatibility,
14291 })
14292 }
14293
14294 fn calculate_basic_similarity(&self, s1: &str, s2: &str) -> f64 {
14296 let distance = self.levenshtein_distance(s1, s2);
14298 let max_len = s1.len().max(s2.len()) as f64;
14299 if max_len == 0.0 {
14300 1.0
14301 } else {
14302 1.0 - (distance as f64 / max_len)
14303 }
14304 }
14305
14306 #[allow(clippy::needless_range_loop)]
14308 fn levenshtein_distance(&self, s1: &str, s2: &str) -> usize {
14309 let len1 = s1.len();
14310 let len2 = s2.len();
14311 let mut matrix = vec![vec![0; len2 + 1]; len1 + 1];
14312
14313 for i in 0..=len1 {
14314 matrix[i][0] = i;
14315 }
14316 for (j, cell) in matrix[0].iter_mut().enumerate().take(len2 + 1) {
14317 *cell = j;
14318 }
14319
14320 for (i, c1) in s1.chars().enumerate() {
14321 for (j, c2) in s2.chars().enumerate() {
14322 let cost = if c1 == c2 { 0 } else { 1 };
14323 matrix[i + 1][j + 1] = (matrix[i][j + 1] + 1)
14324 .min(matrix[i + 1][j] + 1)
14325 .min(matrix[i][j] + cost);
14326 }
14327 }
14328
14329 matrix[len1][len2]
14330 }
14331
14332 fn calculate_structural_similarity(
14334 &self,
14335 _s1: &str,
14336 _s2: &str,
14337 sys1: &LegalSystem,
14338 sys2: &LegalSystem,
14339 ) -> f64 {
14340 if sys1 == sys2 {
14341 0.9
14342 } else {
14343 match (sys1, sys2) {
14344 (LegalSystem::CommonLaw, LegalSystem::CivilLaw)
14345 | (LegalSystem::CivilLaw, LegalSystem::CommonLaw) => 0.6,
14346 _ => 0.5,
14347 }
14348 }
14349 }
14350
14351 fn calculate_functional_equivalence(
14353 &self,
14354 _s1: &str,
14355 _s2: &str,
14356 j1: &Jurisdiction,
14357 j2: &Jurisdiction,
14358 ) -> f64 {
14359 let age_alignment =
14361 if j1.cultural_params.age_of_majority == j2.cultural_params.age_of_majority {
14362 1.0
14363 } else {
14364 0.7
14365 };
14366
14367 let prohibition_alignment =
14368 if j1.cultural_params.prohibitions == j2.cultural_params.prohibitions {
14369 1.0
14370 } else {
14371 0.6
14372 };
14373
14374 (age_alignment + prohibition_alignment) / 2.0
14375 }
14376}
14377
14378impl Default for SemanticEquivalenceDetector {
14379 fn default() -> Self {
14380 Self::new()
14381 }
14382}
14383
14384#[derive(Debug, Clone, Serialize, Deserialize)]
14386pub struct AutoTermMapping {
14387 pub id: String,
14389 pub source_term: String,
14391 pub target_term: String,
14393 pub confidence: f64,
14395 pub context: String,
14397 pub alternatives: Vec<AlternativeMapping>,
14399 pub rationale: String,
14401 pub examples: Vec<String>,
14403}
14404
14405#[derive(Debug, Clone, Serialize, Deserialize)]
14407pub struct AlternativeMapping {
14408 pub term: String,
14410 pub confidence: f64,
14412 pub usage_context: String,
14414}
14415
14416#[derive(Clone)]
14418pub struct AutoTermMapper {
14419 generator: Option<std::sync::Arc<dyn TextGenerator>>,
14421 translation_matrix: TermTranslationMatrix,
14423}
14424
14425impl AutoTermMapper {
14426 pub fn new() -> Self {
14428 Self {
14429 generator: None,
14430 translation_matrix: TermTranslationMatrix::new(),
14431 }
14432 }
14433
14434 pub fn with_generator(generator: std::sync::Arc<dyn TextGenerator>) -> Self {
14436 Self {
14437 generator: Some(generator),
14438 translation_matrix: TermTranslationMatrix::new(),
14439 }
14440 }
14441
14442 pub async fn map_term(
14444 &self,
14445 term: &str,
14446 source_jurisdiction: &Jurisdiction,
14447 target_jurisdiction: &Jurisdiction,
14448 context: &str,
14449 ) -> PortingResult<AutoTermMapping> {
14450 let (target_term, confidence, alternatives, rationale) = if let Some(generator) =
14451 &self.generator
14452 {
14453 let prompt = format!(
14455 "Map legal term from {} to {}:\n\
14456 Term: '{}'\n\
14457 Context: {}\n\
14458 Source legal system: {:?}\n\
14459 Target legal system: {:?}\n\n\
14460 Provide:\n\
14461 1. Best target term\n\
14462 2. Confidence (0.0-1.0)\n\
14463 3. Two alternative mappings with contexts\n\
14464 4. Brief rationale",
14465 source_jurisdiction.name,
14466 target_jurisdiction.name,
14467 term,
14468 context,
14469 source_jurisdiction.legal_system,
14470 target_jurisdiction.legal_system
14471 );
14472
14473 let response = generator
14474 .generate(&prompt)
14475 .await
14476 .map_err(PortingError::Llm)?;
14477
14478 let target = response.lines().next().unwrap_or(term).to_string();
14480 let conf = 0.85;
14481 let alts = vec![
14482 AlternativeMapping {
14483 term: format!("{}_alt1", term),
14484 confidence: 0.7,
14485 usage_context: "Formal legal documents".to_string(),
14486 },
14487 AlternativeMapping {
14488 term: format!("{}_alt2", term),
14489 confidence: 0.6,
14490 usage_context: "Informal proceedings".to_string(),
14491 },
14492 ];
14493 let rat = "AI-based contextual mapping".to_string();
14494
14495 (target, conf, alts, rat)
14496 } else {
14497 let translations = self.translation_matrix.find_translations(
14499 &source_jurisdiction.id,
14500 &target_jurisdiction.id,
14501 term,
14502 );
14503 let target = translations
14504 .iter()
14505 .find(|tr| {
14506 tr.valid_contexts.iter().any(|c| c.contains(context)) || tr.source_term == term
14507 })
14508 .map(|tr| tr.target_term.clone())
14509 .unwrap_or_else(|| term.to_string());
14510 let conf = 0.6;
14511 let alts = vec![];
14512 let rat = "Dictionary-based translation".to_string();
14513
14514 (target, conf, alts, rat)
14515 };
14516
14517 Ok(AutoTermMapping {
14518 id: format!("term-map-{}", uuid::Uuid::new_v4()),
14519 source_term: term.to_string(),
14520 target_term,
14521 confidence,
14522 context: context.to_string(),
14523 alternatives,
14524 rationale,
14525 examples: vec![format!("Example usage: {} in {}", term, context)],
14526 })
14527 }
14528
14529 pub async fn map_terms_batch(
14531 &self,
14532 terms: &[String],
14533 source_jurisdiction: &Jurisdiction,
14534 target_jurisdiction: &Jurisdiction,
14535 context: &str,
14536 ) -> PortingResult<Vec<AutoTermMapping>> {
14537 let mut mappings = Vec::new();
14538
14539 for term in terms {
14540 let mapping = self
14541 .map_term(term, source_jurisdiction, target_jurisdiction, context)
14542 .await?;
14543 mappings.push(mapping);
14544 }
14545
14546 Ok(mappings)
14547 }
14548}
14549
14550impl Default for AutoTermMapper {
14551 fn default() -> Self {
14552 Self::new()
14553 }
14554}
14555
14556#[derive(Debug, Clone, Serialize, Deserialize)]
14558pub struct AiGapAnalysis {
14559 pub id: String,
14561 pub source_statute_id: String,
14563 pub target_jurisdiction: String,
14565 pub gaps: Vec<AiGap>,
14567 pub coverage_score: f64,
14569 pub completeness_assessment: String,
14571 pub critical_gaps: Vec<String>,
14573 pub recommended_actions: Vec<String>,
14575 pub confidence: f64,
14577}
14578
14579#[derive(Debug, Clone, Serialize, Deserialize)]
14581pub struct AiGap {
14582 pub id: String,
14584 pub gap_type: AiGapType,
14586 pub description: String,
14588 pub severity: Severity,
14590 pub impact: String,
14592 pub solutions: Vec<AiGapSolution>,
14594 pub effort_estimate: EffortLevel,
14596 pub dependencies: Vec<String>,
14598}
14599
14600#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14602pub enum AiGapType {
14603 MissingAuthority,
14605 MissingEnforcement,
14607 MissingCulturalAdaptation,
14609 MissingProcedure,
14611 MissingStakeholder,
14613 IncompleteDefinitions,
14615 InsufficientRemedies,
14617}
14618
14619#[derive(Debug, Clone, Serialize, Deserialize)]
14621pub struct AiGapSolution {
14622 pub id: String,
14624 pub description: String,
14626 pub steps: Vec<String>,
14628 pub resources: Vec<String>,
14630 pub success_likelihood: f64,
14632}
14633
14634#[derive(Clone)]
14636pub struct AiGapAnalyzer {
14637 generator: Option<std::sync::Arc<dyn TextGenerator>>,
14639}
14640
14641impl AiGapAnalyzer {
14642 pub fn new() -> Self {
14644 Self { generator: None }
14645 }
14646
14647 pub fn with_generator(generator: std::sync::Arc<dyn TextGenerator>) -> Self {
14649 Self {
14650 generator: Some(generator),
14651 }
14652 }
14653
14654 #[allow(clippy::too_many_arguments)]
14656 pub async fn analyze_gaps(
14657 &self,
14658 statute: &Statute,
14659 source_jurisdiction: &Jurisdiction,
14660 target_jurisdiction: &Jurisdiction,
14661 ) -> PortingResult<AiGapAnalysis> {
14662 let gaps = if let Some(generator) = &self.generator {
14663 let prompt = format!(
14665 "Perform comprehensive gap analysis for porting statute:\n\
14666 Statute: '{}'\n\
14667 From: {} ({:?} legal system)\n\
14668 To: {} ({:?} legal system)\n\n\
14669 Identify gaps in:\n\
14670 1. Legal authority\n\
14671 2. Enforcement mechanisms\n\
14672 3. Cultural adaptation\n\
14673 4. Procedural framework\n\
14674 5. Stakeholder considerations\n\
14675 Provide severity, impact, and solutions for each gap.",
14676 statute.title,
14677 source_jurisdiction.name,
14678 source_jurisdiction.legal_system,
14679 target_jurisdiction.name,
14680 target_jurisdiction.legal_system
14681 );
14682
14683 let response = generator
14684 .generate(&prompt)
14685 .await
14686 .map_err(PortingError::Llm)?;
14687
14688 vec![
14690 AiGap {
14691 id: format!("gap-{}", uuid::Uuid::new_v4()),
14692 gap_type: AiGapType::MissingEnforcement,
14693 description: "Enforcement authority may need designation".to_string(),
14694 severity: Severity::Warning,
14695 impact: "May affect statute effectiveness".to_string(),
14696 solutions: vec![AiGapSolution {
14697 id: format!("sol-{}", uuid::Uuid::new_v4()),
14698 description: "Designate equivalent enforcement body".to_string(),
14699 steps: vec![
14700 "Identify target jurisdiction enforcement agencies".to_string(),
14701 "Map responsibilities".to_string(),
14702 ],
14703 resources: vec!["Legal research".to_string()],
14704 success_likelihood: 0.8,
14705 }],
14706 effort_estimate: EffortLevel::Medium,
14707 dependencies: vec![],
14708 },
14709 AiGap {
14710 id: format!("gap-{}", uuid::Uuid::new_v4()),
14711 gap_type: AiGapType::MissingCulturalAdaptation,
14712 description: format!(
14713 "Cultural adaptation needed: {}",
14714 response.lines().next().unwrap_or("")
14715 ),
14716 severity: Severity::Info,
14717 impact: "Affects cultural appropriateness".to_string(),
14718 solutions: vec![AiGapSolution {
14719 id: format!("sol-{}", uuid::Uuid::new_v4()),
14720 description: "Consult cultural advisors".to_string(),
14721 steps: vec!["Engage local experts".to_string()],
14722 resources: vec!["Cultural consultation".to_string()],
14723 success_likelihood: 0.9,
14724 }],
14725 effort_estimate: EffortLevel::Low,
14726 dependencies: vec![],
14727 },
14728 ]
14729 } else {
14730 vec![AiGap {
14732 id: format!("gap-{}", uuid::Uuid::new_v4()),
14733 gap_type: AiGapType::MissingEnforcement,
14734 description: "Standard enforcement gap check".to_string(),
14735 severity: Severity::Info,
14736 impact: "Standard porting consideration".to_string(),
14737 solutions: vec![],
14738 effort_estimate: EffortLevel::Medium,
14739 dependencies: vec![],
14740 }]
14741 };
14742
14743 let critical_gaps: Vec<String> = gaps
14744 .iter()
14745 .filter(|g| g.severity == Severity::Critical)
14746 .map(|g| g.description.clone())
14747 .collect();
14748
14749 let coverage_score = 1.0 - (gaps.len() as f64 * 0.1).min(0.6);
14750
14751 Ok(AiGapAnalysis {
14752 id: format!("ai-gap-{}", uuid::Uuid::new_v4()),
14753 source_statute_id: statute.id.clone(),
14754 target_jurisdiction: target_jurisdiction.id.clone(),
14755 gaps,
14756 coverage_score,
14757 completeness_assessment: if coverage_score > 0.7 {
14758 "Good coverage with addressable gaps".to_string()
14759 } else {
14760 "Significant gaps require attention".to_string()
14761 },
14762 critical_gaps,
14763 recommended_actions: vec![
14764 "Address critical gaps before implementation".to_string(),
14765 "Conduct stakeholder review".to_string(),
14766 ],
14767 confidence: if self.generator.is_some() { 0.85 } else { 0.65 },
14768 })
14769 }
14770}
14771
14772impl Default for AiGapAnalyzer {
14773 fn default() -> Self {
14774 Self::new()
14775 }
14776}
14777
14778#[derive(Debug, Clone, Serialize, Deserialize)]
14780pub struct ConflictPrediction {
14781 pub id: String,
14783 pub source_statute_id: String,
14785 pub target_jurisdiction: String,
14787 pub predicted_conflicts: Vec<PredictedConflict>,
14789 pub risk_score: f64,
14791 pub risk_assessment: String,
14793 pub preventive_measures: Vec<String>,
14795 pub confidence: f64,
14797}
14798
14799#[derive(Debug, Clone, Serialize, Deserialize)]
14801pub struct PredictedConflict {
14802 pub id: String,
14804 pub conflict_type: ConflictType,
14806 pub description: String,
14808 pub likelihood: f64,
14810 pub severity: Severity,
14812 pub impact: String,
14814 pub indicators: Vec<String>,
14816 pub mitigations: Vec<String>,
14818}
14819
14820#[derive(Clone)]
14822pub struct IntelligentConflictPredictor {
14823 generator: Option<std::sync::Arc<dyn TextGenerator>>,
14825 precedent_db: ConflictPrecedentDatabase,
14827}
14828
14829impl IntelligentConflictPredictor {
14830 pub fn new() -> Self {
14832 Self {
14833 generator: None,
14834 precedent_db: ConflictPrecedentDatabase::new(),
14835 }
14836 }
14837
14838 pub fn with_generator(generator: std::sync::Arc<dyn TextGenerator>) -> Self {
14840 Self {
14841 generator: Some(generator),
14842 precedent_db: ConflictPrecedentDatabase::new(),
14843 }
14844 }
14845
14846 pub async fn predict_conflicts(
14848 &self,
14849 statute: &Statute,
14850 source_jurisdiction: &Jurisdiction,
14851 target_jurisdiction: &Jurisdiction,
14852 ) -> PortingResult<ConflictPrediction> {
14853 let predicted_conflicts = if let Some(generator) = &self.generator {
14854 let prompt = format!(
14856 "Predict potential legal conflicts when porting statute:\n\
14857 Statute: '{}'\n\
14858 From: {} ({:?} legal system)\n\
14859 To: {} ({:?} legal system)\n\n\
14860 Analyze potential conflicts in:\n\
14861 1. Legal authority and jurisdiction\n\
14862 2. Procedural requirements\n\
14863 3. Cultural and ethical norms\n\
14864 4. Existing legislation\n\
14865 5. Constitutional compatibility\n\
14866 For each conflict, provide likelihood, severity, and mitigation.",
14867 statute.title,
14868 source_jurisdiction.name,
14869 source_jurisdiction.legal_system,
14870 target_jurisdiction.name,
14871 target_jurisdiction.legal_system
14872 );
14873
14874 let response = generator
14875 .generate(&prompt)
14876 .await
14877 .map_err(PortingError::Llm)?;
14878
14879 vec![
14881 PredictedConflict {
14882 id: format!("pred-{}", uuid::Uuid::new_v4()),
14883 conflict_type: ConflictType::SystemMismatch,
14884 description: "Legal system procedural differences".to_string(),
14885 likelihood: 0.7,
14886 severity: Severity::Warning,
14887 impact: "May require procedural adaptation".to_string(),
14888 indicators: vec!["Different legal traditions".to_string()],
14889 mitigations: vec![
14890 "Adapt procedures to target system".to_string(),
14891 "Consult legal experts".to_string(),
14892 ],
14893 },
14894 PredictedConflict {
14895 id: format!("pred-{}", uuid::Uuid::new_v4()),
14896 conflict_type: ConflictType::CulturalIncompatibility,
14897 description: format!(
14898 "AI prediction: {}",
14899 response
14900 .lines()
14901 .next()
14902 .unwrap_or("Cultural consideration needed")
14903 ),
14904 likelihood: 0.5,
14905 severity: Severity::Info,
14906 impact: "Cultural sensitivity required".to_string(),
14907 indicators: vec!["Cultural parameter differences".to_string()],
14908 mitigations: vec!["Cultural consultation".to_string()],
14909 },
14910 ]
14911 } else {
14912 let precedents = self.precedent_db.find_relevant_precedents(
14914 &source_jurisdiction.id,
14915 &target_jurisdiction.id,
14916 &ConflictType::SystemMismatch,
14917 );
14918
14919 if !precedents.is_empty() {
14920 vec![PredictedConflict {
14921 id: format!("pred-{}", uuid::Uuid::new_v4()),
14922 conflict_type: ConflictType::SystemMismatch,
14923 description: "Historical conflict pattern detected".to_string(),
14924 likelihood: 0.6,
14925 severity: Severity::Warning,
14926 impact: "Based on historical precedents".to_string(),
14927 indicators: vec!["Similar past conflicts".to_string()],
14928 mitigations: vec!["Apply proven resolution strategies".to_string()],
14929 }]
14930 } else {
14931 vec![]
14932 }
14933 };
14934
14935 let risk_score = if predicted_conflicts.is_empty() {
14936 0.1
14937 } else {
14938 predicted_conflicts
14939 .iter()
14940 .map(|c| c.likelihood)
14941 .sum::<f64>()
14942 / predicted_conflicts.len() as f64
14943 };
14944
14945 Ok(ConflictPrediction {
14946 id: format!("conflict-pred-{}", uuid::Uuid::new_v4()),
14947 source_statute_id: statute.id.clone(),
14948 target_jurisdiction: target_jurisdiction.id.clone(),
14949 predicted_conflicts,
14950 risk_score,
14951 risk_assessment: if risk_score < 0.3 {
14952 "Low conflict risk".to_string()
14953 } else if risk_score < 0.7 {
14954 "Moderate conflict risk - review recommended".to_string()
14955 } else {
14956 "High conflict risk - extensive review required".to_string()
14957 },
14958 preventive_measures: vec![
14959 "Conduct thorough legal review".to_string(),
14960 "Engage stakeholders early".to_string(),
14961 "Plan mitigation strategies".to_string(),
14962 ],
14963 confidence: if self.generator.is_some() { 0.8 } else { 0.6 },
14964 })
14965 }
14966
14967 pub fn analyze_patterns(
14969 &self,
14970 source_jurisdiction: &str,
14971 target_jurisdiction: &str,
14972 ) -> Vec<String> {
14973 let precedents = self.precedent_db.find_relevant_precedents(
14974 source_jurisdiction,
14975 target_jurisdiction,
14976 &ConflictType::SystemMismatch,
14977 );
14978
14979 precedents
14980 .iter()
14981 .map(|p| format!("Pattern: {:?} -> {}", p.conflict_type, p.resolution_used))
14982 .collect()
14983 }
14984}
14985
14986impl Default for IntelligentConflictPredictor {
14987 fn default() -> Self {
14988 Self::new()
14989 }
14990}
14991
14992#[derive(Debug, Clone, Serialize, Deserialize)]
14998pub struct MultiTargetPortingRequest {
14999 pub id: String,
15001 pub source_statute: Statute,
15003 pub source_jurisdiction: Jurisdiction,
15005 pub target_jurisdictions: Vec<Jurisdiction>,
15007 pub options: PortingOptions,
15009 pub resolve_dependencies: bool,
15011 pub enable_cascade: bool,
15013}
15014
15015#[derive(Debug, Clone, Serialize, Deserialize)]
15017pub struct MultiTargetPortingResult {
15018 pub id: String,
15020 pub source_statute_id: String,
15022 pub jurisdiction_results: HashMap<String, PortedStatute>,
15024 pub failures: HashMap<String, String>,
15026 pub success_rate: f64,
15028 pub dependency_log: Vec<String>,
15030 pub cascade_log: Vec<String>,
15032 pub cross_conflicts: Vec<CrossJurisdictionConflict>,
15034}
15035
15036#[derive(Debug, Clone, Serialize, Deserialize)]
15038pub struct CrossJurisdictionConflict {
15039 pub id: String,
15041 pub jurisdictions: Vec<String>,
15043 pub description: String,
15045 pub severity: Severity,
15047 pub resolution: String,
15049}
15050
15051#[derive(Clone)]
15053pub struct MultiTargetPortingEngine {
15054 dependency_resolver: JurisdictionDependencyResolver,
15056}
15057
15058impl MultiTargetPortingEngine {
15059 pub fn new() -> Self {
15061 Self {
15062 dependency_resolver: JurisdictionDependencyResolver::new(),
15063 }
15064 }
15065
15066 pub async fn port_to_multiple_targets(
15068 &self,
15069 request: MultiTargetPortingRequest,
15070 ) -> PortingResult<MultiTargetPortingResult> {
15071 let mut jurisdiction_results = HashMap::new();
15072 let mut failures = HashMap::new();
15073 let mut dependency_log = Vec::new();
15074 let mut cascade_log = Vec::new();
15075
15076 let ordered_jurisdictions = if request.resolve_dependencies {
15078 let deps = self
15079 .dependency_resolver
15080 .resolve_dependencies(&request.target_jurisdictions);
15081 dependency_log.push(format!("Resolved {} dependencies", deps.len()));
15082 deps
15083 } else {
15084 request.target_jurisdictions.clone()
15085 };
15086
15087 for target_jurisdiction in ordered_jurisdictions {
15089 let engine = PortingEngine::new(
15090 request.source_jurisdiction.clone(),
15091 target_jurisdiction.clone(),
15092 );
15093
15094 match engine.port_statute(&request.source_statute, &request.options) {
15095 Ok(ported) => {
15096 jurisdiction_results.insert(target_jurisdiction.id.clone(), ported.clone());
15097
15098 if request.enable_cascade {
15100 cascade_log.push(format!("Cascaded changes to {}", target_jurisdiction.id));
15101 }
15102 }
15103 Err(e) => {
15104 failures.insert(target_jurisdiction.id.clone(), e.to_string());
15105 }
15106 }
15107 }
15108
15109 let success_rate = if jurisdiction_results.is_empty() && failures.is_empty() {
15110 0.0
15111 } else {
15112 jurisdiction_results.len() as f64 / (jurisdiction_results.len() + failures.len()) as f64
15113 };
15114
15115 let cross_conflicts = self.detect_cross_conflicts(&jurisdiction_results);
15117
15118 Ok(MultiTargetPortingResult {
15119 id: format!("multi-port-{}", uuid::Uuid::new_v4()),
15120 source_statute_id: request.source_statute.id.clone(),
15121 jurisdiction_results,
15122 failures,
15123 success_rate,
15124 dependency_log,
15125 cascade_log,
15126 cross_conflicts,
15127 })
15128 }
15129
15130 fn detect_cross_conflicts(
15132 &self,
15133 results: &HashMap<String, PortedStatute>,
15134 ) -> Vec<CrossJurisdictionConflict> {
15135 let mut conflicts = Vec::new();
15136
15137 if results.len() > 1 {
15139 conflicts.push(CrossJurisdictionConflict {
15140 id: format!("cross-conflict-{}", uuid::Uuid::new_v4()),
15141 jurisdictions: results.keys().cloned().collect(),
15142 description: "Potential inconsistency in multi-jurisdiction porting".to_string(),
15143 severity: Severity::Info,
15144 resolution: "Review and harmonize across jurisdictions".to_string(),
15145 });
15146 }
15147
15148 conflicts
15149 }
15150}
15151
15152impl Default for MultiTargetPortingEngine {
15153 fn default() -> Self {
15154 Self::new()
15155 }
15156}
15157
15158#[derive(Debug, Clone, Serialize, Deserialize)]
15160pub struct JurisdictionDependency {
15161 pub id: String,
15163 pub source_jurisdiction: String,
15165 pub target_jurisdiction: String,
15167 pub dependency_type: DependencyType,
15169 pub strength: f64,
15171 pub explanation: String,
15173}
15174
15175#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15177pub enum DependencyType {
15178 LegalSystemCompatibility,
15180 TreatyObligation,
15182 TradeAgreement,
15184 RegionalHarmonization,
15186 ModelLawAdoption,
15188}
15189
15190#[derive(Clone)]
15192pub struct JurisdictionDependencyResolver {
15193 dependencies: HashMap<String, Vec<JurisdictionDependency>>,
15195}
15196
15197impl JurisdictionDependencyResolver {
15198 pub fn new() -> Self {
15200 Self {
15201 dependencies: HashMap::new(),
15202 }
15203 }
15204
15205 #[allow(dead_code)]
15207 pub fn add_dependency(&mut self, dependency: JurisdictionDependency) {
15208 self.dependencies
15209 .entry(dependency.source_jurisdiction.clone())
15210 .or_default()
15211 .push(dependency);
15212 }
15213
15214 pub fn resolve_dependencies(&self, jurisdictions: &[Jurisdiction]) -> Vec<Jurisdiction> {
15216 let mut ordered = jurisdictions.to_vec();
15218
15219 ordered.sort_by_key(|j| match j.legal_system {
15221 LegalSystem::CivilLaw => 0,
15222 LegalSystem::CommonLaw => 1,
15223 _ => 2,
15224 });
15225
15226 ordered
15227 }
15228
15229 #[allow(dead_code)]
15231 pub fn find_dependencies(&self, jurisdiction_id: &str) -> Vec<&JurisdictionDependency> {
15232 self.dependencies
15233 .get(jurisdiction_id)
15234 .map(|v| v.iter().collect())
15235 .unwrap_or_default()
15236 }
15237}
15238
15239impl Default for JurisdictionDependencyResolver {
15240 fn default() -> Self {
15241 Self::new()
15242 }
15243}
15244
15245#[derive(Debug, Clone, Serialize, Deserialize)]
15247pub struct CascadeConfig {
15248 pub id: String,
15250 pub source_jurisdiction: String,
15252 pub cascade_targets: Vec<String>,
15254 pub propagation_rules: Vec<PropagationRule>,
15256 pub auto_propagate: bool,
15258}
15259
15260#[derive(Debug, Clone, Serialize, Deserialize)]
15262pub struct PropagationRule {
15263 pub id: String,
15265 pub name: String,
15267 pub change_type: ChangeType,
15269 pub conditions: Vec<String>,
15271 pub target_jurisdictions: Vec<String>,
15273}
15274
15275#[derive(Debug, Clone, Serialize, Deserialize)]
15277pub struct CascadePropagationResult {
15278 pub id: String,
15280 pub source_statute_id: String,
15282 pub propagated_changes: HashMap<String, Vec<PortingChange>>,
15284 pub conflicts: Vec<String>,
15286 pub success_rate: f64,
15288}
15289
15290#[derive(Clone)]
15292pub struct CascadeChangePropagator {
15293 configs: Vec<CascadeConfig>,
15295}
15296
15297impl CascadeChangePropagator {
15298 pub fn new() -> Self {
15300 Self {
15301 configs: Vec::new(),
15302 }
15303 }
15304
15305 #[allow(dead_code)]
15307 pub fn add_config(&mut self, config: CascadeConfig) {
15308 self.configs.push(config);
15309 }
15310
15311 #[allow(dead_code)]
15313 pub fn propagate_changes(
15314 &self,
15315 source_statute: &Statute,
15316 changes: &[PortingChange],
15317 config: &CascadeConfig,
15318 ) -> CascadePropagationResult {
15319 let mut propagated_changes = HashMap::new();
15320 let conflicts = Vec::new();
15321
15322 for target_jurisdiction in &config.cascade_targets {
15324 let mut target_changes = Vec::new();
15325
15326 for change in changes {
15327 let should_propagate = config.propagation_rules.iter().any(|rule| {
15329 rule.change_type == change.change_type
15330 && (rule.target_jurisdictions.is_empty()
15331 || rule.target_jurisdictions.contains(target_jurisdiction))
15332 });
15333
15334 if should_propagate {
15335 target_changes.push(change.clone());
15336 }
15337 }
15338
15339 if !target_changes.is_empty() {
15340 propagated_changes.insert(target_jurisdiction.clone(), target_changes);
15341 }
15342 }
15343
15344 let total_targets = config.cascade_targets.len();
15345 let successful_propagations = propagated_changes.len();
15346 let success_rate = if total_targets > 0 {
15347 successful_propagations as f64 / total_targets as f64
15348 } else {
15349 0.0
15350 };
15351
15352 CascadePropagationResult {
15353 id: format!("cascade-{}", uuid::Uuid::new_v4()),
15354 source_statute_id: source_statute.id.clone(),
15355 propagated_changes,
15356 conflicts,
15357 success_rate,
15358 }
15359 }
15360}
15361
15362impl Default for CascadeChangePropagator {
15363 fn default() -> Self {
15364 Self::new()
15365 }
15366}
15367
15368#[derive(Debug, Clone, Serialize, Deserialize)]
15370pub struct SynchronizationState {
15371 pub id: String,
15373 pub statute_id: String,
15375 pub jurisdictions: Vec<String>,
15377 pub versions: HashMap<String, String>,
15379 pub status: SyncStatus,
15381 pub last_sync: String,
15383 pub pending_changes: HashMap<String, Vec<PortingChange>>,
15385}
15386
15387#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15389pub enum SyncStatus {
15390 Synchronized,
15392 InProgress,
15394 OutOfSync,
15396 Conflict,
15398}
15399
15400#[derive(Clone)]
15402pub struct CrossJurisdictionSynchronizer {
15403 states: HashMap<String, SynchronizationState>,
15405}
15406
15407impl CrossJurisdictionSynchronizer {
15408 pub fn new() -> Self {
15410 Self {
15411 states: HashMap::new(),
15412 }
15413 }
15414
15415 pub fn start_sync(
15417 &mut self,
15418 statute_id: &str,
15419 jurisdictions: Vec<String>,
15420 ) -> SynchronizationState {
15421 let state = SynchronizationState {
15422 id: format!("sync-{}", uuid::Uuid::new_v4()),
15423 statute_id: statute_id.to_string(),
15424 jurisdictions: jurisdictions.clone(),
15425 versions: jurisdictions
15426 .iter()
15427 .map(|j| (j.clone(), "v1.0".to_string()))
15428 .collect(),
15429 status: SyncStatus::InProgress,
15430 last_sync: chrono::Utc::now().to_rfc3339(),
15431 pending_changes: HashMap::new(),
15432 };
15433
15434 self.states.insert(statute_id.to_string(), state.clone());
15435 state
15436 }
15437
15438 #[allow(dead_code)]
15440 pub fn check_sync_status(&self, statute_id: &str) -> Option<SyncStatus> {
15441 self.states.get(statute_id).map(|s| s.status)
15442 }
15443
15444 #[allow(dead_code)]
15446 pub fn synchronize_changes(
15447 &mut self,
15448 statute_id: &str,
15449 jurisdiction: &str,
15450 changes: Vec<PortingChange>,
15451 ) -> Result<(), String> {
15452 if let Some(state) = self.states.get_mut(statute_id) {
15453 state
15455 .pending_changes
15456 .entry(jurisdiction.to_string())
15457 .or_default()
15458 .extend(changes);
15459
15460 let all_have_changes = state
15462 .jurisdictions
15463 .iter()
15464 .all(|j| state.pending_changes.contains_key(j));
15465
15466 if all_have_changes {
15467 state.status = SyncStatus::Synchronized;
15468 state.last_sync = chrono::Utc::now().to_rfc3339();
15469 } else {
15470 state.status = SyncStatus::OutOfSync;
15471 }
15472
15473 Ok(())
15474 } else {
15475 Err("Synchronization state not found".to_string())
15476 }
15477 }
15478
15479 #[allow(dead_code)]
15481 pub fn get_state(&self, statute_id: &str) -> Option<&SynchronizationState> {
15482 self.states.get(statute_id)
15483 }
15484}
15485
15486impl Default for CrossJurisdictionSynchronizer {
15487 fn default() -> Self {
15488 Self::new()
15489 }
15490}
15491
15492#[derive(Debug, Clone, Serialize, Deserialize)]
15494pub struct HarmonizationRecord {
15495 pub id: String,
15497 pub statute_id: String,
15499 pub jurisdictions: Vec<String>,
15501 pub goal: String,
15503 pub harmonization_score: f64,
15505 pub differences: Vec<HarmonizationDifference>,
15507 pub actions: Vec<HarmonizationAction>,
15509 pub status: HarmonizationStatus,
15511}
15512
15513#[derive(Debug, Clone, Serialize, Deserialize)]
15515pub struct HarmonizationDifference {
15516 pub id: String,
15518 pub jurisdictions: Vec<String>,
15520 pub difference_type: DifferenceType,
15522 pub description: String,
15524 pub impact: f64,
15526}
15527
15528#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15530pub enum DifferenceType {
15531 Terminological,
15533 Procedural,
15535 Cultural,
15537 LegalSystem,
15539 Enforcement,
15541}
15542
15543#[derive(Debug, Clone, Serialize, Deserialize)]
15545pub struct HarmonizationAction {
15546 pub id: String,
15548 pub action_type: String,
15550 pub description: String,
15552 pub jurisdictions_affected: Vec<String>,
15554 pub impact: f64,
15556 pub timestamp: String,
15558}
15559
15560#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15562pub enum HarmonizationStatus {
15563 Planning,
15565 InProgress,
15567 PartiallyHarmonized,
15569 FullyHarmonized,
15571 Failed,
15573}
15574
15575#[derive(Clone)]
15577pub struct HarmonizationTracker {
15578 records: HashMap<String, HarmonizationRecord>,
15580}
15581
15582impl HarmonizationTracker {
15583 pub fn new() -> Self {
15585 Self {
15586 records: HashMap::new(),
15587 }
15588 }
15589
15590 pub fn start_tracking(
15592 &mut self,
15593 statute_id: &str,
15594 jurisdictions: Vec<String>,
15595 goal: String,
15596 ) -> HarmonizationRecord {
15597 let record = HarmonizationRecord {
15598 id: format!("harm-{}", uuid::Uuid::new_v4()),
15599 statute_id: statute_id.to_string(),
15600 jurisdictions,
15601 goal,
15602 harmonization_score: 0.0,
15603 differences: Vec::new(),
15604 actions: Vec::new(),
15605 status: HarmonizationStatus::Planning,
15606 };
15607
15608 self.records.insert(statute_id.to_string(), record.clone());
15609 record
15610 }
15611
15612 #[allow(dead_code)]
15614 pub fn add_difference(
15615 &mut self,
15616 statute_id: &str,
15617 difference: HarmonizationDifference,
15618 ) -> Result<(), String> {
15619 if let Some(record) = self.records.get_mut(statute_id) {
15620 record.differences.push(difference);
15621 self.update_harmonization_score(statute_id)?;
15622 Ok(())
15623 } else {
15624 Err("Harmonization record not found".to_string())
15625 }
15626 }
15627
15628 #[allow(dead_code)]
15630 pub fn record_action(
15631 &mut self,
15632 statute_id: &str,
15633 action: HarmonizationAction,
15634 ) -> Result<(), String> {
15635 if let Some(record) = self.records.get_mut(statute_id) {
15636 record.actions.push(action);
15637 self.update_harmonization_score(statute_id)?;
15638 Ok(())
15639 } else {
15640 Err("Harmonization record not found".to_string())
15641 }
15642 }
15643
15644 fn update_harmonization_score(&mut self, statute_id: &str) -> Result<(), String> {
15646 if let Some(record) = self.records.get_mut(statute_id) {
15647 let difference_penalty = record.differences.len() as f64 * 0.1;
15649 let action_bonus = record.actions.iter().map(|a| a.impact).sum::<f64>();
15650
15651 let score = (1.0 - difference_penalty + action_bonus).clamp(0.0, 1.0);
15652 record.harmonization_score = score;
15653
15654 record.status = if score >= 0.9 {
15656 HarmonizationStatus::FullyHarmonized
15657 } else if score >= 0.6 {
15658 HarmonizationStatus::PartiallyHarmonized
15659 } else {
15660 HarmonizationStatus::InProgress
15661 };
15662
15663 Ok(())
15664 } else {
15665 Err("Harmonization record not found".to_string())
15666 }
15667 }
15668
15669 #[allow(dead_code)]
15671 pub fn get_record(&self, statute_id: &str) -> Option<&HarmonizationRecord> {
15672 self.records.get(statute_id)
15673 }
15674
15675 #[allow(dead_code)]
15677 pub fn all_records(&self) -> Vec<&HarmonizationRecord> {
15678 self.records.values().collect()
15679 }
15680}
15681
15682impl Default for HarmonizationTracker {
15683 fn default() -> Self {
15684 Self::new()
15685 }
15686}
15687
15688#[derive(Debug, Clone, Serialize, Deserialize)]
15694pub struct QualityScore {
15695 pub overall: f64,
15697 pub semantic_preservation: f64,
15699 pub legal_correctness: f64,
15701 pub cultural_adaptation: f64,
15703 pub completeness: f64,
15705 pub consistency: f64,
15707 pub grade: QualityGrade,
15709 pub issues: Vec<QualityIssue>,
15711 pub recommendations: Vec<String>,
15713}
15714
15715#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15717pub enum QualityGrade {
15718 Excellent,
15720 Good,
15722 Acceptable,
15724 Poor,
15726 Unacceptable,
15728}
15729
15730#[derive(Debug, Clone, Serialize, Deserialize)]
15732pub struct QualityIssue {
15733 pub issue_type: QualityIssueType,
15735 pub severity: QualityIssueSeverity,
15737 pub description: String,
15739 pub location: Option<String>,
15741 pub suggested_fix: Option<String>,
15743}
15744
15745#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15747pub enum QualityIssueType {
15748 SemanticDrift,
15750 IncorrectTranslation,
15752 CulturalMismatch,
15754 InconsistentTerminology,
15756 Incompleteness,
15758 LogicalInconsistency,
15760 ComplianceViolation,
15762}
15763
15764#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15766pub enum QualityIssueSeverity {
15767 Critical,
15769 Major,
15771 Minor,
15773 Info,
15775}
15776
15777pub struct QualityScorer {
15779 min_quality_threshold: f64,
15781}
15782
15783impl QualityScorer {
15784 pub fn new() -> Self {
15786 Self {
15787 min_quality_threshold: 0.6,
15788 }
15789 }
15790
15791 #[allow(dead_code)]
15793 pub fn with_threshold(mut self, threshold: f64) -> Self {
15794 self.min_quality_threshold = threshold.clamp(0.0, 1.0);
15795 self
15796 }
15797
15798 pub fn score_porting(&self, ported: &PortedStatute) -> QualityScore {
15800 let mut issues = Vec::new();
15801 let mut recommendations = Vec::new();
15802
15803 let semantic_score = self.score_semantic_preservation(ported, &mut issues);
15805
15806 let legal_score = self.score_legal_correctness(ported, &mut issues);
15808
15809 let cultural_score = self.score_cultural_adaptation(ported, &mut issues);
15811
15812 let completeness_score = self.score_completeness(ported, &mut issues);
15814
15815 let consistency_score = self.score_consistency(ported, &mut issues);
15817
15818 let overall = (semantic_score * 0.25)
15820 + (legal_score * 0.25)
15821 + (cultural_score * 0.2)
15822 + (completeness_score * 0.15)
15823 + (consistency_score * 0.15);
15824
15825 let grade = if overall >= 0.9 {
15827 QualityGrade::Excellent
15828 } else if overall >= 0.75 {
15829 QualityGrade::Good
15830 } else if overall >= 0.6 {
15831 QualityGrade::Acceptable
15832 } else if overall >= 0.4 {
15833 QualityGrade::Poor
15834 } else {
15835 QualityGrade::Unacceptable
15836 };
15837
15838 if overall < 0.9 {
15840 recommendations.push(
15841 "Review semantic preservation to ensure legal meaning is maintained".to_string(),
15842 );
15843 }
15844 if cultural_score < 0.8 {
15845 recommendations
15846 .push("Review cultural adaptations for accuracy and appropriateness".to_string());
15847 }
15848 if !issues.is_empty() {
15849 recommendations.push(format!(
15850 "Address {} quality issues identified",
15851 issues.len()
15852 ));
15853 }
15854
15855 QualityScore {
15856 overall,
15857 semantic_preservation: semantic_score,
15858 legal_correctness: legal_score,
15859 cultural_adaptation: cultural_score,
15860 completeness: completeness_score,
15861 consistency: consistency_score,
15862 grade,
15863 issues,
15864 recommendations,
15865 }
15866 }
15867
15868 fn score_semantic_preservation(
15870 &self,
15871 ported: &PortedStatute,
15872 issues: &mut Vec<QualityIssue>,
15873 ) -> f64 {
15874 let mut score = 1.0;
15875
15876 let critical_changes = ported
15878 .changes
15879 .iter()
15880 .filter(|c| {
15881 matches!(
15882 c.change_type,
15883 ChangeType::ValueAdaptation | ChangeType::Removal
15884 )
15885 })
15886 .count();
15887
15888 if critical_changes > 0 {
15889 score -= 0.1 * critical_changes as f64;
15890 issues.push(QualityIssue {
15891 issue_type: QualityIssueType::SemanticDrift,
15892 severity: QualityIssueSeverity::Major,
15893 description: format!("{} critical changes to statute meaning", critical_changes),
15894 location: None,
15895 suggested_fix: Some(
15896 "Review changes to ensure legal meaning is preserved".to_string(),
15897 ),
15898 });
15899 }
15900
15901 score.max(0.0)
15902 }
15903
15904 fn score_legal_correctness(
15906 &self,
15907 ported: &PortedStatute,
15908 issues: &mut Vec<QualityIssue>,
15909 ) -> f64 {
15910 let mut score: f64 = 1.0;
15911
15912 let translation_changes = ported
15914 .changes
15915 .iter()
15916 .filter(|c| matches!(c.change_type, ChangeType::Translation))
15917 .count();
15918
15919 if translation_changes > 10 {
15920 score -= 0.05;
15921 issues.push(QualityIssue {
15922 issue_type: QualityIssueType::IncorrectTranslation,
15923 severity: QualityIssueSeverity::Minor,
15924 description: format!(
15925 "{} term translations - review for accuracy",
15926 translation_changes
15927 ),
15928 location: None,
15929 suggested_fix: Some(
15930 "Verify legal term translations with jurisdiction experts".to_string(),
15931 ),
15932 });
15933 }
15934
15935 score.max(0.0)
15936 }
15937
15938 fn score_cultural_adaptation(
15940 &self,
15941 ported: &PortedStatute,
15942 issues: &mut Vec<QualityIssue>,
15943 ) -> f64 {
15944 let mut score: f64 = 1.0;
15945
15946 let cultural_changes = ported
15948 .changes
15949 .iter()
15950 .filter(|c| matches!(c.change_type, ChangeType::CulturalAdaptation))
15951 .count();
15952
15953 if cultural_changes == 0 {
15954 score -= 0.2;
15955 issues.push(QualityIssue {
15956 issue_type: QualityIssueType::CulturalMismatch,
15957 severity: QualityIssueSeverity::Major,
15958 description:
15959 "No cultural adaptations applied - may not be suitable for target jurisdiction"
15960 .to_string(),
15961 location: None,
15962 suggested_fix: Some("Apply cultural parameter adaptations".to_string()),
15963 });
15964 }
15965
15966 score.max(0.0)
15967 }
15968
15969 fn score_completeness(&self, ported: &PortedStatute, issues: &mut Vec<QualityIssue>) -> f64 {
15971 let mut score: f64 = 1.0;
15972
15973 if ported.statute.id.is_empty() {
15975 score -= 0.3;
15976 issues.push(QualityIssue {
15977 issue_type: QualityIssueType::Incompleteness,
15978 severity: QualityIssueSeverity::Critical,
15979 description: "Statute ID is empty".to_string(),
15980 location: None,
15981 suggested_fix: Some("Assign a valid statute ID".to_string()),
15982 });
15983 }
15984
15985 if ported.statute.title.is_empty() {
15986 score -= 0.2;
15987 issues.push(QualityIssue {
15988 issue_type: QualityIssueType::Incompleteness,
15989 severity: QualityIssueSeverity::Major,
15990 description: "Statute title is empty".to_string(),
15991 location: None,
15992 suggested_fix: Some("Provide a statute title".to_string()),
15993 });
15994 }
15995
15996 if ported.changes.is_empty() {
15997 score -= 0.1;
15998 issues.push(QualityIssue {
15999 issue_type: QualityIssueType::Incompleteness,
16000 severity: QualityIssueSeverity::Minor,
16001 description: "No changes documented".to_string(),
16002 location: None,
16003 suggested_fix: Some("Document all changes made during porting".to_string()),
16004 });
16005 }
16006
16007 score.max(0.0)
16008 }
16009
16010 fn score_consistency(&self, ported: &PortedStatute, issues: &mut Vec<QualityIssue>) -> f64 {
16012 let mut score: f64 = 1.0;
16013
16014 let term_changes: Vec<_> = ported
16016 .changes
16017 .iter()
16018 .filter(|c| matches!(c.change_type, ChangeType::Translation))
16019 .collect();
16020
16021 if term_changes.len() > 5 {
16023 score -= 0.05;
16024 issues.push(QualityIssue {
16025 issue_type: QualityIssueType::InconsistentTerminology,
16026 severity: QualityIssueSeverity::Minor,
16027 description: "Multiple term translations - verify consistency".to_string(),
16028 location: None,
16029 suggested_fix: Some(
16030 "Ensure consistent translation of legal terms throughout".to_string(),
16031 ),
16032 });
16033 }
16034
16035 score.max(0.0)
16036 }
16037
16038 #[allow(dead_code)]
16040 pub fn meets_threshold(&self, score: &QualityScore) -> bool {
16041 score.overall >= self.min_quality_threshold
16042 }
16043}
16044
16045impl Default for QualityScorer {
16046 fn default() -> Self {
16047 Self::new()
16048 }
16049}
16050
16051#[derive(Debug, Clone, Serialize, Deserialize)]
16053pub struct ConsistencyCheckResult {
16054 pub is_consistent: bool,
16056 pub consistency_score: f64,
16058 pub inconsistencies: Vec<Inconsistency>,
16060 pub suggestions: Vec<String>,
16062}
16063
16064#[derive(Debug, Clone, Serialize, Deserialize)]
16066pub struct Inconsistency {
16067 pub inconsistency_type: InconsistencyType,
16069 pub severity: InconsistencySeverity,
16071 pub description: String,
16073 pub conflicting_elements: Vec<String>,
16075 pub location: Option<String>,
16077}
16078
16079#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16081pub enum InconsistencyType {
16082 TerminologyInconsistency,
16084 ParameterConflict,
16086 LogicalInconsistency,
16088 ReferenceInconsistency,
16090 FormattingInconsistency,
16092}
16093
16094#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16096pub enum InconsistencySeverity {
16097 High,
16099 Medium,
16101 Low,
16103}
16104
16105pub struct ConsistencyVerifier;
16107
16108impl ConsistencyVerifier {
16109 pub fn new() -> Self {
16111 Self
16112 }
16113
16114 pub fn verify(&self, ported: &PortedStatute) -> ConsistencyCheckResult {
16116 let mut inconsistencies = Vec::new();
16117 let mut suggestions = Vec::new();
16118
16119 self.check_terminology_consistency(ported, &mut inconsistencies);
16121
16122 self.check_parameter_consistency(ported, &mut inconsistencies);
16124
16125 self.check_logical_consistency(ported, &mut inconsistencies);
16127
16128 self.check_reference_consistency(ported, &mut inconsistencies);
16130
16131 let consistency_score = if inconsistencies.is_empty() {
16133 1.0
16134 } else {
16135 let penalty = inconsistencies
16136 .iter()
16137 .map(|i| match i.severity {
16138 InconsistencySeverity::High => 0.2,
16139 InconsistencySeverity::Medium => 0.1,
16140 InconsistencySeverity::Low => 0.05,
16141 })
16142 .sum::<f64>();
16143 (1.0 - penalty).max(0.0)
16144 };
16145
16146 let is_consistent = consistency_score >= 0.8;
16147
16148 if !is_consistent {
16150 suggestions.push(
16151 "Review and standardize terminology usage throughout the statute".to_string(),
16152 );
16153 suggestions
16154 .push("Verify that all parameters are consistent and non-conflicting".to_string());
16155 }
16156
16157 ConsistencyCheckResult {
16158 is_consistent,
16159 consistency_score,
16160 inconsistencies,
16161 suggestions,
16162 }
16163 }
16164
16165 fn check_terminology_consistency(
16167 &self,
16168 ported: &PortedStatute,
16169 inconsistencies: &mut Vec<Inconsistency>,
16170 ) {
16171 let term_translations: Vec<_> = ported
16173 .changes
16174 .iter()
16175 .filter(|c| matches!(c.change_type, ChangeType::Translation))
16176 .collect();
16177
16178 if term_translations.len() > 10 {
16179 inconsistencies.push(Inconsistency {
16180 inconsistency_type: InconsistencyType::TerminologyInconsistency,
16181 severity: InconsistencySeverity::Low,
16182 description: format!(
16183 "{} term translations - verify consistent usage",
16184 term_translations.len()
16185 ),
16186 conflicting_elements: vec![],
16187 location: None,
16188 });
16189 }
16190 }
16191
16192 fn check_parameter_consistency(
16194 &self,
16195 ported: &PortedStatute,
16196 inconsistencies: &mut Vec<Inconsistency>,
16197 ) {
16198 let param_changes: Vec<_> = ported
16200 .changes
16201 .iter()
16202 .filter(|c| {
16203 matches!(
16204 c.change_type,
16205 ChangeType::ValueAdaptation | ChangeType::CulturalAdaptation
16206 )
16207 })
16208 .collect();
16209
16210 if param_changes.len() > 5 {
16211 inconsistencies.push(Inconsistency {
16212 inconsistency_type: InconsistencyType::ParameterConflict,
16213 severity: InconsistencySeverity::Medium,
16214 description: format!(
16215 "{} parameter adjustments - verify they don't conflict",
16216 param_changes.len()
16217 ),
16218 conflicting_elements: vec![],
16219 location: None,
16220 });
16221 }
16222 }
16223
16224 fn check_logical_consistency(
16226 &self,
16227 ported: &PortedStatute,
16228 inconsistencies: &mut Vec<Inconsistency>,
16229 ) {
16230 let value_mods: Vec<_> = ported
16232 .changes
16233 .iter()
16234 .filter(|c| matches!(c.change_type, ChangeType::ValueAdaptation))
16235 .collect();
16236
16237 let removals: Vec<_> = ported
16238 .changes
16239 .iter()
16240 .filter(|c| matches!(c.change_type, ChangeType::Removal))
16241 .collect();
16242
16243 if value_mods.len() > 3 && !removals.is_empty() {
16244 inconsistencies.push(Inconsistency {
16245 inconsistency_type: InconsistencyType::LogicalInconsistency,
16246 severity: InconsistencySeverity::High,
16247 description:
16248 "Multiple value adaptations with removals - verify logical consistency"
16249 .to_string(),
16250 conflicting_elements: vec![],
16251 location: None,
16252 });
16253 }
16254 }
16255
16256 fn check_reference_consistency(
16258 &self,
16259 _ported: &PortedStatute,
16260 _inconsistencies: &mut Vec<Inconsistency>,
16261 ) {
16262 }
16265}
16266
16267impl Default for ConsistencyVerifier {
16268 fn default() -> Self {
16269 Self::new()
16270 }
16271}
16272
16273#[derive(Debug, Clone, Serialize, Deserialize)]
16275pub struct CompletenessCheckResult {
16276 pub is_complete: bool,
16278 pub completeness_score: f64,
16280 pub missing_elements: Vec<MissingElement>,
16282 pub optional_elements: Vec<String>,
16284 pub suggestions: Vec<String>,
16286}
16287
16288#[derive(Debug, Clone, Serialize, Deserialize)]
16290pub struct MissingElement {
16291 pub element_type: ElementType,
16293 pub importance: ElementImportance,
16295 pub description: String,
16297 pub expected_location: Option<String>,
16299}
16300
16301#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16303pub enum ElementType {
16304 Metadata,
16306 Effect,
16308 Condition,
16310 CulturalAdaptation,
16312 JurisdictionInfo,
16314 Documentation,
16316 ValidationResult,
16318}
16319
16320#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16322pub enum ElementImportance {
16323 Required,
16325 Recommended,
16327 Optional,
16329}
16330
16331pub struct CompletenessChecker {
16333 check_optional: bool,
16335}
16336
16337impl CompletenessChecker {
16338 pub fn new() -> Self {
16340 Self {
16341 check_optional: false,
16342 }
16343 }
16344
16345 #[allow(dead_code)]
16347 pub fn with_optional_check(mut self, check: bool) -> Self {
16348 self.check_optional = check;
16349 self
16350 }
16351
16352 pub fn check(&self, ported: &PortedStatute) -> CompletenessCheckResult {
16354 let mut missing_elements = Vec::new();
16355 let mut optional_elements = Vec::new();
16356 let mut suggestions = Vec::new();
16357
16358 self.check_required_elements(ported, &mut missing_elements);
16360
16361 self.check_recommended_elements(ported, &mut missing_elements);
16363
16364 if self.check_optional {
16366 self.check_optional_elements(ported, &mut optional_elements);
16367 }
16368
16369 let required_missing = missing_elements
16371 .iter()
16372 .filter(|e| matches!(e.importance, ElementImportance::Required))
16373 .count();
16374
16375 let recommended_missing = missing_elements
16376 .iter()
16377 .filter(|e| matches!(e.importance, ElementImportance::Recommended))
16378 .count();
16379
16380 let completeness_score = if required_missing > 0 {
16381 0.0
16382 } else if recommended_missing > 0 {
16383 0.7 - (0.1 * recommended_missing as f64).min(0.3)
16384 } else {
16385 1.0
16386 };
16387
16388 let is_complete = required_missing == 0 && recommended_missing == 0;
16389
16390 if !is_complete {
16392 if required_missing > 0 {
16393 suggestions.push(format!("Add {} required elements", required_missing));
16394 }
16395 if recommended_missing > 0 {
16396 suggestions.push(format!(
16397 "Add {} recommended elements for better quality",
16398 recommended_missing
16399 ));
16400 }
16401 }
16402
16403 CompletenessCheckResult {
16404 is_complete,
16405 completeness_score,
16406 missing_elements,
16407 optional_elements,
16408 suggestions,
16409 }
16410 }
16411
16412 fn check_required_elements(&self, ported: &PortedStatute, missing: &mut Vec<MissingElement>) {
16414 if ported.statute.id.is_empty() {
16416 missing.push(MissingElement {
16417 element_type: ElementType::Metadata,
16418 importance: ElementImportance::Required,
16419 description: "Statute ID is required".to_string(),
16420 expected_location: Some("statute.id".to_string()),
16421 });
16422 }
16423
16424 if ported.statute.title.is_empty() {
16426 missing.push(MissingElement {
16427 element_type: ElementType::Metadata,
16428 importance: ElementImportance::Required,
16429 description: "Statute title is required".to_string(),
16430 expected_location: Some("statute.title".to_string()),
16431 });
16432 }
16433 }
16434
16435 fn check_recommended_elements(
16437 &self,
16438 ported: &PortedStatute,
16439 missing: &mut Vec<MissingElement>,
16440 ) {
16441 let has_cultural_adaptation = ported
16443 .changes
16444 .iter()
16445 .any(|c| matches!(c.change_type, ChangeType::CulturalAdaptation));
16446
16447 if !has_cultural_adaptation {
16448 missing.push(MissingElement {
16449 element_type: ElementType::CulturalAdaptation,
16450 importance: ElementImportance::Recommended,
16451 description: "Cultural adaptations are recommended for cross-jurisdiction porting"
16452 .to_string(),
16453 expected_location: Some("changes".to_string()),
16454 });
16455 }
16456
16457 if ported.changes.is_empty() {
16459 missing.push(MissingElement {
16460 element_type: ElementType::Documentation,
16461 importance: ElementImportance::Recommended,
16462 description: "Document changes made during porting".to_string(),
16463 expected_location: Some("changes".to_string()),
16464 });
16465 }
16466 }
16467
16468 fn check_optional_elements(&self, _ported: &PortedStatute, optional: &mut Vec<String>) {
16470 optional.push("Detailed implementation notes".to_string());
16471 optional.push("Stakeholder review comments".to_string());
16472 optional.push("Compliance certification".to_string());
16473 }
16474}
16475
16476impl Default for CompletenessChecker {
16477 fn default() -> Self {
16478 Self::new()
16479 }
16480}
16481
16482#[derive(Debug, Clone, Serialize, Deserialize)]
16484pub struct RegressionTest {
16485 pub test_id: String,
16487 pub name: String,
16489 pub source_jurisdiction: String,
16491 pub target_jurisdiction: String,
16493 pub input_statute: String,
16495 pub expected_output: String,
16497 pub quality_baseline: f64,
16499 pub created_at: chrono::DateTime<chrono::Utc>,
16501 pub last_run: Option<chrono::DateTime<chrono::Utc>>,
16503 pub status: RegressionTestStatus,
16505}
16506
16507#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16509pub enum RegressionTestStatus {
16510 Pending,
16512 Passed,
16514 Failed,
16516 Skipped,
16518}
16519
16520#[derive(Debug, Clone, Serialize, Deserialize)]
16522pub struct RegressionTestResult {
16523 pub test_id: String,
16525 pub passed: bool,
16527 pub quality_score: f64,
16529 pub quality_baseline: f64,
16531 pub quality_diff: f64,
16533 pub differences: Vec<String>,
16535 pub run_at: chrono::DateTime<chrono::Utc>,
16537}
16538
16539pub struct RegressionTestManager {
16541 tests: std::collections::HashMap<String, RegressionTest>,
16543 scorer: QualityScorer,
16545}
16546
16547impl RegressionTestManager {
16548 pub fn new() -> Self {
16550 Self {
16551 tests: std::collections::HashMap::new(),
16552 scorer: QualityScorer::new(),
16553 }
16554 }
16555
16556 pub fn add_test(&mut self, test: RegressionTest) {
16558 self.tests.insert(test.test_id.clone(), test);
16559 }
16560
16561 #[allow(dead_code)]
16563 pub fn create_test_from_porting(
16564 &mut self,
16565 test_id: String,
16566 name: String,
16567 source_jurisdiction: String,
16568 target_jurisdiction: String,
16569 input_statute: String,
16570 ported: &PortedStatute,
16571 ) -> Result<(), String> {
16572 let quality = self.scorer.score_porting(ported);
16573
16574 let test = RegressionTest {
16575 test_id: test_id.clone(),
16576 name,
16577 source_jurisdiction,
16578 target_jurisdiction,
16579 input_statute,
16580 expected_output: serde_json::to_string(ported)
16581 .map_err(|e| format!("Failed to serialize ported statute: {}", e))?,
16582 quality_baseline: quality.overall,
16583 created_at: chrono::Utc::now(),
16584 last_run: None,
16585 status: RegressionTestStatus::Pending,
16586 };
16587
16588 self.tests.insert(test_id, test);
16589 Ok(())
16590 }
16591
16592 #[allow(dead_code)]
16594 pub fn run_test(
16595 &mut self,
16596 test_id: &str,
16597 current_result: &PortedStatute,
16598 ) -> Result<RegressionTestResult, String> {
16599 let test = self
16600 .tests
16601 .get_mut(test_id)
16602 .ok_or_else(|| format!("Test {} not found", test_id))?;
16603
16604 let quality = self.scorer.score_porting(current_result);
16606
16607 let quality_diff = quality.overall - test.quality_baseline;
16609 let passed = quality_diff >= -0.05; test.status = if passed {
16613 RegressionTestStatus::Passed
16614 } else {
16615 RegressionTestStatus::Failed
16616 };
16617 test.last_run = Some(chrono::Utc::now());
16618
16619 let mut differences = Vec::new();
16621 if quality_diff < 0.0 {
16622 differences.push(format!(
16623 "Quality regressed by {:.2}%",
16624 -quality_diff * 100.0
16625 ));
16626 }
16627
16628 Ok(RegressionTestResult {
16629 test_id: test_id.to_string(),
16630 passed,
16631 quality_score: quality.overall,
16632 quality_baseline: test.quality_baseline,
16633 quality_diff,
16634 differences,
16635 run_at: chrono::Utc::now(),
16636 })
16637 }
16638
16639 #[allow(dead_code)]
16641 pub fn run_all_tests(
16642 &mut self,
16643 results: &std::collections::HashMap<String, PortedStatute>,
16644 ) -> Vec<RegressionTestResult> {
16645 let test_ids: Vec<_> = self.tests.keys().cloned().collect();
16646 let mut all_results = Vec::new();
16647
16648 for test_id in test_ids {
16649 if let Some(ported) = results.get(&test_id)
16650 && let Ok(result) = self.run_test(&test_id, ported)
16651 {
16652 all_results.push(result);
16653 }
16654 }
16655
16656 all_results
16657 }
16658
16659 #[allow(dead_code)]
16661 pub fn get_statistics(&self) -> RegressionTestStatistics {
16662 let total = self.tests.len();
16663 let mut passed = 0;
16664 let mut failed = 0;
16665 let mut pending = 0;
16666 let mut skipped = 0;
16667
16668 for test in self.tests.values() {
16669 match test.status {
16670 RegressionTestStatus::Passed => passed += 1,
16671 RegressionTestStatus::Failed => failed += 1,
16672 RegressionTestStatus::Pending => pending += 1,
16673 RegressionTestStatus::Skipped => skipped += 1,
16674 }
16675 }
16676
16677 RegressionTestStatistics {
16678 total,
16679 passed,
16680 failed,
16681 pending,
16682 skipped,
16683 pass_rate: if total > 0 {
16684 passed as f64 / total as f64
16685 } else {
16686 0.0
16687 },
16688 }
16689 }
16690
16691 #[allow(dead_code)]
16693 pub fn get_all_tests(&self) -> Vec<&RegressionTest> {
16694 self.tests.values().collect()
16695 }
16696}
16697
16698impl Default for RegressionTestManager {
16699 fn default() -> Self {
16700 Self::new()
16701 }
16702}
16703
16704#[derive(Debug, Clone, Serialize, Deserialize)]
16706pub struct RegressionTestStatistics {
16707 pub total: usize,
16709 pub passed: usize,
16711 pub failed: usize,
16713 pub pending: usize,
16715 pub skipped: usize,
16717 pub pass_rate: f64,
16719}
16720
16721#[derive(Debug, Clone, Serialize, Deserialize)]
16723pub struct DriftDetectionResult {
16724 pub drift_detected: bool,
16726 pub drift_score: f64,
16728 pub category: DriftCategory,
16730 pub drift_issues: Vec<DriftIssue>,
16732 pub recommendations: Vec<String>,
16734}
16735
16736#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16738pub enum DriftCategory {
16739 None,
16741 Minor,
16743 Moderate,
16745 Major,
16747 Critical,
16749}
16750
16751#[derive(Debug, Clone, Serialize, Deserialize)]
16753pub struct DriftIssue {
16754 pub drift_type: DriftType,
16756 pub severity: DriftSeverity,
16758 pub description: String,
16760 pub detected_at: chrono::DateTime<chrono::Utc>,
16762 pub suggested_action: Option<String>,
16764}
16765
16766#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16768pub enum DriftType {
16769 SourceJurisdictionChange,
16771 TargetJurisdictionChange,
16773 CulturalShift,
16775 SemanticDrift,
16777 QualityDegradation,
16779 ComplianceChange,
16781}
16782
16783#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16785pub enum DriftSeverity {
16786 High,
16788 Medium,
16790 Low,
16792}
16793
16794#[derive(Debug, Clone, Serialize, Deserialize)]
16796pub struct DriftSnapshot {
16797 pub snapshot_id: String,
16799 pub statute_id: String,
16801 pub quality_score: f64,
16803 pub compliance_status: String,
16805 pub timestamp: chrono::DateTime<chrono::Utc>,
16807 pub metadata: std::collections::HashMap<String, String>,
16809}
16810
16811pub struct DriftMonitor {
16813 snapshots: std::collections::HashMap<String, Vec<DriftSnapshot>>,
16815 scorer: QualityScorer,
16817 drift_threshold: f64,
16819}
16820
16821impl DriftMonitor {
16822 pub fn new() -> Self {
16824 Self {
16825 snapshots: std::collections::HashMap::new(),
16826 scorer: QualityScorer::new(),
16827 drift_threshold: 0.1, }
16829 }
16830
16831 #[allow(dead_code)]
16833 pub fn with_threshold(mut self, threshold: f64) -> Self {
16834 self.drift_threshold = threshold.clamp(0.0, 1.0);
16835 self
16836 }
16837
16838 pub fn create_snapshot(&mut self, statute_id: String, ported: &PortedStatute) -> String {
16840 let quality = self.scorer.score_porting(ported);
16841
16842 let snapshot_id = uuid::Uuid::new_v4().to_string();
16843 let snapshot = DriftSnapshot {
16844 snapshot_id: snapshot_id.clone(),
16845 statute_id: statute_id.clone(),
16846 quality_score: quality.overall,
16847 compliance_status: "compliant".to_string(),
16848 timestamp: chrono::Utc::now(),
16849 metadata: std::collections::HashMap::new(),
16850 };
16851
16852 self.snapshots.entry(statute_id).or_default().push(snapshot);
16853
16854 snapshot_id
16855 }
16856
16857 pub fn detect_drift(&self, statute_id: &str, current: &PortedStatute) -> DriftDetectionResult {
16859 let mut drift_issues = Vec::new();
16860 let mut recommendations = Vec::new();
16861
16862 let snapshots = self.snapshots.get(statute_id);
16864
16865 let drift_score = if let Some(snapshots) = snapshots {
16866 if snapshots.is_empty() {
16867 0.0
16868 } else {
16869 let latest = &snapshots[snapshots.len() - 1];
16871 let current_quality = self.scorer.score_porting(current);
16872
16873 let quality_diff = (latest.quality_score - current_quality.overall).abs();
16874
16875 if quality_diff > self.drift_threshold {
16876 drift_issues.push(DriftIssue {
16877 drift_type: DriftType::QualityDegradation,
16878 severity: if quality_diff > 0.2 {
16879 DriftSeverity::High
16880 } else if quality_diff > 0.1 {
16881 DriftSeverity::Medium
16882 } else {
16883 DriftSeverity::Low
16884 },
16885 description: format!(
16886 "Quality score changed by {:.2}%",
16887 quality_diff * 100.0
16888 ),
16889 detected_at: chrono::Utc::now(),
16890 suggested_action: Some(
16891 "Review ported statute for quality issues".to_string(),
16892 ),
16893 });
16894 }
16895
16896 quality_diff
16897 }
16898 } else {
16899 0.0
16900 };
16901
16902 let category = if drift_score >= 0.3 {
16903 DriftCategory::Critical
16904 } else if drift_score >= 0.2 {
16905 DriftCategory::Major
16906 } else if drift_score >= 0.1 {
16907 DriftCategory::Moderate
16908 } else if drift_score >= 0.05 {
16909 DriftCategory::Minor
16910 } else {
16911 DriftCategory::None
16912 };
16913
16914 let drift_detected = !drift_issues.is_empty();
16915
16916 if drift_detected {
16917 recommendations.push(
16918 "Review ported statute against current source and target frameworks".to_string(),
16919 );
16920 recommendations.push("Consider re-porting if drift is significant".to_string());
16921 }
16922
16923 DriftDetectionResult {
16924 drift_detected,
16925 drift_score,
16926 category,
16927 drift_issues,
16928 recommendations,
16929 }
16930 }
16931
16932 #[allow(dead_code)]
16934 pub fn get_snapshots(&self, statute_id: &str) -> Option<&Vec<DriftSnapshot>> {
16935 self.snapshots.get(statute_id)
16936 }
16937
16938 #[allow(dead_code)]
16940 pub fn get_drift_trend(&self, statute_id: &str) -> Vec<(chrono::DateTime<chrono::Utc>, f64)> {
16941 if let Some(snapshots) = self.snapshots.get(statute_id) {
16942 if snapshots.len() < 2 {
16943 return Vec::new();
16944 }
16945
16946 let mut trend = Vec::new();
16947 for i in 1..snapshots.len() {
16948 let prev = &snapshots[i - 1];
16949 let curr = &snapshots[i];
16950 let drift = (prev.quality_score - curr.quality_score).abs();
16951 trend.push((curr.timestamp, drift));
16952 }
16953 trend
16954 } else {
16955 Vec::new()
16956 }
16957 }
16958}
16959
16960impl Default for DriftMonitor {
16961 fn default() -> Self {
16962 Self::new()
16963 }
16964}
16965
16966#[derive(Debug, Clone, Serialize, Deserialize)]
16972pub struct ExplanatoryNote {
16973 pub note_id: String,
16975 pub statute_id: String,
16977 pub section: String,
16979 pub explanation: String,
16981 pub reason_for_change: Option<String>,
16983 pub legal_implications: Vec<String>,
16985 pub examples: Vec<String>,
16987 pub cross_references: Vec<String>,
16989 pub generated_at: chrono::DateTime<chrono::Utc>,
16991}
16992
16993pub struct ExplanatoryNoteGenerator;
16995
16996impl ExplanatoryNoteGenerator {
16997 pub fn new() -> Self {
16999 Self
17000 }
17001
17002 pub fn generate_notes(&self, ported: &PortedStatute) -> Vec<ExplanatoryNote> {
17004 let mut notes = Vec::new();
17005
17006 notes.push(self.generate_statute_note(ported));
17008
17009 for (idx, change) in ported.changes.iter().enumerate() {
17011 if self.is_significant_change(change) {
17012 notes.push(self.generate_change_note(ported, change, idx));
17013 }
17014 }
17015
17016 notes
17017 }
17018
17019 fn generate_statute_note(&self, ported: &PortedStatute) -> ExplanatoryNote {
17021 let explanation = format!(
17022 "This statute has been ported from another jurisdiction. It contains {} adaptations to ensure compliance with local legal requirements and cultural norms.",
17023 ported.changes.len()
17024 );
17025
17026 let legal_implications = vec![
17027 "This statute is adapted for the target jurisdiction".to_string(),
17028 format!(
17029 "Compatibility score: {:.2}%",
17030 ported.compatibility_score * 100.0
17031 ),
17032 ];
17033
17034 ExplanatoryNote {
17035 note_id: uuid::Uuid::new_v4().to_string(),
17036 statute_id: ported.statute.id.clone(),
17037 section: "General".to_string(),
17038 explanation,
17039 reason_for_change: Some("Cross-jurisdiction legal framework porting".to_string()),
17040 legal_implications,
17041 examples: vec![],
17042 cross_references: vec![],
17043 generated_at: chrono::Utc::now(),
17044 }
17045 }
17046
17047 fn generate_change_note(
17049 &self,
17050 ported: &PortedStatute,
17051 change: &PortingChange,
17052 idx: usize,
17053 ) -> ExplanatoryNote {
17054 let explanation = format!(
17055 "{} (Change type: {:?})",
17056 change.description, change.change_type
17057 );
17058
17059 let mut legal_implications = vec![change.reason.clone()];
17060
17061 if let (Some(original), Some(adapted)) = (&change.original, &change.adapted) {
17062 legal_implications.push(format!(
17063 "Changed from '{}' to '{}' for local applicability",
17064 original, adapted
17065 ));
17066 }
17067
17068 ExplanatoryNote {
17069 note_id: uuid::Uuid::new_v4().to_string(),
17070 statute_id: ported.statute.id.clone(),
17071 section: format!("Change {}", idx + 1),
17072 explanation,
17073 reason_for_change: Some(change.reason.clone()),
17074 legal_implications,
17075 examples: vec![],
17076 cross_references: vec![],
17077 generated_at: chrono::Utc::now(),
17078 }
17079 }
17080
17081 fn is_significant_change(&self, change: &PortingChange) -> bool {
17083 matches!(
17084 change.change_type,
17085 ChangeType::CulturalAdaptation | ChangeType::ValueAdaptation | ChangeType::Removal
17086 )
17087 }
17088}
17089
17090impl Default for ExplanatoryNoteGenerator {
17091 fn default() -> Self {
17092 Self::new()
17093 }
17094}
17095
17096#[derive(Debug, Clone, Serialize, Deserialize)]
17098pub struct ChangeJustificationReport {
17099 pub report_id: String,
17101 pub statute_id: String,
17103 pub source_jurisdiction: String,
17105 pub target_jurisdiction: String,
17107 pub justifications: Vec<ChangeJustification>,
17109 pub overall_rationale: String,
17111 pub legal_basis: Vec<String>,
17113 pub stakeholder_input: Option<String>,
17115 pub generated_at: chrono::DateTime<chrono::Utc>,
17117}
17118
17119#[derive(Debug, Clone, Serialize, Deserialize)]
17121pub struct ChangeJustification {
17122 pub change_description: String,
17124 pub change_type: ChangeType,
17126 pub justification: String,
17128 pub legal_authority: Option<String>,
17130 pub alternatives_considered: Vec<String>,
17132 pub risk_if_unchanged: Option<String>,
17134}
17135
17136pub struct ChangeJustificationReportGenerator;
17138
17139impl ChangeJustificationReportGenerator {
17140 pub fn new() -> Self {
17142 Self
17143 }
17144
17145 pub fn generate_report(
17147 &self,
17148 ported: &PortedStatute,
17149 source_jurisdiction: &str,
17150 target_jurisdiction: &str,
17151 ) -> ChangeJustificationReport {
17152 let justifications = ported
17153 .changes
17154 .iter()
17155 .map(|change| self.justify_change(change))
17156 .collect();
17157
17158 let overall_rationale = format!(
17159 "This statute was ported from {} to {} to facilitate legal harmonization and knowledge transfer. {} changes were made to ensure local applicability and compliance.",
17160 source_jurisdiction,
17161 target_jurisdiction,
17162 ported.changes.len()
17163 );
17164
17165 let legal_basis = vec![
17166 "Cross-jurisdictional legal framework sharing".to_string(),
17167 "Cultural adaptation requirements".to_string(),
17168 "Local legal compliance mandate".to_string(),
17169 ];
17170
17171 ChangeJustificationReport {
17172 report_id: uuid::Uuid::new_v4().to_string(),
17173 statute_id: ported.statute.id.clone(),
17174 source_jurisdiction: source_jurisdiction.to_string(),
17175 target_jurisdiction: target_jurisdiction.to_string(),
17176 justifications,
17177 overall_rationale,
17178 legal_basis,
17179 stakeholder_input: None,
17180 generated_at: chrono::Utc::now(),
17181 }
17182 }
17183
17184 fn justify_change(&self, change: &PortingChange) -> ChangeJustification {
17186 let justification = match change.change_type {
17187 ChangeType::Translation => "Translation required for language localization".to_string(),
17188 ChangeType::ValueAdaptation => {
17189 "Value adapted to match local legal standards and thresholds".to_string()
17190 }
17191 ChangeType::CulturalAdaptation => {
17192 "Cultural adaptation necessary for local acceptability and compliance".to_string()
17193 }
17194 ChangeType::Removal => {
17195 "Removed due to incompatibility with target jurisdiction laws".to_string()
17196 }
17197 ChangeType::ComplianceAddition => {
17198 "Added to ensure compliance with target jurisdiction requirements".to_string()
17199 }
17200 ChangeType::Incompatible => "Marked as incompatible pending further review".to_string(),
17201 };
17202
17203 let risk_if_unchanged = match change.change_type {
17204 ChangeType::CulturalAdaptation | ChangeType::ValueAdaptation => {
17205 Some("Non-compliance with local legal requirements".to_string())
17206 }
17207 ChangeType::Removal => Some("Potential legal conflict or invalidity".to_string()),
17208 _ => None,
17209 };
17210
17211 ChangeJustification {
17212 change_description: change.description.clone(),
17213 change_type: change.change_type,
17214 justification,
17215 legal_authority: None,
17216 alternatives_considered: vec![],
17217 risk_if_unchanged,
17218 }
17219 }
17220}
17221
17222impl Default for ChangeJustificationReportGenerator {
17223 fn default() -> Self {
17224 Self::new()
17225 }
17226}
17227
17228#[derive(Debug, Clone, Serialize, Deserialize)]
17230pub struct LegislativeHistoryEntry {
17231 pub timestamp: chrono::DateTime<chrono::Utc>,
17233 pub event_type: LegislativeEventType,
17235 pub description: String,
17237 pub actor: Option<String>,
17239 pub related_documents: Vec<String>,
17241}
17242
17243#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17245pub enum LegislativeEventType {
17246 Drafted,
17248 Reviewed,
17250 Amended,
17252 Approved,
17254 Published,
17256 Ported,
17258}
17259
17260#[derive(Debug, Clone, Serialize, Deserialize)]
17262pub struct LegislativeHistory {
17263 pub history_id: String,
17265 pub statute_id: String,
17267 pub original_enactment: Option<String>,
17269 pub porting_date: String,
17271 pub timeline: Vec<LegislativeHistoryEntry>,
17273 pub key_participants: Vec<String>,
17275 pub summary: String,
17277}
17278
17279pub struct LegislativeHistoryCompiler;
17281
17282impl LegislativeHistoryCompiler {
17283 pub fn new() -> Self {
17285 Self
17286 }
17287
17288 pub fn compile_history(&self, ported: &PortedStatute) -> LegislativeHistory {
17290 let mut timeline = Vec::new();
17291
17292 timeline.push(LegislativeHistoryEntry {
17294 timestamp: chrono::Utc::now(),
17295 event_type: LegislativeEventType::Ported,
17296 description: format!("Statute ported with {} adaptations", ported.changes.len()),
17297 actor: Some("Porting System".to_string()),
17298 related_documents: vec![],
17299 });
17300
17301 for change in &ported.changes {
17303 timeline.push(LegislativeHistoryEntry {
17304 timestamp: chrono::Utc::now(),
17305 event_type: LegislativeEventType::Amended,
17306 description: change.description.clone(),
17307 actor: None,
17308 related_documents: vec![],
17309 });
17310 }
17311
17312 let summary = format!(
17313 "This statute was ported from another jurisdiction with {} modifications to ensure local applicability.",
17314 ported.changes.len()
17315 );
17316
17317 LegislativeHistory {
17318 history_id: uuid::Uuid::new_v4().to_string(),
17319 statute_id: ported.statute.id.clone(),
17320 original_enactment: None,
17321 porting_date: chrono::Utc::now().to_rfc3339(),
17322 timeline,
17323 key_participants: vec!["Porting System".to_string()],
17324 summary,
17325 }
17326 }
17327
17328 #[allow(dead_code)]
17330 pub fn add_event(
17331 &self,
17332 history: &mut LegislativeHistory,
17333 event_type: LegislativeEventType,
17334 description: String,
17335 actor: Option<String>,
17336 ) {
17337 history.timeline.push(LegislativeHistoryEntry {
17338 timestamp: chrono::Utc::now(),
17339 event_type,
17340 description,
17341 actor,
17342 related_documents: vec![],
17343 });
17344 }
17345}
17346
17347impl Default for LegislativeHistoryCompiler {
17348 fn default() -> Self {
17349 Self::new()
17350 }
17351}
17352
17353#[derive(Debug, Clone, Serialize, Deserialize)]
17355pub struct ImplementationGuidance {
17356 pub guidance_id: String,
17358 pub statute_id: String,
17360 pub overview: String,
17362 pub prerequisites: Vec<String>,
17364 pub implementation_steps: Vec<ImplementationStep>,
17366 pub compliance_checklist: Vec<String>,
17368 pub common_pitfalls: Vec<String>,
17370 pub resources: Vec<String>,
17372 pub timeline_estimate: Option<String>,
17374 pub generated_at: chrono::DateTime<chrono::Utc>,
17376}
17377
17378#[derive(Debug, Clone, Serialize, Deserialize)]
17380pub struct ImplementationStep {
17381 pub step_number: usize,
17383 pub title: String,
17385 pub description: String,
17387 pub required_actions: Vec<String>,
17389 pub success_criteria: Vec<String>,
17391}
17392
17393pub struct ImplementationGuidanceGenerator;
17395
17396impl ImplementationGuidanceGenerator {
17397 pub fn new() -> Self {
17399 Self
17400 }
17401
17402 pub fn generate_guidance(&self, ported: &PortedStatute) -> ImplementationGuidance {
17404 let overview = format!(
17405 "This guidance provides step-by-step instructions for implementing the ported statute '{}'. The statute has been adapted with {} changes for local compliance.",
17406 ported.statute.title,
17407 ported.changes.len()
17408 );
17409
17410 let prerequisites = vec![
17411 "Review the ported statute in detail".to_string(),
17412 "Ensure all stakeholders are informed".to_string(),
17413 "Verify compliance with local regulations".to_string(),
17414 "Prepare necessary resources".to_string(),
17415 ];
17416
17417 let implementation_steps = self.generate_steps(ported);
17418
17419 let compliance_checklist = vec![
17420 "Verify all cultural adaptations are appropriate".to_string(),
17421 "Confirm legal compliance in target jurisdiction".to_string(),
17422 "Validate translations are accurate".to_string(),
17423 "Ensure stakeholder approval is obtained".to_string(),
17424 ];
17425
17426 let common_pitfalls = vec![
17427 "Overlooking cultural differences".to_string(),
17428 "Insufficient stakeholder consultation".to_string(),
17429 "Inadequate legal review".to_string(),
17430 ];
17431
17432 ImplementationGuidance {
17433 guidance_id: uuid::Uuid::new_v4().to_string(),
17434 statute_id: ported.statute.id.clone(),
17435 overview,
17436 prerequisites,
17437 implementation_steps,
17438 compliance_checklist,
17439 common_pitfalls,
17440 resources: vec![],
17441 timeline_estimate: Some("3-6 months".to_string()),
17442 generated_at: chrono::Utc::now(),
17443 }
17444 }
17445
17446 fn generate_steps(&self, ported: &PortedStatute) -> Vec<ImplementationStep> {
17448 let mut steps = Vec::new();
17449
17450 steps.push(ImplementationStep {
17451 step_number: 1,
17452 title: "Initial Review".to_string(),
17453 description: "Review the ported statute and all adaptations".to_string(),
17454 required_actions: vec![
17455 "Read the full statute text".to_string(),
17456 "Review all change justifications".to_string(),
17457 ],
17458 success_criteria: vec!["All adaptations understood".to_string()],
17459 });
17460
17461 steps.push(ImplementationStep {
17462 step_number: 2,
17463 title: "Stakeholder Consultation".to_string(),
17464 description: "Consult with affected stakeholders".to_string(),
17465 required_actions: vec![
17466 "Identify all affected parties".to_string(),
17467 "Conduct consultation sessions".to_string(),
17468 ],
17469 success_criteria: vec!["Stakeholder feedback incorporated".to_string()],
17470 });
17471
17472 steps.push(ImplementationStep {
17473 step_number: 3,
17474 title: "Legal Validation".to_string(),
17475 description: "Validate legal compliance".to_string(),
17476 required_actions: vec![
17477 "Conduct legal review".to_string(),
17478 "Verify compliance with all regulations".to_string(),
17479 ],
17480 success_criteria: vec!["Legal approval obtained".to_string()],
17481 });
17482
17483 if !ported.changes.is_empty() {
17484 steps.push(ImplementationStep {
17485 step_number: 4,
17486 title: "Implementation of Adaptations".to_string(),
17487 description: format!("Implement {} adaptations", ported.changes.len()),
17488 required_actions: vec![
17489 "Apply all cultural adaptations".to_string(),
17490 "Update documentation".to_string(),
17491 ],
17492 success_criteria: vec!["All changes successfully applied".to_string()],
17493 });
17494 }
17495
17496 steps.push(ImplementationStep {
17497 step_number: steps.len() + 1,
17498 title: "Final Approval and Publication".to_string(),
17499 description: "Obtain final approval and publish".to_string(),
17500 required_actions: vec![
17501 "Submit for final approval".to_string(),
17502 "Publish statute".to_string(),
17503 ],
17504 success_criteria: vec!["Statute officially enacted".to_string()],
17505 });
17506
17507 steps
17508 }
17509}
17510
17511impl Default for ImplementationGuidanceGenerator {
17512 fn default() -> Self {
17513 Self::new()
17514 }
17515}
17516
17517#[derive(Debug, Clone, Serialize, Deserialize)]
17519pub struct TrainingMaterial {
17520 pub material_id: String,
17522 pub statute_id: String,
17524 pub title: String,
17526 pub target_audience: TrainingAudience,
17528 pub learning_objectives: Vec<String>,
17530 pub modules: Vec<TrainingModule>,
17532 pub assessment_questions: Vec<AssessmentQuestion>,
17534 pub estimated_duration: String,
17536 pub generated_at: chrono::DateTime<chrono::Utc>,
17538}
17539
17540#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17542pub enum TrainingAudience {
17543 LegalProfessionals,
17545 GovernmentOfficials,
17547 GeneralPublic,
17549 EnforcementOfficers,
17551}
17552
17553#[derive(Debug, Clone, Serialize, Deserialize)]
17555pub struct TrainingModule {
17556 pub module_number: usize,
17558 pub title: String,
17560 pub content: String,
17562 pub key_points: Vec<String>,
17564 pub examples: Vec<String>,
17566}
17567
17568#[derive(Debug, Clone, Serialize, Deserialize)]
17570pub struct AssessmentQuestion {
17571 pub question_number: usize,
17573 pub question: String,
17575 pub options: Vec<String>,
17577 pub correct_answer: usize,
17579 pub explanation: String,
17581}
17582
17583pub struct TrainingMaterialGenerator;
17585
17586impl TrainingMaterialGenerator {
17587 pub fn new() -> Self {
17589 Self
17590 }
17591
17592 pub fn generate_materials(
17594 &self,
17595 ported: &PortedStatute,
17596 audience: TrainingAudience,
17597 ) -> TrainingMaterial {
17598 let title = format!("Training: {}", ported.statute.title);
17599
17600 let learning_objectives = match audience {
17601 TrainingAudience::LegalProfessionals => vec![
17602 "Understand the legal framework of the ported statute".to_string(),
17603 "Identify all adaptations and their legal basis".to_string(),
17604 "Apply the statute in legal practice".to_string(),
17605 ],
17606 TrainingAudience::GovernmentOfficials => vec![
17607 "Understand the statute's requirements".to_string(),
17608 "Implement the statute in policy".to_string(),
17609 "Ensure compliance across departments".to_string(),
17610 ],
17611 TrainingAudience::GeneralPublic => vec![
17612 "Understand rights and obligations under the statute".to_string(),
17613 "Know how the statute affects daily life".to_string(),
17614 ],
17615 TrainingAudience::EnforcementOfficers => vec![
17616 "Understand enforcement procedures".to_string(),
17617 "Identify violations and apply penalties".to_string(),
17618 ],
17619 };
17620
17621 let modules = self.generate_modules(ported, audience);
17622 let assessment_questions = self.generate_assessment(ported, audience);
17623
17624 let estimated_duration = match audience {
17625 TrainingAudience::LegalProfessionals => "4 hours".to_string(),
17626 TrainingAudience::GovernmentOfficials => "3 hours".to_string(),
17627 TrainingAudience::GeneralPublic => "1 hour".to_string(),
17628 TrainingAudience::EnforcementOfficers => "2 hours".to_string(),
17629 };
17630
17631 TrainingMaterial {
17632 material_id: uuid::Uuid::new_v4().to_string(),
17633 statute_id: ported.statute.id.clone(),
17634 title,
17635 target_audience: audience,
17636 learning_objectives,
17637 modules,
17638 assessment_questions,
17639 estimated_duration,
17640 generated_at: chrono::Utc::now(),
17641 }
17642 }
17643
17644 fn generate_modules(
17646 &self,
17647 ported: &PortedStatute,
17648 _audience: TrainingAudience,
17649 ) -> Vec<TrainingModule> {
17650 let mut modules = Vec::new();
17651
17652 modules.push(TrainingModule {
17653 module_number: 1,
17654 title: "Introduction to the Statute".to_string(),
17655 content: format!(
17656 "This statute, '{}', has been ported from another jurisdiction to facilitate legal harmonization.",
17657 ported.statute.title
17658 ),
17659 key_points: vec![
17660 "Purpose of the statute".to_string(),
17661 "Scope of application".to_string(),
17662 ],
17663 examples: vec![],
17664 });
17665
17666 if !ported.changes.is_empty() {
17667 modules.push(TrainingModule {
17668 module_number: 2,
17669 title: "Key Adaptations".to_string(),
17670 content: format!(
17671 "{} adaptations were made for local compliance.",
17672 ported.changes.len()
17673 ),
17674 key_points: ported
17675 .changes
17676 .iter()
17677 .take(5)
17678 .map(|c| c.description.clone())
17679 .collect(),
17680 examples: vec![],
17681 });
17682 }
17683
17684 modules.push(TrainingModule {
17685 module_number: modules.len() + 1,
17686 title: "Practical Application".to_string(),
17687 content: "How to apply this statute in practice".to_string(),
17688 key_points: vec![
17689 "Implementation procedures".to_string(),
17690 "Common scenarios".to_string(),
17691 ],
17692 examples: vec![],
17693 });
17694
17695 modules
17696 }
17697
17698 fn generate_assessment(
17700 &self,
17701 ported: &PortedStatute,
17702 _audience: TrainingAudience,
17703 ) -> Vec<AssessmentQuestion> {
17704 let mut questions = Vec::new();
17705
17706 questions.push(AssessmentQuestion {
17707 question_number: 1,
17708 question: format!("What is the main purpose of '{}'?", ported.statute.title),
17709 options: vec![
17710 "To provide legal framework".to_string(),
17711 "To regulate commerce".to_string(),
17712 "To enforce penalties".to_string(),
17713 ],
17714 correct_answer: 0,
17715 explanation: "This statute provides the legal framework for its subject matter."
17716 .to_string(),
17717 });
17718
17719 if !ported.changes.is_empty() {
17720 questions.push(AssessmentQuestion {
17721 question_number: 2,
17722 question: "How many adaptations were made to this statute?".to_string(),
17723 options: vec![
17724 format!("{}", ported.changes.len()),
17725 "0".to_string(),
17726 "100".to_string(),
17727 ],
17728 correct_answer: 0,
17729 explanation: format!(
17730 "{} adaptations were made for local compliance.",
17731 ported.changes.len()
17732 ),
17733 });
17734 }
17735
17736 questions
17737 }
17738}
17739
17740impl Default for TrainingMaterialGenerator {
17741 fn default() -> Self {
17742 Self::new()
17743 }
17744}
17745
17746#[derive(Debug, Clone, Serialize, Deserialize)]
17752pub struct ModelLaw {
17753 pub id: String,
17755 pub name: String,
17757 pub issuing_organization: String,
17759 pub version: String,
17761 pub subject_area: String,
17763 pub text: String,
17765 pub adoptions: Vec<ModelLawAdoption>,
17767 pub recommended_adaptations: Vec<String>,
17769 pub created_at: String,
17771 pub updated_at: String,
17773}
17774
17775#[derive(Debug, Clone, Serialize, Deserialize)]
17777pub struct ModelLawAdoption {
17778 pub jurisdiction: String,
17780 pub adoption_date: String,
17782 pub adoption_level: AdoptionLevel,
17784 pub local_adaptations: Vec<String>,
17786 pub implementation_status: ImplementationStatus,
17788 pub notes: String,
17790}
17791
17792#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
17794pub enum AdoptionLevel {
17795 FullAdoption,
17797 SubstantialAdoption,
17799 PartialAdoption,
17801 Inspired,
17803 UnderConsideration,
17805}
17806
17807#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
17809pub enum ImplementationStatus {
17810 Implemented,
17812 PartiallyImplemented,
17814 Enacted,
17816 InLegislativeProcess,
17818 Planned,
17820}
17821
17822impl ModelLaw {
17823 pub fn new(
17825 name: String,
17826 issuing_organization: String,
17827 version: String,
17828 subject_area: String,
17829 text: String,
17830 ) -> Self {
17831 Self {
17832 id: uuid::Uuid::new_v4().to_string(),
17833 name,
17834 issuing_organization,
17835 version,
17836 subject_area,
17837 text,
17838 adoptions: Vec::new(),
17839 recommended_adaptations: Vec::new(),
17840 created_at: chrono::Utc::now().to_rfc3339(),
17841 updated_at: chrono::Utc::now().to_rfc3339(),
17842 }
17843 }
17844
17845 pub fn add_adoption(&mut self, adoption: ModelLawAdoption) {
17847 self.adoptions.push(adoption);
17848 self.updated_at = chrono::Utc::now().to_rfc3339();
17849 }
17850
17851 pub fn get_adoption_rate(&self, total_jurisdictions: usize) -> f64 {
17853 if total_jurisdictions == 0 {
17854 return 0.0;
17855 }
17856 self.adoptions.len() as f64 / total_jurisdictions as f64
17857 }
17858
17859 pub fn get_full_adoptions(&self) -> Vec<&ModelLawAdoption> {
17861 self.adoptions
17862 .iter()
17863 .filter(|a| a.adoption_level == AdoptionLevel::FullAdoption)
17864 .collect()
17865 }
17866}
17867
17868#[derive(Debug, Clone, Serialize, Deserialize)]
17870pub struct TreatyBasedPorting {
17871 pub treaty_id: String,
17873 pub treaty_name: String,
17875 pub treaty_type: TreatyType,
17877 pub signatories: Vec<String>,
17879 pub provisions: Vec<TreatyProvision>,
17881 pub harmonization_requirements: Vec<HarmonizationRequirement>,
17883 pub porting_obligations: Vec<PortingObligation>,
17885 pub status: TreatyStatus,
17887 pub entry_into_force: Option<String>,
17889}
17890
17891#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
17893pub enum TreatyType {
17894 Bilateral,
17896 Multilateral,
17898 Regional,
17900 FrameworkConvention,
17902 Protocol,
17904 MOU,
17906}
17907
17908#[derive(Debug, Clone, Serialize, Deserialize)]
17910pub struct TreatyProvision {
17911 pub id: String,
17913 pub article_number: String,
17915 pub text: String,
17917 pub binding: bool,
17919 pub implementation_deadline: Option<String>,
17921 pub related_law_areas: Vec<String>,
17923}
17924
17925#[derive(Debug, Clone, Serialize, Deserialize)]
17927pub struct HarmonizationRequirement {
17928 pub id: String,
17930 pub description: String,
17932 pub harmonization_level: HarmonizationLevel,
17934 pub affected_areas: Vec<String>,
17936 pub deadline: Option<String>,
17938 pub compliance_status: Vec<(String, ComplianceLevel)>,
17940}
17941
17942#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
17944pub enum HarmonizationLevel {
17945 Complete,
17947 Substantial,
17949 MinimumStandards,
17951 MutualRecognition,
17953 Coordination,
17955}
17956
17957#[derive(Debug, Clone, Serialize, Deserialize)]
17959pub struct PortingObligation {
17960 pub id: String,
17962 pub source_provision: String,
17964 pub required_implementation: String,
17966 pub affected_jurisdictions: Vec<String>,
17968 pub deadline: Option<String>,
17970 pub implementation_status: Vec<(String, ImplementationStatus)>,
17972}
17973
17974#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
17976pub enum TreatyStatus {
17977 Negotiation,
17979 Signed,
17981 InForce,
17983 Suspended,
17985 Terminated,
17987}
17988
17989#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
17991pub enum ComplianceLevel {
17992 FullCompliance,
17994 PartialCompliance,
17996 NonCompliance,
17998 Pending,
18000}
18001
18002impl TreatyBasedPorting {
18003 pub fn new(treaty_name: String, treaty_type: TreatyType, signatories: Vec<String>) -> Self {
18005 Self {
18006 treaty_id: uuid::Uuid::new_v4().to_string(),
18007 treaty_name,
18008 treaty_type,
18009 signatories,
18010 provisions: Vec::new(),
18011 harmonization_requirements: Vec::new(),
18012 porting_obligations: Vec::new(),
18013 status: TreatyStatus::Negotiation,
18014 entry_into_force: None,
18015 }
18016 }
18017
18018 pub fn add_provision(&mut self, provision: TreatyProvision) {
18020 self.provisions.push(provision);
18021 }
18022
18023 pub fn add_harmonization_requirement(&mut self, requirement: HarmonizationRequirement) {
18025 self.harmonization_requirements.push(requirement);
18026 }
18027
18028 pub fn get_compliance_rate(&self, jurisdiction: &str) -> f64 {
18030 let total = self.harmonization_requirements.len();
18031 if total == 0 {
18032 return 1.0;
18033 }
18034
18035 let compliant = self
18036 .harmonization_requirements
18037 .iter()
18038 .filter(|req| {
18039 req.compliance_status.iter().any(|(j, level)| {
18040 j == jurisdiction && *level == ComplianceLevel::FullCompliance
18041 })
18042 })
18043 .count();
18044
18045 compliant as f64 / total as f64
18046 }
18047}
18048
18049#[derive(Debug, Clone, Serialize, Deserialize)]
18051pub struct InternationalStandard {
18052 pub id: String,
18054 pub name: String,
18056 pub issuing_body: String,
18058 pub standard_number: String,
18060 pub subject_area: String,
18062 pub standard_type: StandardType,
18064 pub technical_specs: String,
18066 pub adoption_recommendations: Vec<AdoptionRecommendation>,
18068 pub alignment_status: Vec<AlignmentStatus>,
18070 pub publication_date: String,
18072}
18073
18074#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18076pub enum StandardType {
18077 Technical,
18079 Safety,
18081 Quality,
18083 Environmental,
18085 DataProtection,
18087 Cybersecurity,
18089 BestPractice,
18091}
18092
18093#[derive(Debug, Clone, Serialize, Deserialize)]
18095pub struct AdoptionRecommendation {
18096 pub id: String,
18098 pub target_jurisdiction: String,
18100 pub adoption_approach: String,
18102 pub required_legal_changes: Vec<String>,
18104 pub estimated_timeline: String,
18106 pub priority: AdoptionPriority,
18108}
18109
18110#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
18112pub enum AdoptionPriority {
18113 Critical,
18115 High,
18117 Medium,
18119 Low,
18121}
18122
18123#[derive(Debug, Clone, Serialize, Deserialize)]
18125pub struct AlignmentStatus {
18126 pub jurisdiction: String,
18128 pub alignment_level: AlignmentLevel,
18130 pub deviations: Vec<String>,
18132 pub planned_actions: Vec<String>,
18134 pub last_assessment: String,
18136}
18137
18138#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18140pub enum AlignmentLevel {
18141 FullyAligned,
18143 SubstantiallyAligned,
18145 PartiallyAligned,
18147 MinimalAlignment,
18149 NotAligned,
18151}
18152
18153impl InternationalStandard {
18154 pub fn new(
18156 name: String,
18157 issuing_body: String,
18158 standard_number: String,
18159 subject_area: String,
18160 standard_type: StandardType,
18161 ) -> Self {
18162 Self {
18163 id: uuid::Uuid::new_v4().to_string(),
18164 name,
18165 issuing_body,
18166 standard_number,
18167 subject_area,
18168 standard_type,
18169 technical_specs: String::new(),
18170 adoption_recommendations: Vec::new(),
18171 alignment_status: Vec::new(),
18172 publication_date: chrono::Utc::now().to_rfc3339(),
18173 }
18174 }
18175
18176 pub fn get_global_alignment_rate(&self) -> f64 {
18178 if self.alignment_status.is_empty() {
18179 return 0.0;
18180 }
18181
18182 let aligned = self
18183 .alignment_status
18184 .iter()
18185 .filter(|s| {
18186 matches!(
18187 s.alignment_level,
18188 AlignmentLevel::FullyAligned | AlignmentLevel::SubstantiallyAligned
18189 )
18190 })
18191 .count();
18192
18193 aligned as f64 / self.alignment_status.len() as f64
18194 }
18195}
18196
18197#[derive(Debug, Clone, Serialize, Deserialize)]
18199pub struct BestPractice {
18200 pub id: String,
18202 pub name: String,
18204 pub legal_area: String,
18206 pub description: String,
18208 pub source_jurisdictions: Vec<String>,
18210 pub evidence: Vec<Evidence>,
18212 pub transferability: TransferabilityAssessment,
18214 pub adoptions: Vec<BestPracticeAdoption>,
18216 pub recommended_adaptations: Vec<String>,
18218 pub created_at: String,
18220}
18221
18222#[derive(Debug, Clone, Serialize, Deserialize)]
18224pub struct Evidence {
18225 pub evidence_type: EvidenceType,
18227 pub description: String,
18229 pub source: String,
18231 pub date: String,
18233 pub quality_score: f64,
18235}
18236
18237#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18239pub enum EvidenceType {
18240 EmpiricalResearch,
18242 CaseStudy,
18244 ExpertOpinion,
18246 StatisticalData,
18248 ComparativeAnalysis,
18250 ImplementationReport,
18252}
18253
18254#[derive(Debug, Clone, Serialize, Deserialize)]
18256pub struct TransferabilityAssessment {
18257 pub overall_score: f64,
18259 pub legal_system_compatibility: Vec<(String, f64)>,
18261 pub cultural_adaptability: f64,
18263 pub economic_feasibility: f64,
18265 pub prerequisites: Vec<String>,
18267 pub potential_barriers: Vec<String>,
18269}
18270
18271#[derive(Debug, Clone, Serialize, Deserialize)]
18273pub struct BestPracticeAdoption {
18274 pub jurisdiction: String,
18276 pub adoption_date: String,
18278 pub adaptations: Vec<String>,
18280 pub outcome: OutcomeAssessment,
18282 pub lessons_learned: Vec<String>,
18284}
18285
18286#[derive(Debug, Clone, Serialize, Deserialize)]
18288pub struct OutcomeAssessment {
18289 pub success_level: SuccessLevel,
18291 pub impact_metrics: Vec<(String, f64)>,
18293 pub challenges: Vec<String>,
18295 pub assessment_date: String,
18297}
18298
18299#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18301pub enum SuccessLevel {
18302 HighlySuccessful,
18304 Successful,
18306 ModeratelySuccessful,
18308 LimitedSuccess,
18310 Unsuccessful,
18312}
18313
18314impl BestPractice {
18315 pub fn new(name: String, legal_area: String, description: String) -> Self {
18317 Self {
18318 id: uuid::Uuid::new_v4().to_string(),
18319 name,
18320 legal_area,
18321 description,
18322 source_jurisdictions: Vec::new(),
18323 evidence: Vec::new(),
18324 transferability: TransferabilityAssessment {
18325 overall_score: 0.5,
18326 legal_system_compatibility: Vec::new(),
18327 cultural_adaptability: 0.5,
18328 economic_feasibility: 0.5,
18329 prerequisites: Vec::new(),
18330 potential_barriers: Vec::new(),
18331 },
18332 adoptions: Vec::new(),
18333 recommended_adaptations: Vec::new(),
18334 created_at: chrono::Utc::now().to_rfc3339(),
18335 }
18336 }
18337
18338 pub fn get_success_rate(&self) -> f64 {
18340 if self.adoptions.is_empty() {
18341 return 0.0;
18342 }
18343
18344 let successful = self
18345 .adoptions
18346 .iter()
18347 .filter(|a| {
18348 matches!(
18349 a.outcome.success_level,
18350 SuccessLevel::HighlySuccessful | SuccessLevel::Successful
18351 )
18352 })
18353 .count();
18354
18355 successful as f64 / self.adoptions.len() as f64
18356 }
18357}
18358
18359#[derive(Debug, Clone, Serialize, Deserialize)]
18361pub struct SoftLawConversion {
18362 pub id: String,
18364 pub soft_law_source: SoftLawSource,
18366 pub target_hard_law: HardLawTarget,
18368 pub conversion_strategy: ConversionStrategy,
18370 pub legal_basis: Vec<String>,
18372 pub consultations: Vec<StakeholderConsultation>,
18374 pub implementation_steps: Vec<ConversionImplementationStep>,
18376 pub status: ConversionStatus,
18378 pub created_at: String,
18380}
18381
18382#[derive(Debug, Clone, Serialize, Deserialize)]
18384pub struct SoftLawSource {
18385 pub id: String,
18387 pub name: String,
18389 pub source_type: SoftLawType,
18391 pub issuing_body: String,
18393 pub content: String,
18395 pub binding_force: BindingForce,
18397 pub endorsements: Vec<String>,
18399}
18400
18401#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18403pub enum SoftLawType {
18404 UNResolution,
18406 Guidelines,
18408 Recommendations,
18410 Principles,
18412 CodeOfConduct,
18414 Declaration,
18416 BestPractices,
18418 Standards,
18420}
18421
18422#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18424pub enum BindingForce {
18425 NonBinding,
18427 PoliticalCommitment,
18429 MoralObligation,
18431 QuasiLegal,
18433 LegallyBinding,
18435}
18436
18437#[derive(Debug, Clone, Serialize, Deserialize)]
18439pub struct HardLawTarget {
18440 pub jurisdiction: String,
18442 pub instrument_type: LegalInstrumentType,
18444 pub draft_legislation: String,
18446 pub enforcement_mechanisms: Vec<String>,
18448 pub penalties: Vec<String>,
18450}
18451
18452#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18454pub enum LegalInstrumentType {
18455 PrimaryLegislation,
18457 SecondaryLegislation,
18459 ConstitutionalAmendment,
18461 TreatyImplementation,
18463 AdministrativeRule,
18465}
18466
18467#[derive(Debug, Clone, Serialize, Deserialize)]
18469pub struct ConversionStrategy {
18470 pub strategy_type: ConversionStrategyType,
18472 pub rationale: String,
18474 pub adaptations: Vec<String>,
18476 pub risks: Vec<(String, String)>,
18478 pub timeline: String,
18480}
18481
18482#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18484pub enum ConversionStrategyType {
18485 DirectIncorporation,
18487 AdaptiveIncorporation,
18489 InspiredLegislation,
18491 PhasedImplementation,
18493 PilotProgram,
18495}
18496
18497#[derive(Debug, Clone, Serialize, Deserialize)]
18499pub struct StakeholderConsultation {
18500 pub stakeholder_group: String,
18502 pub consultation_date: String,
18504 pub feedback: Vec<String>,
18506 pub concerns: Vec<String>,
18508 pub incorporated_proposals: Vec<String>,
18510}
18511
18512#[derive(Debug, Clone, Serialize, Deserialize)]
18514pub struct ConversionImplementationStep {
18515 pub step_number: usize,
18517 pub description: String,
18519 pub responsible_party: String,
18521 pub deadline: Option<String>,
18523 pub status: ConversionStepStatus,
18525 pub dependencies: Vec<usize>,
18527}
18528
18529#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18531pub enum ConversionStepStatus {
18532 NotStarted,
18534 InProgress,
18536 Completed,
18538 Blocked,
18540 Cancelled,
18542}
18543
18544#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18546pub enum ConversionStatus {
18547 Planning,
18549 Drafting,
18551 Consultation,
18553 LegislativeReview,
18555 Enacted,
18557 Implementing,
18559 Implemented,
18561 Abandoned,
18563}
18564
18565impl SoftLawConversion {
18566 pub fn new(
18568 soft_law_source: SoftLawSource,
18569 target_hard_law: HardLawTarget,
18570 conversion_strategy: ConversionStrategy,
18571 ) -> Self {
18572 Self {
18573 id: uuid::Uuid::new_v4().to_string(),
18574 soft_law_source,
18575 target_hard_law,
18576 conversion_strategy,
18577 legal_basis: Vec::new(),
18578 consultations: Vec::new(),
18579 implementation_steps: Vec::new(),
18580 status: ConversionStatus::Planning,
18581 created_at: chrono::Utc::now().to_rfc3339(),
18582 }
18583 }
18584
18585 pub fn get_implementation_progress(&self) -> f64 {
18587 if self.implementation_steps.is_empty() {
18588 return 0.0;
18589 }
18590
18591 let completed = self
18592 .implementation_steps
18593 .iter()
18594 .filter(|step| step.status == ConversionStepStatus::Completed)
18595 .count();
18596
18597 (completed as f64 / self.implementation_steps.len() as f64) * 100.0
18598 }
18599
18600 pub fn add_implementation_step(&mut self, step: ConversionImplementationStep) {
18602 self.implementation_steps.push(step);
18603 }
18604}
18605
18606#[derive(Debug, Clone, Serialize, Deserialize)]
18612pub struct RegulatoryChangeTracker {
18613 pub id: String,
18615 pub monitored_jurisdictions: Vec<String>,
18617 pub tracked_areas: Vec<String>,
18619 pub detected_changes: Vec<RegulatoryChange>,
18621 pub subscriptions: Vec<ChangeSubscription>,
18623 pub last_update: String,
18625 pub status: TrackerStatus,
18627}
18628
18629#[derive(Debug, Clone, Serialize, Deserialize)]
18631pub struct RegulatoryChange {
18632 pub id: String,
18634 pub jurisdiction: String,
18636 pub regulatory_area: String,
18638 pub change_type: RegulatoryChangeType,
18640 pub description: String,
18642 pub source_reference: String,
18644 pub detected_at: String,
18646 pub effective_date: Option<String>,
18648 pub impact_severity: ImpactSeverity,
18650 pub affected_statutes: Vec<String>,
18652 pub porting_implications: Vec<String>,
18654}
18655
18656#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18658pub enum RegulatoryChangeType {
18659 NewLegislation,
18661 Amendment,
18663 Repeal,
18665 NewRegulation,
18667 CourtDecision,
18669 AdministrativeGuidance,
18671 EmergencyOrder,
18673 SunsetProvision,
18675}
18676
18677#[derive(Debug, Clone, Serialize, Deserialize)]
18679pub struct ChangeSubscription {
18680 pub id: String,
18682 pub subscriber_id: String,
18684 pub jurisdictions: Vec<String>,
18686 pub areas: Vec<String>,
18688 pub min_severity: ImpactSeverity,
18690 pub notification_channels: Vec<NotificationChannel>,
18692 pub active: bool,
18694 pub created_at: String,
18696}
18697
18698#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18700pub enum TrackerStatus {
18701 Active,
18703 Paused,
18705 Error,
18707 Maintenance,
18709}
18710
18711impl RegulatoryChangeTracker {
18712 pub fn new(monitored_jurisdictions: Vec<String>, tracked_areas: Vec<String>) -> Self {
18714 Self {
18715 id: uuid::Uuid::new_v4().to_string(),
18716 monitored_jurisdictions,
18717 tracked_areas,
18718 detected_changes: Vec::new(),
18719 subscriptions: Vec::new(),
18720 last_update: chrono::Utc::now().to_rfc3339(),
18721 status: TrackerStatus::Active,
18722 }
18723 }
18724
18725 pub fn add_change(&mut self, change: RegulatoryChange) {
18727 self.detected_changes.push(change);
18728 self.last_update = chrono::Utc::now().to_rfc3339();
18729 }
18730
18731 pub fn subscribe(&mut self, subscription: ChangeSubscription) {
18733 self.subscriptions.push(subscription);
18734 }
18735
18736 pub fn get_recent_changes(&self, hours: i64) -> Vec<&RegulatoryChange> {
18738 let cutoff = chrono::Utc::now() - chrono::Duration::hours(hours);
18739 let cutoff_str = cutoff.to_rfc3339();
18740
18741 self.detected_changes
18742 .iter()
18743 .filter(|change| change.detected_at >= cutoff_str)
18744 .collect()
18745 }
18746
18747 pub fn get_changes_by_jurisdiction(&self, jurisdiction: &str) -> Vec<&RegulatoryChange> {
18749 self.detected_changes
18750 .iter()
18751 .filter(|change| change.jurisdiction == jurisdiction)
18752 .collect()
18753 }
18754
18755 pub fn get_critical_changes(&self) -> Vec<&RegulatoryChange> {
18757 self.detected_changes
18758 .iter()
18759 .filter(|change| change.impact_severity == ImpactSeverity::Severe)
18760 .collect()
18761 }
18762}
18763
18764#[derive(Debug, Clone, Serialize, Deserialize)]
18766pub struct AutomaticPortingTrigger {
18767 pub id: String,
18769 pub name: String,
18771 pub source_jurisdiction: String,
18773 pub target_jurisdictions: Vec<String>,
18775 pub conditions: Vec<TriggerCondition>,
18777 pub porting_options: PortingOptions,
18779 pub status: TriggerStatus,
18781 pub execution_history: Vec<TriggerExecution>,
18783 pub created_at: String,
18785}
18786
18787#[derive(Debug, Clone, Serialize, Deserialize)]
18789pub struct TriggerCondition {
18790 pub id: String,
18792 pub condition_type: TriggerConditionType,
18794 pub parameters: Vec<(String, String)>,
18796 pub is_met: bool,
18798}
18799
18800#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18802pub enum TriggerConditionType {
18803 NewLegislation,
18805 StatuteAmendment,
18807 TreatyDeadline,
18809 HarmonizationUpdate,
18811 ModelLawAdoption,
18813 CourtPrecedent,
18815 ScheduledReview,
18817}
18818
18819#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18821pub enum TriggerStatus {
18822 Active,
18824 Disabled,
18826 Executing,
18828 Completed,
18830 Failed,
18832}
18833
18834#[derive(Debug, Clone, Serialize, Deserialize)]
18836pub struct TriggerExecution {
18837 pub id: String,
18839 pub executed_at: String,
18841 pub triggered_by: Vec<String>,
18843 pub porting_results: Vec<String>,
18845 pub success: bool,
18847 pub notes: String,
18849}
18850
18851impl AutomaticPortingTrigger {
18852 pub fn new(
18854 name: String,
18855 source_jurisdiction: String,
18856 target_jurisdictions: Vec<String>,
18857 porting_options: PortingOptions,
18858 ) -> Self {
18859 Self {
18860 id: uuid::Uuid::new_v4().to_string(),
18861 name,
18862 source_jurisdiction,
18863 target_jurisdictions,
18864 conditions: Vec::new(),
18865 porting_options,
18866 status: TriggerStatus::Active,
18867 execution_history: Vec::new(),
18868 created_at: chrono::Utc::now().to_rfc3339(),
18869 }
18870 }
18871
18872 pub fn add_condition(&mut self, condition: TriggerCondition) {
18874 self.conditions.push(condition);
18875 }
18876
18877 pub fn check_conditions(&self) -> bool {
18879 !self.conditions.is_empty() && self.conditions.iter().all(|c| c.is_met)
18880 }
18881
18882 pub fn record_execution(&mut self, execution: TriggerExecution) {
18884 self.execution_history.push(execution);
18885 }
18886
18887 pub fn get_success_rate(&self) -> f64 {
18889 if self.execution_history.is_empty() {
18890 return 0.0;
18891 }
18892
18893 let successful = self.execution_history.iter().filter(|e| e.success).count();
18894
18895 successful as f64 / self.execution_history.len() as f64
18896 }
18897}
18898
18899#[derive(Debug, Clone, Serialize, Deserialize)]
18901pub struct AdaptationAlert {
18902 pub id: String,
18904 pub title: String,
18906 pub description: String,
18908 pub severity: AlertSeverity,
18910 pub affected_jurisdictions: Vec<String>,
18912 pub affected_statutes: Vec<String>,
18914 pub recommended_actions: Vec<RecommendedAction>,
18916 pub status: AlertStatus,
18918 pub created_at: String,
18920 pub expires_at: Option<String>,
18922}
18923
18924#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
18926pub enum AlertSeverity {
18927 Urgent,
18929 High,
18931 Medium,
18933 Low,
18935 Info,
18937}
18938
18939#[derive(Debug, Clone, Serialize, Deserialize)]
18941pub struct RecommendedAction {
18942 pub id: String,
18944 pub action: String,
18946 pub priority: ActionPriority,
18948 pub estimated_effort: String,
18950 pub deadline: Option<String>,
18952 pub prerequisites: Vec<String>,
18954}
18955
18956#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
18958pub enum ActionPriority {
18959 Immediate,
18961 ShortTerm,
18963 MediumTerm,
18965 LongTerm,
18967 Optional,
18969}
18970
18971#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18973pub enum AlertStatus {
18974 Active,
18976 Acknowledged,
18978 InProgress,
18980 Resolved,
18982 Dismissed,
18984 Expired,
18986}
18987
18988impl AdaptationAlert {
18989 pub fn new(
18991 title: String,
18992 description: String,
18993 severity: AlertSeverity,
18994 affected_jurisdictions: Vec<String>,
18995 ) -> Self {
18996 Self {
18997 id: uuid::Uuid::new_v4().to_string(),
18998 title,
18999 description,
19000 severity,
19001 affected_jurisdictions,
19002 affected_statutes: Vec::new(),
19003 recommended_actions: Vec::new(),
19004 status: AlertStatus::Active,
19005 created_at: chrono::Utc::now().to_rfc3339(),
19006 expires_at: None,
19007 }
19008 }
19009
19010 pub fn add_action(&mut self, action: RecommendedAction) {
19012 self.recommended_actions.push(action);
19013 }
19014
19015 pub fn acknowledge(&mut self) {
19017 if self.status == AlertStatus::Active {
19018 self.status = AlertStatus::Acknowledged;
19019 }
19020 }
19021
19022 pub fn resolve(&mut self) {
19024 self.status = AlertStatus::Resolved;
19025 }
19026
19027 pub fn get_high_priority_actions(&self) -> Vec<&RecommendedAction> {
19029 self.recommended_actions
19030 .iter()
19031 .filter(|action| {
19032 matches!(
19033 action.priority,
19034 ActionPriority::Immediate | ActionPriority::ShortTerm
19035 )
19036 })
19037 .collect()
19038 }
19039}
19040
19041#[derive(Debug, Clone, Serialize, Deserialize)]
19043pub struct EmergingLawWarning {
19044 pub id: String,
19046 pub title: String,
19048 pub jurisdiction: String,
19050 pub description: String,
19052 pub warning_level: WarningLevel,
19054 pub confidence_score: f64,
19056 pub data_sources: Vec<DataSource>,
19058 pub predicted_timeline: String,
19060 pub potential_impact: Vec<String>,
19062 pub indicators: Vec<EmergingLawIndicator>,
19064 pub created_at: String,
19066 pub updated_at: String,
19068}
19069
19070#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
19072pub enum WarningLevel {
19073 Imminent,
19075 NearTerm,
19077 MediumTerm,
19079 LongTerm,
19081 EarlySignal,
19083}
19084
19085#[derive(Debug, Clone, Serialize, Deserialize)]
19087pub struct DataSource {
19088 pub source_type: SourceType,
19090 pub source_id: String,
19092 pub description: String,
19094 pub reliability: f64,
19096 pub last_accessed: String,
19098}
19099
19100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
19102pub enum SourceType {
19103 LegislativeProposal,
19105 PolicyWhitePaper,
19107 ParliamentaryDebate,
19109 RegulatoryConsultation,
19111 AcademicResearch,
19113 IndustryReport,
19115 MediaCoverage,
19117 InternationalTrend,
19119}
19120
19121#[derive(Debug, Clone, Serialize, Deserialize)]
19123pub struct EmergingLawIndicator {
19124 pub name: String,
19126 pub value: f64,
19128 pub threshold: f64,
19130 pub trend: TrendDirection,
19132 pub last_measured: String,
19134}
19135
19136#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
19138pub enum TrendDirection {
19139 Increasing,
19141 Stable,
19143 Decreasing,
19145 Volatile,
19147}
19148
19149impl EmergingLawWarning {
19150 pub fn new(
19152 title: String,
19153 jurisdiction: String,
19154 description: String,
19155 warning_level: WarningLevel,
19156 confidence_score: f64,
19157 ) -> Self {
19158 Self {
19159 id: uuid::Uuid::new_v4().to_string(),
19160 title,
19161 jurisdiction,
19162 description,
19163 warning_level,
19164 confidence_score,
19165 data_sources: Vec::new(),
19166 predicted_timeline: String::new(),
19167 potential_impact: Vec::new(),
19168 indicators: Vec::new(),
19169 created_at: chrono::Utc::now().to_rfc3339(),
19170 updated_at: chrono::Utc::now().to_rfc3339(),
19171 }
19172 }
19173
19174 pub fn add_data_source(&mut self, source: DataSource) {
19176 self.data_sources.push(source);
19177 self.updated_at = chrono::Utc::now().to_rfc3339();
19178 }
19179
19180 pub fn add_indicator(&mut self, indicator: EmergingLawIndicator) {
19182 self.indicators.push(indicator);
19183 self.updated_at = chrono::Utc::now().to_rfc3339();
19184 }
19185
19186 pub fn get_average_reliability(&self) -> f64 {
19188 if self.data_sources.is_empty() {
19189 return 0.0;
19190 }
19191
19192 let sum: f64 = self.data_sources.iter().map(|s| s.reliability).sum();
19193 sum / self.data_sources.len() as f64
19194 }
19195
19196 pub fn has_threshold_breach(&self) -> bool {
19198 self.indicators.iter().any(|i| i.value >= i.threshold)
19199 }
19200}
19201
19202#[derive(Debug, Clone, Serialize, Deserialize)]
19204pub struct PredictivePortingRecommendation {
19205 pub id: String,
19207 pub source_jurisdiction: String,
19209 pub target_jurisdiction: String,
19211 pub recommended_statute: String,
19213 pub reason: String,
19215 pub success_probability: f64,
19217 pub predicted_benefits: Vec<PredictedBenefit>,
19219 pub predicted_challenges: Vec<PredictedChallenge>,
19221 pub recommended_timing: RecommendedTiming,
19223 pub model_version: String,
19225 pub confidence_intervals: Vec<(String, f64, f64)>,
19227 pub created_at: String,
19229}
19230
19231#[derive(Debug, Clone, Serialize, Deserialize)]
19233pub struct PredictedBenefit {
19234 pub benefit_type: BenefitType,
19236 pub description: String,
19238 pub impact_score: f64,
19240 pub time_to_realization: String,
19242}
19243
19244#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
19246pub enum BenefitType {
19247 LegalHarmonization,
19249 EconomicEfficiency,
19251 ReducedComplianceBurden,
19253 ImprovedClarity,
19255 InternationalCooperation,
19257 InnovationEnablement,
19259}
19260
19261#[derive(Debug, Clone, Serialize, Deserialize)]
19263pub struct PredictedChallenge {
19264 pub challenge_type: ChallengeType,
19266 pub description: String,
19268 pub severity_score: f64,
19270 pub mitigation_strategies: Vec<String>,
19272}
19273
19274#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
19276pub enum ChallengeType {
19277 CulturalIncompatibility,
19279 LegalSystemMismatch,
19281 PoliticalResistance,
19283 EconomicBarriers,
19285 TechnicalDifficulty,
19287 StakeholderOpposition,
19289}
19290
19291#[derive(Debug, Clone, Serialize, Deserialize)]
19293pub struct RecommendedTiming {
19294 pub optimal_start: String,
19296 pub latest_start: String,
19298 pub expected_duration: String,
19300 pub rationale: String,
19302 pub opportunity_factors: Vec<String>,
19304}
19305
19306impl PredictivePortingRecommendation {
19307 #[allow(clippy::too_many_arguments)]
19309 pub fn new(
19310 source_jurisdiction: String,
19311 target_jurisdiction: String,
19312 recommended_statute: String,
19313 reason: String,
19314 success_probability: f64,
19315 recommended_timing: RecommendedTiming,
19316 model_version: String,
19317 ) -> Self {
19318 Self {
19319 id: uuid::Uuid::new_v4().to_string(),
19320 source_jurisdiction,
19321 target_jurisdiction,
19322 recommended_statute,
19323 reason,
19324 success_probability,
19325 predicted_benefits: Vec::new(),
19326 predicted_challenges: Vec::new(),
19327 recommended_timing,
19328 model_version,
19329 confidence_intervals: Vec::new(),
19330 created_at: chrono::Utc::now().to_rfc3339(),
19331 }
19332 }
19333
19334 pub fn add_benefit(&mut self, benefit: PredictedBenefit) {
19336 self.predicted_benefits.push(benefit);
19337 }
19338
19339 pub fn add_challenge(&mut self, challenge: PredictedChallenge) {
19341 self.predicted_challenges.push(challenge);
19342 }
19343
19344 pub fn get_benefit_score(&self) -> f64 {
19346 if self.predicted_benefits.is_empty() {
19347 return 0.0;
19348 }
19349
19350 let sum: f64 = self.predicted_benefits.iter().map(|b| b.impact_score).sum();
19351 sum / self.predicted_benefits.len() as f64
19352 }
19353
19354 pub fn get_challenge_severity(&self) -> f64 {
19356 if self.predicted_challenges.is_empty() {
19357 return 0.0;
19358 }
19359
19360 let sum: f64 = self
19361 .predicted_challenges
19362 .iter()
19363 .map(|c| c.severity_score)
19364 .sum();
19365 sum / self.predicted_challenges.len() as f64
19366 }
19367
19368 pub fn get_risk_adjusted_probability(&self) -> f64 {
19370 let challenge_penalty = self.get_challenge_severity() * 0.3;
19371 (self.success_probability - challenge_penalty).max(0.0)
19372 }
19373}
19374
19375#[cfg(test)]
19376mod tests {
19377 use super::*;
19378 use legalis_core::{Effect, EffectType};
19379 use legalis_i18n::{CulturalParams, LegalSystem, Locale};
19380
19381 fn test_jurisdiction_jp() -> Jurisdiction {
19382 Jurisdiction::new("JP", "Japan", Locale::new("ja").with_country("JP"))
19383 .with_legal_system(LegalSystem::CivilLaw)
19384 .with_cultural_params(CulturalParams::japan())
19385 }
19386
19387 fn test_jurisdiction_us() -> Jurisdiction {
19388 Jurisdiction::new("US", "United States", Locale::new("en").with_country("US"))
19389 .with_legal_system(LegalSystem::CommonLaw)
19390 .with_cultural_params(CulturalParams::for_country("US"))
19391 }
19392
19393 #[test]
19394 fn test_port_statute() {
19395 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19396 let statute = Statute::new(
19397 "adult-rights",
19398 "成人権法",
19399 Effect::new(EffectType::Grant, "Complete legal capacity"),
19400 );
19401
19402 let options = PortingOptions {
19403 apply_cultural_params: true,
19404 ..Default::default()
19405 };
19406
19407 let result = engine.port_statute(&statute, &options).unwrap();
19408 assert!(result.statute.id.starts_with("us-"));
19409 }
19410
19411 #[test]
19412 fn test_compatibility_report() {
19413 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19414 let statutes = [Statute::new(
19415 "test",
19416 "Test",
19417 Effect::new(EffectType::Grant, "Test"),
19418 )];
19419
19420 let report = engine.generate_report(&statutes);
19421 assert!(report.compatibility_score > 0.0);
19422 }
19423
19424 #[test]
19425 fn test_conflict_detection() {
19426 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19427 let statute = Statute::new(
19428 "test",
19429 "Test Statute",
19430 Effect::new(EffectType::Grant, "Test"),
19431 );
19432
19433 let conflicts = engine.detect_conflicts(&statute);
19434 assert!(!conflicts.is_empty());
19436 }
19437
19438 #[test]
19439 fn test_semantic_validation() {
19440 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19441 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19442
19443 let options = PortingOptions::default();
19444 let ported = engine.port_statute(&statute, &options).unwrap();
19445
19446 let validation = engine.validate_semantics(&statute, &ported);
19447 assert!(validation.preservation_score >= 0.0);
19448 assert!(validation.preservation_score <= 1.0);
19449 }
19450
19451 #[test]
19452 fn test_risk_assessment() {
19453 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19454 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19455
19456 let options = PortingOptions::default();
19457 let ported = engine.port_statute(&statute, &options).unwrap();
19458
19459 let assessment = engine.assess_risks(&ported);
19460 assert!(assessment.risk_score >= 0.0);
19461 assert!(assessment.risk_score <= 1.0);
19462 }
19463
19464 #[test]
19465 fn test_partial_porting() {
19466 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19467 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19468
19469 let options = PortingOptions::default();
19470 let section_ids = vec!["section1".to_string(), "section2".to_string()];
19471
19472 let result = engine
19473 .port_sections(&statute, §ion_ids, &options)
19474 .unwrap();
19475 assert!(result.statute.id.starts_with("us-"));
19476 assert!(!result.changes.is_empty());
19477 }
19478
19479 #[test]
19480 fn test_reverse_porting() {
19481 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19482 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19483
19484 let _changes = engine.reverse_port_analysis(&statute).unwrap();
19485 }
19488
19489 #[tokio::test]
19490 async fn test_batch_port() {
19491 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19492 let statutes = [
19493 Statute::new("test1", "Test 1", Effect::new(EffectType::Grant, "Test 1")),
19494 Statute::new("test2", "Test 2", Effect::new(EffectType::Grant, "Test 2")),
19495 ];
19496
19497 let options = PortingOptions {
19498 generate_report: true,
19499 detect_conflicts: true,
19500 validate_semantics: true,
19501 ..Default::default()
19502 };
19503
19504 let result = engine.batch_port(&statutes, &options).await.unwrap();
19505 assert_eq!(result.statutes.len(), 2);
19506 assert!(result.report.is_some());
19507 assert!(!result.conflicts.is_empty());
19508 assert!(result.semantic_validation.is_some());
19509 assert!(result.risk_assessment.is_some());
19510 }
19511
19512 #[test]
19513 fn test_bilateral_agreement() {
19514 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19515 let agreement = engine.create_bilateral_agreement(AgreementType::MutualRecognition);
19516
19517 assert_eq!(agreement.source_jurisdiction, "JP");
19518 assert_eq!(agreement.target_jurisdiction, "US");
19519 assert!(!agreement.mutual_recognition.is_empty());
19520 assert!(!agreement.adaptation_protocols.is_empty());
19521 }
19522
19523 #[test]
19524 fn test_regulatory_equivalence() {
19525 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us())
19526 .with_equivalence_mappings(vec![EquivalenceMapping {
19527 source_regulation: "test".to_string(),
19528 target_regulation: "us-test".to_string(),
19529 equivalence_score: 0.9,
19530 differences: vec!["Minor terminology differences".to_string()],
19531 notes: "Highly equivalent".to_string(),
19532 }]);
19533
19534 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19535 let mappings = engine.find_regulatory_equivalence(&statute);
19536
19537 assert_eq!(mappings.len(), 1);
19538 assert_eq!(mappings[0].equivalence_score, 0.9);
19539 }
19540
19541 #[tokio::test]
19542 async fn test_similar_statutes() {
19543 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19544 let statute = Statute::new(
19545 "test",
19546 "Adult Rights Law",
19547 Effect::new(EffectType::Grant, "Test"),
19548 );
19549
19550 let candidates = vec![
19551 Statute::new(
19552 "c1",
19553 "Adult Rights Statute",
19554 Effect::new(EffectType::Grant, "C1"),
19555 ),
19556 Statute::new(
19557 "c2",
19558 "Child Protection Law",
19559 Effect::new(EffectType::Grant, "C2"),
19560 ),
19561 Statute::new(
19562 "c3",
19563 "Adult Legal Capacity",
19564 Effect::new(EffectType::Grant, "C3"),
19565 ),
19566 ];
19567
19568 let similar = engine.find_similar_statutes(&statute, &candidates).await;
19569 assert!(!similar.is_empty());
19570 assert!(similar[0].1 >= 0.3);
19572 }
19573
19574 #[test]
19575 fn test_term_replacement() {
19576 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us())
19577 .with_term_replacements(vec![TermReplacement {
19578 source_term: "成人".to_string(),
19579 target_term: "adult".to_string(),
19580 context: None,
19581 confidence: 0.95,
19582 }]);
19583
19584 let mut statute = Statute::new(
19585 "test",
19586 "成人 Rights Law",
19587 Effect::new(EffectType::Grant, "Test"),
19588 );
19589 let replacements = engine.apply_term_replacement(&mut statute);
19590
19591 assert_eq!(replacements.len(), 1);
19592 assert!(statute.title.contains("adult"));
19593 }
19594
19595 #[test]
19596 fn test_contextual_adjustment() {
19597 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19598 let statute = Statute::new(
19599 "test",
19600 "Fine Payment Law",
19601 Effect::new(EffectType::Grant, "Test"),
19602 );
19603
19604 let adjustments = engine.adjust_parameters_contextually(&statute);
19605 assert!(!adjustments.is_empty());
19607 }
19608
19609 #[test]
19610 fn test_workflow_creation() {
19611 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19612 let workflow = engine.create_workflow("test-statute".to_string());
19613
19614 assert_eq!(workflow.state, WorkflowState::Initiated);
19615 assert_eq!(workflow.pending_steps.len(), 4);
19616 assert_eq!(workflow.approvals.len(), 2);
19617 }
19618
19619 #[test]
19620 fn test_workflow_advancement() {
19621 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19622 let mut workflow = engine.create_workflow("test-statute".to_string());
19623
19624 engine.advance_workflow(&mut workflow).unwrap();
19625 assert_eq!(workflow.state, WorkflowState::InProgress);
19626 assert_eq!(workflow.completed_steps.len(), 1);
19627 assert_eq!(workflow.pending_steps.len(), 3);
19628 }
19629
19630 #[test]
19631 fn test_versioned_statute() {
19632 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19633 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19634 let options = PortingOptions::default();
19635 let ported = engine.port_statute(&statute, &options).unwrap();
19636
19637 let versioned = engine.create_versioned_statute(
19638 ported,
19639 1,
19640 "test_user".to_string(),
19641 "Initial version".to_string(),
19642 );
19643
19644 assert_eq!(versioned.version, 1);
19645 assert!(versioned.previous_hash.is_none());
19646 assert!(!versioned.hash.is_empty());
19647 }
19648
19649 #[test]
19650 fn test_version_comparison() {
19651 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19652 let statute1 = Statute::new("test", "Test V1", Effect::new(EffectType::Grant, "Test"));
19653 let statute2 = Statute::new("test", "Test V2", Effect::new(EffectType::Grant, "Test"));
19654
19655 let options = PortingOptions::default();
19656 let ported1 = engine.port_statute(&statute1, &options).unwrap();
19657 let ported2 = engine.port_statute(&statute2, &options).unwrap();
19658
19659 let v1 = engine.create_versioned_statute(ported1, 1, "user".to_string(), "V1".to_string());
19660 let v2 = engine.create_versioned_statute(ported2, 2, "user".to_string(), "V2".to_string());
19661
19662 let differences = engine.compare_versions(&v1, &v2);
19663 assert!(!differences.is_empty());
19664 }
19665
19666 #[test]
19667 fn test_submit_for_review() {
19668 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19669 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19670 let options = PortingOptions::default();
19671 let ported = engine.port_statute(&statute, &options).unwrap();
19672
19673 let review_request = engine.submit_for_review(ported);
19674
19675 assert_eq!(review_request.status, ReviewStatus::Pending);
19676 assert_eq!(review_request.source_jurisdiction, "JP");
19677 assert_eq!(review_request.target_jurisdiction, "US");
19678 assert!(review_request.assigned_expert.is_none());
19679 assert!(review_request.reviews.is_empty());
19680 }
19681
19682 #[test]
19683 fn test_assign_expert() {
19684 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19685 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19686 let options = PortingOptions::default();
19687 let ported = engine.port_statute(&statute, &options).unwrap();
19688
19689 let mut review_request = engine.submit_for_review(ported);
19690 engine.assign_expert(&mut review_request, "expert-001".to_string());
19691
19692 assert_eq!(review_request.status, ReviewStatus::Assigned);
19693 assert_eq!(
19694 review_request.assigned_expert,
19695 Some("expert-001".to_string())
19696 );
19697 }
19698
19699 #[test]
19700 fn test_add_expert_review_approve() {
19701 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19702 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19703 let options = PortingOptions::default();
19704 let ported = engine.port_statute(&statute, &options).unwrap();
19705
19706 let mut review_request = engine.submit_for_review(ported);
19707
19708 let expert_review = ExpertReview {
19709 id: "review-001".to_string(),
19710 expert_id: "expert-001".to_string(),
19711 expert_name: "Dr. Legal Expert".to_string(),
19712 qualifications: vec!["Bar License".to_string(), "PhD in Law".to_string()],
19713 reviewed_at: chrono::Utc::now().to_rfc3339(),
19714 recommendation: ReviewRecommendation::Approve,
19715 comments: Vec::new(),
19716 confidence: 0.95,
19717 concerns: Vec::new(),
19718 suggested_modifications: Vec::new(),
19719 };
19720
19721 engine
19722 .add_expert_review(&mut review_request, expert_review)
19723 .unwrap();
19724
19725 assert_eq!(review_request.status, ReviewStatus::Approved);
19726 assert_eq!(review_request.reviews.len(), 1);
19727 }
19728
19729 #[test]
19730 fn test_add_expert_review_reject() {
19731 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19732 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19733 let options = PortingOptions::default();
19734 let ported = engine.port_statute(&statute, &options).unwrap();
19735
19736 let mut review_request = engine.submit_for_review(ported);
19737
19738 let expert_review = ExpertReview {
19739 id: "review-001".to_string(),
19740 expert_id: "expert-001".to_string(),
19741 expert_name: "Dr. Legal Expert".to_string(),
19742 qualifications: vec!["Bar License".to_string()],
19743 reviewed_at: chrono::Utc::now().to_rfc3339(),
19744 recommendation: ReviewRecommendation::Reject,
19745 comments: Vec::new(),
19746 confidence: 0.9,
19747 concerns: vec!["Major legal incompatibility".to_string()],
19748 suggested_modifications: vec!["Complete revision required".to_string()],
19749 };
19750
19751 engine
19752 .add_expert_review(&mut review_request, expert_review)
19753 .unwrap();
19754
19755 assert_eq!(review_request.status, ReviewStatus::Rejected);
19756 }
19757
19758 #[test]
19759 fn test_create_review_comment() {
19760 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19761
19762 let comment = engine.create_review_comment(
19763 Some("section-1".to_string()),
19764 "This section needs clarification".to_string(),
19765 Severity::Warning,
19766 "Clarity".to_string(),
19767 );
19768
19769 assert!(comment.section.is_some());
19770 assert_eq!(comment.text, "This section needs clarification");
19771 assert_eq!(comment.severity, Severity::Warning);
19772 assert_eq!(comment.category, "Clarity");
19773 }
19774
19775 #[test]
19776 fn test_compliance_check() {
19777 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19778 let statute = Statute::new(
19779 "test",
19780 "Test Statute",
19781 Effect::new(EffectType::Grant, "Test"),
19782 );
19783 let options = PortingOptions {
19784 apply_cultural_params: true,
19785 ..Default::default()
19786 };
19787 let ported = engine.port_statute(&statute, &options).unwrap();
19788
19789 let result = engine.check_compliance(&ported);
19790
19791 assert!(!result.checks.is_empty());
19792 assert!(result.compliance_score >= 0.0);
19793 assert!(result.compliance_score <= 1.0);
19794 assert!(!result.recommendations.is_empty());
19795 }
19796
19797 #[test]
19798 fn test_compliance_check_detects_issues() {
19799 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19800 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19801 let options = PortingOptions::default();
19802 let ported = engine.port_statute(&statute, &options).unwrap();
19803
19804 let result = engine.check_compliance(&ported);
19805
19806 assert!(!result.violations.is_empty());
19808 assert_eq!(result.status, ComplianceStatus::RequiresReview);
19809 }
19810
19811 #[test]
19812 fn test_batch_compliance_check() {
19813 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19814 let statutes = [
19815 Statute::new("test1", "Test 1", Effect::new(EffectType::Grant, "Test 1")),
19816 Statute::new("test2", "Test 2", Effect::new(EffectType::Grant, "Test 2")),
19817 ];
19818
19819 let options = PortingOptions {
19820 apply_cultural_params: true,
19821 ..Default::default()
19822 };
19823
19824 let ported: Vec<PortedStatute> = statutes
19825 .iter()
19826 .map(|s| engine.port_statute(s, &options).unwrap())
19827 .collect();
19828
19829 let results = engine.batch_check_compliance(&ported);
19830
19831 assert_eq!(results.len(), 2);
19832 assert!(results.iter().all(|r| r.compliance_score >= 0.0));
19833 }
19834
19835 #[test]
19836 fn test_compliance_summary() {
19837 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19838 let statutes = [
19839 Statute::new("test1", "Test 1", Effect::new(EffectType::Grant, "Test 1")),
19840 Statute::new("test2", "Test 2", Effect::new(EffectType::Grant, "Test 2")),
19841 ];
19842
19843 let options = PortingOptions {
19844 apply_cultural_params: true,
19845 ..Default::default()
19846 };
19847
19848 let ported: Vec<PortedStatute> = statutes
19849 .iter()
19850 .map(|s| engine.port_statute(s, &options).unwrap())
19851 .collect();
19852
19853 let results = engine.batch_check_compliance(&ported);
19854 let summary = engine.generate_compliance_summary(&results);
19855
19856 assert_eq!(summary.total_statutes, 2);
19857 assert!(summary.average_compliance_score >= 0.0);
19858 assert!(summary.average_compliance_score <= 1.0);
19859 }
19860
19861 #[test]
19862 fn test_export_compatibility_report_json() {
19863 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19864 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19865 let report = engine.generate_report(&[statute]);
19866
19867 let json = engine
19868 .export_compatibility_report(&report, ExportFormat::Json)
19869 .unwrap();
19870
19871 assert!(json.contains("compatibility_score"));
19872 assert!(json.contains("findings"));
19873 }
19874
19875 #[test]
19876 fn test_export_compatibility_report_markdown() {
19877 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19878 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19879 let report = engine.generate_report(&[statute]);
19880
19881 let md = engine
19882 .export_compatibility_report(&report, ExportFormat::Markdown)
19883 .unwrap();
19884
19885 assert!(md.contains("# Compatibility Report"));
19886 assert!(md.contains("Compatibility Score"));
19887 }
19888
19889 #[tokio::test]
19890 async fn test_export_porting_output() {
19891 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19892 let statutes = [Statute::new(
19893 "test",
19894 "Test",
19895 Effect::new(EffectType::Grant, "Test"),
19896 )];
19897
19898 let options = PortingOptions::default();
19899 let output = engine.batch_port(&statutes, &options).await.unwrap();
19900
19901 let json = engine
19902 .export_porting_output(&output, ExportFormat::Json)
19903 .unwrap();
19904 assert!(json.contains("statutes"));
19905
19906 let md = engine
19907 .export_porting_output(&output, ExportFormat::Markdown)
19908 .unwrap();
19909 assert!(md.contains("# Porting Output"));
19910 }
19911
19912 #[test]
19913 fn test_tfidf_similarity() {
19914 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19915 let statute1 = Statute::new(
19916 "test1",
19917 "Adult Rights Law",
19918 Effect::new(EffectType::Grant, "Test"),
19919 );
19920 let statute2 = Statute::new(
19921 "test2",
19922 "Adult Rights Act",
19923 Effect::new(EffectType::Grant, "Test"),
19924 );
19925 let statute3 = Statute::new(
19926 "test3",
19927 "Child Protection Law",
19928 Effect::new(EffectType::Grant, "Test"),
19929 );
19930
19931 let sim12 = engine.calculate_tfidf_similarity(&statute1, &statute2);
19932 let sim13 = engine.calculate_tfidf_similarity(&statute1, &statute3);
19933
19934 assert!(sim12 > sim13);
19935 assert!((0.0..=1.0).contains(&sim12));
19936 assert!((0.0..=1.0).contains(&sim13));
19937 }
19938
19939 #[test]
19940 fn test_create_template() {
19941 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19942 let template = engine.create_template(
19943 "Civil Law Template".to_string(),
19944 "Template for civil law statutes".to_string(),
19945 vec!["civil".to_string(), "commercial".to_string()],
19946 );
19947
19948 assert_eq!(template.name, "Civil Law Template");
19949 assert_eq!(template.statute_types.len(), 2);
19950 assert!(!template.contextual_rules.is_empty());
19951 }
19952
19953 #[test]
19954 fn test_apply_template() {
19955 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19956 let template = engine.create_template(
19957 "Test Template".to_string(),
19958 "Test".to_string(),
19959 vec!["test".to_string()],
19960 );
19961
19962 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19963 let ported = engine.apply_template(&statute, &template).unwrap();
19964
19965 assert!(ported.statute.id.starts_with("us-"));
19966 }
19967
19968 #[test]
19969 fn test_generate_conflict_resolutions() {
19970 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
19971 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
19972 let conflicts = engine.detect_conflicts(&statute);
19973
19974 let resolutions = engine.generate_conflict_resolutions(&conflicts);
19975
19976 assert!(!resolutions.is_empty());
19977 for resolution in &resolutions {
19978 assert!(resolution.priority >= 1 && resolution.priority <= 10);
19979 }
19980
19981 for i in 1..resolutions.len() {
19983 assert!(resolutions[i - 1].priority >= resolutions[i].priority);
19984 }
19985 }
19986
19987 #[test]
19992 fn test_conflict_precedent_database() {
19993 let mut db = ConflictPrecedentDatabase::new();
19994
19995 let precedent1 = ConflictPrecedent {
19996 id: "prec-1".to_string(),
19997 source_jurisdiction: "JP".to_string(),
19998 target_jurisdiction: "US".to_string(),
19999 conflict_type: ConflictType::SystemMismatch,
20000 description: "Legal system mismatch resolved".to_string(),
20001 resolution_used: "Adapt procedural elements".to_string(),
20002 effectiveness: 0.85,
20003 resolved_by: Some("Expert A".to_string()),
20004 resolved_at: "2024-01-01T00:00:00Z".to_string(),
20005 lessons_learned: vec!["Focus on procedural adaptation".to_string()],
20006 applicable_statute_types: vec!["commercial".to_string()],
20007 tags: vec!["system-mismatch".to_string()],
20008 };
20009
20010 let precedent2 = ConflictPrecedent {
20011 id: "prec-2".to_string(),
20012 source_jurisdiction: "JP".to_string(),
20013 target_jurisdiction: "US".to_string(),
20014 conflict_type: ConflictType::CulturalIncompatibility,
20015 description: "Cultural conflict resolved".to_string(),
20016 resolution_used: "Local adaptation with consultation".to_string(),
20017 effectiveness: 0.75,
20018 resolved_by: Some("Expert B".to_string()),
20019 resolved_at: "2024-01-02T00:00:00Z".to_string(),
20020 lessons_learned: vec!["Involve local stakeholders".to_string()],
20021 applicable_statute_types: vec!["social".to_string()],
20022 tags: vec!["cultural".to_string()],
20023 };
20024
20025 db.add_precedent(precedent1);
20026 db.add_precedent(precedent2);
20027
20028 assert_eq!(db.all_precedents().len(), 2);
20029
20030 let relevant = db.find_relevant_precedents("JP", "US", &ConflictType::SystemMismatch);
20031 assert_eq!(relevant.len(), 1);
20032 assert_eq!(relevant[0].id, "prec-1");
20033
20034 let effective = db.get_effective_precedents();
20035 assert_eq!(effective.len(), 2);
20036 }
20037
20038 #[test]
20039 fn test_conflict_detector_severity_analysis() {
20040 let mut detector = ConflictDetector::new();
20041
20042 let precedent = ConflictPrecedent {
20044 id: "prec-1".to_string(),
20045 source_jurisdiction: "JP".to_string(),
20046 target_jurisdiction: "US".to_string(),
20047 conflict_type: ConflictType::Contradiction,
20048 description: "Test conflict".to_string(),
20049 resolution_used: "Test resolution".to_string(),
20050 effectiveness: 0.9,
20051 resolved_by: None,
20052 resolved_at: "2024-01-01T00:00:00Z".to_string(),
20053 lessons_learned: vec![],
20054 applicable_statute_types: vec![],
20055 tags: vec![],
20056 };
20057 detector.precedent_db.add_precedent(precedent);
20058
20059 let jp = test_jurisdiction_jp();
20060 let us = test_jurisdiction_us();
20061
20062 let conflict = ConflictReport {
20063 statute_id: "test".to_string(),
20064 conflict_type: ConflictType::Contradiction,
20065 description: "Test conflict".to_string(),
20066 severity: Severity::Warning,
20067 resolutions: vec!["Test resolution".to_string()],
20068 };
20069
20070 let severity = detector.analyze_severity(&conflict, &jp, &us);
20071
20072 assert!(matches!(
20074 severity,
20075 Severity::Warning | Severity::Error | Severity::Critical
20076 ));
20077 }
20078
20079 #[test]
20080 fn test_conflict_detector_recommend_strategies() {
20081 let mut detector = ConflictDetector::new();
20082
20083 let precedent = ConflictPrecedent {
20085 id: "prec-1".to_string(),
20086 source_jurisdiction: "JP".to_string(),
20087 target_jurisdiction: "US".to_string(),
20088 conflict_type: ConflictType::SystemMismatch,
20089 description: "Legal system mismatch".to_string(),
20090 resolution_used: "Gradual adaptation with expert review".to_string(),
20091 effectiveness: 0.85,
20092 resolved_by: Some("Expert A".to_string()),
20093 resolved_at: "2024-01-01T00:00:00Z".to_string(),
20094 lessons_learned: vec![],
20095 applicable_statute_types: vec![],
20096 tags: vec![],
20097 };
20098 detector.precedent_db.add_precedent(precedent);
20099
20100 let template = NegotiatedResolutionTemplate {
20102 id: "template-1".to_string(),
20103 name: "System Mismatch Template".to_string(),
20104 conflict_types: vec![ConflictType::SystemMismatch],
20105 source_patterns: vec!["JP".to_string()],
20106 target_patterns: vec!["US".to_string()],
20107 approach: "Bilateral adaptation protocol".to_string(),
20108 negotiation_steps: vec![],
20109 fallback_strategies: vec![],
20110 success_rate: 0.8,
20111 stakeholders: vec![],
20112 required_approvals: vec![],
20113 };
20114 detector.add_template(template);
20115
20116 let jp = test_jurisdiction_jp();
20117 let us = test_jurisdiction_us();
20118
20119 let conflict = ConflictReport {
20120 statute_id: "test".to_string(),
20121 conflict_type: ConflictType::SystemMismatch,
20122 description: "System mismatch".to_string(),
20123 severity: Severity::Warning,
20124 resolutions: vec!["Default resolution".to_string()],
20125 };
20126
20127 let strategies = detector.recommend_strategies(&conflict, &jp, &us);
20128
20129 assert!(!strategies.is_empty());
20130 assert!(strategies.iter().any(|s| s.contains("effective")));
20132 assert!(strategies.iter().any(|s| s.contains("template")));
20133 }
20134
20135 #[test]
20136 fn test_conflict_resolution_workflow_creation() {
20137 let detector = ConflictDetector::new();
20138
20139 let conflict = ConflictReport {
20140 statute_id: "test".to_string(),
20141 conflict_type: ConflictType::Contradiction,
20142 description: "Critical conflict".to_string(),
20143 severity: Severity::Critical,
20144 resolutions: vec!["Manual review required".to_string()],
20145 };
20146
20147 let workflow = detector.create_resolution_workflow(conflict);
20148
20149 assert_eq!(workflow.state, ResolutionWorkflowState::InitialAssessment);
20150 assert_eq!(workflow.escalation_level, EscalationLevel::Critical);
20151 assert!(workflow.stakeholder_reviews.is_empty());
20152 assert!(workflow.expert_consultations.is_empty());
20153 assert!(workflow.proposed_resolution.is_none());
20154 assert!(workflow.final_decision.is_none());
20155 }
20156
20157 #[test]
20158 fn test_negotiated_resolution_template() {
20159 let template = NegotiatedResolutionTemplate {
20160 id: "template-1".to_string(),
20161 name: "Cultural Adaptation Template".to_string(),
20162 conflict_types: vec![ConflictType::CulturalIncompatibility],
20163 source_patterns: vec!["CivilLaw".to_string()],
20164 target_patterns: vec!["CommonLaw".to_string()],
20165 approach: "Phased adaptation with stakeholder consultation".to_string(),
20166 negotiation_steps: vec![
20167 NegotiationStep {
20168 step_number: 1,
20169 description: "Initial stakeholder meeting".to_string(),
20170 involved_parties: vec![
20171 "Legal experts".to_string(),
20172 "Cultural advisors".to_string(),
20173 ],
20174 expected_outcome: "Agreement on adaptation scope".to_string(),
20175 estimated_days: 5,
20176 },
20177 NegotiationStep {
20178 step_number: 2,
20179 description: "Draft adaptation proposal".to_string(),
20180 involved_parties: vec!["Legal drafters".to_string()],
20181 expected_outcome: "Initial proposal document".to_string(),
20182 estimated_days: 10,
20183 },
20184 ],
20185 fallback_strategies: vec![
20186 "Escalate to bilateral commission".to_string(),
20187 "Seek international arbitration".to_string(),
20188 ],
20189 success_rate: 0.75,
20190 stakeholders: vec![
20191 "Source jurisdiction legal authority".to_string(),
20192 "Target jurisdiction legal authority".to_string(),
20193 "Cultural representatives".to_string(),
20194 ],
20195 required_approvals: vec![
20196 "Legal committee".to_string(),
20197 "Cultural affairs ministry".to_string(),
20198 ],
20199 };
20200
20201 assert_eq!(template.negotiation_steps.len(), 2);
20202 assert_eq!(template.fallback_strategies.len(), 2);
20203 assert_eq!(template.stakeholders.len(), 3);
20204 assert!(template.success_rate > 0.5);
20205 assert!(
20206 template
20207 .conflict_types
20208 .contains(&ConflictType::CulturalIncompatibility)
20209 );
20210 }
20211
20212 #[test]
20213 fn test_escalation_level_ordering() {
20214 assert!(EscalationLevel::Routine < EscalationLevel::Elevated);
20215 assert!(EscalationLevel::Elevated < EscalationLevel::High);
20216 assert!(EscalationLevel::High < EscalationLevel::Critical);
20217 }
20218
20219 #[test]
20220 fn test_stakeholder_review() {
20221 let review = StakeholderReview {
20222 reviewer_id: "reviewer-1".to_string(),
20223 reviewer_name: "Jane Smith".to_string(),
20224 role: "Legal Counsel".to_string(),
20225 reviewed_at: "2024-01-01T00:00:00Z".to_string(),
20226 recommendation: StakeholderRecommendation::ApproveWithModifications,
20227 comments: "Approve with minor adjustments to cultural references".to_string(),
20228 concerns: vec!["Potential cultural sensitivity issue in section 3".to_string()],
20229 modifications: vec![
20230 "Adjust terminology in section 3".to_string(),
20231 "Add explanatory note for cultural context".to_string(),
20232 ],
20233 };
20234
20235 assert_eq!(
20236 review.recommendation,
20237 StakeholderRecommendation::ApproveWithModifications
20238 );
20239 assert_eq!(review.concerns.len(), 1);
20240 assert_eq!(review.modifications.len(), 2);
20241 }
20242
20243 #[test]
20244 fn test_expert_consultation() {
20245 let consultation = ExpertConsultation {
20246 id: "consult-1".to_string(),
20247 expert_id: "expert-123".to_string(),
20248 expert_name: "Dr. John Doe".to_string(),
20249 expertise_area: "International Legal Systems".to_string(),
20250 consulted_at: "2024-01-01T00:00:00Z".to_string(),
20251 opinion: "The proposed adaptation is sound but requires additional safeguards"
20252 .to_string(),
20253 recommended_approach: "Implement with monitoring period".to_string(),
20254 confidence: 0.9,
20255 legal_references: vec![
20256 "Treaty on Legal Harmonization, Art. 12".to_string(),
20257 "Case Law: Smith v. State (2020)".to_string(),
20258 ],
20259 };
20260
20261 assert_eq!(consultation.confidence, 0.9);
20262 assert_eq!(consultation.legal_references.len(), 2);
20263 assert!(consultation.opinion.contains("safeguards"));
20264 }
20265
20266 #[test]
20267 fn test_resolution_decision() {
20268 let decision = ResolutionDecision {
20269 id: "decision-1".to_string(),
20270 decision_maker_id: "dm-123".to_string(),
20271 decision_maker_role: "Chief Legal Officer".to_string(),
20272 decided_at: "2024-01-01T00:00:00Z".to_string(),
20273 chosen_strategy: "Gradual implementation with monitoring".to_string(),
20274 rationale: "Balances legal requirements with practical concerns".to_string(),
20275 implementation_plan: vec![
20276 "Phase 1: Pilot program in limited jurisdictions".to_string(),
20277 "Phase 2: Full implementation with review checkpoints".to_string(),
20278 "Phase 3: Final assessment and adjustments".to_string(),
20279 ],
20280 monitoring_requirements: vec![
20281 "Monthly compliance reports".to_string(),
20282 "Quarterly stakeholder reviews".to_string(),
20283 ],
20284 accepted_risks: vec!["Potential initial resistance from local authorities".to_string()],
20285 };
20286
20287 assert_eq!(decision.implementation_plan.len(), 3);
20288 assert_eq!(decision.monitoring_requirements.len(), 2);
20289 assert_eq!(decision.accepted_risks.len(), 1);
20290 }
20291
20292 #[tokio::test]
20297 async fn test_ai_assistant_creation() {
20298 let assistant = AiPortingAssistant::new();
20299 assert!(assistant.generator.is_none());
20300
20301 let assistant_default = AiPortingAssistant::default();
20302 assert!(assistant_default.generator.is_none());
20303 }
20304
20305 #[tokio::test]
20306 async fn test_llm_adaptation_suggestions() {
20307 let assistant = AiPortingAssistant::new();
20308 let jp = test_jurisdiction_jp();
20309 let us = test_jurisdiction_us();
20310 let statute = Statute::new(
20311 "test",
20312 "Test Statute",
20313 Effect::new(EffectType::Grant, "Rights"),
20314 );
20315
20316 let suggestions = assistant
20317 .generate_adaptation_suggestions(&statute, &jp, &us)
20318 .await
20319 .unwrap();
20320
20321 assert!(!suggestions.is_empty());
20322 let first = &suggestions[0];
20323 assert_eq!(first.statute_id, "test");
20324 assert!(first.confidence > 0.0 && first.confidence <= 1.0);
20325 assert!(!first.suggestion.is_empty());
20326 assert!(matches!(
20327 first.category,
20328 AdaptationCategory::Procedural | AdaptationCategory::Cultural
20329 ));
20330 }
20331
20332 #[tokio::test]
20333 async fn test_similar_statute_discovery() {
20334 let assistant = AiPortingAssistant::new();
20335 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Rights"));
20336 let jurisdictions = vec![test_jurisdiction_jp(), test_jurisdiction_us()];
20337
20338 let similar = assistant
20339 .discover_similar_statutes(&statute, &jurisdictions)
20340 .await
20341 .unwrap();
20342
20343 assert!(!similar.is_empty());
20345
20346 for sim in &similar {
20347 assert!(sim.similarity_score > 0.0 && sim.similarity_score <= 1.0);
20348 assert!(!sim.matching_features.is_empty());
20349 }
20350
20351 for i in 1..similar.len() {
20353 assert!(similar[i - 1].similarity_score >= similar[i].similarity_score);
20354 }
20355 }
20356
20357 #[tokio::test]
20358 async fn test_gap_analysis() {
20359 let assistant = AiPortingAssistant::new();
20360 let jp = test_jurisdiction_jp();
20361 let us = test_jurisdiction_us();
20362 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Rights"));
20363
20364 let gap_analysis = assistant.analyze_gaps(&statute, &jp, &us).await.unwrap();
20365
20366 assert_eq!(gap_analysis.source_statute_id, "test");
20367 assert!(gap_analysis.coverage_score >= 0.0 && gap_analysis.coverage_score <= 1.0);
20368 assert!(!gap_analysis.gaps.is_empty());
20369 assert!(!gap_analysis.recommendations.is_empty());
20370
20371 for gap in &gap_analysis.gaps {
20372 assert!(!gap.description.is_empty());
20373 assert!(!gap.missing_element.is_empty());
20374 assert!(!gap.solutions.is_empty());
20375 }
20376 }
20377
20378 #[tokio::test]
20379 async fn test_cultural_sensitivity_analysis() {
20380 let assistant = AiPortingAssistant::new();
20381 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Rights"));
20382
20383 let mut params = CulturalParams::for_country("US");
20385 params.prohibitions.push("alcohol".to_string());
20386
20387 let jurisdiction = Jurisdiction::new("TEST", "Test", Locale::new("en").with_country("US"))
20388 .with_legal_system(LegalSystem::CommonLaw)
20389 .with_cultural_params(params);
20390
20391 let analysis = assistant
20392 .check_cultural_sensitivity(&statute, &jurisdiction)
20393 .await
20394 .unwrap();
20395
20396 assert_eq!(analysis.statute_id, "test");
20397 assert!(analysis.sensitivity_score >= 0.0 && analysis.sensitivity_score <= 1.0);
20398 assert!(!analysis.issues.is_empty());
20399 assert!(!analysis.assessment.is_empty());
20400
20401 for issue in &analysis.issues {
20402 assert!(!issue.description.is_empty());
20403 assert!(!issue.explanation.is_empty());
20404 }
20405 }
20406
20407 #[tokio::test]
20408 async fn test_plain_language_explanation() {
20409 let assistant = AiPortingAssistant::new();
20410 let statute = Statute::new(
20411 "test",
20412 "Test Statute",
20413 Effect::new(EffectType::Grant, "Rights"),
20414 );
20415
20416 for audience_level in [
20417 AudienceLevel::GeneralPublic,
20418 AudienceLevel::Business,
20419 AudienceLevel::Government,
20420 AudienceLevel::Legal,
20421 AudienceLevel::Academic,
20422 ] {
20423 let explanation = assistant
20424 .generate_plain_explanation(&statute, audience_level)
20425 .await
20426 .unwrap();
20427
20428 assert_eq!(explanation.statute_id, "test");
20429 assert_eq!(explanation.audience_level, audience_level);
20430 assert!(!explanation.summary.is_empty());
20431 assert!(!explanation.explanation.is_empty());
20432 assert!(!explanation.key_points.is_empty());
20433 assert!(explanation.readability_score > 0.0 && explanation.readability_score <= 1.0);
20434 }
20435 }
20436
20437 #[test]
20438 fn test_adaptation_category() {
20439 let categories = vec![
20440 AdaptationCategory::Terminology,
20441 AdaptationCategory::Procedural,
20442 AdaptationCategory::Cultural,
20443 AdaptationCategory::Numerical,
20444 AdaptationCategory::Structural,
20445 AdaptationCategory::LegalPrinciple,
20446 AdaptationCategory::Compliance,
20447 ];
20448
20449 for category in categories {
20450 assert!(matches!(
20451 category,
20452 AdaptationCategory::Terminology
20453 | AdaptationCategory::Procedural
20454 | AdaptationCategory::Cultural
20455 | AdaptationCategory::Numerical
20456 | AdaptationCategory::Structural
20457 | AdaptationCategory::LegalPrinciple
20458 | AdaptationCategory::Compliance
20459 ));
20460 }
20461 }
20462
20463 #[test]
20464 fn test_gap_types() {
20465 let gap_types = vec![
20466 GapType::MissingConcept,
20467 GapType::MissingProcedure,
20468 GapType::MissingEnforcement,
20469 GapType::MissingSafeguard,
20470 GapType::InsufficientSpecificity,
20471 GapType::MissingCulturalElement,
20472 ];
20473
20474 for gap_type in gap_types {
20475 assert!(matches!(
20476 gap_type,
20477 GapType::MissingConcept
20478 | GapType::MissingProcedure
20479 | GapType::MissingEnforcement
20480 | GapType::MissingSafeguard
20481 | GapType::InsufficientSpecificity
20482 | GapType::MissingCulturalElement
20483 ));
20484 }
20485 }
20486
20487 #[test]
20488 fn test_cultural_issue_types() {
20489 let issue_types = vec![
20490 CulturalIssueType::Religious,
20491 CulturalIssueType::Traditional,
20492 CulturalIssueType::SocialNorm,
20493 CulturalIssueType::Gender,
20494 CulturalIssueType::Family,
20495 CulturalIssueType::Language,
20496 CulturalIssueType::Historical,
20497 ];
20498
20499 for issue_type in issue_types {
20500 assert!(matches!(
20501 issue_type,
20502 CulturalIssueType::Religious
20503 | CulturalIssueType::Traditional
20504 | CulturalIssueType::SocialNorm
20505 | CulturalIssueType::Gender
20506 | CulturalIssueType::Family
20507 | CulturalIssueType::Language
20508 | CulturalIssueType::Historical
20509 ));
20510 }
20511 }
20512
20513 #[test]
20514 fn test_feature_types() {
20515 let feature_types = vec![
20516 FeatureType::LegalEffect,
20517 FeatureType::Structure,
20518 FeatureType::Terminology,
20519 FeatureType::Scope,
20520 FeatureType::Conditions,
20521 FeatureType::Remedies,
20522 ];
20523
20524 for feature_type in feature_types {
20525 assert!(matches!(
20526 feature_type,
20527 FeatureType::LegalEffect
20528 | FeatureType::Structure
20529 | FeatureType::Terminology
20530 | FeatureType::Scope
20531 | FeatureType::Conditions
20532 | FeatureType::Remedies
20533 ));
20534 }
20535 }
20536
20537 #[test]
20538 fn test_audience_levels() {
20539 let levels = [
20540 AudienceLevel::GeneralPublic,
20541 AudienceLevel::Business,
20542 AudienceLevel::Government,
20543 AudienceLevel::Legal,
20544 AudienceLevel::Academic,
20545 ];
20546
20547 for level in levels {
20548 assert!(matches!(
20549 level,
20550 AudienceLevel::GeneralPublic
20551 | AudienceLevel::Business
20552 | AudienceLevel::Government
20553 | AudienceLevel::Legal
20554 | AudienceLevel::Academic
20555 ));
20556 }
20557 }
20558
20559 #[tokio::test]
20560 async fn test_multi_hop_port() {
20561 let jp = test_jurisdiction_jp();
20562 let us = test_jurisdiction_us();
20563 let uk = Jurisdiction::new("UK", "United Kingdom", Locale::new("en").with_country("GB"))
20564 .with_legal_system(LegalSystem::CommonLaw)
20565 .with_cultural_params(CulturalParams::for_country("GB"));
20566
20567 let engine = PortingEngine::new(jp, us);
20568 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Test"));
20569
20570 let options = PortingOptions {
20571 apply_cultural_params: true,
20572 ..Default::default()
20573 };
20574
20575 let chain = engine
20576 .multi_hop_port(&statute, &[uk], &options)
20577 .await
20578 .unwrap();
20579
20580 assert_eq!(chain.hop_results.len(), 2);
20581 assert!(chain.chain_score >= 0.0 && chain.chain_score <= 1.0);
20583 assert_eq!(chain.source_jurisdiction, "JP");
20584 assert_eq!(chain.target_jurisdiction, "US");
20585 assert_eq!(chain.intermediate_hops.len(), 1);
20586 }
20587
20588 #[test]
20589 fn test_record_history() {
20590 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
20591 let options = PortingOptions::default();
20592
20593 let history = engine.record_history(
20594 "test-statute".to_string(),
20595 "user-001".to_string(),
20596 &options,
20597 true,
20598 None,
20599 );
20600
20601 assert_eq!(history.statute_id, "test-statute");
20602 assert_eq!(history.user, "user-001");
20603 assert!(history.success);
20604 assert!(history.error.is_none());
20605 }
20606
20607 #[test]
20608 fn test_build_lineage() {
20609 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
20610 let options = PortingOptions::default();
20611
20612 let history = vec![
20613 engine.record_history(
20614 "statute-1".to_string(),
20615 "user".to_string(),
20616 &options,
20617 true,
20618 None,
20619 ),
20620 engine.record_history(
20621 "statute-2".to_string(),
20622 "user".to_string(),
20623 &options,
20624 true,
20625 None,
20626 ),
20627 ];
20628
20629 let lineage = engine.build_lineage("original-id".to_string(), "JP".to_string(), &history);
20630
20631 assert_eq!(lineage.original_id, "original-id");
20632 assert_eq!(lineage.original_jurisdiction, "JP");
20633 assert!(lineage.total_ports <= 2);
20634 }
20635
20636 #[test]
20637 fn test_generate_diff() {
20638 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
20639 let statute = Statute::new(
20640 "test",
20641 "Original Title",
20642 Effect::new(EffectType::Grant, "Test"),
20643 );
20644
20645 let options = PortingOptions::default();
20646 let ported = engine.port_statute(&statute, &options).unwrap();
20647
20648 let diff = engine.generate_diff(&statute, &ported);
20649
20650 assert_eq!(diff.original_id, "test");
20651 assert!(diff.similarity_score >= 0.0 && diff.similarity_score <= 1.0);
20652 assert!(!diff.differences.is_empty());
20653 }
20654
20655 #[test]
20656 fn test_export_diff_markdown() {
20657 let engine = PortingEngine::new(test_jurisdiction_jp(), test_jurisdiction_us());
20658 let statute = Statute::new("test", "Original", Effect::new(EffectType::Grant, "Test"));
20659
20660 let options = PortingOptions::default();
20661 let ported = engine.port_statute(&statute, &options).unwrap();
20662
20663 let diff = engine.generate_diff(&statute, &ported);
20664 let md = engine.export_diff_markdown(&diff);
20665
20666 assert!(md.contains("# Statute Diff"));
20667 assert!(md.contains("Similarity Score"));
20668 assert!(md.contains("```diff"));
20669 }
20670
20671 #[test]
20676 fn test_jurisdiction_profile_creation() {
20677 let profile = JurisdictionProfile::new(
20678 String::from("US"),
20679 String::from("United States"),
20680 LegalSystemType::CommonLaw,
20681 );
20682
20683 assert_eq!(profile.code, "US");
20684 assert_eq!(profile.name, "United States");
20685 assert_eq!(profile.legal_system, LegalSystemType::CommonLaw);
20686 assert!(profile.official_languages.is_empty());
20687 }
20688
20689 #[test]
20690 fn test_court_hierarchy() {
20691 let mut hierarchy = CourtHierarchy::new();
20692
20693 hierarchy.add_court(Court {
20694 name: String::from("Supreme Court"),
20695 level: CourtLevel::Supreme,
20696 jurisdiction: String::from("Federal"),
20697 precedent_setting: true,
20698 judges: Some(9),
20699 url: None,
20700 });
20701
20702 hierarchy.add_court(Court {
20703 name: String::from("District Court"),
20704 level: CourtLevel::District,
20705 jurisdiction: String::from("Regional"),
20706 precedent_setting: false,
20707 judges: Some(100),
20708 url: None,
20709 });
20710
20711 assert_eq!(hierarchy.courts.len(), 2);
20712 assert_eq!(hierarchy.courts_by_level(CourtLevel::Supreme).len(), 1);
20713 assert_eq!(hierarchy.courts_by_level(CourtLevel::District).len(), 1);
20714 }
20715
20716 #[test]
20717 fn test_legislative_process() {
20718 let process = LegislativeProcess::new(String::from("Congress"), String::from("House"))
20719 .with_upper_house(String::from("Senate"));
20720
20721 assert!(process.is_bicameral);
20722 assert_eq!(process.upper_house, Some(String::from("Senate")));
20723 assert!(process.stages.contains(&LegislativeStage::UpperHouse));
20724 }
20725
20726 #[test]
20727 fn test_constitutional_framework() {
20728 let mut framework = ConstitutionalFramework::new();
20729 framework.add_feature(ConstitutionalFeature::WrittenConstitution);
20730 framework.add_feature(ConstitutionalFeature::BillOfRights);
20731 framework.add_feature(ConstitutionalFeature::Federalism);
20732
20733 assert!(framework.has_feature(ConstitutionalFeature::WrittenConstitution));
20734 assert!(framework.has_feature(ConstitutionalFeature::BillOfRights));
20735 assert!(framework.has_feature(ConstitutionalFeature::Federalism));
20736 assert!(!framework.has_feature(ConstitutionalFeature::ParliamentarySovereignty));
20737 assert_eq!(framework.features.len(), 3);
20738 }
20739
20740 #[test]
20741 fn test_jurisdiction_compatibility_score() {
20742 let us = JurisdictionProfile::new(
20743 String::from("US"),
20744 String::from("United States"),
20745 LegalSystemType::CommonLaw,
20746 );
20747
20748 let gb = JurisdictionProfile::new(
20749 String::from("GB"),
20750 String::from("United Kingdom"),
20751 LegalSystemType::CommonLaw,
20752 );
20753
20754 let jp = JurisdictionProfile::new(
20755 String::from("JP"),
20756 String::from("Japan"),
20757 LegalSystemType::CivilLaw,
20758 );
20759
20760 let us_gb_score = us.compatibility_score(&gb);
20762 let us_jp_score = us.compatibility_score(&jp);
20763
20764 assert!(us_gb_score > us_jp_score);
20765 assert!((0.0..=1.0).contains(&us_gb_score));
20766 assert!((0.0..=1.0).contains(&us_jp_score));
20767 }
20768
20769 #[test]
20770 fn test_jurisdiction_database() {
20771 let mut db = JurisdictionDatabase::new();
20772
20773 let us = JurisdictionProfile::new(
20774 String::from("US"),
20775 String::from("United States"),
20776 LegalSystemType::CommonLaw,
20777 );
20778
20779 let jp = JurisdictionProfile::new(
20780 String::from("JP"),
20781 String::from("Japan"),
20782 LegalSystemType::CivilLaw,
20783 );
20784
20785 db.add_profile(us);
20786 db.add_profile(jp);
20787
20788 assert!(db.get_profile("US").is_some());
20789 assert!(db.get_profile("JP").is_some());
20790 assert!(db.get_profile("FR").is_none());
20791 assert_eq!(db.list_codes().len(), 2);
20792 }
20793
20794 #[test]
20795 fn test_find_by_legal_system() {
20796 let db = JurisdictionDatabase::with_major_jurisdictions();
20797
20798 let common_law = db.find_by_legal_system(LegalSystemType::CommonLaw);
20799 let civil_law = db.find_by_legal_system(LegalSystemType::CivilLaw);
20800
20801 assert!(common_law.len() >= 2); assert!(civil_law.len() >= 3); }
20804
20805 #[test]
20806 fn test_find_compatible_jurisdictions() {
20807 let db = JurisdictionDatabase::with_major_jurisdictions();
20808
20809 let compatible = db.find_compatible("US", 0.5);
20810
20811 assert!(!compatible.is_empty());
20812
20813 for i in 0..compatible.len().saturating_sub(1) {
20815 assert!(compatible[i].1 >= compatible[i + 1].1);
20816 }
20817
20818 for (_, score) in &compatible {
20820 assert!(*score >= 0.5);
20821 }
20822 }
20823
20824 #[test]
20825 fn test_major_jurisdictions_database() {
20826 let db = JurisdictionDatabase::with_major_jurisdictions();
20827
20828 let us = db.get_profile("US").expect("US profile should exist");
20830 assert_eq!(us.name, "United States");
20831 assert_eq!(us.legal_system, LegalSystemType::CommonLaw);
20832 assert!(
20833 us.constitutional_framework
20834 .has_feature(ConstitutionalFeature::Federalism)
20835 );
20836 assert!(us.legislative_process.is_bicameral);
20837 assert!(us.court_hierarchy.has_jury_trials);
20838
20839 let jp = db.get_profile("JP").expect("JP profile should exist");
20841 assert_eq!(jp.name, "Japan");
20842 assert_eq!(jp.legal_system, LegalSystemType::CivilLaw);
20843 assert!(
20844 jp.constitutional_framework
20845 .has_feature(ConstitutionalFeature::ParliamentarySystem)
20846 );
20847 assert!(!jp.court_hierarchy.has_jury_trials);
20848
20849 let de = db.get_profile("DE").expect("DE profile should exist");
20851 assert_eq!(de.name, "Germany");
20852 assert!(
20853 de.constitutional_framework
20854 .has_feature(ConstitutionalFeature::Federalism)
20855 );
20856 assert!(de.court_hierarchy.constitutional_court.is_some());
20857
20858 let gb = db.get_profile("GB").expect("GB profile should exist");
20860 assert_eq!(gb.name, "United Kingdom");
20861 assert!(!gb.constitutional_framework.has_written_constitution);
20862 assert!(
20863 gb.constitutional_framework
20864 .has_feature(ConstitutionalFeature::ParliamentarySovereignty)
20865 );
20866
20867 let fr = db.get_profile("FR").expect("FR profile should exist");
20869 assert_eq!(fr.name, "France");
20870 assert!(
20871 fr.constitutional_framework
20872 .has_feature(ConstitutionalFeature::SemiPresidentialSystem)
20873 );
20874 }
20875
20876 #[test]
20877 fn test_court_level_ordering() {
20878 assert!(CourtLevel::Local < CourtLevel::District);
20879 assert!(CourtLevel::District < CourtLevel::Appellate);
20880 assert!(CourtLevel::Appellate < CourtLevel::Supreme);
20881 assert!(CourtLevel::Supreme < CourtLevel::International);
20882 }
20883
20884 #[test]
20885 fn test_legislative_stage_ordering() {
20886 assert!(LegislativeStage::Drafting < LegislativeStage::Committee);
20887 assert!(LegislativeStage::Committee < LegislativeStage::FirstReading);
20888 assert!(LegislativeStage::FirstReading < LegislativeStage::SecondReading);
20889 assert!(LegislativeStage::SecondReading < LegislativeStage::ThirdReading);
20890 assert!(LegislativeStage::ThirdReading < LegislativeStage::UpperHouse);
20891 assert!(LegislativeStage::UpperHouse < LegislativeStage::Executive);
20892 assert!(LegislativeStage::Executive < LegislativeStage::Publication);
20893 }
20894
20895 #[test]
20900 fn test_concept_equivalence() {
20901 let equiv = ConceptEquivalence::new(String::from("contract"), String::from("契約"), 0.95)
20902 .with_context(String::from("civil law"))
20903 .with_notes(String::from("Direct translation"));
20904
20905 assert_eq!(equiv.source_concept, "contract");
20906 assert_eq!(equiv.target_concept, "契約");
20907 assert_eq!(equiv.equivalence_score, 0.95);
20908 assert!((equiv.semantic_distance - 0.05).abs() < 0.0001);
20909 assert_eq!(equiv.context.len(), 1);
20910 assert!(equiv.notes.is_some());
20911 }
20912
20913 #[test]
20914 fn test_concept_equivalence_database() {
20915 let mut db = ConceptEquivalenceDatabase::new();
20916
20917 db.add_equivalence(
20918 String::from("US->JP"),
20919 ConceptEquivalence::new(String::from("contract"), String::from("契約"), 0.95),
20920 );
20921
20922 db.add_equivalence(
20923 String::from("US->JP"),
20924 ConceptEquivalence::new(String::from("tort"), String::from("不法行為"), 0.9),
20925 );
20926
20927 let matches = db.find_equivalences("US", "JP", "contract");
20928 assert_eq!(matches.len(), 1);
20929
20930 let best = db.best_match("US", "JP", "contract");
20931 assert!(best.is_some());
20932 assert_eq!(best.unwrap().target_concept, "契約");
20933 }
20934
20935 #[test]
20936 fn test_term_translation() {
20937 let translation = TermTranslation::new(
20938 String::from("felony"),
20939 String::from("US"),
20940 String::from("重罪"),
20941 String::from("JP"),
20942 0.9,
20943 true,
20944 );
20945
20946 assert_eq!(translation.source_term, "felony");
20947 assert_eq!(translation.target_term, "重罪");
20948 assert_eq!(translation.accuracy, 0.9);
20949 assert!(translation.is_direct);
20950 }
20951
20952 #[test]
20953 fn test_term_translation_matrix() {
20954 let matrix = TermTranslationMatrix::with_common_translations();
20955
20956 let translations = matrix.find_translations("US", "JP", "felony");
20957 assert!(!translations.is_empty());
20958
20959 let best = matrix.best_translation("US", "JP", "felony", None);
20960 assert!(best.is_some());
20961 assert_eq!(best.unwrap().target_term, "重罪");
20962 }
20963
20964 #[test]
20965 fn test_term_translation_context() {
20966 let mut matrix = TermTranslationMatrix::new();
20967
20968 let mut criminal_trans = TermTranslation::new(
20969 String::from("charge"),
20970 String::from("US"),
20971 String::from("起訴"),
20972 String::from("JP"),
20973 0.9,
20974 true,
20975 );
20976 criminal_trans.valid_contexts = vec![String::from("criminal")];
20977
20978 let mut civil_trans = TermTranslation::new(
20979 String::from("charge"),
20980 String::from("US"),
20981 String::from("料金"),
20982 String::from("JP"),
20983 0.8,
20984 true,
20985 );
20986 civil_trans.valid_contexts = vec![String::from("civil"), String::from("contract")];
20987
20988 matrix.add_translation(criminal_trans);
20989 matrix.add_translation(civil_trans);
20990
20991 let criminal_best = matrix.best_translation("US", "JP", "charge", Some("criminal"));
20992 assert_eq!(criminal_best.unwrap().target_term, "起訴");
20993
20994 let civil_best = matrix.best_translation("US", "JP", "charge", Some("civil"));
20995 assert_eq!(civil_best.unwrap().target_term, "料金");
20996 }
20997
20998 #[test]
20999 fn test_semantic_distance_calculator() {
21000 let mut concept_db = ConceptEquivalenceDatabase::new();
21001
21002 concept_db.add_equivalence(
21003 String::from("US->JP"),
21004 ConceptEquivalence::new(String::from("contract"), String::from("契約"), 0.95),
21005 );
21006
21007 let calculator = SemanticDistanceCalculator::new(concept_db);
21008
21009 let distance = calculator.calculate_distance("US", "JP", "contract", "契約");
21010 assert!((0.0..=1.0).contains(&distance));
21011 assert!(distance < 0.1); }
21013
21014 #[test]
21015 fn test_levenshtein_distance() {
21016 let concept_db = ConceptEquivalenceDatabase::new();
21017 let calculator = SemanticDistanceCalculator::new(concept_db);
21018
21019 let dist1 = calculator.calculate_distance("US", "JP", "test", "test");
21021 assert_eq!(dist1, 0.0);
21022
21023 let dist2 = calculator.calculate_distance("US", "JP", "contract", "compact");
21025 assert!(dist2 > 0.0 && dist2 < 1.0);
21026 }
21027
21028 #[test]
21029 fn test_context_aware_term_mapper() {
21030 let matrix = TermTranslationMatrix::with_common_translations();
21031 let mut mapper = ContextAwareTermMapper::new(matrix);
21032
21033 mapper.add_context_rule(
21034 String::from("criminal"),
21035 vec![String::from("crime"), String::from("offense")],
21036 );
21037
21038 let mapped = mapper.map_term("US", "JP", "felony", "serious crime");
21039 assert!(mapped.is_some());
21040 assert_eq!(mapped.unwrap(), "重罪");
21041 }
21042
21043 #[test]
21044 fn test_legal_dictionary() {
21045 let dict = LegalDictionary::us_dictionary();
21046
21047 assert_eq!(dict.jurisdiction, "US");
21048 assert!(!dict.terms.is_empty());
21049
21050 let felony = dict.find_term("felony");
21051 assert!(felony.is_some());
21052 assert_eq!(felony.unwrap().domain, "criminal");
21053
21054 let criminal_terms = dict.get_by_domain("criminal");
21055 assert!(criminal_terms.len() >= 2);
21056 }
21057
21058 #[test]
21059 fn test_japan_dictionary() {
21060 let dict = LegalDictionary::japan_dictionary();
21061
21062 assert_eq!(dict.jurisdiction, "JP");
21063 assert!(!dict.terms.is_empty());
21064
21065 let felony = dict.find_term("重罪");
21066 assert!(felony.is_some());
21067
21068 let criminal_terms = dict.get_by_domain("criminal");
21069 assert!(criminal_terms.len() >= 2);
21070 }
21071
21072 #[test]
21073 fn test_legal_term_creation() {
21074 let term = LegalTerm::new(
21075 String::from("contract"),
21076 String::from("An agreement between parties"),
21077 String::from("US"),
21078 String::from("civil"),
21079 );
21080
21081 assert_eq!(term.term, "contract");
21082 assert_eq!(term.jurisdiction, "US");
21083 assert_eq!(term.domain, "civil");
21084 assert!(term.related_terms.is_empty());
21085 }
21086
21087 #[test]
21088 fn test_term_translation_matrix_get_terms() {
21089 let mut matrix = TermTranslationMatrix::new();
21090
21091 matrix.add_term(LegalTerm::new(
21092 String::from("felony"),
21093 String::from("Serious crime"),
21094 String::from("US"),
21095 String::from("criminal"),
21096 ));
21097
21098 matrix.add_term(LegalTerm::new(
21099 String::from("tort"),
21100 String::from("Civil wrong"),
21101 String::from("US"),
21102 String::from("civil"),
21103 ));
21104
21105 let us_terms = matrix.get_terms("US");
21106 assert_eq!(us_terms.len(), 2);
21107
21108 let criminal_terms = matrix.get_terms_by_domain("US", "criminal");
21109 assert_eq!(criminal_terms.len(), 1);
21110 assert_eq!(criminal_terms[0].term, "felony");
21111 }
21112
21113 #[test]
21118 fn test_cultural_exception() {
21119 let exception = CulturalException::new(
21120 CulturalExceptionType::Religious,
21121 String::from("US"),
21122 String::from("Religious accommodation"),
21123 )
21124 .with_legal_basis(String::from("Title VII"))
21125 .with_domain(String::from("employment"));
21126
21127 assert_eq!(exception.exception_type, CulturalExceptionType::Religious);
21128 assert_eq!(exception.jurisdiction, "US");
21129 assert!(exception.legal_basis.is_some());
21130 assert_eq!(exception.applicable_domains.len(), 1);
21131 }
21132
21133 #[test]
21134 fn test_cultural_exception_registry() {
21135 let registry = CulturalExceptionRegistry::with_common_exceptions();
21136
21137 let us_exceptions = registry.get_exceptions("US");
21138 assert!(!us_exceptions.is_empty());
21139
21140 let jp_religious = registry.get_by_type("JP", CulturalExceptionType::Religious);
21141 assert!(!jp_religious.is_empty());
21142 }
21143
21144 #[test]
21145 fn test_holiday_calendar() {
21146 let mut calendar = HolidayCalendar::new(String::from("US"), CalendarSystem::Gregorian);
21147
21148 let holiday = Holiday::new(
21149 String::from("Independence Day"),
21150 HolidayType::National,
21151 String::from("US"),
21152 )
21153 .with_fixed_date(7, 4)
21154 .as_legal_holiday();
21155
21156 calendar.add_holiday(holiday);
21157
21158 assert_eq!(calendar.holidays.len(), 1);
21159 assert_eq!(calendar.calendar_system, CalendarSystem::Gregorian);
21160 }
21161
21162 #[test]
21163 fn test_us_calendar() {
21164 let calendar = HolidayCalendar::us_calendar();
21165
21166 assert_eq!(calendar.jurisdiction, "US");
21167 assert_eq!(calendar.calendar_system, CalendarSystem::Gregorian);
21168 assert!(calendar.holidays.len() >= 2);
21169
21170 let national_holidays = calendar.get_by_type(HolidayType::National);
21171 assert!(national_holidays.len() >= 2);
21172 }
21173
21174 #[test]
21175 fn test_japan_calendar() {
21176 let calendar = HolidayCalendar::japan_calendar();
21177
21178 assert_eq!(calendar.jurisdiction, "JP");
21179 assert_eq!(calendar.calendar_system, CalendarSystem::Japanese);
21180 assert!(calendar.holidays.len() >= 2);
21181 }
21182
21183 #[test]
21184 fn test_currency() {
21185 assert_eq!(Currency::USD.code(), "USD");
21186 assert_eq!(Currency::JPY.symbol(), "¥");
21187 assert_eq!(Currency::EUR.code(), "EUR");
21188 assert_eq!(Currency::GBP.symbol(), "£");
21189 }
21190
21191 #[test]
21192 fn test_monetary_conversion() {
21193 let conversion = MonetaryConversion::new(100.0, Currency::USD, Currency::JPY, 150.0);
21194
21195 assert_eq!(conversion.source_amount, 100.0);
21196 assert_eq!(conversion.source_currency, Currency::USD);
21197 assert_eq!(conversion.target_amount, 15000.0);
21198 assert_eq!(conversion.target_currency, Currency::JPY);
21199 assert_eq!(conversion.exchange_rate, 150.0);
21200 }
21201
21202 #[test]
21203 fn test_monetary_conversion_threshold() {
21204 let conversion = MonetaryConversion::new(100.0, Currency::USD, Currency::JPY, 150.0);
21205
21206 assert!(conversion.exceeds_threshold(10000.0));
21207 assert!(!conversion.exceeds_threshold(20000.0));
21208 }
21209
21210 #[test]
21211 fn test_monetary_adapter() {
21212 let adapter = MonetaryAdapter::with_common_rates();
21213
21214 let conversion = adapter.convert(1000.0, Currency::USD, Currency::JPY);
21215 assert!(conversion.is_some());
21216
21217 let conv = conversion.unwrap();
21218 assert_eq!(conv.target_amount, 150_000.0);
21219 }
21220
21221 #[test]
21222 fn test_age_of_majority() {
21223 let age = AgeOfMajority::new(String::from("US"), 18);
21224
21225 assert_eq!(age.jurisdiction, "US");
21226 assert_eq!(age.age, 18);
21227 assert!(age.exceptions.is_empty());
21228 }
21229
21230 #[test]
21231 fn test_age_of_majority_mapper() {
21232 let mapper = AgeOfMajorityMapper::with_common_jurisdictions();
21233
21234 let us_age = mapper.get_age("US");
21235 assert!(us_age.is_some());
21236 assert_eq!(us_age.unwrap().age, 18);
21237
21238 let jp_age = mapper.get_age("JP");
21239 assert!(jp_age.is_some());
21240 assert_eq!(jp_age.unwrap().age, 18);
21241 }
21242
21243 #[test]
21244 fn test_age_mapping() {
21245 let mapper = AgeOfMajorityMapper::with_common_jurisdictions();
21246
21247 let mapping = mapper.map_age_reference("US", "JP");
21249 assert!(mapping.is_none());
21250 }
21251
21252 #[test]
21253 fn test_legal_capacity_rule() {
21254 let rule = LegalCapacityRule::new(LegalCapacityType::Contractual, String::from("US"), 18);
21255
21256 assert_eq!(rule.capacity_type, LegalCapacityType::Contractual);
21257 assert_eq!(rule.jurisdiction, "US");
21258 assert_eq!(rule.minimum_age, 18);
21259 }
21260
21261 #[test]
21262 fn test_legal_capacity_adapter() {
21263 let adapter = LegalCapacityAdapter::with_common_rules();
21264
21265 let us_rules = adapter.get_rules("US");
21266 assert!(!us_rules.is_empty());
21267
21268 let us_contract = adapter.get_rule("US", LegalCapacityType::Contractual);
21269 assert!(us_contract.is_some());
21270 assert_eq!(us_contract.unwrap().minimum_age, 18);
21271 }
21272
21273 #[test]
21274 fn test_legal_capacity_differences() {
21275 let adapter = LegalCapacityAdapter::with_common_rules();
21276
21277 let us_criminal = adapter.get_rule("US", LegalCapacityType::CriminalResponsibility);
21278 let jp_criminal = adapter.get_rule("JP", LegalCapacityType::CriminalResponsibility);
21279
21280 assert!(us_criminal.is_some());
21281 assert!(jp_criminal.is_some());
21282
21283 assert_eq!(us_criminal.unwrap().minimum_age, 18);
21285 assert_eq!(jp_criminal.unwrap().minimum_age, 14);
21286 }
21287
21288 #[test]
21293 fn test_cultural_context_analysis_creation() {
21294 let mut analysis = CulturalContextAnalysis::new(String::from("US"));
21295
21296 assert_eq!(analysis.jurisdiction, "US");
21297 assert_eq!(analysis.social_norms.len(), 0);
21298 assert_eq!(analysis.power_distance, 0.5);
21299 assert_eq!(analysis.individualism_score, 0.0);
21300
21301 let norm = SocialNorm {
21302 description: "Individual freedom valued".to_string(),
21303 category: NormCategory::Public,
21304 strength: 0.9,
21305 legally_recognized: true,
21306 };
21307 analysis.add_norm(norm);
21308 assert_eq!(analysis.social_norms.len(), 1);
21309 }
21310
21311 #[test]
21312 fn test_cultural_context_compatibility() {
21313 let mut us_context = CulturalContextAnalysis::new(String::from("US"));
21314 us_context.power_distance = 0.4;
21315 us_context.individualism_score = 0.9;
21316 us_context.uncertainty_avoidance = 0.5;
21317 us_context.time_orientation = 0.3;
21318
21319 let mut jp_context = CulturalContextAnalysis::new(String::from("JP"));
21320 jp_context.power_distance = 0.6;
21321 jp_context.individualism_score = -0.3;
21322 jp_context.uncertainty_avoidance = 0.8;
21323 jp_context.time_orientation = 0.7;
21324
21325 let compatibility = us_context.assess_compatibility(&jp_context);
21326 assert!((0.0..=1.0).contains(&compatibility));
21327 assert!(compatibility < 0.8);
21329 }
21330
21331 #[test]
21332 fn test_cultural_context_historical_factors() {
21333 let mut analysis = CulturalContextAnalysis::new(String::from("US"));
21334
21335 let factor = HistoricalFactor {
21336 description: "Common law tradition from English colonial period".to_string(),
21337 period: "1600-1776".to_string(),
21338 impact: 0.9,
21339 legal_principles: vec!["Stare decisis".to_string(), "Jury trials".to_string()],
21340 };
21341 analysis.add_historical_factor(factor);
21342
21343 assert_eq!(analysis.historical_context.len(), 1);
21344 assert_eq!(analysis.historical_context[0].impact, 0.9);
21345 }
21346
21347 #[test]
21348 fn test_cultural_trends() {
21349 let mut analysis = CulturalContextAnalysis::new(String::from("US"));
21350
21351 let trend = CulturalTrend {
21352 description: "Increasing acceptance of same-sex marriage".to_string(),
21353 direction: 1.0,
21354 velocity: 0.7,
21355 legal_status: TrendLegalStatus::Codified,
21356 };
21357 analysis.add_trend(trend);
21358
21359 assert_eq!(analysis.cultural_trends.len(), 1);
21360 assert_eq!(
21361 analysis.cultural_trends[0].legal_status,
21362 TrendLegalStatus::Codified
21363 );
21364 }
21365
21366 #[test]
21367 fn test_local_practice_integration() {
21368 let mut integration = LocalPracticeIntegration::new(String::from("US"));
21369
21370 let practice = LocalPractice {
21371 name: "Handshake agreements".to_string(),
21372 description: "Verbal contracts sealed with handshake".to_string(),
21373 practice_type: PracticeType::Contract,
21374 geographic_scope: GeographicScope::Regional("Rural areas".to_string()),
21375 prevalence: 0.75,
21376 legal_status: PracticeLegalStatus::Tolerated,
21377 conflicts_with_law: false,
21378 related_statutes: vec![],
21379 };
21380
21381 integration.add_practice(practice);
21382 assert_eq!(integration.practices.len(), 1);
21383 assert_eq!(integration.practices[0].prevalence, 0.75);
21384 }
21385
21386 #[test]
21387 fn test_local_practice_recommendations() {
21388 let mut integration = LocalPracticeIntegration::new(String::from("US"));
21389
21390 let practice = LocalPractice {
21391 name: "Community mediation".to_string(),
21392 description: "Local elders mediate disputes".to_string(),
21393 practice_type: PracticeType::DisputeResolution,
21394 geographic_scope: GeographicScope::Community("Tribal community".to_string()),
21395 prevalence: 0.85,
21396 legal_status: PracticeLegalStatus::Tolerated,
21397 conflicts_with_law: false,
21398 related_statutes: vec![],
21399 };
21400
21401 integration.add_practice(practice);
21402 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Rights"));
21403 integration.generate_recommendations(&statute);
21404
21405 assert!(!integration.recommendations.is_empty());
21407 assert_eq!(
21408 integration.recommendations[0].recommendation_type,
21409 RecommendationType::Codify
21410 );
21411 }
21412
21413 #[test]
21414 fn test_geographic_scope_variants() {
21415 let national = GeographicScope::National;
21416 let regional = GeographicScope::Regional("Midwest".to_string());
21417 let local = GeographicScope::Local("Chicago".to_string());
21418 let _community = GeographicScope::Community("Amish".to_string());
21419
21420 assert_eq!(national, GeographicScope::National);
21421 assert_ne!(regional, local);
21422 }
21423
21424 #[test]
21425 fn test_customary_law_consideration() {
21426 let mut consideration = CustomaryLawConsideration::new(String::from("NZ"));
21427
21428 let customary = CustomaryLaw {
21429 name: "Maori fishing rights".to_string(),
21430 description: "Traditional fishing grounds reserved".to_string(),
21431 subject: CustomarySubject::Fishing,
21432 age_years: 800,
21433 geographic_scope: GeographicScope::Regional("Coastal areas".to_string()),
21434 recognition: CustomaryRecognition::Incorporated,
21435 binding_force: 0.9,
21436 modern_compatibility: 0.85,
21437 };
21438
21439 consideration.add_customary_law(customary);
21440 assert_eq!(consideration.customary_laws.len(), 1);
21441 assert_eq!(
21442 consideration.customary_laws[0].subject,
21443 CustomarySubject::Fishing
21444 );
21445 }
21446
21447 #[test]
21448 fn test_customary_statutory_interaction() {
21449 let mut consideration = CustomaryLawConsideration::new(String::from("NZ"));
21450
21451 let customary = CustomaryLaw {
21452 name: "Traditional land use".to_string(),
21453 description: "Customary land rights".to_string(),
21454 subject: CustomarySubject::Land,
21455 age_years: 1000,
21456 geographic_scope: GeographicScope::National,
21457 recognition: CustomaryRecognition::Incorporated,
21458 binding_force: 0.95,
21459 modern_compatibility: 0.9,
21460 };
21461
21462 let statute = Statute::new(
21463 "land-statute",
21464 "Land Act",
21465 Effect::new(EffectType::Grant, "Property rights"),
21466 );
21467 let interaction_type = consideration.analyze_interaction(&statute, &customary);
21468
21469 assert_eq!(interaction_type, InteractionType::Harmonious);
21471 assert_eq!(consideration.interactions.len(), 1);
21472 }
21473
21474 #[test]
21475 fn test_customary_recognition_levels() {
21476 let incorporated = CustomaryRecognition::Incorporated;
21477 let supplementary = CustomaryRecognition::Supplementary;
21478 let _acknowledged = CustomaryRecognition::Acknowledged;
21479 let _informal = CustomaryRecognition::Informal;
21480 let unrecognized = CustomaryRecognition::Unrecognized;
21481
21482 assert_eq!(incorporated, CustomaryRecognition::Incorporated);
21483 assert_ne!(supplementary, unrecognized);
21484 }
21485
21486 #[test]
21487 fn test_religious_law_compatibility() {
21488 let mut compatibility = ReligiousLawCompatibility::new(String::from("IL"));
21489
21490 let system = ReligiousLawSystem {
21491 name: "Halakha".to_string(),
21492 religion: Religion::Judaism,
21493 legal_status: ReligiousLegalStatus::PersonalStatus,
21494 population_percentage: 75.0,
21495 subject_matters: vec![ReligiousSubject::Marriage, ReligiousSubject::Divorce],
21496 civil_interaction: CivilReligiousInteraction::DualSystem,
21497 };
21498
21499 compatibility.add_religious_system(system);
21500 assert_eq!(compatibility.religious_systems.len(), 1);
21501 assert_eq!(
21502 compatibility.religious_systems[0].religion,
21503 Religion::Judaism
21504 );
21505 }
21506
21507 #[test]
21508 fn test_religious_compatibility_assessment() {
21509 let mut compatibility = ReligiousLawCompatibility::new(String::from("IL"));
21510
21511 let system = ReligiousLawSystem {
21512 name: "Jewish Law".to_string(),
21513 religion: Religion::Judaism,
21514 legal_status: ReligiousLegalStatus::PersonalStatus,
21515 population_percentage: 75.0,
21516 subject_matters: vec![ReligiousSubject::Marriage],
21517 civil_interaction: CivilReligiousInteraction::DualSystem,
21518 };
21519
21520 compatibility.add_religious_system(system);
21521 let statute = Statute::new(
21522 "marriage-law",
21523 "Marriage Act",
21524 Effect::new(EffectType::Grant, "Marriage rights"),
21525 );
21526 compatibility.assess_compatibility(&statute);
21527
21528 assert_eq!(compatibility.assessments.len(), 1);
21529 assert!(compatibility.assessments[0].compatibility_score > 0.0);
21530 assert!(!compatibility.assessments[0].accommodations.is_empty());
21531 }
21532
21533 #[test]
21534 fn test_religion_types() {
21535 let islam = Religion::Islam;
21536 let judaism = Religion::Judaism;
21537 let _hinduism = Religion::Hinduism;
21538 let _catholicism = Religion::Catholicism;
21539 let buddhism = Religion::Buddhism;
21540 let _other = Religion::Other;
21541
21542 assert_eq!(islam, Religion::Islam);
21543 assert_ne!(judaism, buddhism);
21544 }
21545
21546 #[test]
21547 fn test_civil_religious_interaction_types() {
21548 let separated = CivilReligiousInteraction::Separated;
21549 let dual = CivilReligiousInteraction::DualSystem;
21550
21551 assert_eq!(separated, CivilReligiousInteraction::Separated);
21552 assert_ne!(separated, dual);
21553 }
21554
21555 #[test]
21556 fn test_indigenous_rights_assessment() {
21557 let mut assessment = IndigenousRightsAssessment::new(String::from("CA"));
21558
21559 let people = IndigenousPeople {
21560 name: "First Nations".to_string(),
21561 population: 1_500_000,
21562 territories: vec!["British Columbia".to_string(), "Alberta".to_string()],
21563 recognition_status: IndigenousRecognition::TreatyRecognized,
21564 self_governance: GovernanceLevel::Autonomous,
21565 };
21566
21567 assessment.add_people(people);
21568 assert_eq!(assessment.indigenous_peoples.len(), 1);
21569 assert_eq!(assessment.indigenous_peoples[0].population, 1_500_000);
21570 }
21571
21572 #[test]
21573 fn test_indigenous_rights() {
21574 let mut assessment = IndigenousRightsAssessment::new(String::from("CA"));
21575
21576 let right = IndigenousRight {
21577 description: "Right to self-determination".to_string(),
21578 category: IndigenousRightCategory::SelfDetermination,
21579 legal_basis: vec![
21580 "UNDRIP Article 3".to_string(),
21581 "Constitution Act 1982".to_string(),
21582 ],
21583 geographic_scope: Some(vec!["National".to_string()]),
21584 limitations: vec![],
21585 };
21586
21587 assessment.add_right(right);
21588 assert_eq!(assessment.recognized_rights.len(), 1);
21589 assert_eq!(
21590 assessment.recognized_rights[0].category,
21591 IndigenousRightCategory::SelfDetermination
21592 );
21593 }
21594
21595 #[test]
21596 fn test_indigenous_impact_assessment() {
21597 let mut assessment = IndigenousRightsAssessment::new(String::from("CA"));
21598
21599 let people = IndigenousPeople {
21600 name: "Inuit".to_string(),
21601 population: 65_000,
21602 territories: vec!["Nunavut".to_string()],
21603 recognition_status: IndigenousRecognition::ConstitutionallyRecognized,
21604 self_governance: GovernanceLevel::Autonomous,
21605 };
21606
21607 assessment.add_people(people);
21608 let statute = Statute::new(
21609 "resource-law",
21610 "Resource Development Act",
21611 Effect::new(EffectType::Prohibition, "Land use"),
21612 );
21613 let impact_score = assessment.assess_impact(&statute);
21614
21615 assert!((-1.0..=1.0).contains(&impact_score));
21616 assert_eq!(assessment.impact_assessments.len(), 1);
21617 assert!(
21618 !assessment.impact_assessments[0]
21619 .mitigation_measures
21620 .is_empty()
21621 );
21622 }
21623
21624 #[test]
21625 fn test_indigenous_consultation_requirements() {
21626 let mut assessment = IndigenousRightsAssessment::new(String::from("CA"));
21627
21628 let people = IndigenousPeople {
21629 name: "Métis".to_string(),
21630 population: 587_000,
21631 territories: vec!["Manitoba".to_string()],
21632 recognition_status: IndigenousRecognition::ConstitutionallyRecognized,
21633 self_governance: GovernanceLevel::Limited,
21634 };
21635
21636 assessment.add_people(people);
21637 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Rights"));
21638 assessment.assess_impact(&statute);
21639
21640 assert!(!assessment.check_consultation_requirements());
21642 }
21643
21644 #[test]
21645 fn test_indigenous_right_categories() {
21646 let land = IndigenousRightCategory::Land;
21647 let culture = IndigenousRightCategory::Culture;
21648 let language = IndigenousRightCategory::Language;
21649 let _resources = IndigenousRightCategory::Resources;
21650
21651 assert_eq!(land, IndigenousRightCategory::Land);
21652 assert_ne!(culture, language);
21653 }
21654
21655 #[test]
21656 fn test_governance_levels() {
21657 let sovereign = GovernanceLevel::Sovereign;
21658 let autonomous = GovernanceLevel::Autonomous;
21659 let _limited = GovernanceLevel::Limited;
21660 let _consultation = GovernanceLevel::Consultation;
21661 let none = GovernanceLevel::None;
21662
21663 assert_eq!(sovereign, GovernanceLevel::Sovereign);
21664 assert_ne!(autonomous, none);
21665 }
21666
21667 #[test]
21668 fn test_impact_type_classifications() {
21669 let positive = ImpactType::Positive;
21670 let neutral = ImpactType::Neutral;
21671 let negative = ImpactType::Negative;
21672 let _mixed = ImpactType::Mixed;
21673
21674 assert_eq!(positive, ImpactType::Positive);
21675 assert_ne!(neutral, negative);
21676 }
21677
21678 #[test]
21683 fn test_cost_benefit_projection_creation() {
21684 let projection = CostBenefitProjection::new(
21685 "test-statute".to_string(),
21686 "US".to_string(),
21687 "JP".to_string(),
21688 );
21689
21690 assert_eq!(projection.statute_id, "test-statute");
21691 assert_eq!(projection.source_jurisdiction, "US");
21692 assert_eq!(projection.target_jurisdiction, "JP");
21693 assert_eq!(projection.total_cost, 0.0);
21694 assert_eq!(projection.total_benefit, 0.0);
21695 assert_eq!(projection.net_benefit, 0.0);
21696 }
21697
21698 #[test]
21699 fn test_cost_benefit_with_costs_and_benefits() {
21700 let mut projection =
21701 CostBenefitProjection::new("test".to_string(), "US".to_string(), "JP".to_string());
21702
21703 let cost = PortingCost {
21704 category: CostCategory::Legal,
21705 description: "Legal review".to_string(),
21706 amount: 50000.0,
21707 timeframe: CostTimeframe::OneTime,
21708 certainty: 0.9,
21709 };
21710
21711 let benefit = PortingBenefit {
21712 category: BenefitCategory::Economic,
21713 description: "Trade facilitation".to_string(),
21714 monetary_value: Some(200000.0),
21715 qualitative_value: "Enhanced business environment".to_string(),
21716 timeframe: CostTimeframe::Annual,
21717 certainty: 0.8,
21718 };
21719
21720 projection.add_cost(cost);
21721 projection.add_benefit(benefit);
21722
21723 assert_eq!(projection.total_cost, 50000.0);
21724 assert_eq!(projection.total_benefit, 200000.0);
21725 assert_eq!(projection.net_benefit, 150000.0);
21726 assert_eq!(projection.benefit_cost_ratio, 4.0);
21727 assert!(projection.payback_period.is_some());
21728 }
21729
21730 #[test]
21731 fn test_cost_categories() {
21732 let legal = CostCategory::Legal;
21733 let translation = CostCategory::Translation;
21734 let consultation = CostCategory::Consultation;
21735
21736 assert_eq!(legal, CostCategory::Legal);
21737 assert_ne!(translation, consultation);
21738 }
21739
21740 #[test]
21741 fn test_cost_timeframe_variants() {
21742 let one_time = CostTimeframe::OneTime;
21743 let annual = CostTimeframe::Annual;
21744 let multi_year = CostTimeframe::MultiYear(5);
21745
21746 assert_eq!(one_time, CostTimeframe::OneTime);
21747 assert_eq!(annual, CostTimeframe::Annual);
21748 assert_eq!(multi_year, CostTimeframe::MultiYear(5));
21749 }
21750
21751 #[test]
21752 fn test_benefit_categories() {
21753 let economic = BenefitCategory::Economic;
21754 let social = BenefitCategory::Social;
21755 let legal = BenefitCategory::Legal;
21756
21757 assert_eq!(economic, BenefitCategory::Economic);
21758 assert_ne!(social, legal);
21759 }
21760
21761 #[test]
21762 fn test_market_impact_assessment() {
21763 let assessment = MarketImpactAssessment::new("test-statute".to_string(), "US".to_string());
21764
21765 assert_eq!(assessment.statute_id, "test-statute");
21766 assert_eq!(assessment.jurisdiction, "US");
21767 assert_eq!(assessment.impact_score, 0.0);
21768 assert_eq!(assessment.affected_sectors.len(), 0);
21769 }
21770
21771 #[test]
21772 fn test_market_sector_impact() {
21773 let mut assessment = MarketImpactAssessment::new("test".to_string(), "US".to_string());
21774
21775 let sector = MarketSector {
21776 name: "Technology".to_string(),
21777 size_percentage: 15.0,
21778 businesses_affected: 5000,
21779 impact_type: ImpactType::Positive,
21780 impact_magnitude: 0.7,
21781 };
21782
21783 assessment.add_sector(sector);
21784
21785 assert_eq!(assessment.affected_sectors.len(), 1);
21786 assert!(assessment.impact_score > 0.0); }
21788
21789 #[test]
21790 fn test_market_impact_score_calculation() {
21791 let mut assessment = MarketImpactAssessment::new("test".to_string(), "US".to_string());
21792
21793 let positive_sector = MarketSector {
21794 name: "Tech".to_string(),
21795 size_percentage: 10.0,
21796 businesses_affected: 1000,
21797 impact_type: ImpactType::Positive,
21798 impact_magnitude: 0.8,
21799 };
21800
21801 let negative_sector = MarketSector {
21802 name: "Traditional".to_string(),
21803 size_percentage: 5.0,
21804 businesses_affected: 500,
21805 impact_type: ImpactType::Negative,
21806 impact_magnitude: 0.6,
21807 };
21808
21809 assessment.add_sector(positive_sector);
21810 assessment.add_sector(negative_sector);
21811
21812 assert!(assessment.impact_score > 0.0);
21814 }
21815
21816 #[test]
21817 fn test_barrier_types() {
21818 let regulatory = BarrierType::Regulatory;
21819 let cost = BarrierType::Cost;
21820 let technical = BarrierType::Technical;
21821
21822 assert_eq!(regulatory, BarrierType::Regulatory);
21823 assert_ne!(cost, technical);
21824 }
21825
21826 #[test]
21827 fn test_compliance_cost_estimation() {
21828 let estimation =
21829 ComplianceCostEstimation::new("test-statute".to_string(), "US".to_string());
21830
21831 assert_eq!(estimation.statute_id, "test-statute");
21832 assert_eq!(estimation.total_burden, 0.0);
21833 assert_eq!(estimation.average_cost_per_entity, 0.0);
21834 }
21835
21836 #[test]
21837 fn test_compliance_cost_calculation() {
21838 let mut estimation = ComplianceCostEstimation::new("test".to_string(), "US".to_string());
21839
21840 let direct_cost = ComplianceCost {
21841 cost_type: ComplianceCostType::Administrative,
21842 description: "Form filing".to_string(),
21843 amount: 10000.0,
21844 frequency: CostTimeframe::Annual,
21845 certainty: 0.95,
21846 };
21847
21848 let indirect_cost = ComplianceCost {
21849 cost_type: ComplianceCostType::Opportunity,
21850 description: "Time spent on compliance".to_string(),
21851 amount: 5000.0,
21852 frequency: CostTimeframe::Annual,
21853 certainty: 0.7,
21854 };
21855
21856 let entity = AffectedEntity {
21857 entity_type: EntityType::SME,
21858 count: 100,
21859 average_cost: 150.0,
21860 capacity: ComplianceCapacity::Moderate,
21861 };
21862
21863 estimation.add_direct_cost(direct_cost);
21864 estimation.add_indirect_cost(indirect_cost);
21865 estimation.add_affected_entity(entity);
21866
21867 assert_eq!(estimation.total_burden, 15000.0);
21868 assert_eq!(estimation.average_cost_per_entity, 150.0);
21869 }
21870
21871 #[test]
21872 fn test_compliance_cost_types() {
21873 let admin = ComplianceCostType::Administrative;
21874 let reporting = ComplianceCostType::Reporting;
21875 let audit = ComplianceCostType::Audit;
21876
21877 assert_eq!(admin, ComplianceCostType::Administrative);
21878 assert_ne!(reporting, audit);
21879 }
21880
21881 #[test]
21882 fn test_entity_types() {
21883 let large = EntityType::LargeBusiness;
21884 let sme = EntityType::SME;
21885 let individual = EntityType::Individual;
21886
21887 assert_eq!(large, EntityType::LargeBusiness);
21888 assert_ne!(sme, individual);
21889 }
21890
21891 #[test]
21892 fn test_compliance_capacity_levels() {
21893 let high = ComplianceCapacity::High;
21894 let moderate = ComplianceCapacity::Moderate;
21895 let _low = ComplianceCapacity::Low;
21896 let insufficient = ComplianceCapacity::Insufficient;
21897
21898 assert_eq!(high, ComplianceCapacity::High);
21899 assert_ne!(moderate, insufficient);
21900 }
21901
21902 #[test]
21903 fn test_business_impact_report_creation() {
21904 let report = BusinessImpactReport::new("test-statute".to_string(), "US".to_string());
21905
21906 assert_eq!(report.statute_id, "test-statute");
21907 assert_eq!(report.jurisdiction, "US");
21908 assert_eq!(report.business_climate_score, 0.0);
21909 assert!(report.executive_summary.is_empty());
21910 }
21911
21912 #[test]
21913 fn test_business_impact_summary_generation() {
21914 let mut report = BusinessImpactReport::new("test".to_string(), "US".to_string());
21915
21916 report.sector_impacts.push(SectorImpact {
21917 sector: "Tech".to_string(),
21918 description: "Positive impact".to_string(),
21919 jobs_impact: 100,
21920 revenue_impact_percent: 5.0,
21921 investment_impact: "Increased".to_string(),
21922 });
21923
21924 report.sector_impacts.push(SectorImpact {
21925 sector: "Manufacturing".to_string(),
21926 description: "Moderate impact".to_string(),
21927 jobs_impact: -20,
21928 revenue_impact_percent: -2.0,
21929 investment_impact: "Stable".to_string(),
21930 });
21931
21932 report.business_climate_score = 0.6;
21933 report.generate_summary();
21934
21935 assert!(!report.executive_summary.is_empty());
21936 assert!(report.executive_summary.contains("2 sectors"));
21937 }
21938
21939 #[test]
21940 fn test_risk_level_with_negligible() {
21941 let negligible = RiskLevel::Negligible;
21942 let low = RiskLevel::Low;
21943 let _medium = RiskLevel::Medium;
21944 let high = RiskLevel::High;
21945 let critical = RiskLevel::Critical;
21946
21947 assert_eq!(negligible, RiskLevel::Negligible);
21948 assert_ne!(low, high);
21949 assert_eq!(critical, RiskLevel::Critical);
21950 }
21951
21952 #[test]
21953 fn test_industry_consultation_creation() {
21954 let consultation = IndustryConsultation::new("test-statute".to_string(), "US".to_string());
21955
21956 assert_eq!(consultation.statute_id, "test-statute");
21957 assert_eq!(consultation.jurisdiction, "US");
21958 assert_eq!(consultation.associations.len(), 0);
21959 assert_eq!(consultation.responses.len(), 0);
21960 assert_eq!(consultation.feedback_analysis.response_count, 0);
21961 }
21962
21963 #[test]
21964 fn test_industry_association_management() {
21965 let mut consultation = IndustryConsultation::new("test".to_string(), "US".to_string());
21966
21967 let association = IndustryAssociation {
21968 name: "Tech Industry Association".to_string(),
21969 sector: "Technology".to_string(),
21970 member_count: 500,
21971 contact: "contact@example.com".to_string(),
21972 status: ConsultationStatus::Invited,
21973 };
21974
21975 consultation.add_association(association);
21976
21977 assert_eq!(consultation.associations.len(), 1);
21978 assert_eq!(
21979 consultation.associations[0].name,
21980 "Tech Industry Association"
21981 );
21982 }
21983
21984 #[test]
21985 fn test_consultation_response_analysis() {
21986 let mut consultation = IndustryConsultation::new("test".to_string(), "US".to_string());
21987
21988 let response1 = ConsultationResponse {
21989 organization: "Org1".to_string(),
21990 date: "2024-01-01".to_string(),
21991 support_level: 0.8,
21992 concerns: vec!["Cost".to_string(), "Timeline".to_string()],
21993 suggestions: vec!["Phase implementation".to_string()],
21994 claimed_impacts: vec!["10% cost increase".to_string()],
21995 };
21996
21997 let response2 = ConsultationResponse {
21998 organization: "Org2".to_string(),
21999 date: "2024-01-02".to_string(),
22000 support_level: 0.6,
22001 concerns: vec!["Cost".to_string()],
22002 suggestions: vec![],
22003 claimed_impacts: vec![],
22004 };
22005
22006 consultation.add_response(response1);
22007 consultation.add_response(response2);
22008
22009 assert_eq!(consultation.feedback_analysis.response_count, 2);
22010 assert_eq!(consultation.feedback_analysis.average_support, 0.7);
22011 assert!(!consultation.feedback_analysis.common_concerns.is_empty());
22013 }
22014
22015 #[test]
22016 fn test_consultation_status_variants() {
22017 let not_contacted = ConsultationStatus::NotContacted;
22018 let invited = ConsultationStatus::Invited;
22019 let responded = ConsultationStatus::Responded;
22020 let declined = ConsultationStatus::Declined;
22021
22022 assert_eq!(not_contacted, ConsultationStatus::NotContacted);
22023 assert_ne!(invited, responded);
22024 assert_eq!(declined, ConsultationStatus::Declined);
22025 }
22026
22027 #[test]
22030 fn test_compliance_checker() {
22031 let us = test_jurisdiction_us();
22032 let checker = TargetJurisdictionChecker::new(us);
22033
22034 let statute = Statute::new(
22035 "test-statute",
22036 "Test Administrative Procedure",
22037 Effect::new(EffectType::Grant, "Administrative rights"),
22038 );
22039
22040 let result = checker.check_compliance(&statute);
22041
22042 assert!(!result.id.is_empty());
22043 assert!(!result.checked_regulations.is_empty());
22044 assert!(result.compliance_score >= 0.0 && result.compliance_score <= 1.0);
22045 }
22046
22047 #[test]
22048 fn test_compliance_severity_levels() {
22049 let us = test_jurisdiction_us();
22050 let checker = TargetJurisdictionChecker::new(us);
22051
22052 let statute = Statute::new(
22053 "test-statute",
22054 "Test Statute",
22055 Effect::new(EffectType::Grant, "Rights"),
22056 );
22057
22058 let result = checker.check_compliance(&statute);
22059
22060 for issue in &result.issues {
22062 assert!(matches!(
22063 issue.severity,
22064 ComplianceSeverity::Critical
22065 | ComplianceSeverity::High
22066 | ComplianceSeverity::Medium
22067 | ComplianceSeverity::Low
22068 | ComplianceSeverity::Info
22069 ));
22070 }
22071 }
22072
22073 #[test]
22074 fn test_constitutional_analyzer() {
22075 let us = test_jurisdiction_us();
22076 let analyzer = ConstitutionalAnalyzer::new(us);
22077
22078 let statute = Statute::new(
22079 "test-statute",
22080 "Test Constitutional Statute",
22081 Effect::new(EffectType::Grant, "Freedom rights"),
22082 );
22083
22084 let result = analyzer.analyze(&statute);
22085
22086 assert!(!result.id.is_empty());
22087 assert!(result.compatibility_score >= 0.0 && result.compatibility_score <= 1.0);
22088 assert!(!result.relevant_provisions.is_empty());
22089 assert!(!result.recommended_amendments.is_empty());
22090 }
22091
22092 #[test]
22093 fn test_constitutional_provisions_us() {
22094 let us = test_jurisdiction_us();
22095 let analyzer = ConstitutionalAnalyzer::new(us);
22096
22097 let statute = Statute::new(
22098 "test-statute",
22099 "Test Statute",
22100 Effect::new(EffectType::Grant, "Rights"),
22101 );
22102
22103 let result = analyzer.analyze(&statute);
22104
22105 assert!(
22107 result
22108 .relevant_provisions
22109 .iter()
22110 .any(|p| p.contains("Amendment"))
22111 );
22112 }
22113
22114 #[test]
22115 fn test_constitutional_provisions_japan() {
22116 let jp = test_jurisdiction_jp();
22117 let analyzer = ConstitutionalAnalyzer::new(jp);
22118
22119 let statute = Statute::new(
22120 "test-statute",
22121 "Test Statute",
22122 Effect::new(EffectType::Grant, "Rights"),
22123 );
22124
22125 let result = analyzer.analyze(&statute);
22126
22127 assert!(
22129 result
22130 .relevant_provisions
22131 .iter()
22132 .any(|p| p.contains("Article") || p.contains("憲法"))
22133 );
22134 }
22135
22136 #[test]
22137 fn test_treaty_compliance_checker() {
22138 let us = test_jurisdiction_us();
22139 let checker = TreatyTargetJurisdictionChecker::new(us);
22140
22141 let statute = Statute::new(
22142 "test-statute",
22143 "Test Human Rights Statute",
22144 Effect::new(EffectType::Grant, "Human rights"),
22145 );
22146
22147 let result = checker.check_compliance(&statute);
22148
22149 assert!(!result.id.is_empty());
22150 assert!(result.compliance_score >= 0.0 && result.compliance_score <= 1.0);
22151 assert!(!result.checked_treaties.is_empty());
22152 assert!(!result.recommendations.is_empty());
22153 }
22154
22155 #[test]
22156 fn test_treaty_database() {
22157 let us = test_jurisdiction_us();
22158 let checker = TreatyTargetJurisdictionChecker::new(us);
22159
22160 let statute = Statute::new(
22161 "test-statute",
22162 "Test Statute",
22163 Effect::new(EffectType::Grant, "Rights"),
22164 );
22165
22166 let result = checker.check_compliance(&statute);
22167
22168 assert!(
22170 result
22171 .checked_treaties
22172 .iter()
22173 .any(|t| t.contains("International Covenant") || t.contains("Rights"))
22174 );
22175 }
22176
22177 #[test]
22178 fn test_human_rights_assessor() {
22179 let us = test_jurisdiction_us();
22180 let assessor = HumanRightsAssessor::new(us);
22181
22182 let statute = Statute::new(
22183 "test-statute",
22184 "Test Human Rights Statute",
22185 Effect::new(EffectType::Grant, "Fundamental rights"),
22186 );
22187
22188 let result = assessor.assess(&statute);
22189
22190 assert!(!result.id.is_empty());
22191 assert!(result.impact_score >= -1.0 && result.impact_score <= 1.0);
22192 assert!(!result.mitigation_measures.is_empty());
22193 assert!(!result.summary.is_empty());
22194 }
22195
22196 #[test]
22197 fn test_human_rights_impact_types() {
22198 let us = test_jurisdiction_us();
22199 let assessor = HumanRightsAssessor::new(us);
22200
22201 let statute = Statute::new(
22202 "test-statute",
22203 "Test Statute",
22204 Effect::new(EffectType::Grant, "Rights"),
22205 );
22206
22207 let result = assessor.assess(&statute);
22208
22209 for right in &result.affected_rights {
22211 assert!(matches!(
22212 right.impact,
22213 RightImpactType::Enhancement
22214 | RightImpactType::Neutral
22215 | RightImpactType::Restriction
22216 | RightImpactType::Violation
22217 ));
22218 }
22219 }
22220
22221 #[test]
22222 fn test_enforceability_predictor() {
22223 let us = test_jurisdiction_us();
22224 let predictor = EnforceabilityPredictor::new(us);
22225
22226 let statute = Statute::new(
22227 "test-statute",
22228 "Test Enforcement Statute",
22229 Effect::new(EffectType::Grant, "Enforcement powers"),
22230 );
22231
22232 let result = predictor.predict(&statute);
22233
22234 assert!(!result.id.is_empty());
22235 assert!(result.enforceability_score >= 0.0 && result.enforceability_score <= 1.0);
22236 assert!(!result.required_mechanisms.is_empty());
22237 assert!(!result.recommendations.is_empty());
22238 }
22239
22240 #[test]
22241 fn test_enforcement_challenge_types() {
22242 let us = test_jurisdiction_us();
22243 let predictor = EnforceabilityPredictor::new(us);
22244
22245 let statute = Statute::new(
22246 "test-statute",
22247 "Test Statute",
22248 Effect::new(EffectType::Grant, "Rights"),
22249 );
22250
22251 let result = predictor.predict(&statute);
22252
22253 for challenge in &result.challenges {
22255 assert!(matches!(
22256 challenge.challenge_type,
22257 EnforcementChallengeType::Authority
22258 | EnforcementChallengeType::Resources
22259 | EnforcementChallengeType::Technical
22260 | EnforcementChallengeType::Cultural
22261 | EnforcementChallengeType::Administrative
22262 | EnforcementChallengeType::Monitoring
22263 ));
22264 }
22265 }
22266
22267 #[test]
22268 fn test_validation_framework_creation() {
22269 let us = test_jurisdiction_us();
22270 let framework = ValidationFramework::new(us);
22271
22272 let statute = Statute::new(
22273 "test-statute",
22274 "Test Validation Statute",
22275 Effect::new(EffectType::Grant, "Rights"),
22276 );
22277
22278 let result = framework.validate(&statute);
22279
22280 assert!(!result.id.is_empty());
22281 assert!(result.overall_score >= 0.0 && result.overall_score <= 1.0);
22282 assert!(!result.summary.is_empty());
22283 }
22284
22285 #[test]
22286 fn test_validation_framework_comprehensive() {
22287 let us = test_jurisdiction_us();
22288 let framework = ValidationFramework::new(us);
22289
22290 let statute = Statute::new(
22291 "test-statute",
22292 "Test Comprehensive Statute",
22293 Effect::new(EffectType::Grant, "Comprehensive rights"),
22294 );
22295
22296 let result = framework.validate(&statute);
22297
22298 assert!(!result.compliance.id.is_empty());
22300 assert!(!result.constitutional.id.is_empty());
22301 assert!(!result.treaty_compliance.id.is_empty());
22302 assert!(!result.human_rights.id.is_empty());
22303 assert!(!result.enforceability.id.is_empty());
22304 }
22305
22306 #[test]
22307 fn test_validation_overall_score_calculation() {
22308 let us = test_jurisdiction_us();
22309 let framework = ValidationFramework::new(us);
22310
22311 let statute = Statute::new(
22312 "test-statute",
22313 "Test Score Statute",
22314 Effect::new(EffectType::Grant, "Rights"),
22315 );
22316
22317 let result = framework.validate(&statute);
22318
22319 let expected_score = (result.compliance.compliance_score
22321 + result.constitutional.compatibility_score
22322 + result.treaty_compliance.compliance_score
22323 + result.enforceability.enforceability_score
22324 + (result.human_rights.impact_score + 1.0) / 2.0)
22325 / 5.0;
22326
22327 assert!((result.overall_score - expected_score).abs() < 0.001);
22328 }
22329
22330 #[test]
22331 fn test_validation_passed_criteria() {
22332 let us = test_jurisdiction_us();
22333 let framework = ValidationFramework::new(us);
22334
22335 let statute = Statute::new(
22336 "test-statute",
22337 "Test Passing Statute",
22338 Effect::new(EffectType::Grant, "Rights"),
22339 );
22340
22341 let result = framework.validate(&statute);
22342
22343 if result.passed {
22345 assert!(result.compliance.is_compliant);
22346 assert!(result.constitutional.is_compatible);
22347 assert!(result.treaty_compliance.is_compliant);
22348 assert!(result.human_rights.impact_score >= 0.0);
22349 assert!(result.enforceability.is_enforceable);
22350 }
22351 }
22352
22353 #[test]
22354 fn test_pre_porting_feasibility_analysis() {
22355 let jp = test_jurisdiction_jp();
22356 let us = test_jurisdiction_us();
22357 let analyzer = PrePortingFeasibilityAnalyzer::new(jp, us);
22358
22359 let statute = Statute::new(
22360 "test-statute",
22361 "Test Feasibility Statute",
22362 Effect::new(EffectType::Grant, "Rights"),
22363 );
22364
22365 let analysis = analyzer.analyze(&statute);
22366
22367 assert!(!analysis.id.is_empty());
22369 assert!(analysis.feasibility_score >= 0.0 && analysis.feasibility_score <= 1.0);
22370 assert!(analysis.technical_feasibility >= 0.0 && analysis.technical_feasibility <= 1.0);
22371 assert!(analysis.legal_feasibility >= 0.0 && analysis.legal_feasibility <= 1.0);
22372 assert!(analysis.cultural_feasibility >= 0.0 && analysis.cultural_feasibility <= 1.0);
22373 assert!(analysis.economic_feasibility >= 0.0 && analysis.economic_feasibility <= 1.0);
22374 assert!(analysis.political_feasibility >= 0.0 && analysis.political_feasibility <= 1.0);
22375
22376 assert!(!analysis.factors.is_empty());
22378
22379 assert!(!analysis.prerequisites.is_empty());
22381
22382 assert!(analysis.estimated_time_days > 0);
22384 assert!(analysis.estimated_cost_usd > 0.0);
22385
22386 assert!(!analysis.recommended_approach.is_empty());
22388 assert!(!analysis.alternatives.is_empty());
22389 }
22390
22391 #[test]
22392 fn test_feasibility_recommendation_levels() {
22393 let jp = test_jurisdiction_jp();
22395 let us = test_jurisdiction_us();
22396 let analyzer = PrePortingFeasibilityAnalyzer::new(jp.clone(), us.clone());
22397
22398 let statute = Statute::new("test", "Test", Effect::new(EffectType::Grant, "Rights"));
22399
22400 let analysis = analyzer.analyze(&statute);
22401
22402 match analysis.recommendation {
22404 FeasibilityRecommendation::StronglyRecommended => {
22405 assert!(analysis.feasibility_score >= 0.85);
22406 }
22407 FeasibilityRecommendation::Recommended => {
22408 assert!(analysis.feasibility_score >= 0.7 && analysis.feasibility_score < 0.85);
22409 }
22410 FeasibilityRecommendation::Conditional => {
22411 assert!(analysis.feasibility_score >= 0.5 && analysis.feasibility_score < 0.7);
22412 }
22413 FeasibilityRecommendation::NotRecommended => {
22414 assert!(analysis.feasibility_score >= 0.3 && analysis.feasibility_score < 0.5);
22415 }
22416 FeasibilityRecommendation::StronglyNotRecommended => {
22417 assert!(analysis.feasibility_score < 0.3);
22418 }
22419 }
22420 }
22421
22422 #[test]
22423 fn test_feasibility_factor_categories() {
22424 let factor = FeasibilityFactor {
22425 id: "test-factor".to_string(),
22426 category: FeasibilityCategory::Technical,
22427 name: "Test Factor".to_string(),
22428 impact: -0.2,
22429 severity: FeasibilitySeverity::Moderate,
22430 description: "Test description".to_string(),
22431 mitigation_strategies: vec!["Strategy 1".to_string()],
22432 };
22433
22434 assert_eq!(factor.category, FeasibilityCategory::Technical);
22435 assert_eq!(factor.severity, FeasibilitySeverity::Moderate);
22436 assert_eq!(factor.impact, -0.2);
22437 }
22438
22439 #[test]
22440 fn test_compliance_issue_categories() {
22441 let issue = ValidationComplianceIssue {
22442 id: "test-issue".to_string(),
22443 severity: ComplianceSeverity::Medium,
22444 category: ComplianceCategory::Regulatory,
22445 description: "Test issue".to_string(),
22446 conflicting_regulation: "test-reg".to_string(),
22447 suggested_resolution: Some("Test resolution".to_string()),
22448 };
22449
22450 assert!(matches!(
22451 issue.category,
22452 ComplianceCategory::Constitutional
22453 | ComplianceCategory::Regulatory
22454 | ComplianceCategory::Procedural
22455 | ComplianceCategory::Cultural
22456 | ComplianceCategory::Technical
22457 | ComplianceCategory::Administrative
22458 ));
22459 }
22460
22461 #[test]
22462 fn test_impact_severity_levels() {
22463 let severities = [
22464 ImpactSeverity::Severe,
22465 ImpactSeverity::Moderate,
22466 ImpactSeverity::Minor,
22467 ImpactSeverity::Negligible,
22468 ];
22469
22470 for severity in severities {
22471 assert!(matches!(
22472 severity,
22473 ImpactSeverity::Severe
22474 | ImpactSeverity::Moderate
22475 | ImpactSeverity::Minor
22476 | ImpactSeverity::Negligible
22477 ));
22478 }
22479 }
22480
22481 #[test]
22484 fn test_project_creation() {
22485 let mut manager = PortingProjectManager::new();
22486 let project = manager.create_project(
22487 "Test Project".to_string(),
22488 "Test description".to_string(),
22489 "JP".to_string(),
22490 "US".to_string(),
22491 );
22492
22493 assert!(!project.id.is_empty());
22494 assert_eq!(project.name, "Test Project");
22495 assert_eq!(project.status, ProjectStatus::Planning);
22496 assert!(project.statute_ids.is_empty());
22497 assert!(project.stakeholders.is_empty());
22498 }
22499
22500 #[test]
22501 fn test_project_status_update() {
22502 let mut manager = PortingProjectManager::new();
22503 let project = manager.create_project(
22504 "Test".to_string(),
22505 "Desc".to_string(),
22506 "JP".to_string(),
22507 "US".to_string(),
22508 );
22509
22510 manager.update_status(&project.id, ProjectStatus::InProgress);
22511
22512 let updated = manager.get_project(&project.id).unwrap();
22513 assert_eq!(updated.status, ProjectStatus::InProgress);
22514 }
22515
22516 #[test]
22517 fn test_add_statute_to_project() {
22518 let mut manager = PortingProjectManager::new();
22519 let project = manager.create_project(
22520 "Test".to_string(),
22521 "Desc".to_string(),
22522 "JP".to_string(),
22523 "US".to_string(),
22524 );
22525
22526 manager.add_statute(&project.id, "statute-1".to_string());
22527 manager.add_statute(&project.id, "statute-2".to_string());
22528
22529 let updated = manager.get_project(&project.id).unwrap();
22530 assert_eq!(updated.statute_ids.len(), 2);
22531 assert!(updated.statute_ids.contains(&"statute-1".to_string()));
22532 }
22533
22534 #[test]
22535 fn test_add_stakeholder_to_project() {
22536 let mut manager = PortingProjectManager::new();
22537 let project = manager.create_project(
22538 "Test".to_string(),
22539 "Desc".to_string(),
22540 "JP".to_string(),
22541 "US".to_string(),
22542 );
22543
22544 let stakeholder = Stakeholder {
22545 id: "stakeholder-1".to_string(),
22546 name: "John Doe".to_string(),
22547 email: "john@example.com".to_string(),
22548 role: StakeholderRole::LegalExpert,
22549 notification_preferences: NotificationPreferences {
22550 on_status_change: true,
22551 on_deadline_approaching: true,
22552 on_assignment: true,
22553 on_review_request: true,
22554 channels: vec![NotificationChannel::Email],
22555 },
22556 };
22557
22558 manager.add_stakeholder(&project.id, stakeholder);
22559
22560 let updated = manager.get_project(&project.id).unwrap();
22561 assert_eq!(updated.stakeholders.len(), 1);
22562 assert_eq!(updated.stakeholders[0].name, "John Doe");
22563 }
22564
22565 #[test]
22566 fn test_add_milestone() {
22567 let mut manager = PortingProjectManager::new();
22568 let project = manager.create_project(
22569 "Test".to_string(),
22570 "Desc".to_string(),
22571 "JP".to_string(),
22572 "US".to_string(),
22573 );
22574
22575 let milestone = Milestone {
22576 id: "milestone-1".to_string(),
22577 name: "Complete Draft".to_string(),
22578 description: "Complete initial draft".to_string(),
22579 target_date: "2025-12-31T00:00:00Z".to_string(),
22580 completed: false,
22581 completed_date: None,
22582 dependencies: Vec::new(),
22583 };
22584
22585 manager.add_milestone(&project.id, milestone);
22586
22587 let updated = manager.get_project(&project.id).unwrap();
22588 assert_eq!(updated.timeline.milestones.len(), 1);
22589 }
22590
22591 #[test]
22592 fn test_complete_milestone() {
22593 let mut manager = PortingProjectManager::new();
22594 let project = manager.create_project(
22595 "Test".to_string(),
22596 "Desc".to_string(),
22597 "JP".to_string(),
22598 "US".to_string(),
22599 );
22600
22601 let milestone = Milestone {
22602 id: "milestone-1".to_string(),
22603 name: "Complete Draft".to_string(),
22604 description: "Complete initial draft".to_string(),
22605 target_date: "2025-12-31T00:00:00Z".to_string(),
22606 completed: false,
22607 completed_date: None,
22608 dependencies: Vec::new(),
22609 };
22610
22611 manager.add_milestone(&project.id, milestone);
22612 manager.complete_milestone(&project.id, "milestone-1");
22613
22614 let updated = manager.get_project(&project.id).unwrap();
22615 assert!(updated.timeline.milestones[0].completed);
22616 assert!(updated.timeline.milestones[0].completed_date.is_some());
22617 }
22618
22619 #[test]
22620 fn test_list_projects_by_status() {
22621 let mut manager = PortingProjectManager::new();
22622
22623 manager.create_project(
22624 "P1".to_string(),
22625 "D1".to_string(),
22626 "JP".to_string(),
22627 "US".to_string(),
22628 );
22629 let p2 = manager.create_project(
22630 "P2".to_string(),
22631 "D2".to_string(),
22632 "JP".to_string(),
22633 "US".to_string(),
22634 );
22635 manager.update_status(&p2.id, ProjectStatus::InProgress);
22636
22637 let in_progress = manager.list_projects_by_status(ProjectStatus::InProgress);
22638 assert_eq!(in_progress.len(), 1);
22639
22640 let planning = manager.list_projects_by_status(ProjectStatus::Planning);
22641 assert_eq!(planning.len(), 1);
22642 }
22643
22644 #[test]
22645 fn test_review_workflow_creation() {
22646 let mut workflow = StakeholderReviewWorkflow::new();
22647
22648 let step = ReviewWorkflowStep {
22649 id: "step-1".to_string(),
22650 name: "Legal Review".to_string(),
22651 order: 1,
22652 required_reviewers: vec!["reviewer-1".to_string()],
22653 optional_reviewers: Vec::new(),
22654 min_approvals: 1,
22655 status: ReviewStepStatus::Pending,
22656 reviews: Vec::new(),
22657 };
22658
22659 workflow.create_workflow("project-1".to_string(), vec![step]);
22660
22661 let status = workflow.get_workflow_status("project-1");
22662 assert!(status.is_some());
22663 assert_eq!(status.unwrap().len(), 1);
22664 }
22665
22666 #[test]
22667 fn test_submit_review() {
22668 let mut workflow = StakeholderReviewWorkflow::new();
22669
22670 let step = ReviewWorkflowStep {
22671 id: "step-1".to_string(),
22672 name: "Legal Review".to_string(),
22673 order: 1,
22674 required_reviewers: vec!["reviewer-1".to_string()],
22675 optional_reviewers: Vec::new(),
22676 min_approvals: 1,
22677 status: ReviewStepStatus::Pending,
22678 reviews: Vec::new(),
22679 };
22680
22681 workflow.create_workflow("project-1".to_string(), vec![step]);
22682
22683 let review = WorkflowReview {
22684 id: "review-1".to_string(),
22685 reviewer_id: "reviewer-1".to_string(),
22686 decision: ReviewDecision::Approve,
22687 comments: "Looks good".to_string(),
22688 reviewed_at: chrono::Utc::now().to_rfc3339(),
22689 recommended_changes: Vec::new(),
22690 };
22691
22692 workflow.submit_review("project-1", "step-1", review);
22693
22694 let status = workflow.get_workflow_status("project-1").unwrap();
22695 assert_eq!(status[0].reviews.len(), 1);
22696 assert_eq!(status[0].status, ReviewStepStatus::Approved);
22697 }
22698
22699 #[test]
22700 fn test_version_control_iteration() {
22701 let mut vc = PortingVersionControl::new();
22702
22703 let iteration = vc.create_iteration(
22704 "project-1".to_string(),
22705 "statute snapshot v1".to_string(),
22706 "user-1".to_string(),
22707 "Initial version".to_string(),
22708 );
22709
22710 assert_eq!(iteration.iteration_number, 1);
22711 assert_eq!(iteration.statute_snapshot, "statute snapshot v1");
22712 assert_eq!(iteration.project_id, "project-1");
22713 }
22714
22715 #[test]
22716 fn test_multiple_iterations() {
22717 let mut vc = PortingVersionControl::new();
22718
22719 vc.create_iteration(
22720 "project-1".to_string(),
22721 "v1".to_string(),
22722 "user-1".to_string(),
22723 "First".to_string(),
22724 );
22725
22726 vc.create_iteration(
22727 "project-1".to_string(),
22728 "v2".to_string(),
22729 "user-1".to_string(),
22730 "Second".to_string(),
22731 );
22732
22733 let iterations = vc.get_iterations("project-1").unwrap();
22734 assert_eq!(iterations.len(), 2);
22735 assert_eq!(iterations[0].iteration_number, 1);
22736 assert_eq!(iterations[1].iteration_number, 2);
22737 }
22738
22739 #[test]
22740 fn test_get_specific_iteration() {
22741 let mut vc = PortingVersionControl::new();
22742
22743 vc.create_iteration(
22744 "project-1".to_string(),
22745 "v1".to_string(),
22746 "user-1".to_string(),
22747 "First".to_string(),
22748 );
22749
22750 vc.create_iteration(
22751 "project-1".to_string(),
22752 "v2".to_string(),
22753 "user-1".to_string(),
22754 "Second".to_string(),
22755 );
22756
22757 let iteration = vc.get_iteration("project-1", 2).unwrap();
22758 assert_eq!(iteration.statute_snapshot, "v2");
22759 }
22760
22761 #[test]
22762 fn test_revert_iteration() {
22763 let mut vc = PortingVersionControl::new();
22764
22765 vc.create_iteration(
22766 "project-1".to_string(),
22767 "v1".to_string(),
22768 "user-1".to_string(),
22769 "First".to_string(),
22770 );
22771
22772 vc.create_iteration(
22773 "project-1".to_string(),
22774 "v2".to_string(),
22775 "user-1".to_string(),
22776 "Second".to_string(),
22777 );
22778
22779 let reverted = vc.revert_to_iteration("project-1", 1, "user-2".to_string());
22780 assert!(reverted.is_some());
22781
22782 let iterations = vc.get_iterations("project-1").unwrap();
22783 assert_eq!(iterations.len(), 3);
22784 assert_eq!(iterations[2].statute_snapshot, "v1");
22785 }
22786
22787 #[test]
22788 fn test_create_branch() {
22789 let mut vc = PortingVersionControl::new();
22790
22791 vc.create_iteration(
22793 "project-1".to_string(),
22794 "v1".to_string(),
22795 "user-1".to_string(),
22796 "Version 1".to_string(),
22797 );
22798
22799 let branch = vc
22801 .create_branch(
22802 "project-1".to_string(),
22803 "feature-x".to_string(),
22804 1,
22805 "user-1".to_string(),
22806 "Working on feature X".to_string(),
22807 )
22808 .unwrap();
22809
22810 assert_eq!(branch.branch, Some("feature-x".to_string()));
22811 assert_eq!(branch.statute_snapshot, "v1");
22812 assert!(branch.tags.contains(&"branch".to_string()));
22813
22814 let branches = vc.get_branches("project-1");
22816 assert_eq!(branches.len(), 1);
22817 assert!(branches.contains(&"feature-x".to_string()));
22818 }
22819
22820 #[test]
22821 fn test_branch_iterations() {
22822 let mut vc = PortingVersionControl::new();
22823
22824 vc.create_iteration(
22825 "project-1".to_string(),
22826 "v1".to_string(),
22827 "user-1".to_string(),
22828 "Version 1".to_string(),
22829 );
22830
22831 vc.create_branch(
22832 "project-1".to_string(),
22833 "feature-a".to_string(),
22834 1,
22835 "user-1".to_string(),
22836 "Branch A".to_string(),
22837 );
22838
22839 vc.create_branch(
22840 "project-1".to_string(),
22841 "feature-b".to_string(),
22842 1,
22843 "user-1".to_string(),
22844 "Branch B".to_string(),
22845 );
22846
22847 let branch_a_iterations = vc.get_branch_iterations("project-1", "feature-a");
22848 assert_eq!(branch_a_iterations.len(), 1);
22849 assert_eq!(branch_a_iterations[0].branch, Some("feature-a".to_string()));
22850
22851 let branches = vc.get_branches("project-1");
22852 assert_eq!(branches.len(), 2);
22853 }
22854
22855 #[test]
22856 fn test_merge_branch() {
22857 let mut vc = PortingVersionControl::new();
22858
22859 vc.create_iteration(
22860 "project-1".to_string(),
22861 "v1".to_string(),
22862 "user-1".to_string(),
22863 "Version 1".to_string(),
22864 );
22865
22866 vc.create_branch(
22867 "project-1".to_string(),
22868 "feature-x".to_string(),
22869 1,
22870 "user-1".to_string(),
22871 "Feature X".to_string(),
22872 );
22873
22874 let merged = vc
22875 .merge_branch(
22876 "project-1".to_string(),
22877 "feature-x".to_string(),
22878 None,
22879 "user-1".to_string(),
22880 "Merged feature X".to_string(),
22881 )
22882 .unwrap();
22883
22884 assert_eq!(merged.branch, None); assert!(merged.notes.contains("Merged feature-x"));
22886 assert!(merged.tags.contains(&"merge".to_string()));
22887 }
22888
22889 #[test]
22890 fn test_generate_changelog() {
22891 let mut vc = PortingVersionControl::new();
22892
22893 vc.create_iteration(
22894 "project-1".to_string(),
22895 "v1".to_string(),
22896 "user-1".to_string(),
22897 "Initial version".to_string(),
22898 );
22899
22900 vc.create_iteration(
22901 "project-1".to_string(),
22902 "v2".to_string(),
22903 "user-2".to_string(),
22904 "Updated statute".to_string(),
22905 );
22906
22907 let changelog = vc.generate_changelog("project-1").unwrap();
22908
22909 assert_eq!(changelog.project_id, "project-1");
22910 assert_eq!(changelog.total_iterations, 2);
22911 assert_eq!(changelog.entries.len(), 2);
22912 assert_eq!(changelog.entries[0].iteration_number, 1);
22913 assert_eq!(changelog.entries[1].iteration_number, 2);
22914 }
22915
22916 #[test]
22917 fn test_changelog_export_markdown() {
22918 let mut vc = PortingVersionControl::new();
22919
22920 vc.create_iteration(
22921 "project-1".to_string(),
22922 "v1".to_string(),
22923 "user-1".to_string(),
22924 "Initial version".to_string(),
22925 );
22926
22927 let changelog = vc.generate_changelog("project-1").unwrap();
22928 let markdown = changelog.to_markdown();
22929
22930 assert!(markdown.contains("# Porting Changelog"));
22931 assert!(markdown.contains("project-1"));
22932 assert!(markdown.contains("## Iteration 1"));
22933 assert!(markdown.contains("user-1"));
22934 }
22935
22936 #[test]
22937 fn test_changelog_export_json() {
22938 let mut vc = PortingVersionControl::new();
22939
22940 vc.create_iteration(
22941 "project-1".to_string(),
22942 "v1".to_string(),
22943 "user-1".to_string(),
22944 "Initial version".to_string(),
22945 );
22946
22947 let changelog = vc.generate_changelog("project-1").unwrap();
22948 let json = changelog.to_json().unwrap();
22949
22950 assert!(json.contains("project-1"));
22951 assert!(json.contains("user-1"));
22952 }
22953
22954 #[test]
22955 fn test_approval_chain_creation() {
22956 let mut manager = ApprovalChainManager::new();
22957
22958 let step = ApprovalStep {
22959 id: "step-1".to_string(),
22960 name: "Manager Approval".to_string(),
22961 order: 1,
22962 approvers: vec!["manager-1".to_string()],
22963 approval_mode: ApprovalMode::Any,
22964 status: ApprovalStepStatus::Pending,
22965 approvals: Vec::new(),
22966 auto_approve_after: None,
22967 };
22968
22969 let chain = manager.create_chain("Test Chain".to_string(), vec![step]);
22970
22971 assert!(!chain.id.is_empty());
22972 assert_eq!(chain.name, "Test Chain");
22973 assert_eq!(chain.status, ApprovalChainStatus::NotStarted);
22974 assert_eq!(chain.steps.len(), 1);
22975 }
22976
22977 #[test]
22978 fn test_submit_approval() {
22979 let mut manager = ApprovalChainManager::new();
22980
22981 let step = ApprovalStep {
22982 id: "step-1".to_string(),
22983 name: "Manager Approval".to_string(),
22984 order: 1,
22985 approvers: vec!["manager-1".to_string()],
22986 approval_mode: ApprovalMode::Any,
22987 status: ApprovalStepStatus::Pending,
22988 approvals: Vec::new(),
22989 auto_approve_after: None,
22990 };
22991
22992 let chain = manager.create_chain("Test Chain".to_string(), vec![step]);
22993
22994 let approval = ApprovalRecord {
22995 id: "approval-1".to_string(),
22996 approver_id: "manager-1".to_string(),
22997 approved: true,
22998 comments: "Approved".to_string(),
22999 approved_at: chrono::Utc::now().to_rfc3339(),
23000 };
23001
23002 manager.submit_approval(&chain.id, "step-1", approval);
23003
23004 let updated = manager.get_chain(&chain.id).unwrap();
23005 assert_eq!(updated.steps[0].approvals.len(), 1);
23006 assert_eq!(updated.steps[0].status, ApprovalStepStatus::Approved);
23007 }
23008
23009 #[test]
23010 fn test_approval_mode_all() {
23011 let mut manager = ApprovalChainManager::new();
23012
23013 let step = ApprovalStep {
23014 id: "step-1".to_string(),
23015 name: "Multi Approval".to_string(),
23016 order: 1,
23017 approvers: vec!["approver-1".to_string(), "approver-2".to_string()],
23018 approval_mode: ApprovalMode::All,
23019 status: ApprovalStepStatus::Pending,
23020 approvals: Vec::new(),
23021 auto_approve_after: None,
23022 };
23023
23024 let chain = manager.create_chain("Test Chain".to_string(), vec![step]);
23025
23026 let approval1 = ApprovalRecord {
23027 id: "approval-1".to_string(),
23028 approver_id: "approver-1".to_string(),
23029 approved: true,
23030 comments: "OK".to_string(),
23031 approved_at: chrono::Utc::now().to_rfc3339(),
23032 };
23033
23034 manager.submit_approval(&chain.id, "step-1", approval1);
23035 let updated = manager.get_chain(&chain.id).unwrap();
23036 assert_eq!(updated.steps[0].status, ApprovalStepStatus::Pending);
23037
23038 let approval2 = ApprovalRecord {
23039 id: "approval-2".to_string(),
23040 approver_id: "approver-2".to_string(),
23041 approved: true,
23042 comments: "OK".to_string(),
23043 approved_at: chrono::Utc::now().to_rfc3339(),
23044 };
23045
23046 manager.submit_approval(&chain.id, "step-1", approval2);
23047 let updated = manager.get_chain(&chain.id).unwrap();
23048 assert_eq!(updated.steps[0].status, ApprovalStepStatus::Approved);
23049 }
23050
23051 #[test]
23052 fn test_notification_manager() {
23053 let mut manager = NotificationManager::new();
23054
23055 let notification = Notification {
23056 id: "notif-1".to_string(),
23057 recipient_id: "user-1".to_string(),
23058 notification_type: NotificationType::StatusChange,
23059 title: "Status Changed".to_string(),
23060 message: "Project status changed to InProgress".to_string(),
23061 project_id: Some("project-1".to_string()),
23062 priority: NotificationPriority::Normal,
23063 created_at: chrono::Utc::now().to_rfc3339(),
23064 read: false,
23065 channels: vec![NotificationChannel::Email],
23066 };
23067
23068 manager.send_notification(notification);
23069
23070 let notifications = manager.get_notifications("user-1");
23071 assert_eq!(notifications.len(), 1);
23072 assert_eq!(notifications[0].title, "Status Changed");
23073 assert!(!notifications[0].read);
23074 }
23075
23076 #[test]
23077 fn test_mark_notification_as_read() {
23078 let mut manager = NotificationManager::new();
23079
23080 let notification = Notification {
23081 id: "notif-1".to_string(),
23082 recipient_id: "user-1".to_string(),
23083 notification_type: NotificationType::StatusChange,
23084 title: "Test".to_string(),
23085 message: "Test message".to_string(),
23086 project_id: None,
23087 priority: NotificationPriority::Normal,
23088 created_at: chrono::Utc::now().to_rfc3339(),
23089 read: false,
23090 channels: vec![NotificationChannel::Email],
23091 };
23092
23093 manager.send_notification(notification);
23094 manager.mark_as_read("user-1", "notif-1");
23095
23096 let notifications = manager.get_notifications("user-1");
23097 assert!(notifications[0].read);
23098 }
23099
23100 #[test]
23101 fn test_deadline_tracker() {
23102 let mut manager = NotificationManager::new();
23103
23104 let deadline = DeadlineTracker {
23105 id: "deadline-1".to_string(),
23106 project_id: "project-1".to_string(),
23107 name: "Final Review".to_string(),
23108 deadline: "2026-01-15T00:00:00Z".to_string(),
23109 warning_days: 7,
23110 status: DeadlineStatus::OnTrack,
23111 assigned_to: vec!["user-1".to_string()],
23112 };
23113
23114 manager.add_deadline(deadline);
23115
23116 let deadlines = manager.get_deadlines("project-1");
23117 assert_eq!(deadlines.len(), 1);
23118 assert_eq!(deadlines[0].name, "Final Review");
23119 }
23120
23121 #[test]
23122 fn test_check_approaching_deadlines() {
23123 let mut manager = NotificationManager::new();
23124
23125 let now = chrono::Utc::now();
23126 let deadline_date = now + chrono::Duration::days(5);
23127
23128 let deadline = DeadlineTracker {
23129 id: "deadline-1".to_string(),
23130 project_id: "project-1".to_string(),
23131 name: "Urgent Deadline".to_string(),
23132 deadline: deadline_date.to_rfc3339(),
23133 warning_days: 7,
23134 status: DeadlineStatus::Approaching,
23135 assigned_to: vec!["user-1".to_string()],
23136 };
23137
23138 manager.add_deadline(deadline);
23139
23140 let notifications = manager.check_deadlines();
23141 assert!(!notifications.is_empty());
23142 assert_eq!(
23143 notifications[0].notification_type,
23144 NotificationType::DeadlineApproaching
23145 );
23146 }
23147
23148 #[test]
23149 fn test_project_status_enum() {
23150 assert!(matches!(ProjectStatus::Planning, ProjectStatus::Planning));
23151 assert!(matches!(
23152 ProjectStatus::InProgress,
23153 ProjectStatus::InProgress
23154 ));
23155 assert!(matches!(ProjectStatus::Completed, ProjectStatus::Completed));
23156 }
23157
23158 #[test]
23159 fn test_stakeholder_roles() {
23160 let role = StakeholderRole::LegalExpert;
23161 assert_eq!(role, StakeholderRole::LegalExpert);
23162
23163 let roles = [
23164 StakeholderRole::ProjectManager,
23165 StakeholderRole::LegalExpert,
23166 StakeholderRole::TechnicalReviewer,
23167 StakeholderRole::Approver,
23168 StakeholderRole::Observer,
23169 StakeholderRole::Contributor,
23170 ];
23171
23172 assert_eq!(roles.len(), 6);
23173 }
23174
23175 #[test]
23176 fn test_notification_channels() {
23177 let channels = [
23178 NotificationChannel::Email,
23179 NotificationChannel::InApp,
23180 NotificationChannel::Sms,
23181 NotificationChannel::Webhook,
23182 ];
23183
23184 assert_eq!(channels.len(), 4);
23185 }
23186
23187 #[test]
23188 fn test_iteration_change_types() {
23189 assert!(matches!(
23190 IterationChangeType::Addition,
23191 IterationChangeType::Addition
23192 ));
23193 assert!(matches!(
23194 IterationChangeType::Modification,
23195 IterationChangeType::Modification
23196 ));
23197 assert!(matches!(
23198 IterationChangeType::Deletion,
23199 IterationChangeType::Deletion
23200 ));
23201 assert!(matches!(
23202 IterationChangeType::Restructure,
23203 IterationChangeType::Restructure
23204 ));
23205 }
23206
23207 #[test]
23212 fn test_executive_summary_generator() {
23213 let generator = ExecutiveSummaryGenerator::new();
23214 let project = create_test_project();
23215 let statutes = create_test_ported_statutes(3);
23216
23217 let summary = generator.generate(&project, &statutes);
23218
23219 assert_eq!(summary.project_id, project.id);
23220 assert_eq!(summary.statutes_count, 3);
23221 assert!(summary.compatibility_score >= 0.0 && summary.compatibility_score <= 1.0);
23222 assert!(!summary.key_findings.is_empty());
23223 assert!(!summary.recommendations.is_empty());
23224 assert!(!summary.stakeholders.is_empty());
23225 }
23226
23227 #[test]
23228 fn test_executive_summary_risk_levels() {
23229 let generator = ExecutiveSummaryGenerator::new();
23230 let project = create_test_project();
23231
23232 let high_compat_statutes = vec![create_test_ported_statute_with_score(0.9)];
23234 let summary = generator.generate(&project, &high_compat_statutes);
23235 assert_eq!(summary.risk_level, RiskLevel::Low);
23236
23237 let low_compat_statutes = vec![create_test_ported_statute_with_score(0.3)];
23239 let summary = generator.generate(&project, &low_compat_statutes);
23240 assert_eq!(summary.risk_level, RiskLevel::High);
23241 }
23242
23243 #[test]
23244 fn test_risk_assessment_report_generator() {
23245 let generator = RiskAssessmentReportGenerator::new();
23246 let project = create_test_project();
23247 let risk_assessments = vec![create_test_risk_assessment()];
23248
23249 let report = generator.generate(&project, &risk_assessments);
23250
23251 assert_eq!(report.project_id, project.id);
23252 assert!(report.overall_risk_score >= 0.0 && report.overall_risk_score <= 1.0);
23253 assert!(!report.risks_by_category.is_empty());
23254 assert!(!report.mitigation_strategies.is_empty());
23255 }
23256
23257 #[test]
23258 fn test_risk_matrix_categorization() {
23259 let generator = RiskAssessmentReportGenerator::new();
23260 let _project = create_test_project();
23261
23262 let mut risks_by_category: HashMap<RiskCategory, Vec<Risk>> = HashMap::new();
23263 risks_by_category.insert(
23264 RiskCategory::Legal,
23265 vec![
23266 Risk {
23267 id: "risk-1".to_string(),
23268 category: RiskCategory::Legal,
23269 description: "High-high risk".to_string(),
23270 likelihood: RiskLevel::High,
23271 impact: 0.9,
23272 severity: RiskLevel::High,
23273 },
23274 Risk {
23275 id: "risk-2".to_string(),
23276 category: RiskCategory::Legal,
23277 description: "Low-low risk".to_string(),
23278 likelihood: RiskLevel::Low,
23279 impact: 0.2,
23280 severity: RiskLevel::Low,
23281 },
23282 ],
23283 );
23284
23285 let matrix = generator.build_risk_matrix(&risks_by_category);
23286
23287 assert!(!matrix.critical.is_empty());
23288 assert!(!matrix.low.is_empty());
23289 }
23290
23291 #[test]
23292 fn test_implementation_roadmap_generator() {
23293 let generator = ImplementationRoadmapGenerator::new();
23294 let project = create_test_project();
23295 let statutes = create_test_ported_statutes(5);
23296
23297 let roadmap = generator.generate(&project, &statutes);
23298
23299 assert_eq!(roadmap.project_id, project.id);
23300 assert_eq!(roadmap.phases.len(), 4); assert!(!roadmap.critical_path.is_empty());
23302 assert!(!roadmap.resource_requirements.personnel.is_empty());
23303 assert!(roadmap.estimated_duration_days > 0);
23304 }
23305
23306 #[test]
23307 fn test_implementation_phases_dependencies() {
23308 let generator = ImplementationRoadmapGenerator::new();
23309 let project = create_test_project();
23310 let statutes = create_test_ported_statutes(2);
23311
23312 let roadmap = generator.generate(&project, &statutes);
23313
23314 assert!(roadmap.phases[0].dependencies.is_empty());
23316
23317 assert!(!roadmap.phases[1].dependencies.is_empty());
23319 assert!(!roadmap.phases[2].dependencies.is_empty());
23320 assert!(!roadmap.phases[3].dependencies.is_empty());
23321 }
23322
23323 #[test]
23324 fn test_cost_benefit_analyzer() {
23325 let analyzer = CostBenefitAnalyzer::new();
23326 let project = create_test_project();
23327 let roadmap = ImplementationRoadmapGenerator::new()
23328 .generate(&project, &create_test_ported_statutes(3));
23329 let statutes = create_test_ported_statutes(3);
23330
23331 let analysis = analyzer.analyze(&project, &roadmap, &statutes);
23332
23333 assert_eq!(analysis.project_id, project.id);
23334 assert!(analysis.total_costs.total_five_year > 0.0);
23335 assert!(analysis.total_benefits.quantifiable_benefits >= 0.0);
23336 assert!(analysis.net_present_value.is_finite());
23337 assert!(!analysis.total_benefits.qualitative_benefits.is_empty());
23338 }
23339
23340 #[test]
23341 fn test_cost_benefit_recommendations() {
23342 let analyzer = CostBenefitAnalyzer::new();
23343 let project = create_test_project();
23344
23345 let high_compat_statutes = vec![
23347 create_test_ported_statute_with_score(0.95),
23348 create_test_ported_statute_with_score(0.92),
23349 create_test_ported_statute_with_score(0.90),
23350 ];
23351 let roadmap =
23352 ImplementationRoadmapGenerator::new().generate(&project, &high_compat_statutes);
23353 let analysis = analyzer.analyze(&project, &roadmap, &high_compat_statutes);
23354
23355 assert!(matches!(
23357 analysis.recommendation,
23358 CBARecommendation::StronglyRecommend | CBARecommendation::RecommendWithConditions
23359 ));
23360 }
23361
23362 #[test]
23363 fn test_compliance_certification_manager() {
23364 let mut manager = ComplianceCertificationManager::new();
23365 let project_id = "test-project".to_string();
23366 let validation_results = vec![create_test_validation_result(0.85)];
23367 let certifier = CertifierInfo {
23368 name: "John Doe".to_string(),
23369 organization: "Legal Standards Board".to_string(),
23370 credentials: vec!["Licensed Attorney".to_string()],
23371 contact: "john@example.com".to_string(),
23372 };
23373
23374 let cert = manager.issue_certification(project_id.clone(), validation_results, certifier);
23375
23376 assert_eq!(cert.project_id, project_id);
23377 assert_eq!(cert.certification_level, CertificationLevel::Enhanced);
23378 assert_eq!(cert.status, CertificationStatus::Certified);
23379 assert!(cert.signature.is_some());
23380 assert!(cert.expiration_date.is_some());
23381 }
23382
23383 #[test]
23384 fn test_certification_levels() {
23385 let mut manager = ComplianceCertificationManager::new();
23386 let certifier = CertifierInfo {
23387 name: "Jane Smith".to_string(),
23388 organization: "Compliance Authority".to_string(),
23389 credentials: vec!["Certified Auditor".to_string()],
23390 contact: "jane@example.com".to_string(),
23391 };
23392
23393 let full_cert = manager.issue_certification(
23395 "proj1".to_string(),
23396 vec![create_test_validation_result(0.96)],
23397 certifier.clone(),
23398 );
23399 assert_eq!(full_cert.certification_level, CertificationLevel::Full);
23400
23401 let enhanced_cert = manager.issue_certification(
23403 "proj2".to_string(),
23404 vec![create_test_validation_result(0.88)],
23405 certifier.clone(),
23406 );
23407 assert_eq!(
23408 enhanced_cert.certification_level,
23409 CertificationLevel::Enhanced
23410 );
23411
23412 let standard_cert = manager.issue_certification(
23414 "proj3".to_string(),
23415 vec![create_test_validation_result(0.78)],
23416 certifier.clone(),
23417 );
23418 assert_eq!(
23419 standard_cert.certification_level,
23420 CertificationLevel::Standard
23421 );
23422
23423 let provisional_cert = manager.issue_certification(
23425 "proj4".to_string(),
23426 vec![create_test_validation_result(0.65)],
23427 certifier,
23428 );
23429 assert_eq!(
23430 provisional_cert.certification_level,
23431 CertificationLevel::Provisional
23432 );
23433 }
23434
23435 #[test]
23436 fn test_certification_revocation() {
23437 let mut manager = ComplianceCertificationManager::new();
23438 let certifier = CertifierInfo {
23439 name: "Test Certifier".to_string(),
23440 organization: "Test Org".to_string(),
23441 credentials: vec!["Test Credential".to_string()],
23442 contact: "test@example.com".to_string(),
23443 };
23444
23445 let cert = manager.issue_certification(
23446 "test-proj".to_string(),
23447 vec![create_test_validation_result(0.85)],
23448 certifier,
23449 );
23450
23451 let cert_id = cert.id.clone();
23452
23453 assert!(manager.revoke_certification(&cert_id).is_some());
23455
23456 let revoked_cert = manager.get_certification(&cert_id).unwrap();
23458 assert_eq!(revoked_cert.status, CertificationStatus::Revoked);
23459 }
23460
23461 #[test]
23466 fn test_bilateral_agreement_template_library() {
23467 let library = BilateralAgreementTemplateLibrary::new();
23468
23469 let templates = library.list_templates();
23471 assert!(!templates.is_empty());
23472
23473 let template = library.get_template("general-bilateral").unwrap();
23475 assert_eq!(template.id, "general-bilateral");
23476 assert!(!template.sections.is_empty());
23477 assert!(!template.required_parameters.is_empty());
23478 }
23479
23480 #[test]
23481 fn test_template_agreement_generation() {
23482 let library = BilateralAgreementTemplateLibrary::new();
23483
23484 let mut parameters = HashMap::new();
23485 parameters.insert(
23486 "source_jurisdiction".to_string(),
23487 "United States".to_string(),
23488 );
23489 parameters.insert("target_jurisdiction".to_string(), "Japan".to_string());
23490 parameters.insert("purpose".to_string(), "legal cooperation".to_string());
23491
23492 let agreement = library.generate_agreement("general-bilateral", ¶meters);
23493
23494 assert!(agreement.is_some());
23495 let text = agreement.unwrap();
23496 assert!(text.contains("United States"));
23497 assert!(text.contains("Japan"));
23498 assert!(text.contains("legal cooperation"));
23499 }
23500
23501 #[test]
23502 fn test_add_custom_template() {
23503 let mut library = BilateralAgreementTemplateLibrary::new();
23504
23505 let custom_template = BilateralAgreementTemplate {
23506 id: "custom-test".to_string(),
23507 name: "Custom Test Template".to_string(),
23508 description: "A custom template for testing".to_string(),
23509 applicable_systems: vec![LegalSystem::CivilLaw],
23510 sections: vec![TemplateSection {
23511 section_number: 1,
23512 title: "Test Section".to_string(),
23513 content_template: "Test content for {{param1}}".to_string(),
23514 required: true,
23515 }],
23516 required_parameters: vec![TemplateParameter {
23517 name: "param1".to_string(),
23518 description: "Test parameter".to_string(),
23519 parameter_type: ParameterType::String,
23520 default_value: None,
23521 }],
23522 optional_parameters: vec![],
23523 };
23524
23525 library.add_template(custom_template);
23526 assert!(library.get_template("custom-test").is_some());
23527 }
23528
23529 #[test]
23530 fn test_regulatory_sandbox_manager() {
23531 let mut manager = RegulatorySandboxManager::new();
23532
23533 let sandbox = manager.create_sandbox(
23534 "Test Sandbox".to_string(),
23535 "Testing ported statutes".to_string(),
23536 vec!["statute-1".to_string(), "statute-2".to_string()],
23537 );
23538
23539 assert_eq!(sandbox.status, SandboxStatus::Planning);
23540 assert_eq!(sandbox.test_statutes.len(), 2);
23541 assert!(sandbox.scenarios.is_empty());
23542 assert!(sandbox.results.is_empty());
23543 }
23544
23545 #[test]
23546 fn test_sandbox_scenario_and_results() {
23547 let mut manager = RegulatorySandboxManager::new();
23548
23549 let sandbox = manager.create_sandbox(
23550 "Test Sandbox".to_string(),
23551 "Testing".to_string(),
23552 vec!["statute-1".to_string()],
23553 );
23554 let sandbox_id = sandbox.id.clone();
23555
23556 let scenario = TestScenario {
23558 id: "scenario-1".to_string(),
23559 name: "Basic Test".to_string(),
23560 description: "Test basic functionality".to_string(),
23561 parameters: HashMap::new(),
23562 expected_outcomes: vec!["Outcome 1".to_string()],
23563 };
23564 assert!(manager.add_scenario(&sandbox_id, scenario).is_some());
23565
23566 assert!(manager.activate_sandbox(&sandbox_id).is_some());
23568 let sandbox = manager.get_sandbox(&sandbox_id).unwrap();
23569 assert_eq!(sandbox.status, SandboxStatus::Active);
23570
23571 let result = SandboxTestResult {
23573 scenario_id: "scenario-1".to_string(),
23574 status: TestStatus::Passed,
23575 actual_outcomes: vec!["Outcome 1".to_string()],
23576 issues: vec![],
23577 recommendations: vec![],
23578 test_date: chrono::Utc::now().to_rfc3339(),
23579 };
23580 assert!(manager.record_result(&sandbox_id, result).is_some());
23581
23582 assert!(manager.complete_sandbox(&sandbox_id).is_some());
23584 let sandbox = manager.get_sandbox(&sandbox_id).unwrap();
23585 assert_eq!(sandbox.status, SandboxStatus::Completed);
23586 assert!(sandbox.end_date.is_some());
23587 }
23588
23589 #[test]
23590 fn test_affected_party_notification_manager() {
23591 let mut manager = AffectedPartyNotificationManager::new();
23592
23593 let notification = manager.send_notification(
23594 "proj-1".to_string(),
23595 "New Porting Initiative".to_string(),
23596 "We are porting statutes from jurisdiction A to B".to_string(),
23597 vec![
23598 AffectedPartyCategory::GeneralPublic,
23599 AffectedPartyCategory::LegalProfessionals,
23600 ],
23601 Some(30),
23602 );
23603
23604 assert_eq!(notification.project_id, "proj-1");
23605 assert_eq!(notification.affected_categories.len(), 2);
23606 assert!(notification.response_deadline.is_some());
23607 assert!(notification.channels.contains(&NotificationChannel::Email));
23608 }
23609
23610 #[test]
23611 fn test_notification_feedback() {
23612 let mut manager = AffectedPartyNotificationManager::new();
23613
23614 let notification = manager.send_notification(
23615 "proj-1".to_string(),
23616 "Test".to_string(),
23617 "Content".to_string(),
23618 vec![AffectedPartyCategory::GeneralPublic],
23619 None,
23620 );
23621 let notif_id = notification.id.clone();
23622
23623 let feedback = PublicFeedback {
23625 id: uuid::Uuid::new_v4().to_string(),
23626 submitter: Some("John Citizen".to_string()),
23627 category: FeedbackCategory::Support,
23628 content: "I support this initiative".to_string(),
23629 submitted_at: chrono::Utc::now().to_rfc3339(),
23630 };
23631
23632 assert!(manager.record_feedback(¬if_id, feedback).is_some());
23633
23634 let feedback_list = manager.list_feedback(¬if_id).unwrap();
23635 assert_eq!(feedback_list.len(), 1);
23636 }
23637
23638 #[test]
23639 fn test_public_comment_period_manager() {
23640 let mut manager = PublicCommentPeriodManager::new();
23641
23642 let period = manager.open_comment_period(
23643 "proj-1".to_string(),
23644 "Public Comment Period".to_string(),
23645 "Comments on proposed statute porting".to_string(),
23646 60,
23647 );
23648
23649 assert_eq!(period.status, CommentPeriodStatus::Open);
23650 assert_eq!(period.project_id, "proj-1");
23651 assert!(period.comments.is_empty());
23652 assert!(period.documents.is_empty());
23653 }
23654
23655 #[test]
23656 fn test_comment_period_document_management() {
23657 let mut manager = PublicCommentPeriodManager::new();
23658
23659 let period = manager.open_comment_period(
23660 "proj-1".to_string(),
23661 "Test Period".to_string(),
23662 "Description".to_string(),
23663 30,
23664 );
23665 let period_id = period.id.clone();
23666
23667 let document = CommentDocument {
23669 id: "doc-1".to_string(),
23670 title: "Draft Statute".to_string(),
23671 document_type: DocumentType::DraftStatute,
23672 description: "Draft version for review".to_string(),
23673 url: "https://example.com/draft.pdf".to_string(),
23674 };
23675
23676 assert!(manager.add_document(&period_id, document).is_some());
23677
23678 let period = manager.get_period(&period_id).unwrap();
23679 assert_eq!(period.documents.len(), 1);
23680 }
23681
23682 #[test]
23683 fn test_comment_submission() {
23684 let mut manager = PublicCommentPeriodManager::new();
23685
23686 let period = manager.open_comment_period(
23687 "proj-1".to_string(),
23688 "Test Period".to_string(),
23689 "Description".to_string(),
23690 30,
23691 );
23692 let period_id = period.id.clone();
23693
23694 let comment = PublicComment {
23696 id: uuid::Uuid::new_v4().to_string(),
23697 commenter: CommenterInfo {
23698 name: Some("Jane Doe".to_string()),
23699 organization: Some("Citizens Alliance".to_string()),
23700 email: Some("jane@example.com".to_string()),
23701 affiliation: AffectedPartyCategory::GeneralPublic,
23702 },
23703 comment_text: "I have concerns about section 3".to_string(),
23704 document_id: None,
23705 section_reference: Some("Section 3".to_string()),
23706 submitted_at: chrono::Utc::now().to_rfc3339(),
23707 category: FeedbackCategory::Concern,
23708 };
23709
23710 assert!(manager.submit_comment(&period_id, comment).is_some());
23711
23712 let comments = manager.list_comments(&period_id).unwrap();
23713 assert_eq!(comments.len(), 1);
23714 }
23715
23716 #[test]
23717 fn test_comment_period_extension() {
23718 let mut manager = PublicCommentPeriodManager::new();
23719
23720 let period = manager.open_comment_period(
23721 "proj-1".to_string(),
23722 "Test Period".to_string(),
23723 "Description".to_string(),
23724 30,
23725 );
23726 let period_id = period.id.clone();
23727 let original_end = period.end_date.clone();
23728
23729 assert!(manager.extend_period(&period_id, 15).is_some());
23731
23732 let period = manager.get_period(&period_id).unwrap();
23733 assert_eq!(period.status, CommentPeriodStatus::Extended);
23734 assert_ne!(period.end_date, original_end);
23735 }
23736
23737 #[test]
23738 fn test_comment_period_closure() {
23739 let mut manager = PublicCommentPeriodManager::new();
23740
23741 let period = manager.open_comment_period(
23742 "proj-1".to_string(),
23743 "Test Period".to_string(),
23744 "Description".to_string(),
23745 30,
23746 );
23747 let period_id = period.id.clone();
23748
23749 assert!(manager.close_period(&period_id).is_some());
23751
23752 let period = manager.get_period(&period_id).unwrap();
23753 assert_eq!(period.status, CommentPeriodStatus::Closed);
23754
23755 let comment = PublicComment {
23757 id: uuid::Uuid::new_v4().to_string(),
23758 commenter: CommenterInfo {
23759 name: None,
23760 organization: None,
23761 email: None,
23762 affiliation: AffectedPartyCategory::GeneralPublic,
23763 },
23764 comment_text: "Late comment".to_string(),
23765 document_id: None,
23766 section_reference: None,
23767 submitted_at: chrono::Utc::now().to_rfc3339(),
23768 category: FeedbackCategory::Question,
23769 };
23770
23771 assert!(manager.submit_comment(&period_id, comment).is_none());
23772 }
23773
23774 #[test]
23775 fn test_comment_summary_generation() {
23776 let mut manager = PublicCommentPeriodManager::new();
23777
23778 let period = manager.open_comment_period(
23779 "proj-1".to_string(),
23780 "Test Period".to_string(),
23781 "Description".to_string(),
23782 30,
23783 );
23784 let period_id = period.id.clone();
23785
23786 for i in 0..5 {
23788 let comment = PublicComment {
23789 id: format!("comment-{}", i),
23790 commenter: CommenterInfo {
23791 name: Some(format!("Commenter {}", i)),
23792 organization: None,
23793 email: None,
23794 affiliation: if i % 2 == 0 {
23795 AffectedPartyCategory::GeneralPublic
23796 } else {
23797 AffectedPartyCategory::Businesses
23798 },
23799 },
23800 comment_text: format!("Comment {}", i),
23801 document_id: None,
23802 section_reference: None,
23803 submitted_at: chrono::Utc::now().to_rfc3339(),
23804 category: if i % 2 == 0 {
23805 FeedbackCategory::Support
23806 } else {
23807 FeedbackCategory::Concern
23808 },
23809 };
23810 manager.submit_comment(&period_id, comment).unwrap();
23811 }
23812
23813 let summary = manager.generate_comment_summary(&period_id).unwrap();
23814
23815 assert_eq!(summary.total_comments, 5);
23816 assert!(!summary.category_breakdown.is_empty());
23817 assert!(!summary.affiliation_breakdown.is_empty());
23818 assert!(!summary.key_themes.is_empty());
23819 }
23820
23821 #[test]
23822 fn test_discussion_thread() {
23823 let mut manager = DiscussionThreadManager::new();
23824
23825 let thread = manager.create_thread(
23826 "project-1".to_string(),
23827 "Section 5 Discussion".to_string(),
23828 "Discuss changes to section 5".to_string(),
23829 "user-1".to_string(),
23830 vec!["section-5".to_string()],
23831 );
23832
23833 assert!(!thread.id.is_empty());
23834 assert_eq!(thread.status, ThreadStatus::Open);
23835 assert_eq!(thread.project_id, "project-1");
23836 }
23837
23838 #[test]
23839 fn test_discussion_thread_comments() {
23840 let mut manager = DiscussionThreadManager::new();
23841
23842 let thread = manager.create_thread(
23843 "project-1".to_string(),
23844 "Test Thread".to_string(),
23845 "Context".to_string(),
23846 "user-1".to_string(),
23847 vec![],
23848 );
23849
23850 let comment1 = manager
23851 .add_comment(
23852 &thread.id,
23853 "user-1".to_string(),
23854 "First comment".to_string(),
23855 None,
23856 )
23857 .unwrap();
23858
23859 let _reply = manager
23860 .add_comment(
23861 &thread.id,
23862 "user-2".to_string(),
23863 "Reply to first".to_string(),
23864 Some(comment1.id.clone()),
23865 )
23866 .unwrap();
23867
23868 let thread_after = manager.get_thread(&thread.id).unwrap();
23869 assert_eq!(thread_after.comments.len(), 1);
23870 assert_eq!(thread_after.comments[0].replies.len(), 1);
23871 }
23872
23873 #[test]
23874 fn test_upvote_comment() {
23875 let mut manager = DiscussionThreadManager::new();
23876
23877 let thread = manager.create_thread(
23878 "project-1".to_string(),
23879 "Test".to_string(),
23880 "Context".to_string(),
23881 "user-1".to_string(),
23882 vec![],
23883 );
23884
23885 let comment = manager
23886 .add_comment(
23887 &thread.id,
23888 "user-1".to_string(),
23889 "Comment".to_string(),
23890 None,
23891 )
23892 .unwrap();
23893
23894 manager
23895 .upvote_comment(&thread.id, &comment.id, "user-2".to_string())
23896 .unwrap();
23897
23898 let thread_after = manager.get_thread(&thread.id).unwrap();
23899 assert_eq!(thread_after.comments[0].upvotes, 1);
23900 }
23901
23902 #[test]
23903 fn test_resolve_thread() {
23904 let mut manager = DiscussionThreadManager::new();
23905
23906 let thread = manager.create_thread(
23907 "project-1".to_string(),
23908 "Test".to_string(),
23909 "Context".to_string(),
23910 "user-1".to_string(),
23911 vec![],
23912 );
23913
23914 manager
23915 .resolve_thread(&thread.id, "user-1".to_string())
23916 .unwrap();
23917
23918 let thread_after = manager.get_thread(&thread.id).unwrap();
23919 assert_eq!(thread_after.status, ThreadStatus::Resolved);
23920 assert_eq!(thread_after.resolved_by, Some("user-1".to_string()));
23921 }
23922
23923 #[test]
23924 fn test_voting_creation() {
23925 let mut manager = VotingManager::new();
23926
23927 let options = vec![
23928 VoteOption {
23929 id: "opt-1".to_string(),
23930 text: "Option 1".to_string(),
23931 description: "First option".to_string(),
23932 vote_count: 0,
23933 },
23934 VoteOption {
23935 id: "opt-2".to_string(),
23936 text: "Option 2".to_string(),
23937 description: "Second option".to_string(),
23938 vote_count: 0,
23939 },
23940 ];
23941
23942 let vote = manager.create_vote(
23943 "project-1".to_string(),
23944 "Test Vote".to_string(),
23945 "Vote on approach".to_string(),
23946 VoteType::SingleChoice,
23947 options,
23948 vec!["user-1".to_string(), "user-2".to_string()],
23949 24,
23950 );
23951
23952 assert!(!vote.id.is_empty());
23953 assert_eq!(vote.status, VoteStatus::Active);
23954 }
23955
23956 #[test]
23957 fn test_cast_vote() {
23958 let mut manager = VotingManager::new();
23959
23960 let options = vec![VoteOption {
23961 id: "opt-1".to_string(),
23962 text: "Option 1".to_string(),
23963 description: "First option".to_string(),
23964 vote_count: 0,
23965 }];
23966
23967 let vote = manager.create_vote(
23968 "project-1".to_string(),
23969 "Test".to_string(),
23970 "Description".to_string(),
23971 VoteType::SingleChoice,
23972 options,
23973 vec!["user-1".to_string()],
23974 24,
23975 );
23976
23977 manager
23978 .cast_vote(&vote.id, "user-1".to_string(), vec!["opt-1".to_string()])
23979 .unwrap();
23980
23981 let vote_after = manager.get_vote(&vote.id).unwrap();
23982 assert_eq!(vote_after.votes_cast.len(), 1);
23983 }
23984
23985 #[test]
23986 fn test_close_vote() {
23987 let mut manager = VotingManager::new();
23988
23989 let options = vec![
23990 VoteOption {
23991 id: "opt-1".to_string(),
23992 text: "Option 1".to_string(),
23993 description: "First".to_string(),
23994 vote_count: 0,
23995 },
23996 VoteOption {
23997 id: "opt-2".to_string(),
23998 text: "Option 2".to_string(),
23999 description: "Second".to_string(),
24000 vote_count: 0,
24001 },
24002 ];
24003
24004 let vote = manager.create_vote(
24005 "project-1".to_string(),
24006 "Test".to_string(),
24007 "Desc".to_string(),
24008 VoteType::SingleChoice,
24009 options,
24010 vec!["user-1".to_string(), "user-2".to_string()],
24011 24,
24012 );
24013
24014 manager
24015 .cast_vote(&vote.id, "user-1".to_string(), vec!["opt-1".to_string()])
24016 .unwrap();
24017
24018 let result = manager.close_vote(&vote.id).unwrap();
24019
24020 assert_eq!(result.total_eligible, 2);
24021 assert_eq!(result.total_votes, 1);
24022 assert_eq!(result.participation_rate, 0.5);
24023 }
24024
24025 #[test]
24026 fn test_stakeholder_impact_tracker() {
24027 let mut tracker = StakeholderImpactTracker::new();
24028
24029 let impact = tracker.record_impact(
24030 "project-1".to_string(),
24031 "stakeholder-1".to_string(),
24032 StakeholderImpactLevel::High,
24033 StakeholderImpactCategory::Economic,
24034 "Significant cost increase".to_string(),
24035 0.8,
24036 ImpactTimeframe::ShortTerm,
24037 vec!["Budget allocation".to_string()],
24038 );
24039
24040 assert!(!impact.id.is_empty());
24041 assert_eq!(impact.impact_level, StakeholderImpactLevel::High);
24042 assert!(!impact.notification_sent);
24043 }
24044
24045 #[test]
24046 fn test_stakeholder_impact_notifications() {
24047 let mut tracker = StakeholderImpactTracker::new();
24048
24049 let impact = tracker.record_impact(
24050 "project-1".to_string(),
24051 "stakeholder-1".to_string(),
24052 StakeholderImpactLevel::Critical,
24053 StakeholderImpactCategory::Legal,
24054 "Critical legal issue".to_string(),
24055 0.9,
24056 ImpactTimeframe::Immediate,
24057 vec![],
24058 );
24059
24060 let unnotified = tracker.get_unnotified_critical_impacts("project-1");
24061 assert_eq!(unnotified.len(), 1);
24062
24063 tracker.mark_notified("project-1", &impact.id).unwrap();
24064
24065 let unnotified_after = tracker.get_unnotified_critical_impacts("project-1");
24066 assert_eq!(unnotified_after.len(), 0);
24067 }
24068
24069 #[test]
24070 fn test_stakeholder_impact_summary() {
24071 let mut tracker = StakeholderImpactTracker::new();
24072
24073 tracker.record_impact(
24074 "project-1".to_string(),
24075 "stakeholder-1".to_string(),
24076 StakeholderImpactLevel::High,
24077 StakeholderImpactCategory::Economic,
24078 "Impact 1".to_string(),
24079 0.8,
24080 ImpactTimeframe::ShortTerm,
24081 vec![],
24082 );
24083
24084 tracker.record_impact(
24085 "project-1".to_string(),
24086 "stakeholder-2".to_string(),
24087 StakeholderImpactLevel::Medium,
24088 StakeholderImpactCategory::Operational,
24089 "Impact 2".to_string(),
24090 0.5,
24091 ImpactTimeframe::MediumTerm,
24092 vec![],
24093 );
24094
24095 let summary = tracker.get_impact_summary("project-1");
24096 assert_eq!(*summary.get(&StakeholderImpactLevel::High).unwrap(), 1);
24097 assert_eq!(*summary.get(&StakeholderImpactLevel::Medium).unwrap(), 1);
24098 }
24099
24100 #[test]
24101 fn test_public_hearing_scheduling() {
24102 let mut manager = PublicCommentPeriodManager::new();
24103
24104 let period = manager.open_comment_period(
24105 "proj-1".to_string(),
24106 "Test Period".to_string(),
24107 "Description".to_string(),
24108 30,
24109 );
24110 let period_id = period.id.clone();
24111
24112 let hearing = PublicHearing {
24114 id: "hearing-1".to_string(),
24115 title: "Public Hearing on Statute Porting".to_string(),
24116 datetime: "2025-02-15T10:00:00Z".to_string(),
24117 location: "City Hall, Room 101".to_string(),
24118 virtual_link: Some("https://meeting.example.com/hearing1".to_string()),
24119 agenda: vec![
24120 "Opening remarks".to_string(),
24121 "Presentation of ported statutes".to_string(),
24122 "Public questions and comments".to_string(),
24123 ],
24124 registration_required: true,
24125 };
24126
24127 assert!(manager.schedule_hearing(&period_id, hearing).is_some());
24128
24129 let period = manager.get_period(&period_id).unwrap();
24130 assert_eq!(period.hearings.len(), 1);
24131 assert_eq!(period.hearings[0].agenda.len(), 3);
24132 }
24133
24134 #[test]
24139 fn test_quality_scorer_creation() {
24140 let scorer = QualityScorer::new();
24141 assert_eq!(scorer.min_quality_threshold, 0.6);
24142
24143 let scorer_custom = QualityScorer::new().with_threshold(0.8);
24144 assert_eq!(scorer_custom.min_quality_threshold, 0.8);
24145 }
24146
24147 #[test]
24148 fn test_quality_scoring_with_changes() {
24149 let scorer = QualityScorer::new();
24150
24151 let mut statute = Statute::new(
24152 "test-1",
24153 "Test Statute",
24154 Effect::new(EffectType::Grant, "Test"),
24155 );
24156 statute.id = "test-statute".to_string();
24157
24158 let ported = PortedStatute {
24159 original_id: "original-1".to_string(),
24160 statute,
24161 changes: vec![
24162 PortingChange {
24163 change_type: ChangeType::CulturalAdaptation,
24164 description: "Adapted age parameter".to_string(),
24165 original: Some("20".to_string()),
24166 adapted: Some("18".to_string()),
24167 reason: "Age of majority differs between jurisdictions".to_string(),
24168 },
24169 PortingChange {
24170 change_type: ChangeType::Translation,
24171 description: "Translated legal term".to_string(),
24172 original: Some("契約".to_string()),
24173 adapted: Some("contract".to_string()),
24174 reason: "Translation to target language".to_string(),
24175 },
24176 ],
24177 locale: Locale::new("en").with_country("US"),
24178 compatibility_score: 0.85,
24179 };
24180
24181 let quality = scorer.score_porting(&ported);
24182
24183 assert!(quality.overall >= 0.0 && quality.overall <= 1.0);
24184 assert!(quality.semantic_preservation >= 0.0);
24185 assert!(quality.legal_correctness >= 0.0);
24186 assert!(quality.cultural_adaptation >= 0.0);
24187 assert!(quality.completeness >= 0.0);
24188 assert!(quality.consistency >= 0.0);
24189 }
24190
24191 #[test]
24192 fn test_quality_scoring_empty_statute() {
24193 let scorer = QualityScorer::new();
24194
24195 let statute = Statute::new("", "", Effect::new(EffectType::Grant, "Test"));
24196
24197 let ported = PortedStatute {
24198 original_id: "original-1".to_string(),
24199 statute,
24200 changes: vec![],
24201 locale: Locale::new("en").with_country("US"),
24202 compatibility_score: 0.5,
24203 };
24204
24205 let quality = scorer.score_porting(&ported);
24206
24207 assert!(
24213 quality.overall < 0.9,
24214 "Quality score is {}",
24215 quality.overall
24216 );
24217 assert!(
24218 (quality.completeness - 0.4).abs() < 0.01,
24219 "Completeness score is {}",
24220 quality.completeness
24221 );
24222 assert!(!quality.issues.is_empty());
24223 assert!(
24224 quality
24225 .issues
24226 .iter()
24227 .any(|i| matches!(i.issue_type, QualityIssueType::Incompleteness))
24228 );
24229 }
24230
24231 #[test]
24232 fn test_quality_grade_classification() {
24233 let scorer = QualityScorer::new();
24234
24235 let excellent = PortedStatute {
24236 original_id: "test".to_string(),
24237 statute: Statute::new(
24238 "id-1",
24239 "Test Statute",
24240 Effect::new(EffectType::Grant, "Test"),
24241 ),
24242 changes: vec![PortingChange {
24243 change_type: ChangeType::CulturalAdaptation,
24244 description: "Test".to_string(),
24245 original: None,
24246 adapted: None,
24247 reason: "Test reason".to_string(),
24248 }],
24249 locale: Locale::new("en").with_country("US"),
24250 compatibility_score: 1.0,
24251 };
24252
24253 let quality = scorer.score_porting(&excellent);
24254 assert!(matches!(
24255 quality.grade,
24256 QualityGrade::Good | QualityGrade::Excellent
24257 ));
24258 }
24259
24260 #[test]
24261 fn test_quality_scorer_meets_threshold() {
24262 let scorer = QualityScorer::new().with_threshold(0.7);
24263
24264 let score = QualityScore {
24265 overall: 0.8,
24266 semantic_preservation: 0.8,
24267 legal_correctness: 0.8,
24268 cultural_adaptation: 0.8,
24269 completeness: 0.8,
24270 consistency: 0.8,
24271 grade: QualityGrade::Good,
24272 issues: vec![],
24273 recommendations: vec![],
24274 };
24275
24276 assert!(scorer.meets_threshold(&score));
24277
24278 let low_score = QualityScore {
24279 overall: 0.5,
24280 semantic_preservation: 0.5,
24281 legal_correctness: 0.5,
24282 cultural_adaptation: 0.5,
24283 completeness: 0.5,
24284 consistency: 0.5,
24285 grade: QualityGrade::Poor,
24286 issues: vec![],
24287 recommendations: vec![],
24288 };
24289
24290 assert!(!scorer.meets_threshold(&low_score));
24291 }
24292
24293 #[test]
24294 fn test_consistency_verifier_creation() {
24295 let verifier = ConsistencyVerifier::new();
24296 let _ = verifier; }
24298
24299 #[test]
24300 fn test_consistency_verification_consistent() {
24301 let verifier = ConsistencyVerifier::new();
24302
24303 let ported = PortedStatute {
24304 original_id: "test".to_string(),
24305 statute: Statute::new(
24306 "id-1",
24307 "Test Statute",
24308 Effect::new(EffectType::Grant, "Test"),
24309 ),
24310 changes: vec![],
24311 locale: Locale::new("en").with_country("US"),
24312 compatibility_score: 1.0,
24313 };
24314
24315 let result = verifier.verify(&ported);
24316
24317 assert!(result.is_consistent);
24318 assert_eq!(result.consistency_score, 1.0);
24319 assert!(result.inconsistencies.is_empty());
24320 }
24321
24322 #[test]
24323 fn test_consistency_verification_with_many_changes() {
24324 let verifier = ConsistencyVerifier::new();
24325
24326 let mut changes = vec![];
24327 for i in 0..15 {
24328 changes.push(PortingChange {
24329 change_type: ChangeType::Translation,
24330 description: format!("Translation {}", i),
24331 original: Some(format!("old-{}", i)),
24332 adapted: Some(format!("new-{}", i)),
24333 reason: format!("Translation reason {}", i),
24334 });
24335 }
24336
24337 let ported = PortedStatute {
24338 original_id: "test".to_string(),
24339 statute: Statute::new(
24340 "id-1",
24341 "Test Statute",
24342 Effect::new(EffectType::Grant, "Test"),
24343 ),
24344 changes,
24345 locale: Locale::new("en").with_country("US"),
24346 compatibility_score: 0.8,
24347 };
24348
24349 let result = verifier.verify(&ported);
24350
24351 assert!(!result.inconsistencies.is_empty());
24352 assert!(result.inconsistencies.iter().any(|i| matches!(
24353 i.inconsistency_type,
24354 InconsistencyType::TerminologyInconsistency
24355 )));
24356 }
24357
24358 #[test]
24359 fn test_consistency_verification_logical_inconsistency() {
24360 let verifier = ConsistencyVerifier::new();
24361
24362 let mut changes = vec![];
24363 for i in 0..4 {
24365 changes.push(PortingChange {
24366 change_type: ChangeType::ValueAdaptation,
24367 description: format!("Value adaptation {}", i),
24368 original: Some(format!("old-{}", i)),
24369 adapted: Some(format!("new-{}", i)),
24370 reason: "Value adaptation".to_string(),
24371 });
24372 }
24373 changes.push(PortingChange {
24375 change_type: ChangeType::Removal,
24376 description: "Removed incompatible clause".to_string(),
24377 original: Some("incompatible".to_string()),
24378 adapted: None,
24379 reason: "Incompatible with target jurisdiction".to_string(),
24380 });
24381
24382 let ported = PortedStatute {
24383 original_id: "test".to_string(),
24384 statute: Statute::new(
24385 "id-1",
24386 "Test Statute",
24387 Effect::new(EffectType::Grant, "Test"),
24388 ),
24389 changes,
24390 locale: Locale::new("en").with_country("US"),
24391 compatibility_score: 0.7,
24392 };
24393
24394 let result = verifier.verify(&ported);
24395
24396 assert!(!result.inconsistencies.is_empty());
24397 assert!(result.inconsistencies.iter().any(|i| matches!(
24398 i.inconsistency_type,
24399 InconsistencyType::LogicalInconsistency
24400 )));
24401 }
24402
24403 #[test]
24404 fn test_completeness_checker_creation() {
24405 let checker = CompletenessChecker::new();
24406 assert!(!checker.check_optional);
24407
24408 let checker_with_optional = CompletenessChecker::new().with_optional_check(true);
24409 assert!(checker_with_optional.check_optional);
24410 }
24411
24412 #[test]
24413 fn test_completeness_check_complete() {
24414 let checker = CompletenessChecker::new();
24415
24416 let ported = PortedStatute {
24417 original_id: "test".to_string(),
24418 statute: Statute::new(
24419 "id-1",
24420 "Test Statute",
24421 Effect::new(EffectType::Grant, "Test"),
24422 ),
24423 changes: vec![PortingChange {
24424 change_type: ChangeType::CulturalAdaptation,
24425 description: "Test change".to_string(),
24426 original: None,
24427 adapted: None,
24428 reason: "Cultural adaptation test".to_string(),
24429 }],
24430 locale: Locale::new("en").with_country("US"),
24431 compatibility_score: 1.0,
24432 };
24433
24434 let result = checker.check(&ported);
24435
24436 assert!(result.is_complete);
24437 assert_eq!(result.completeness_score, 1.0);
24438 assert!(result.missing_elements.is_empty());
24439 }
24440
24441 #[test]
24442 fn test_completeness_check_missing_required() {
24443 let checker = CompletenessChecker::new();
24444
24445 let statute = Statute::new("", "", Effect::new(EffectType::Grant, "Test"));
24446
24447 let ported = PortedStatute {
24448 original_id: "test".to_string(),
24449 statute,
24450 changes: vec![],
24451 locale: Locale::new("en").with_country("US"),
24452 compatibility_score: 0.5,
24453 };
24454
24455 let result = checker.check(&ported);
24456
24457 assert!(!result.is_complete);
24458 assert_eq!(result.completeness_score, 0.0);
24459 assert!(
24460 result
24461 .missing_elements
24462 .iter()
24463 .any(|e| matches!(e.importance, ElementImportance::Required))
24464 );
24465 }
24466
24467 #[test]
24468 fn test_completeness_check_missing_recommended() {
24469 let checker = CompletenessChecker::new();
24470
24471 let ported = PortedStatute {
24472 original_id: "test".to_string(),
24473 statute: Statute::new(
24474 "id-1",
24475 "Test Statute",
24476 Effect::new(EffectType::Grant, "Test"),
24477 ),
24478 changes: vec![], locale: Locale::new("en").with_country("US"),
24480 compatibility_score: 0.8,
24481 };
24482
24483 let result = checker.check(&ported);
24484
24485 assert!(!result.is_complete);
24486 assert!(result.completeness_score > 0.0 && result.completeness_score < 1.0);
24487 assert!(
24488 result
24489 .missing_elements
24490 .iter()
24491 .any(|e| matches!(e.importance, ElementImportance::Recommended))
24492 );
24493 }
24494
24495 #[test]
24496 fn test_regression_test_manager_creation() {
24497 let manager = RegressionTestManager::new();
24498 let stats = manager.get_statistics();
24499
24500 assert_eq!(stats.total, 0);
24501 assert_eq!(stats.pass_rate, 0.0);
24502 }
24503
24504 #[test]
24505 fn test_regression_test_add() {
24506 let mut manager = RegressionTestManager::new();
24507
24508 let test = RegressionTest {
24509 test_id: "test-1".to_string(),
24510 name: "Test Porting".to_string(),
24511 source_jurisdiction: "JP".to_string(),
24512 target_jurisdiction: "US".to_string(),
24513 input_statute: "{}".to_string(),
24514 expected_output: "{}".to_string(),
24515 quality_baseline: 0.8,
24516 created_at: chrono::Utc::now(),
24517 last_run: None,
24518 status: RegressionTestStatus::Pending,
24519 };
24520
24521 manager.add_test(test);
24522
24523 let stats = manager.get_statistics();
24524 assert_eq!(stats.total, 1);
24525 assert_eq!(stats.pending, 1);
24526 }
24527
24528 #[test]
24529 fn test_regression_test_run() {
24530 let mut manager = RegressionTestManager::new();
24531
24532 let ported = PortedStatute {
24533 original_id: "test".to_string(),
24534 statute: Statute::new(
24535 "id-1",
24536 "Test Statute",
24537 Effect::new(EffectType::Grant, "Test"),
24538 ),
24539 changes: vec![PortingChange {
24540 change_type: ChangeType::CulturalAdaptation,
24541 description: "Test".to_string(),
24542 original: None,
24543 adapted: None,
24544 reason: "Test reason".to_string(),
24545 }],
24546 locale: Locale::new("en").with_country("US"),
24547 compatibility_score: 0.9,
24548 };
24549
24550 let test = RegressionTest {
24551 test_id: "test-1".to_string(),
24552 name: "Test Porting".to_string(),
24553 source_jurisdiction: "JP".to_string(),
24554 target_jurisdiction: "US".to_string(),
24555 input_statute: "{}".to_string(),
24556 expected_output: "{}".to_string(),
24557 quality_baseline: 0.8,
24558 created_at: chrono::Utc::now(),
24559 last_run: None,
24560 status: RegressionTestStatus::Pending,
24561 };
24562
24563 manager.add_test(test);
24564
24565 let result = manager.run_test("test-1", &ported);
24566 assert!(result.is_ok());
24567
24568 let result = result.unwrap();
24569 assert!(result.passed);
24570 assert!(result.quality_score >= 0.0);
24571 }
24572
24573 #[test]
24574 fn test_regression_test_statistics() {
24575 let mut manager = RegressionTestManager::new();
24576
24577 for i in 0..5 {
24578 let test = RegressionTest {
24579 test_id: format!("test-{}", i),
24580 name: format!("Test {}", i),
24581 source_jurisdiction: "JP".to_string(),
24582 target_jurisdiction: "US".to_string(),
24583 input_statute: "{}".to_string(),
24584 expected_output: "{}".to_string(),
24585 quality_baseline: 0.8,
24586 created_at: chrono::Utc::now(),
24587 last_run: None,
24588 status: if i % 2 == 0 {
24589 RegressionTestStatus::Passed
24590 } else {
24591 RegressionTestStatus::Failed
24592 },
24593 };
24594 manager.add_test(test);
24595 }
24596
24597 let stats = manager.get_statistics();
24598 assert_eq!(stats.total, 5);
24599 assert_eq!(stats.passed, 3); assert_eq!(stats.failed, 2); assert_eq!(stats.pass_rate, 0.6);
24602 }
24603
24604 #[test]
24605 fn test_drift_monitor_creation() {
24606 let monitor = DriftMonitor::new();
24607 assert_eq!(monitor.drift_threshold, 0.1);
24608
24609 let monitor_custom = DriftMonitor::new().with_threshold(0.2);
24610 assert_eq!(monitor_custom.drift_threshold, 0.2);
24611 }
24612
24613 #[test]
24614 fn test_drift_monitor_snapshot_creation() {
24615 let mut monitor = DriftMonitor::new();
24616
24617 let ported = PortedStatute {
24618 original_id: "test".to_string(),
24619 statute: Statute::new(
24620 "id-1",
24621 "Test Statute",
24622 Effect::new(EffectType::Grant, "Test"),
24623 ),
24624 changes: vec![PortingChange {
24625 change_type: ChangeType::CulturalAdaptation,
24626 description: "Test".to_string(),
24627 original: None,
24628 adapted: None,
24629 reason: "Test reason".to_string(),
24630 }],
24631 locale: Locale::new("en").with_country("US"),
24632 compatibility_score: 0.9,
24633 };
24634
24635 let snapshot_id = monitor.create_snapshot("statute-1".to_string(), &ported);
24636
24637 assert!(!snapshot_id.is_empty());
24638
24639 let snapshots = monitor.get_snapshots("statute-1");
24640 assert!(snapshots.is_some());
24641 assert_eq!(snapshots.unwrap().len(), 1);
24642 }
24643
24644 #[test]
24645 fn test_drift_detection_no_drift() {
24646 let mut monitor = DriftMonitor::new();
24647
24648 let ported = PortedStatute {
24649 original_id: "test".to_string(),
24650 statute: Statute::new(
24651 "id-1",
24652 "Test Statute",
24653 Effect::new(EffectType::Grant, "Test"),
24654 ),
24655 changes: vec![PortingChange {
24656 change_type: ChangeType::CulturalAdaptation,
24657 description: "Test".to_string(),
24658 original: None,
24659 adapted: None,
24660 reason: "Test reason".to_string(),
24661 }],
24662 locale: Locale::new("en").with_country("US"),
24663 compatibility_score: 0.9,
24664 };
24665
24666 monitor.create_snapshot("statute-1".to_string(), &ported);
24667
24668 let result = monitor.detect_drift("statute-1", &ported);
24669
24670 assert!(!result.drift_detected);
24671 assert!(result.drift_issues.is_empty());
24672 }
24673
24674 #[test]
24675 fn test_drift_detection_with_new_snapshot() {
24676 let mut monitor = DriftMonitor::new();
24677
24678 let ported1 = PortedStatute {
24679 original_id: "test".to_string(),
24680 statute: Statute::new(
24681 "id-1",
24682 "Test Statute",
24683 Effect::new(EffectType::Grant, "Test"),
24684 ),
24685 changes: vec![PortingChange {
24686 change_type: ChangeType::CulturalAdaptation,
24687 description: "Test".to_string(),
24688 original: None,
24689 adapted: None,
24690 reason: "Test reason".to_string(),
24691 }],
24692 locale: Locale::new("en").with_country("US"),
24693 compatibility_score: 0.9,
24694 };
24695
24696 monitor.create_snapshot("statute-1".to_string(), &ported1);
24697
24698 let mut ported2 = ported1.clone();
24700 ported2.statute.id = "".to_string(); let result = monitor.detect_drift("statute-1", &ported2);
24703
24704 assert!(result.drift_score >= 0.0);
24706 }
24707
24708 #[test]
24709 fn test_drift_trend_tracking() {
24710 let mut monitor = DriftMonitor::new();
24711
24712 let ported = PortedStatute {
24713 original_id: "test".to_string(),
24714 statute: Statute::new(
24715 "id-1",
24716 "Test Statute",
24717 Effect::new(EffectType::Grant, "Test"),
24718 ),
24719 changes: vec![PortingChange {
24720 change_type: ChangeType::CulturalAdaptation,
24721 description: "Test".to_string(),
24722 original: None,
24723 adapted: None,
24724 reason: "Test reason".to_string(),
24725 }],
24726 locale: Locale::new("en").with_country("US"),
24727 compatibility_score: 0.9,
24728 };
24729
24730 monitor.create_snapshot("statute-1".to_string(), &ported);
24732 monitor.create_snapshot("statute-1".to_string(), &ported);
24733
24734 let trend = monitor.get_drift_trend("statute-1");
24735
24736 assert_eq!(trend.len(), 1); }
24738
24739 #[test]
24740 fn test_drift_category_classification() {
24741 let result = DriftDetectionResult {
24742 drift_detected: false,
24743 drift_score: 0.0,
24744 category: DriftCategory::None,
24745 drift_issues: vec![],
24746 recommendations: vec![],
24747 };
24748
24749 assert!(matches!(result.category, DriftCategory::None));
24750 }
24751
24752 #[test]
24757 fn test_explanatory_note_generator() {
24758 let generator = ExplanatoryNoteGenerator::new();
24759
24760 let ported = PortedStatute {
24761 original_id: "test".to_string(),
24762 statute: Statute::new(
24763 "id-1",
24764 "Test Statute",
24765 Effect::new(EffectType::Grant, "Test"),
24766 ),
24767 changes: vec![
24768 PortingChange {
24769 change_type: ChangeType::CulturalAdaptation,
24770 description: "Adapted parameter".to_string(),
24771 original: Some("20".to_string()),
24772 adapted: Some("18".to_string()),
24773 reason: "Age difference".to_string(),
24774 },
24775 PortingChange {
24776 change_type: ChangeType::Translation,
24777 description: "Translated term".to_string(),
24778 original: Some("契約".to_string()),
24779 adapted: Some("contract".to_string()),
24780 reason: "Language localization".to_string(),
24781 },
24782 ],
24783 locale: Locale::new("en").with_country("US"),
24784 compatibility_score: 0.9,
24785 };
24786
24787 let notes = generator.generate_notes(&ported);
24788
24789 assert_eq!(notes.len(), 2);
24791 assert_eq!(notes[0].section, "General");
24792 assert!(!notes[0].explanation.is_empty());
24793 }
24794
24795 #[test]
24796 fn test_explanatory_note_significant_changes_only() {
24797 let generator = ExplanatoryNoteGenerator::new();
24798
24799 let ported = PortedStatute {
24800 original_id: "test".to_string(),
24801 statute: Statute::new(
24802 "id-1",
24803 "Test Statute",
24804 Effect::new(EffectType::Grant, "Test"),
24805 ),
24806 changes: vec![PortingChange {
24807 change_type: ChangeType::Translation,
24808 description: "Translation".to_string(),
24809 original: None,
24810 adapted: None,
24811 reason: "Test".to_string(),
24812 }],
24813 locale: Locale::new("en").with_country("US"),
24814 compatibility_score: 0.9,
24815 };
24816
24817 let notes = generator.generate_notes(&ported);
24818
24819 assert_eq!(notes.len(), 1);
24821 assert_eq!(notes[0].section, "General");
24822 }
24823
24824 #[test]
24825 fn test_change_justification_report_generator() {
24826 let generator = ChangeJustificationReportGenerator::new();
24827
24828 let ported = PortedStatute {
24829 original_id: "test".to_string(),
24830 statute: Statute::new(
24831 "id-1",
24832 "Test Statute",
24833 Effect::new(EffectType::Grant, "Test"),
24834 ),
24835 changes: vec![
24836 PortingChange {
24837 change_type: ChangeType::CulturalAdaptation,
24838 description: "Cultural adaptation".to_string(),
24839 original: Some("old".to_string()),
24840 adapted: Some("new".to_string()),
24841 reason: "Culture".to_string(),
24842 },
24843 PortingChange {
24844 change_type: ChangeType::ValueAdaptation,
24845 description: "Value adaptation".to_string(),
24846 original: Some("20".to_string()),
24847 adapted: Some("18".to_string()),
24848 reason: "Age threshold".to_string(),
24849 },
24850 ],
24851 locale: Locale::new("en").with_country("US"),
24852 compatibility_score: 0.85,
24853 };
24854
24855 let report = generator.generate_report(&ported, "JP", "US");
24856
24857 assert_eq!(report.source_jurisdiction, "JP");
24858 assert_eq!(report.target_jurisdiction, "US");
24859 assert_eq!(report.justifications.len(), 2);
24860 assert!(!report.overall_rationale.is_empty());
24861 assert!(!report.legal_basis.is_empty());
24862
24863 assert!(report.justifications[0].risk_if_unchanged.is_some());
24865 assert!(report.justifications[1].risk_if_unchanged.is_some());
24866 }
24867
24868 #[test]
24869 fn test_change_justification_types() {
24870 let generator = ChangeJustificationReportGenerator::new();
24871
24872 let change_removal = PortingChange {
24873 change_type: ChangeType::Removal,
24874 description: "Removed clause".to_string(),
24875 original: Some("old".to_string()),
24876 adapted: None,
24877 reason: "Incompatible".to_string(),
24878 };
24879
24880 let justification = generator.justify_change(&change_removal);
24881 assert!(justification.justification.contains("incompatibility"));
24882 assert!(justification.risk_if_unchanged.is_some());
24883 }
24884
24885 #[test]
24886 fn test_legislative_history_compiler() {
24887 let compiler = LegislativeHistoryCompiler::new();
24888
24889 let ported = PortedStatute {
24890 original_id: "test".to_string(),
24891 statute: Statute::new(
24892 "id-1",
24893 "Test Statute",
24894 Effect::new(EffectType::Grant, "Test"),
24895 ),
24896 changes: vec![
24897 PortingChange {
24898 change_type: ChangeType::CulturalAdaptation,
24899 description: "Adapted".to_string(),
24900 original: None,
24901 adapted: None,
24902 reason: "Test".to_string(),
24903 },
24904 PortingChange {
24905 change_type: ChangeType::ValueAdaptation,
24906 description: "Value change".to_string(),
24907 original: None,
24908 adapted: None,
24909 reason: "Test".to_string(),
24910 },
24911 ],
24912 locale: Locale::new("en").with_country("US"),
24913 compatibility_score: 0.9,
24914 };
24915
24916 let history = compiler.compile_history(&ported);
24917
24918 assert_eq!(history.statute_id, "id-1");
24919 assert_eq!(history.timeline.len(), 3);
24921 assert!(
24922 history
24923 .timeline
24924 .iter()
24925 .any(|e| matches!(e.event_type, LegislativeEventType::Ported))
24926 );
24927 assert_eq!(
24928 history
24929 .timeline
24930 .iter()
24931 .filter(|e| matches!(e.event_type, LegislativeEventType::Amended))
24932 .count(),
24933 2
24934 );
24935 assert!(!history.summary.is_empty());
24936 assert!(!history.key_participants.is_empty());
24937 }
24938
24939 #[test]
24940 fn test_legislative_history_add_event() {
24941 let compiler = LegislativeHistoryCompiler::new();
24942
24943 let ported = PortedStatute {
24944 original_id: "test".to_string(),
24945 statute: Statute::new(
24946 "id-1",
24947 "Test Statute",
24948 Effect::new(EffectType::Grant, "Test"),
24949 ),
24950 changes: vec![],
24951 locale: Locale::new("en").with_country("US"),
24952 compatibility_score: 1.0,
24953 };
24954
24955 let mut history = compiler.compile_history(&ported);
24956 let initial_count = history.timeline.len();
24957
24958 compiler.add_event(
24959 &mut history,
24960 LegislativeEventType::Reviewed,
24961 "Reviewed by legal team".to_string(),
24962 Some("Legal Team".to_string()),
24963 );
24964
24965 assert_eq!(history.timeline.len(), initial_count + 1);
24966 assert!(
24967 history
24968 .timeline
24969 .last()
24970 .unwrap()
24971 .actor
24972 .as_ref()
24973 .unwrap()
24974 .contains("Legal Team")
24975 );
24976 }
24977
24978 #[test]
24979 fn test_implementation_guidance_generator() {
24980 let generator = ImplementationGuidanceGenerator::new();
24981
24982 let ported = PortedStatute {
24983 original_id: "test".to_string(),
24984 statute: Statute::new(
24985 "id-1",
24986 "Test Statute",
24987 Effect::new(EffectType::Grant, "Test"),
24988 ),
24989 changes: vec![PortingChange {
24990 change_type: ChangeType::CulturalAdaptation,
24991 description: "Cultural change".to_string(),
24992 original: None,
24993 adapted: None,
24994 reason: "Test".to_string(),
24995 }],
24996 locale: Locale::new("en").with_country("US"),
24997 compatibility_score: 0.9,
24998 };
24999
25000 let guidance = generator.generate_guidance(&ported);
25001
25002 assert_eq!(guidance.statute_id, "id-1");
25003 assert!(!guidance.overview.is_empty());
25004 assert!(!guidance.prerequisites.is_empty());
25005 assert!(!guidance.implementation_steps.is_empty());
25006 assert!(!guidance.compliance_checklist.is_empty());
25007 assert!(!guidance.common_pitfalls.is_empty());
25008
25009 assert_eq!(guidance.implementation_steps.len(), 5);
25011 assert_eq!(guidance.implementation_steps[0].step_number, 1);
25012 assert_eq!(guidance.implementation_steps[0].title, "Initial Review");
25013 }
25014
25015 #[test]
25016 fn test_implementation_guidance_steps_without_changes() {
25017 let generator = ImplementationGuidanceGenerator::new();
25018
25019 let ported = PortedStatute {
25020 original_id: "test".to_string(),
25021 statute: Statute::new(
25022 "id-1",
25023 "Test Statute",
25024 Effect::new(EffectType::Grant, "Test"),
25025 ),
25026 changes: vec![],
25027 locale: Locale::new("en").with_country("US"),
25028 compatibility_score: 1.0,
25029 };
25030
25031 let guidance = generator.generate_guidance(&ported);
25032
25033 assert_eq!(guidance.implementation_steps.len(), 4);
25035 assert!(
25036 !guidance
25037 .implementation_steps
25038 .iter()
25039 .any(|s| s.title.contains("Adaptations"))
25040 );
25041 }
25042
25043 #[test]
25044 fn test_training_material_generator() {
25045 let generator = TrainingMaterialGenerator::new();
25046
25047 let ported = PortedStatute {
25048 original_id: "test".to_string(),
25049 statute: Statute::new(
25050 "id-1",
25051 "Test Statute",
25052 Effect::new(EffectType::Grant, "Test"),
25053 ),
25054 changes: vec![PortingChange {
25055 change_type: ChangeType::CulturalAdaptation,
25056 description: "Adaptation".to_string(),
25057 original: None,
25058 adapted: None,
25059 reason: "Test".to_string(),
25060 }],
25061 locale: Locale::new("en").with_country("US"),
25062 compatibility_score: 0.9,
25063 };
25064
25065 let material = generator.generate_materials(&ported, TrainingAudience::LegalProfessionals);
25066
25067 assert_eq!(material.statute_id, "id-1");
25068 assert_eq!(
25069 material.target_audience,
25070 TrainingAudience::LegalProfessionals
25071 );
25072 assert!(!material.title.is_empty());
25073 assert!(!material.learning_objectives.is_empty());
25074 assert!(!material.modules.is_empty());
25075 assert!(!material.assessment_questions.is_empty());
25076 assert_eq!(material.estimated_duration, "4 hours");
25077 }
25078
25079 #[test]
25080 fn test_training_material_different_audiences() {
25081 let generator = TrainingMaterialGenerator::new();
25082
25083 let ported = PortedStatute {
25084 original_id: "test".to_string(),
25085 statute: Statute::new(
25086 "id-1",
25087 "Test Statute",
25088 Effect::new(EffectType::Grant, "Test"),
25089 ),
25090 changes: vec![],
25091 locale: Locale::new("en").with_country("US"),
25092 compatibility_score: 1.0,
25093 };
25094
25095 let legal = generator.generate_materials(&ported, TrainingAudience::LegalProfessionals);
25096 let govt = generator.generate_materials(&ported, TrainingAudience::GovernmentOfficials);
25097 let public = generator.generate_materials(&ported, TrainingAudience::GeneralPublic);
25098 let enforcement =
25099 generator.generate_materials(&ported, TrainingAudience::EnforcementOfficers);
25100
25101 assert_eq!(legal.estimated_duration, "4 hours");
25102 assert_eq!(govt.estimated_duration, "3 hours");
25103 assert_eq!(public.estimated_duration, "1 hour");
25104 assert_eq!(enforcement.estimated_duration, "2 hours");
25105
25106 assert_ne!(legal.learning_objectives, public.learning_objectives);
25108 }
25109
25110 #[test]
25111 fn test_training_material_modules() {
25112 let generator = TrainingMaterialGenerator::new();
25113
25114 let ported = PortedStatute {
25115 original_id: "test".to_string(),
25116 statute: Statute::new(
25117 "id-1",
25118 "Test Statute",
25119 Effect::new(EffectType::Grant, "Test"),
25120 ),
25121 changes: vec![
25122 PortingChange {
25123 change_type: ChangeType::CulturalAdaptation,
25124 description: "Change 1".to_string(),
25125 original: None,
25126 adapted: None,
25127 reason: "Test".to_string(),
25128 },
25129 PortingChange {
25130 change_type: ChangeType::ValueAdaptation,
25131 description: "Change 2".to_string(),
25132 original: None,
25133 adapted: None,
25134 reason: "Test".to_string(),
25135 },
25136 ],
25137 locale: Locale::new("en").with_country("US"),
25138 compatibility_score: 0.9,
25139 };
25140
25141 let material = generator.generate_materials(&ported, TrainingAudience::GeneralPublic);
25142
25143 assert_eq!(material.modules.len(), 3);
25145 assert_eq!(material.modules[0].title, "Introduction to the Statute");
25146 assert_eq!(material.modules[1].title, "Key Adaptations");
25147 assert_eq!(material.modules[2].title, "Practical Application");
25148 }
25149
25150 #[test]
25151 fn test_training_material_assessment() {
25152 let generator = TrainingMaterialGenerator::new();
25153
25154 let ported = PortedStatute {
25155 original_id: "test".to_string(),
25156 statute: Statute::new(
25157 "id-1",
25158 "Test Statute",
25159 Effect::new(EffectType::Grant, "Test"),
25160 ),
25161 changes: vec![PortingChange {
25162 change_type: ChangeType::CulturalAdaptation,
25163 description: "Change".to_string(),
25164 original: None,
25165 adapted: None,
25166 reason: "Test".to_string(),
25167 }],
25168 locale: Locale::new("en").with_country("US"),
25169 compatibility_score: 0.9,
25170 };
25171
25172 let material = generator.generate_materials(&ported, TrainingAudience::LegalProfessionals);
25173
25174 assert_eq!(material.assessment_questions.len(), 2);
25176 assert_eq!(material.assessment_questions[0].question_number, 1);
25177 assert_eq!(material.assessment_questions[1].question_number, 2);
25178 assert_eq!(material.assessment_questions[0].options.len(), 3);
25179 assert!(material.assessment_questions[0].correct_answer < 3);
25180 }
25181
25182 fn create_test_project() -> PortingProject {
25187 PortingProject {
25188 id: "test-project-1".to_string(),
25189 name: "Test Porting Project".to_string(),
25190 description: "A test project".to_string(),
25191 source_jurisdiction: "JP".to_string(),
25192 target_jurisdiction: "US".to_string(),
25193 status: ProjectStatus::InProgress,
25194 statute_ids: vec!["statute-1".to_string(), "statute-2".to_string()],
25195 stakeholders: vec![Stakeholder {
25196 id: "stakeholder-1".to_string(),
25197 name: "John Doe".to_string(),
25198 email: "john@example.com".to_string(),
25199 role: StakeholderRole::ProjectManager,
25200 notification_preferences: NotificationPreferences {
25201 on_status_change: true,
25202 on_deadline_approaching: true,
25203 on_assignment: false,
25204 on_review_request: true,
25205 channels: vec![NotificationChannel::Email, NotificationChannel::InApp],
25206 },
25207 }],
25208 timeline: ProjectTimeline {
25209 start_date: chrono::Utc::now().to_rfc3339(),
25210 end_date: (chrono::Utc::now() + chrono::Duration::days(180)).to_rfc3339(),
25211 milestones: vec![],
25212 current_phase: "Implementation".to_string(),
25213 },
25214 created_at: chrono::Utc::now().to_rfc3339(),
25215 updated_at: chrono::Utc::now().to_rfc3339(),
25216 metadata: HashMap::new(),
25217 }
25218 }
25219
25220 fn create_test_ported_statutes(count: usize) -> Vec<PortedStatute> {
25221 (0..count)
25222 .map(|i| PortedStatute {
25223 original_id: format!("statute-{}", i),
25224 statute: {
25225 let id = format!("ported-{}", i);
25226 let title = format!("Test Statute {}", i);
25227 Statute::new(&id, &title, Effect::new(EffectType::Grant, "Test effect"))
25228 },
25229 changes: vec![],
25230 locale: Locale::new("en").with_country("US"),
25231 compatibility_score: 0.85,
25232 })
25233 .collect()
25234 }
25235
25236 fn create_test_ported_statute_with_score(score: f64) -> PortedStatute {
25237 PortedStatute {
25238 original_id: "test-statute".to_string(),
25239 statute: Statute::new(
25240 "ported-statute",
25241 "Test Statute",
25242 Effect::new(EffectType::Grant, "Test"),
25243 ),
25244 changes: vec![],
25245 locale: Locale::new("en").with_country("US"),
25246 compatibility_score: score,
25247 }
25248 }
25249
25250 fn create_test_risk_assessment() -> RiskAssessment {
25251 RiskAssessment {
25252 risk_score: 0.5,
25253 risk_level: RiskLevel::Medium,
25254 risks: vec![Risk {
25255 id: "risk-1".to_string(),
25256 category: RiskCategory::Legal,
25257 description: "Legal system mismatch".to_string(),
25258 likelihood: RiskLevel::Medium,
25259 impact: 0.6,
25260 severity: RiskLevel::Medium,
25261 }],
25262 mitigations: vec!["Consult with legal experts".to_string()],
25263 }
25264 }
25265
25266 fn create_test_validation_result(score: f64) -> ValidationResult {
25267 ValidationResult {
25268 id: uuid::Uuid::new_v4().to_string(),
25269 passed: score >= 0.75,
25270 overall_score: score,
25271 compliance: TargetJurisdictionComplianceCheck {
25272 id: uuid::Uuid::new_v4().to_string(),
25273 is_compliant: true,
25274 compliance_score: score,
25275 issues: vec![],
25276 recommendations: vec![],
25277 checked_regulations: vec![],
25278 },
25279 constitutional: ConstitutionalAnalysis {
25280 id: uuid::Uuid::new_v4().to_string(),
25281 is_compatible: true,
25282 compatibility_score: score,
25283 issues: vec![],
25284 relevant_provisions: vec![],
25285 recommended_amendments: vec![],
25286 },
25287 treaty_compliance: TreatyComplianceResult {
25288 id: uuid::Uuid::new_v4().to_string(),
25289 is_compliant: true,
25290 compliance_score: score,
25291 conflicts: vec![],
25292 checked_treaties: vec![],
25293 recommendations: vec![],
25294 },
25295 human_rights: HumanRightsAssessment {
25296 id: uuid::Uuid::new_v4().to_string(),
25297 impact_score: 0.0,
25298 affected_rights: vec![],
25299 vulnerable_groups: vec![],
25300 mitigation_measures: vec![],
25301 summary: "No human rights concerns identified".to_string(),
25302 },
25303 enforceability: EnforceabilityPrediction {
25304 id: uuid::Uuid::new_v4().to_string(),
25305 is_enforceable: true,
25306 enforceability_score: score,
25307 challenges: vec![],
25308 required_mechanisms: vec![],
25309 estimated_cost: None,
25310 recommendations: vec![],
25311 },
25312 summary: format!("Validation passed with score {:.2}", score),
25313 }
25314 }
25315
25316 #[test]
25321 fn test_ported_statute_simulation_creation() {
25322 let params = SimulationParameters {
25323 population_size: 100000,
25324 time_horizon_years: 5,
25325 simulation_runs: 1000,
25326 confidence_level: 0.95,
25327 enforcement_intensity: 0.8,
25328 compliance_culture: 0.7,
25329 };
25330
25331 let simulation =
25332 PortedStatuteSimulation::new("statute-1".to_string(), "US".to_string(), params.clone());
25333
25334 assert_eq!(simulation.statute_id, "statute-1");
25335 assert_eq!(simulation.jurisdiction, "US");
25336 assert_eq!(simulation.parameters.population_size, 100000);
25337 assert_eq!(simulation.outcomes.len(), 0);
25338 assert_eq!(simulation.unintended_consequences.len(), 0);
25339 }
25340
25341 #[test]
25342 fn test_simulation_add_outcomes() {
25343 let params = SimulationParameters {
25344 population_size: 50000,
25345 time_horizon_years: 3,
25346 simulation_runs: 500,
25347 confidence_level: 0.95,
25348 enforcement_intensity: 0.7,
25349 compliance_culture: 0.8,
25350 };
25351
25352 let mut simulation =
25353 PortedStatuteSimulation::new("statute-1".to_string(), "JP".to_string(), params);
25354
25355 let outcome1 = SimulationOutcome {
25356 category: OutcomeCategory::PositiveIntended,
25357 description: "Increased compliance".to_string(),
25358 probability: 0.85,
25359 magnitude: 0.75,
25360 affected_population_pct: 80.0,
25361 timeframe: "1-2 years".to_string(),
25362 };
25363
25364 let outcome2 = SimulationOutcome {
25365 category: OutcomeCategory::NegativeUnintended,
25366 description: "Increased administrative burden".to_string(),
25367 probability: 0.6,
25368 magnitude: 0.4,
25369 affected_population_pct: 20.0,
25370 timeframe: "6 months".to_string(),
25371 };
25372
25373 simulation.add_outcome(outcome1);
25374 simulation.add_outcome(outcome2);
25375
25376 assert_eq!(simulation.outcomes.len(), 2);
25377
25378 let negative_outcomes = simulation.likely_negative_outcomes();
25379 assert_eq!(negative_outcomes.len(), 1);
25380 assert_eq!(
25381 negative_outcomes[0].description,
25382 "Increased administrative burden"
25383 );
25384 }
25385
25386 #[test]
25387 fn test_unintended_consequences() {
25388 let params = SimulationParameters {
25389 population_size: 1000000,
25390 time_horizon_years: 10,
25391 simulation_runs: 2000,
25392 confidence_level: 0.99,
25393 enforcement_intensity: 0.9,
25394 compliance_culture: 0.6,
25395 };
25396
25397 let mut simulation =
25398 PortedStatuteSimulation::new("statute-2".to_string(), "GB".to_string(), params);
25399
25400 let consequence1 = UnintendedConsequence {
25401 description: "Market distortion".to_string(),
25402 severity: 0.8,
25403 likelihood: 0.7,
25404 affected_groups: vec!["Small businesses".to_string()],
25405 mitigation_strategies: vec!["Exemptions for small entities".to_string()],
25406 };
25407
25408 let consequence2 = UnintendedConsequence {
25409 description: "Minor compliance confusion".to_string(),
25410 severity: 0.3,
25411 likelihood: 0.5,
25412 affected_groups: vec!["General public".to_string()],
25413 mitigation_strategies: vec!["Public education campaign".to_string()],
25414 };
25415
25416 simulation.add_unintended_consequence(consequence1);
25417 simulation.add_unintended_consequence(consequence2);
25418
25419 assert_eq!(simulation.unintended_consequences.len(), 2);
25420
25421 let high_severity = simulation.high_severity_consequences();
25422 assert_eq!(high_severity.len(), 1);
25423 assert_eq!(high_severity[0].description, "Market distortion");
25424 }
25425
25426 #[test]
25427 fn test_comparative_outcome_analysis() {
25428 let mut analysis = ComparativeOutcomeAnalysis::new(
25429 "JP".to_string(),
25430 "US".to_string(),
25431 "statute-1".to_string(),
25432 );
25433
25434 let comparison1 = OutcomeComparison {
25435 outcome: "Compliance rate".to_string(),
25436 source_value: 85.0,
25437 target_value: 75.0,
25438 difference_pct: -11.76,
25439 significance: 0.02,
25440 explanation: "Different compliance cultures".to_string(),
25441 };
25442
25443 let comparison2 = OutcomeComparison {
25444 outcome: "Implementation cost".to_string(),
25445 source_value: 1000000.0,
25446 target_value: 1500000.0,
25447 difference_pct: 50.0,
25448 significance: 0.01,
25449 explanation: "Higher labor costs".to_string(),
25450 };
25451
25452 analysis.add_comparison(comparison1);
25453 analysis.add_comparison(comparison2);
25454
25455 assert_eq!(analysis.comparisons.len(), 2);
25456 assert!(analysis.similarity_score > 0.0);
25457 assert!(analysis.similarity_score <= 1.0);
25458
25459 let significant = analysis.significant_differences();
25460 assert_eq!(significant.len(), 1);
25461 assert_eq!(significant[0].outcome, "Implementation cost");
25462 }
25463
25464 #[test]
25465 fn test_key_differences() {
25466 let mut analysis = ComparativeOutcomeAnalysis::new(
25467 "US".to_string(),
25468 "DE".to_string(),
25469 "statute-2".to_string(),
25470 );
25471
25472 let diff = KeyDifference {
25473 category: DifferenceCategory::Cultural,
25474 description: "Privacy expectations differ significantly".to_string(),
25475 impact: 0.9,
25476 requires_adaptation: true,
25477 };
25478
25479 analysis.add_key_difference(diff);
25480
25481 assert_eq!(analysis.key_differences.len(), 1);
25482 assert!(analysis.key_differences[0].requires_adaptation);
25483 }
25484
25485 #[test]
25486 fn test_population_impact_modeling() {
25487 let mut model = PopulationImpactModeling::new("statute-1".to_string(), "US".to_string());
25488
25489 let segment1 = PopulationSegment {
25490 name: "Working age adults".to_string(),
25491 size: 150000000,
25492 percentage: 60.0,
25493 impact_level: 0.7,
25494 impact_type: PopulationImpactType::ModeratelyBeneficial,
25495 effects: vec!["Improved workplace safety".to_string()],
25496 vulnerability_factors: vec![],
25497 };
25498
25499 let segment2 = PopulationSegment {
25500 name: "Small business owners".to_string(),
25501 size: 10000000,
25502 percentage: 4.0,
25503 impact_level: 0.6,
25504 impact_type: PopulationImpactType::ModeratelyHarmful,
25505 effects: vec!["Increased compliance costs".to_string()],
25506 vulnerability_factors: vec!["Limited resources".to_string()],
25507 };
25508
25509 model.add_segment(segment1);
25510 model.add_segment(segment2);
25511
25512 assert_eq!(model.segments.len(), 2);
25513 assert!(model.overall_impact != 0.0);
25514
25515 let negative = model.negatively_impacted_segments();
25516 assert_eq!(negative.len(), 1);
25517 assert_eq!(negative[0].name, "Small business owners");
25518 }
25519
25520 #[test]
25521 fn test_equity_assessment() {
25522 let mut model = PopulationImpactModeling::new("statute-2".to_string(), "JP".to_string());
25523
25524 for i in 0..5 {
25526 let segment = PopulationSegment {
25527 name: format!("Segment {}", i),
25528 size: 20000000,
25529 percentage: 20.0,
25530 impact_level: (i as f64 + 1.0) * 0.2,
25531 impact_type: PopulationImpactType::ModeratelyBeneficial,
25532 effects: vec![],
25533 vulnerability_factors: vec![],
25534 };
25535 model.add_segment(segment);
25536 }
25537
25538 assert!(model.equity_assessment.gini_coefficient >= 0.0);
25540 assert!(model.equity_assessment.gini_coefficient <= 1.0);
25541 assert!(model.equity_assessment.equity_score >= 0.0);
25542 assert!(model.equity_assessment.equity_score <= 1.0);
25543 }
25544
25545 #[test]
25546 fn test_enforcement_simulation() {
25547 let mut simulation = EnforcementSimulation::new("statute-1".to_string(), "US".to_string());
25548
25549 let strategy1 = EnforcementStrategy {
25550 name: "Strict enforcement".to_string(),
25551 mechanisms: vec![EnforcementMechanism {
25552 mechanism_type: MechanismType::Inspection,
25553 description: "Regular inspections".to_string(),
25554 frequency: "Monthly".to_string(),
25555 effectiveness: 0.9,
25556 }],
25557 penalties: vec![Penalty {
25558 violation_type: "Non-compliance".to_string(),
25559 amount: 10000.0,
25560 currency: "USD".to_string(),
25561 additional_sanctions: vec![],
25562 deterrence: 0.8,
25563 }],
25564 monitoring: MonitoringApproach {
25565 approach_type: MonitoringType::Continuous,
25566 coverage: 100.0,
25567 frequency: "Daily".to_string(),
25568 technology: vec!["Automated sensors".to_string()],
25569 },
25570 resources: ResourceAllocation {
25571 personnel: 100,
25572 budget: 5000000.0,
25573 currency: "USD".to_string(),
25574 equipment: vec!["Inspection tools".to_string()],
25575 training_hours: 1000.0,
25576 },
25577 };
25578
25579 let scenario1 = EnforcementScenario {
25580 name: "High enforcement".to_string(),
25581 strategy: strategy1,
25582 compliance_rate: 0.95,
25583 cost: 5000000.0,
25584 currency: "USD".to_string(),
25585 effectiveness: 0.9,
25586 public_acceptance: 0.6,
25587 risks: vec![],
25588 };
25589
25590 simulation.add_scenario(scenario1);
25591
25592 assert_eq!(simulation.scenarios.len(), 1);
25593 assert!(simulation.optimal_strategy.is_some());
25594 assert!(simulation.efficiency_score > 0.0);
25595 }
25596
25597 #[test]
25598 fn test_enforcement_optimal_strategy() {
25599 let mut simulation = EnforcementSimulation::new("statute-2".to_string(), "JP".to_string());
25600
25601 for i in 0..3 {
25603 let strategy = EnforcementStrategy {
25604 name: format!("Strategy {}", i),
25605 mechanisms: vec![],
25606 penalties: vec![],
25607 monitoring: MonitoringApproach {
25608 approach_type: MonitoringType::Periodic,
25609 coverage: 50.0,
25610 frequency: "Weekly".to_string(),
25611 technology: vec![],
25612 },
25613 resources: ResourceAllocation {
25614 personnel: 10 * (i + 1),
25615 budget: 100000.0 * (i + 1) as f64,
25616 currency: "JPY".to_string(),
25617 equipment: vec![],
25618 training_hours: 100.0,
25619 },
25620 };
25621
25622 let scenario = EnforcementScenario {
25623 name: format!("Scenario {}", i),
25624 strategy,
25625 compliance_rate: 0.7 + (i as f64 * 0.1),
25626 cost: 100000.0 * (i + 1) as f64,
25627 currency: "JPY".to_string(),
25628 effectiveness: 0.6 + (i as f64 * 0.15),
25629 public_acceptance: 0.8,
25630 risks: vec![],
25631 };
25632
25633 simulation.add_scenario(scenario);
25634 }
25635
25636 assert_eq!(simulation.scenarios.len(), 3);
25637 assert!(simulation.optimal_strategy.is_some());
25638
25639 let high_eff = simulation.high_effectiveness_scenarios();
25640 assert!(!high_eff.is_empty());
25641 }
25642
25643 #[test]
25644 fn test_ab_testing_framework_creation() {
25645 let config = TestConfiguration {
25646 sample_size: 10000,
25647 duration_days: 30,
25648 significance_threshold: 0.05,
25649 minimum_effect: 0.1,
25650 primary_metric: "Compliance rate".to_string(),
25651 secondary_metrics: vec!["Cost".to_string(), "User satisfaction".to_string()],
25652 };
25653
25654 let framework = ABTestingFramework::new("statute-1".to_string(), "US".to_string(), config);
25655
25656 assert_eq!(framework.statute_id, "statute-1");
25657 assert_eq!(framework.jurisdiction, "US");
25658 assert_eq!(framework.status, ABTestStatus::Setup);
25659 assert_eq!(framework.config.sample_size, 10000);
25660 }
25661
25662 #[test]
25663 fn test_ab_testing_add_variants() {
25664 let config = TestConfiguration {
25665 sample_size: 5000,
25666 duration_days: 60,
25667 significance_threshold: 0.05,
25668 minimum_effect: 0.15,
25669 primary_metric: "Effectiveness".to_string(),
25670 secondary_metrics: vec![],
25671 };
25672
25673 let mut framework =
25674 ABTestingFramework::new("statute-2".to_string(), "JP".to_string(), config);
25675
25676 let variant1 = PortingVariant {
25677 id: "v1".to_string(),
25678 name: "Strict approach".to_string(),
25679 ported_statute_id: "ported-1".to_string(),
25680 differences: vec!["Stricter penalties".to_string()],
25681 hypothesis: "Higher penalties improve compliance".to_string(),
25682 traffic_allocation: 0.5,
25683 };
25684
25685 let variant2 = PortingVariant {
25686 id: "v2".to_string(),
25687 name: "Lenient approach".to_string(),
25688 ported_statute_id: "ported-2".to_string(),
25689 differences: vec!["Education focus".to_string()],
25690 hypothesis: "Education improves long-term compliance".to_string(),
25691 traffic_allocation: 0.5,
25692 };
25693
25694 framework.add_variant(variant1);
25695 framework.add_variant(variant2);
25696
25697 assert_eq!(framework.variants.len(), 2);
25698 }
25699
25700 #[test]
25701 fn test_ab_testing_start_validation() {
25702 let config = TestConfiguration {
25703 sample_size: 1000,
25704 duration_days: 14,
25705 significance_threshold: 0.05,
25706 minimum_effect: 0.1,
25707 primary_metric: "Success rate".to_string(),
25708 secondary_metrics: vec![],
25709 };
25710
25711 let mut framework =
25712 ABTestingFramework::new("statute-3".to_string(), "GB".to_string(), config);
25713
25714 let result = framework.start_test();
25716 assert!(result.is_err());
25717
25718 framework.add_variant(PortingVariant {
25720 id: "v1".to_string(),
25721 name: "Variant 1".to_string(),
25722 ported_statute_id: "p1".to_string(),
25723 differences: vec![],
25724 hypothesis: "Test".to_string(),
25725 traffic_allocation: 0.5,
25726 });
25727
25728 framework.add_variant(PortingVariant {
25729 id: "v2".to_string(),
25730 name: "Variant 2".to_string(),
25731 ported_statute_id: "p2".to_string(),
25732 differences: vec![],
25733 hypothesis: "Test".to_string(),
25734 traffic_allocation: 0.5,
25735 });
25736
25737 let result = framework.start_test();
25738 assert!(result.is_ok());
25739 assert_eq!(framework.status, ABTestStatus::Running);
25740 }
25741
25742 #[test]
25743 fn test_ab_testing_traffic_allocation_validation() {
25744 let config = TestConfiguration {
25745 sample_size: 1000,
25746 duration_days: 14,
25747 significance_threshold: 0.05,
25748 minimum_effect: 0.1,
25749 primary_metric: "Metric".to_string(),
25750 secondary_metrics: vec![],
25751 };
25752
25753 let mut framework =
25754 ABTestingFramework::new("statute-4".to_string(), "DE".to_string(), config);
25755
25756 framework.add_variant(PortingVariant {
25758 id: "v1".to_string(),
25759 name: "Variant 1".to_string(),
25760 ported_statute_id: "p1".to_string(),
25761 differences: vec![],
25762 hypothesis: "Test".to_string(),
25763 traffic_allocation: 0.6,
25764 });
25765
25766 framework.add_variant(PortingVariant {
25767 id: "v2".to_string(),
25768 name: "Variant 2".to_string(),
25769 ported_statute_id: "p2".to_string(),
25770 differences: vec![],
25771 hypothesis: "Test".to_string(),
25772 traffic_allocation: 0.6,
25773 });
25774
25775 let result = framework.start_test();
25776 assert!(result.is_err());
25777 }
25778
25779 #[test]
25780 fn test_ab_testing_results() {
25781 let config = TestConfiguration {
25782 sample_size: 2000,
25783 duration_days: 30,
25784 significance_threshold: 0.05,
25785 minimum_effect: 0.1,
25786 primary_metric: "Compliance".to_string(),
25787 secondary_metrics: vec![],
25788 };
25789
25790 let mut framework =
25791 ABTestingFramework::new("statute-5".to_string(), "FR".to_string(), config);
25792
25793 framework.add_variant(PortingVariant {
25794 id: "v1".to_string(),
25795 name: "Control".to_string(),
25796 ported_statute_id: "p1".to_string(),
25797 differences: vec![],
25798 hypothesis: "Baseline".to_string(),
25799 traffic_allocation: 0.5,
25800 });
25801
25802 framework.add_variant(PortingVariant {
25803 id: "v2".to_string(),
25804 name: "Treatment".to_string(),
25805 ported_statute_id: "p2".to_string(),
25806 differences: vec!["Enhanced communication".to_string()],
25807 hypothesis: "Better communication improves compliance".to_string(),
25808 traffic_allocation: 0.5,
25809 });
25810
25811 let _ = framework.start_test();
25812
25813 let mut secondary_metrics = HashMap::new();
25815 secondary_metrics.insert("Cost".to_string(), 50000.0);
25816
25817 let results = ABTestResults {
25818 performances: vec![
25819 VariantPerformance {
25820 variant_id: "v1".to_string(),
25821 primary_metric_value: 0.75,
25822 secondary_metric_values: secondary_metrics.clone(),
25823 sample_size: 1000,
25824 compliance_rate: 0.75,
25825 user_satisfaction: 0.7,
25826 confidence_interval: (0.72, 0.78),
25827 },
25828 VariantPerformance {
25829 variant_id: "v2".to_string(),
25830 primary_metric_value: 0.82,
25831 secondary_metric_values: secondary_metrics,
25832 sample_size: 1000,
25833 compliance_rate: 0.82,
25834 user_satisfaction: 0.85,
25835 confidence_interval: (0.79, 0.85),
25836 },
25837 ],
25838 winner_id: Some("v2".to_string()),
25839 statistically_significant: true,
25840 confidence_level: 0.95,
25841 recommendations: vec!["Deploy treatment variant".to_string()],
25842 completed_at: chrono::Utc::now().to_rfc3339(),
25843 };
25844
25845 framework.record_results(results);
25846
25847 assert_eq!(framework.status, ABTestStatus::Completed);
25848 assert!(framework.results.is_some());
25849
25850 let winner = framework.get_winner();
25851 assert!(winner.is_some());
25852 assert_eq!(winner.unwrap().name, "Treatment");
25853 }
25854
25855 #[test]
25860 fn test_model_law_creation() {
25861 let model_law = ModelLaw::new(
25862 "UNCITRAL Model Law on Electronic Commerce".to_string(),
25863 "UNCITRAL".to_string(),
25864 "1.0".to_string(),
25865 "Electronic Commerce".to_string(),
25866 "Model law text...".to_string(),
25867 );
25868
25869 assert!(!model_law.id.is_empty());
25870 assert_eq!(model_law.name, "UNCITRAL Model Law on Electronic Commerce");
25871 assert_eq!(model_law.issuing_organization, "UNCITRAL");
25872 assert_eq!(model_law.version, "1.0");
25873 assert_eq!(model_law.subject_area, "Electronic Commerce");
25874 assert!(model_law.adoptions.is_empty());
25875 }
25876
25877 #[test]
25878 fn test_model_law_adoption_tracking() {
25879 let mut model_law = ModelLaw::new(
25880 "Model Law on Arbitration".to_string(),
25881 "UNCITRAL".to_string(),
25882 "2.0".to_string(),
25883 "International Arbitration".to_string(),
25884 "Model law text...".to_string(),
25885 );
25886
25887 let adoption = ModelLawAdoption {
25888 jurisdiction: "JP".to_string(),
25889 adoption_date: "2023-01-01".to_string(),
25890 adoption_level: AdoptionLevel::FullAdoption,
25891 local_adaptations: vec!["Minor translation adjustments".to_string()],
25892 implementation_status: ImplementationStatus::Implemented,
25893 notes: "Fully adopted".to_string(),
25894 };
25895
25896 model_law.add_adoption(adoption);
25897
25898 assert_eq!(model_law.adoptions.len(), 1);
25899 assert_eq!(model_law.adoptions[0].jurisdiction, "JP");
25900 assert_eq!(
25901 model_law.adoptions[0].adoption_level,
25902 AdoptionLevel::FullAdoption
25903 );
25904 }
25905
25906 #[test]
25907 fn test_model_law_adoption_rate() {
25908 let mut model_law = ModelLaw::new(
25909 "Model Law".to_string(),
25910 "UNCITRAL".to_string(),
25911 "1.0".to_string(),
25912 "Commerce".to_string(),
25913 "Text".to_string(),
25914 );
25915
25916 for i in 0..3 {
25918 model_law.add_adoption(ModelLawAdoption {
25919 jurisdiction: format!("Country{}", i),
25920 adoption_date: "2023-01-01".to_string(),
25921 adoption_level: AdoptionLevel::FullAdoption,
25922 local_adaptations: Vec::new(),
25923 implementation_status: ImplementationStatus::Implemented,
25924 notes: String::new(),
25925 });
25926 }
25927
25928 let rate = model_law.get_adoption_rate(10);
25929 assert_eq!(rate, 0.3); }
25931
25932 #[test]
25933 fn test_model_law_full_adoptions_filter() {
25934 let mut model_law = ModelLaw::new(
25935 "Model Law".to_string(),
25936 "UNCITRAL".to_string(),
25937 "1.0".to_string(),
25938 "Commerce".to_string(),
25939 "Text".to_string(),
25940 );
25941
25942 model_law.add_adoption(ModelLawAdoption {
25943 jurisdiction: "JP".to_string(),
25944 adoption_date: "2023-01-01".to_string(),
25945 adoption_level: AdoptionLevel::FullAdoption,
25946 local_adaptations: Vec::new(),
25947 implementation_status: ImplementationStatus::Implemented,
25948 notes: String::new(),
25949 });
25950
25951 model_law.add_adoption(ModelLawAdoption {
25952 jurisdiction: "US".to_string(),
25953 adoption_date: "2023-01-01".to_string(),
25954 adoption_level: AdoptionLevel::PartialAdoption,
25955 local_adaptations: Vec::new(),
25956 implementation_status: ImplementationStatus::Implemented,
25957 notes: String::new(),
25958 });
25959
25960 let full_adoptions = model_law.get_full_adoptions();
25961 assert_eq!(full_adoptions.len(), 1);
25962 assert_eq!(full_adoptions[0].jurisdiction, "JP");
25963 }
25964
25965 #[test]
25966 fn test_treaty_based_porting_creation() {
25967 let treaty = TreatyBasedPorting::new(
25968 "GDPR Adequacy Agreement".to_string(),
25969 TreatyType::Bilateral,
25970 vec!["EU".to_string(), "JP".to_string()],
25971 );
25972
25973 assert!(!treaty.treaty_id.is_empty());
25974 assert_eq!(treaty.treaty_name, "GDPR Adequacy Agreement");
25975 assert_eq!(treaty.treaty_type, TreatyType::Bilateral);
25976 assert_eq!(treaty.signatories.len(), 2);
25977 assert_eq!(treaty.status, TreatyStatus::Negotiation);
25978 assert!(treaty.provisions.is_empty());
25979 }
25980
25981 #[test]
25982 fn test_treaty_provision_management() {
25983 let mut treaty = TreatyBasedPorting::new(
25984 "Treaty".to_string(),
25985 TreatyType::Multilateral,
25986 vec!["JP".to_string(), "US".to_string()],
25987 );
25988
25989 let provision = TreatyProvision {
25990 id: uuid::Uuid::new_v4().to_string(),
25991 article_number: "Article 1".to_string(),
25992 text: "Data protection requirements".to_string(),
25993 binding: true,
25994 implementation_deadline: Some("2024-01-01".to_string()),
25995 related_law_areas: vec!["Data Protection".to_string()],
25996 };
25997
25998 treaty.add_provision(provision);
25999
26000 assert_eq!(treaty.provisions.len(), 1);
26001 assert_eq!(treaty.provisions[0].article_number, "Article 1");
26002 assert!(treaty.provisions[0].binding);
26003 }
26004
26005 #[test]
26006 fn test_treaty_compliance_rate() {
26007 let mut treaty = TreatyBasedPorting::new(
26008 "Treaty".to_string(),
26009 TreatyType::Multilateral,
26010 vec!["JP".to_string(), "US".to_string()],
26011 );
26012
26013 let requirement1 = HarmonizationRequirement {
26014 id: uuid::Uuid::new_v4().to_string(),
26015 description: "Req 1".to_string(),
26016 harmonization_level: HarmonizationLevel::Complete,
26017 affected_areas: Vec::new(),
26018 deadline: None,
26019 compliance_status: vec![
26020 ("JP".to_string(), ComplianceLevel::FullCompliance),
26021 ("US".to_string(), ComplianceLevel::PartialCompliance),
26022 ],
26023 };
26024
26025 let requirement2 = HarmonizationRequirement {
26026 id: uuid::Uuid::new_v4().to_string(),
26027 description: "Req 2".to_string(),
26028 harmonization_level: HarmonizationLevel::Substantial,
26029 affected_areas: Vec::new(),
26030 deadline: None,
26031 compliance_status: vec![
26032 ("JP".to_string(), ComplianceLevel::FullCompliance),
26033 ("US".to_string(), ComplianceLevel::NonCompliance),
26034 ],
26035 };
26036
26037 treaty.add_harmonization_requirement(requirement1);
26038 treaty.add_harmonization_requirement(requirement2);
26039
26040 let jp_rate = treaty.get_compliance_rate("JP");
26041 assert_eq!(jp_rate, 1.0); let us_rate = treaty.get_compliance_rate("US");
26044 assert_eq!(us_rate, 0.0); }
26046
26047 #[test]
26048 fn test_harmonization_levels() {
26049 let levels = [
26050 HarmonizationLevel::Complete,
26051 HarmonizationLevel::Substantial,
26052 HarmonizationLevel::MinimumStandards,
26053 HarmonizationLevel::MutualRecognition,
26054 HarmonizationLevel::Coordination,
26055 ];
26056
26057 assert_eq!(levels.len(), 5);
26058 assert_eq!(levels[0], HarmonizationLevel::Complete);
26059 }
26060
26061 #[test]
26062 fn test_international_standard_creation() {
26063 let standard = InternationalStandard::new(
26064 "ISO 27001".to_string(),
26065 "ISO".to_string(),
26066 "27001:2013".to_string(),
26067 "Information Security".to_string(),
26068 StandardType::Cybersecurity,
26069 );
26070
26071 assert!(!standard.id.is_empty());
26072 assert_eq!(standard.name, "ISO 27001");
26073 assert_eq!(standard.issuing_body, "ISO");
26074 assert_eq!(standard.standard_number, "27001:2013");
26075 assert_eq!(standard.standard_type, StandardType::Cybersecurity);
26076 assert!(standard.alignment_status.is_empty());
26077 }
26078
26079 #[test]
26080 fn test_international_standard_alignment_rate() {
26081 let mut standard = InternationalStandard::new(
26082 "ISO 9001".to_string(),
26083 "ISO".to_string(),
26084 "9001:2015".to_string(),
26085 "Quality Management".to_string(),
26086 StandardType::Quality,
26087 );
26088
26089 standard.alignment_status.push(AlignmentStatus {
26090 jurisdiction: "JP".to_string(),
26091 alignment_level: AlignmentLevel::FullyAligned,
26092 deviations: Vec::new(),
26093 planned_actions: Vec::new(),
26094 last_assessment: chrono::Utc::now().to_rfc3339(),
26095 });
26096
26097 standard.alignment_status.push(AlignmentStatus {
26098 jurisdiction: "US".to_string(),
26099 alignment_level: AlignmentLevel::SubstantiallyAligned,
26100 deviations: vec!["Minor deviation".to_string()],
26101 planned_actions: Vec::new(),
26102 last_assessment: chrono::Utc::now().to_rfc3339(),
26103 });
26104
26105 standard.alignment_status.push(AlignmentStatus {
26106 jurisdiction: "GB".to_string(),
26107 alignment_level: AlignmentLevel::PartiallyAligned,
26108 deviations: Vec::new(),
26109 planned_actions: Vec::new(),
26110 last_assessment: chrono::Utc::now().to_rfc3339(),
26111 });
26112
26113 let rate = standard.get_global_alignment_rate();
26114 assert!((rate - 0.666).abs() < 0.01); }
26116
26117 #[test]
26118 fn test_standard_types() {
26119 let types = [
26120 StandardType::Technical,
26121 StandardType::Safety,
26122 StandardType::Quality,
26123 StandardType::Environmental,
26124 StandardType::DataProtection,
26125 StandardType::Cybersecurity,
26126 StandardType::BestPractice,
26127 ];
26128
26129 assert_eq!(types.len(), 7);
26130 }
26131
26132 #[test]
26133 fn test_best_practice_creation() {
26134 let practice = BestPractice::new(
26135 "Regulatory Sandbox".to_string(),
26136 "Financial Regulation".to_string(),
26137 "Allow innovation under controlled conditions".to_string(),
26138 );
26139
26140 assert!(!practice.id.is_empty());
26141 assert_eq!(practice.name, "Regulatory Sandbox");
26142 assert_eq!(practice.legal_area, "Financial Regulation");
26143 assert!(practice.evidence.is_empty());
26144 assert!(practice.adoptions.is_empty());
26145 }
26146
26147 #[test]
26148 fn test_best_practice_success_rate() {
26149 let mut practice = BestPractice::new(
26150 "Practice".to_string(),
26151 "Area".to_string(),
26152 "Description".to_string(),
26153 );
26154
26155 practice.adoptions.push(BestPracticeAdoption {
26156 jurisdiction: "JP".to_string(),
26157 adoption_date: "2023-01-01".to_string(),
26158 adaptations: Vec::new(),
26159 outcome: OutcomeAssessment {
26160 success_level: SuccessLevel::HighlySuccessful,
26161 impact_metrics: Vec::new(),
26162 challenges: Vec::new(),
26163 assessment_date: chrono::Utc::now().to_rfc3339(),
26164 },
26165 lessons_learned: Vec::new(),
26166 });
26167
26168 practice.adoptions.push(BestPracticeAdoption {
26169 jurisdiction: "US".to_string(),
26170 adoption_date: "2023-01-01".to_string(),
26171 adaptations: Vec::new(),
26172 outcome: OutcomeAssessment {
26173 success_level: SuccessLevel::Successful,
26174 impact_metrics: Vec::new(),
26175 challenges: Vec::new(),
26176 assessment_date: chrono::Utc::now().to_rfc3339(),
26177 },
26178 lessons_learned: Vec::new(),
26179 });
26180
26181 practice.adoptions.push(BestPracticeAdoption {
26182 jurisdiction: "GB".to_string(),
26183 adoption_date: "2023-01-01".to_string(),
26184 adaptations: Vec::new(),
26185 outcome: OutcomeAssessment {
26186 success_level: SuccessLevel::LimitedSuccess,
26187 impact_metrics: Vec::new(),
26188 challenges: Vec::new(),
26189 assessment_date: chrono::Utc::now().to_rfc3339(),
26190 },
26191 lessons_learned: Vec::new(),
26192 });
26193
26194 let rate = practice.get_success_rate();
26195 assert!((rate - 0.666).abs() < 0.01); }
26197
26198 #[test]
26199 fn test_evidence_types() {
26200 let types = [
26201 EvidenceType::EmpiricalResearch,
26202 EvidenceType::CaseStudy,
26203 EvidenceType::ExpertOpinion,
26204 EvidenceType::StatisticalData,
26205 EvidenceType::ComparativeAnalysis,
26206 EvidenceType::ImplementationReport,
26207 ];
26208
26209 assert_eq!(types.len(), 6);
26210 }
26211
26212 #[test]
26213 fn test_soft_law_conversion_creation() {
26214 let soft_law = SoftLawSource {
26215 id: uuid::Uuid::new_v4().to_string(),
26216 name: "UN Guiding Principles on Business and Human Rights".to_string(),
26217 source_type: SoftLawType::Principles,
26218 issuing_body: "United Nations".to_string(),
26219 content: "Protect, Respect, Remedy framework".to_string(),
26220 binding_force: BindingForce::MoralObligation,
26221 endorsements: vec!["Multiple countries".to_string()],
26222 };
26223
26224 let hard_law = HardLawTarget {
26225 jurisdiction: "JP".to_string(),
26226 instrument_type: LegalInstrumentType::PrimaryLegislation,
26227 draft_legislation: "Draft Act on Corporate Due Diligence".to_string(),
26228 enforcement_mechanisms: vec!["Fines".to_string(), "Sanctions".to_string()],
26229 penalties: vec!["Up to ¥100M fine".to_string()],
26230 };
26231
26232 let strategy = ConversionStrategy {
26233 strategy_type: ConversionStrategyType::AdaptiveIncorporation,
26234 rationale: "Adapt to Japanese legal context".to_string(),
26235 adaptations: vec!["Adjust to keiretsu structure".to_string()],
26236 risks: vec![(
26237 "Business resistance".to_string(),
26238 "Gradual phase-in".to_string(),
26239 )],
26240 timeline: "2 years".to_string(),
26241 };
26242
26243 let conversion = SoftLawConversion::new(soft_law, hard_law, strategy);
26244
26245 assert!(!conversion.id.is_empty());
26246 assert_eq!(
26247 conversion.soft_law_source.name,
26248 "UN Guiding Principles on Business and Human Rights"
26249 );
26250 assert_eq!(conversion.target_hard_law.jurisdiction, "JP");
26251 assert_eq!(conversion.status, ConversionStatus::Planning);
26252 assert!(conversion.implementation_steps.is_empty());
26253 }
26254
26255 #[test]
26256 fn test_soft_law_conversion_progress() {
26257 let soft_law = SoftLawSource {
26258 id: uuid::Uuid::new_v4().to_string(),
26259 name: "Guidelines".to_string(),
26260 source_type: SoftLawType::Guidelines,
26261 issuing_body: "UN".to_string(),
26262 content: "Content".to_string(),
26263 binding_force: BindingForce::NonBinding,
26264 endorsements: Vec::new(),
26265 };
26266
26267 let hard_law = HardLawTarget {
26268 jurisdiction: "US".to_string(),
26269 instrument_type: LegalInstrumentType::SecondaryLegislation,
26270 draft_legislation: "Draft".to_string(),
26271 enforcement_mechanisms: Vec::new(),
26272 penalties: Vec::new(),
26273 };
26274
26275 let strategy = ConversionStrategy {
26276 strategy_type: ConversionStrategyType::DirectIncorporation,
26277 rationale: "Direct".to_string(),
26278 adaptations: Vec::new(),
26279 risks: Vec::new(),
26280 timeline: "1 year".to_string(),
26281 };
26282
26283 let mut conversion = SoftLawConversion::new(soft_law, hard_law, strategy);
26284
26285 conversion.add_implementation_step(ConversionImplementationStep {
26286 step_number: 1,
26287 description: "Step 1".to_string(),
26288 responsible_party: "Ministry".to_string(),
26289 deadline: None,
26290 status: ConversionStepStatus::Completed,
26291 dependencies: Vec::new(),
26292 });
26293
26294 conversion.add_implementation_step(ConversionImplementationStep {
26295 step_number: 2,
26296 description: "Step 2".to_string(),
26297 responsible_party: "Ministry".to_string(),
26298 deadline: None,
26299 status: ConversionStepStatus::InProgress,
26300 dependencies: vec![1],
26301 });
26302
26303 let progress = conversion.get_implementation_progress();
26304 assert_eq!(progress, 50.0); }
26306
26307 #[test]
26308 fn test_soft_law_types() {
26309 let types = [
26310 SoftLawType::UNResolution,
26311 SoftLawType::Guidelines,
26312 SoftLawType::Recommendations,
26313 SoftLawType::Principles,
26314 SoftLawType::CodeOfConduct,
26315 SoftLawType::Declaration,
26316 SoftLawType::BestPractices,
26317 SoftLawType::Standards,
26318 ];
26319
26320 assert_eq!(types.len(), 8);
26321 }
26322
26323 #[test]
26324 fn test_binding_force_levels() {
26325 let forces = [
26326 BindingForce::NonBinding,
26327 BindingForce::PoliticalCommitment,
26328 BindingForce::MoralObligation,
26329 BindingForce::QuasiLegal,
26330 BindingForce::LegallyBinding,
26331 ];
26332
26333 assert_eq!(forces.len(), 5);
26334 }
26335
26336 #[test]
26337 fn test_legal_instrument_types() {
26338 let types = [
26339 LegalInstrumentType::PrimaryLegislation,
26340 LegalInstrumentType::SecondaryLegislation,
26341 LegalInstrumentType::ConstitutionalAmendment,
26342 LegalInstrumentType::TreatyImplementation,
26343 LegalInstrumentType::AdministrativeRule,
26344 ];
26345
26346 assert_eq!(types.len(), 5);
26347 }
26348
26349 #[test]
26350 fn test_conversion_strategy_types() {
26351 let strategies = [
26352 ConversionStrategyType::DirectIncorporation,
26353 ConversionStrategyType::AdaptiveIncorporation,
26354 ConversionStrategyType::InspiredLegislation,
26355 ConversionStrategyType::PhasedImplementation,
26356 ConversionStrategyType::PilotProgram,
26357 ];
26358
26359 assert_eq!(strategies.len(), 5);
26360 }
26361
26362 #[test]
26363 fn test_conversion_step_status() {
26364 let statuses = [
26365 ConversionStepStatus::NotStarted,
26366 ConversionStepStatus::InProgress,
26367 ConversionStepStatus::Completed,
26368 ConversionStepStatus::Blocked,
26369 ConversionStepStatus::Cancelled,
26370 ];
26371
26372 assert_eq!(statuses.len(), 5);
26373 }
26374
26375 #[test]
26376 fn test_treaty_status_transitions() {
26377 let statuses = [
26378 TreatyStatus::Negotiation,
26379 TreatyStatus::Signed,
26380 TreatyStatus::InForce,
26381 TreatyStatus::Suspended,
26382 TreatyStatus::Terminated,
26383 ];
26384
26385 assert_eq!(statuses.len(), 5);
26386 }
26387
26388 #[test]
26389 fn test_adoption_priority_ordering() {
26390 let mut priorities = [
26391 AdoptionPriority::Low,
26392 AdoptionPriority::Critical,
26393 AdoptionPriority::Medium,
26394 AdoptionPriority::High,
26395 ];
26396
26397 priorities.sort();
26398
26399 assert_eq!(priorities[0], AdoptionPriority::Critical);
26400 assert_eq!(priorities[1], AdoptionPriority::High);
26401 assert_eq!(priorities[2], AdoptionPriority::Medium);
26402 assert_eq!(priorities[3], AdoptionPriority::Low);
26403 }
26404
26405 #[test]
26410 fn test_regulatory_change_tracker_creation() {
26411 let tracker = RegulatoryChangeTracker::new(
26412 vec!["JP".to_string(), "US".to_string()],
26413 vec![
26414 "Data Protection".to_string(),
26415 "Financial Services".to_string(),
26416 ],
26417 );
26418
26419 assert!(!tracker.id.is_empty());
26420 assert_eq!(tracker.monitored_jurisdictions.len(), 2);
26421 assert_eq!(tracker.tracked_areas.len(), 2);
26422 assert_eq!(tracker.status, TrackerStatus::Active);
26423 assert!(tracker.detected_changes.is_empty());
26424 }
26425
26426 #[test]
26427 fn test_add_regulatory_change() {
26428 let mut tracker =
26429 RegulatoryChangeTracker::new(vec!["JP".to_string()], vec!["Privacy".to_string()]);
26430
26431 let change = RegulatoryChange {
26432 id: uuid::Uuid::new_v4().to_string(),
26433 jurisdiction: "JP".to_string(),
26434 regulatory_area: "Privacy".to_string(),
26435 change_type: RegulatoryChangeType::NewLegislation,
26436 description: "New privacy law enacted".to_string(),
26437 source_reference: "Act No. 123".to_string(),
26438 detected_at: chrono::Utc::now().to_rfc3339(),
26439 effective_date: Some("2024-06-01".to_string()),
26440 impact_severity: ImpactSeverity::Severe,
26441 affected_statutes: vec!["Privacy Act".to_string()],
26442 porting_implications: vec!["Requires updates to ported statutes".to_string()],
26443 };
26444
26445 tracker.add_change(change);
26446
26447 assert_eq!(tracker.detected_changes.len(), 1);
26448 assert_eq!(tracker.detected_changes[0].jurisdiction, "JP");
26449 assert_eq!(
26450 tracker.detected_changes[0].change_type,
26451 RegulatoryChangeType::NewLegislation
26452 );
26453 }
26454
26455 #[test]
26456 fn test_get_changes_by_jurisdiction() {
26457 let mut tracker = RegulatoryChangeTracker::new(
26458 vec!["JP".to_string(), "US".to_string()],
26459 vec!["Privacy".to_string()],
26460 );
26461
26462 tracker.add_change(RegulatoryChange {
26463 id: uuid::Uuid::new_v4().to_string(),
26464 jurisdiction: "JP".to_string(),
26465 regulatory_area: "Privacy".to_string(),
26466 change_type: RegulatoryChangeType::NewLegislation,
26467 description: "JP law".to_string(),
26468 source_reference: "Act No. 1".to_string(),
26469 detected_at: chrono::Utc::now().to_rfc3339(),
26470 effective_date: None,
26471 impact_severity: ImpactSeverity::Severe,
26472 affected_statutes: Vec::new(),
26473 porting_implications: Vec::new(),
26474 });
26475
26476 tracker.add_change(RegulatoryChange {
26477 id: uuid::Uuid::new_v4().to_string(),
26478 jurisdiction: "US".to_string(),
26479 regulatory_area: "Privacy".to_string(),
26480 change_type: RegulatoryChangeType::Amendment,
26481 description: "US law".to_string(),
26482 source_reference: "USC 123".to_string(),
26483 detected_at: chrono::Utc::now().to_rfc3339(),
26484 effective_date: None,
26485 impact_severity: ImpactSeverity::Moderate,
26486 affected_statutes: Vec::new(),
26487 porting_implications: Vec::new(),
26488 });
26489
26490 let jp_changes = tracker.get_changes_by_jurisdiction("JP");
26491 assert_eq!(jp_changes.len(), 1);
26492 assert_eq!(jp_changes[0].jurisdiction, "JP");
26493 }
26494
26495 #[test]
26496 fn test_get_critical_changes() {
26497 let mut tracker =
26498 RegulatoryChangeTracker::new(vec!["JP".to_string()], vec!["Security".to_string()]);
26499
26500 tracker.add_change(RegulatoryChange {
26501 id: uuid::Uuid::new_v4().to_string(),
26502 jurisdiction: "JP".to_string(),
26503 regulatory_area: "Security".to_string(),
26504 change_type: RegulatoryChangeType::EmergencyOrder,
26505 description: "Critical change".to_string(),
26506 source_reference: "Emergency Order 1".to_string(),
26507 detected_at: chrono::Utc::now().to_rfc3339(),
26508 effective_date: None,
26509 impact_severity: ImpactSeverity::Severe,
26510 affected_statutes: Vec::new(),
26511 porting_implications: Vec::new(),
26512 });
26513
26514 tracker.add_change(RegulatoryChange {
26515 id: uuid::Uuid::new_v4().to_string(),
26516 jurisdiction: "JP".to_string(),
26517 regulatory_area: "Security".to_string(),
26518 change_type: RegulatoryChangeType::AdministrativeGuidance,
26519 description: "Low priority change".to_string(),
26520 source_reference: "Guidance 1".to_string(),
26521 detected_at: chrono::Utc::now().to_rfc3339(),
26522 effective_date: None,
26523 impact_severity: ImpactSeverity::Minor,
26524 affected_statutes: Vec::new(),
26525 porting_implications: Vec::new(),
26526 });
26527
26528 let critical = tracker.get_critical_changes();
26529 assert_eq!(critical.len(), 1);
26530 assert_eq!(critical[0].impact_severity, ImpactSeverity::Severe);
26531 }
26532
26533 #[test]
26534 fn test_automatic_porting_trigger_creation() {
26535 let trigger = AutomaticPortingTrigger::new(
26536 "Auto-port privacy laws".to_string(),
26537 "JP".to_string(),
26538 vec!["US".to_string(), "GB".to_string()],
26539 PortingOptions::default(),
26540 );
26541
26542 assert!(!trigger.id.is_empty());
26543 assert_eq!(trigger.name, "Auto-port privacy laws");
26544 assert_eq!(trigger.source_jurisdiction, "JP");
26545 assert_eq!(trigger.target_jurisdictions.len(), 2);
26546 assert_eq!(trigger.status, TriggerStatus::Active);
26547 assert!(trigger.conditions.is_empty());
26548 }
26549
26550 #[test]
26551 fn test_trigger_condition_checking() {
26552 let mut trigger = AutomaticPortingTrigger::new(
26553 "Test trigger".to_string(),
26554 "JP".to_string(),
26555 vec!["US".to_string()],
26556 PortingOptions::default(),
26557 );
26558
26559 trigger.add_condition(TriggerCondition {
26560 id: uuid::Uuid::new_v4().to_string(),
26561 condition_type: TriggerConditionType::NewLegislation,
26562 parameters: Vec::new(),
26563 is_met: true,
26564 });
26565
26566 trigger.add_condition(TriggerCondition {
26567 id: uuid::Uuid::new_v4().to_string(),
26568 condition_type: TriggerConditionType::StatuteAmendment,
26569 parameters: Vec::new(),
26570 is_met: true,
26571 });
26572
26573 assert!(trigger.check_conditions());
26574 }
26575
26576 #[test]
26577 fn test_trigger_execution_tracking() {
26578 let mut trigger = AutomaticPortingTrigger::new(
26579 "Test trigger".to_string(),
26580 "JP".to_string(),
26581 vec!["US".to_string()],
26582 PortingOptions::default(),
26583 );
26584
26585 trigger.record_execution(TriggerExecution {
26586 id: uuid::Uuid::new_v4().to_string(),
26587 executed_at: chrono::Utc::now().to_rfc3339(),
26588 triggered_by: vec!["NewLegislation".to_string()],
26589 porting_results: vec!["statute_123".to_string()],
26590 success: true,
26591 notes: "Successful execution".to_string(),
26592 });
26593
26594 trigger.record_execution(TriggerExecution {
26595 id: uuid::Uuid::new_v4().to_string(),
26596 executed_at: chrono::Utc::now().to_rfc3339(),
26597 triggered_by: vec!["StatuteAmendment".to_string()],
26598 porting_results: Vec::new(),
26599 success: false,
26600 notes: "Failed execution".to_string(),
26601 });
26602
26603 assert_eq!(trigger.execution_history.len(), 2);
26604 assert_eq!(trigger.get_success_rate(), 0.5); }
26606
26607 #[test]
26608 fn test_adaptation_alert_creation() {
26609 let alert = AdaptationAlert::new(
26610 "Critical Adaptation Needed".to_string(),
26611 "GDPR compliance gap identified".to_string(),
26612 AlertSeverity::Urgent,
26613 vec!["JP".to_string(), "US".to_string()],
26614 );
26615
26616 assert!(!alert.id.is_empty());
26617 assert_eq!(alert.title, "Critical Adaptation Needed");
26618 assert_eq!(alert.severity, AlertSeverity::Urgent);
26619 assert_eq!(alert.status, AlertStatus::Active);
26620 assert_eq!(alert.affected_jurisdictions.len(), 2);
26621 }
26622
26623 #[test]
26624 fn test_alert_acknowledgment() {
26625 let mut alert = AdaptationAlert::new(
26626 "Test Alert".to_string(),
26627 "Description".to_string(),
26628 AlertSeverity::High,
26629 vec!["JP".to_string()],
26630 );
26631
26632 assert_eq!(alert.status, AlertStatus::Active);
26633
26634 alert.acknowledge();
26635 assert_eq!(alert.status, AlertStatus::Acknowledged);
26636 }
26637
26638 #[test]
26639 fn test_alert_recommended_actions() {
26640 let mut alert = AdaptationAlert::new(
26641 "Test Alert".to_string(),
26642 "Description".to_string(),
26643 AlertSeverity::Medium,
26644 vec!["JP".to_string()],
26645 );
26646
26647 alert.add_action(RecommendedAction {
26648 id: uuid::Uuid::new_v4().to_string(),
26649 action: "Immediate review required".to_string(),
26650 priority: ActionPriority::Immediate,
26651 estimated_effort: "2 hours".to_string(),
26652 deadline: Some("2024-01-01".to_string()),
26653 prerequisites: Vec::new(),
26654 });
26655
26656 alert.add_action(RecommendedAction {
26657 id: uuid::Uuid::new_v4().to_string(),
26658 action: "Long-term planning".to_string(),
26659 priority: ActionPriority::LongTerm,
26660 estimated_effort: "1 week".to_string(),
26661 deadline: None,
26662 prerequisites: Vec::new(),
26663 });
26664
26665 assert_eq!(alert.recommended_actions.len(), 2);
26666
26667 let high_priority = alert.get_high_priority_actions();
26668 assert_eq!(high_priority.len(), 1);
26669 assert_eq!(high_priority[0].priority, ActionPriority::Immediate);
26670 }
26671
26672 #[test]
26673 fn test_emerging_law_warning_creation() {
26674 let warning = EmergingLawWarning::new(
26675 "AI Regulation Emerging".to_string(),
26676 "JP".to_string(),
26677 "New AI safety regulations being drafted".to_string(),
26678 WarningLevel::NearTerm,
26679 0.75,
26680 );
26681
26682 assert!(!warning.id.is_empty());
26683 assert_eq!(warning.title, "AI Regulation Emerging");
26684 assert_eq!(warning.jurisdiction, "JP");
26685 assert_eq!(warning.warning_level, WarningLevel::NearTerm);
26686 assert_eq!(warning.confidence_score, 0.75);
26687 assert!(warning.data_sources.is_empty());
26688 }
26689
26690 #[test]
26691 fn test_emerging_law_data_sources() {
26692 let mut warning = EmergingLawWarning::new(
26693 "Test Warning".to_string(),
26694 "US".to_string(),
26695 "Description".to_string(),
26696 WarningLevel::MediumTerm,
26697 0.65,
26698 );
26699
26700 warning.add_data_source(DataSource {
26701 source_type: SourceType::LegislativeProposal,
26702 source_id: "HB-123".to_string(),
26703 description: "House Bill 123".to_string(),
26704 reliability: 0.9,
26705 last_accessed: chrono::Utc::now().to_rfc3339(),
26706 });
26707
26708 warning.add_data_source(DataSource {
26709 source_type: SourceType::MediaCoverage,
26710 source_id: "News-456".to_string(),
26711 description: "News article".to_string(),
26712 reliability: 0.6,
26713 last_accessed: chrono::Utc::now().to_rfc3339(),
26714 });
26715
26716 assert_eq!(warning.data_sources.len(), 2);
26717 let avg_reliability = warning.get_average_reliability();
26718 assert!((avg_reliability - 0.75).abs() < 0.01); }
26720
26721 #[test]
26722 fn test_emerging_law_indicators() {
26723 let mut warning = EmergingLawWarning::new(
26724 "Test Warning".to_string(),
26725 "JP".to_string(),
26726 "Description".to_string(),
26727 WarningLevel::LongTerm,
26728 0.5,
26729 );
26730
26731 warning.add_indicator(EmergingLawIndicator {
26732 name: "Legislative activity".to_string(),
26733 value: 8.5,
26734 threshold: 7.0,
26735 trend: TrendDirection::Increasing,
26736 last_measured: chrono::Utc::now().to_rfc3339(),
26737 });
26738
26739 warning.add_indicator(EmergingLawIndicator {
26740 name: "Public interest".to_string(),
26741 value: 4.0,
26742 threshold: 5.0,
26743 trend: TrendDirection::Stable,
26744 last_measured: chrono::Utc::now().to_rfc3339(),
26745 });
26746
26747 assert_eq!(warning.indicators.len(), 2);
26748 assert!(warning.has_threshold_breach()); }
26750
26751 #[test]
26752 fn test_predictive_porting_recommendation_creation() {
26753 let timing = RecommendedTiming {
26754 optimal_start: "2024-01-01".to_string(),
26755 latest_start: "2024-03-01".to_string(),
26756 expected_duration: "6 months".to_string(),
26757 rationale: "Window of political opportunity".to_string(),
26758 opportunity_factors: vec!["Legislative session".to_string()],
26759 };
26760
26761 let recommendation = PredictivePortingRecommendation::new(
26762 "JP".to_string(),
26763 "US".to_string(),
26764 "Data Protection Act".to_string(),
26765 "High compatibility and need".to_string(),
26766 0.85,
26767 timing,
26768 "v2.0".to_string(),
26769 );
26770
26771 assert!(!recommendation.id.is_empty());
26772 assert_eq!(recommendation.source_jurisdiction, "JP");
26773 assert_eq!(recommendation.target_jurisdiction, "US");
26774 assert_eq!(recommendation.success_probability, 0.85);
26775 assert_eq!(recommendation.model_version, "v2.0");
26776 }
26777
26778 #[test]
26779 fn test_predicted_benefits_and_challenges() {
26780 let timing = RecommendedTiming {
26781 optimal_start: "2024-01-01".to_string(),
26782 latest_start: "2024-03-01".to_string(),
26783 expected_duration: "6 months".to_string(),
26784 rationale: "Good timing".to_string(),
26785 opportunity_factors: Vec::new(),
26786 };
26787
26788 let mut recommendation = PredictivePortingRecommendation::new(
26789 "JP".to_string(),
26790 "US".to_string(),
26791 "Test Statute".to_string(),
26792 "Test reason".to_string(),
26793 0.8,
26794 timing,
26795 "v1.0".to_string(),
26796 );
26797
26798 recommendation.add_benefit(PredictedBenefit {
26799 benefit_type: BenefitType::LegalHarmonization,
26800 description: "Improved harmonization".to_string(),
26801 impact_score: 0.9,
26802 time_to_realization: "1 year".to_string(),
26803 });
26804
26805 recommendation.add_benefit(PredictedBenefit {
26806 benefit_type: BenefitType::EconomicEfficiency,
26807 description: "Cost savings".to_string(),
26808 impact_score: 0.7,
26809 time_to_realization: "2 years".to_string(),
26810 });
26811
26812 recommendation.add_challenge(PredictedChallenge {
26813 challenge_type: ChallengeType::CulturalIncompatibility,
26814 description: "Cultural differences".to_string(),
26815 severity_score: 0.4,
26816 mitigation_strategies: vec!["Adaptation".to_string()],
26817 });
26818
26819 assert_eq!(recommendation.predicted_benefits.len(), 2);
26820 assert_eq!(recommendation.predicted_challenges.len(), 1);
26821
26822 let benefit_score = recommendation.get_benefit_score();
26823 assert!((benefit_score - 0.8).abs() < 0.01); let challenge_severity = recommendation.get_challenge_severity();
26826 assert_eq!(challenge_severity, 0.4);
26827
26828 let risk_adjusted = recommendation.get_risk_adjusted_probability();
26829 assert!((risk_adjusted - 0.68).abs() < 0.01); }
26831
26832 #[test]
26833 fn test_regulatory_change_types() {
26834 let types = [
26835 RegulatoryChangeType::NewLegislation,
26836 RegulatoryChangeType::Amendment,
26837 RegulatoryChangeType::Repeal,
26838 RegulatoryChangeType::NewRegulation,
26839 RegulatoryChangeType::CourtDecision,
26840 RegulatoryChangeType::AdministrativeGuidance,
26841 RegulatoryChangeType::EmergencyOrder,
26842 RegulatoryChangeType::SunsetProvision,
26843 ];
26844
26845 assert_eq!(types.len(), 8);
26846 }
26847
26848 #[test]
26849 fn test_impact_severity_ordering() {
26850 let severities = [
26851 ImpactSeverity::Minor,
26852 ImpactSeverity::Severe,
26853 ImpactSeverity::Moderate,
26854 ImpactSeverity::Negligible,
26855 ];
26856
26857 assert_eq!(severities.len(), 4);
26858 }
26859
26860 #[test]
26861 fn test_v32_notification_channels() {
26862 let channels = [
26863 NotificationChannel::Email,
26864 NotificationChannel::Sms,
26865 NotificationChannel::Website,
26866 NotificationChannel::Webhook,
26867 NotificationChannel::InApp,
26868 NotificationChannel::PublicNotice,
26869 ];
26870
26871 assert_eq!(channels.len(), 6);
26872 }
26873
26874 #[test]
26875 fn test_alert_severity_ordering() {
26876 let mut severities = [
26877 AlertSeverity::Low,
26878 AlertSeverity::Urgent,
26879 AlertSeverity::Medium,
26880 AlertSeverity::High,
26881 AlertSeverity::Info,
26882 ];
26883
26884 severities.sort();
26885
26886 assert_eq!(severities[0], AlertSeverity::Urgent);
26887 assert_eq!(severities[4], AlertSeverity::Info);
26888 }
26889
26890 #[test]
26891 fn test_warning_level_ordering() {
26892 let mut levels = [
26893 WarningLevel::LongTerm,
26894 WarningLevel::Imminent,
26895 WarningLevel::MediumTerm,
26896 WarningLevel::NearTerm,
26897 WarningLevel::EarlySignal,
26898 ];
26899
26900 levels.sort();
26901
26902 assert_eq!(levels[0], WarningLevel::Imminent);
26903 assert_eq!(levels[4], WarningLevel::EarlySignal);
26904 }
26905
26906 #[test]
26907 fn test_source_types() {
26908 let types = [
26909 SourceType::LegislativeProposal,
26910 SourceType::PolicyWhitePaper,
26911 SourceType::ParliamentaryDebate,
26912 SourceType::RegulatoryConsultation,
26913 SourceType::AcademicResearch,
26914 SourceType::IndustryReport,
26915 SourceType::MediaCoverage,
26916 SourceType::InternationalTrend,
26917 ];
26918
26919 assert_eq!(types.len(), 8);
26920 }
26921
26922 #[test]
26923 fn test_benefit_types() {
26924 let types = [
26925 BenefitType::LegalHarmonization,
26926 BenefitType::EconomicEfficiency,
26927 BenefitType::ReducedComplianceBurden,
26928 BenefitType::ImprovedClarity,
26929 BenefitType::InternationalCooperation,
26930 BenefitType::InnovationEnablement,
26931 ];
26932
26933 assert_eq!(types.len(), 6);
26934 }
26935
26936 #[test]
26937 fn test_challenge_types() {
26938 let types = [
26939 ChallengeType::CulturalIncompatibility,
26940 ChallengeType::LegalSystemMismatch,
26941 ChallengeType::PoliticalResistance,
26942 ChallengeType::EconomicBarriers,
26943 ChallengeType::TechnicalDifficulty,
26944 ChallengeType::StakeholderOpposition,
26945 ];
26946
26947 assert_eq!(types.len(), 6);
26948 }
26949}