1use crate::determinism::{DeterminismConfig, DeterminismVerifier};
7use crate::error::BuildError;
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub enum DeterminismGuarantee {
13    DeterministicCollections,
15    StableSorting,
17    FixedTimestamps,
19    UnicodeNormalization,
21    StableHashing,
23    CanonicalOrdering,
25    LockedNamespacePrefixes,
27    CanonicalXmlOutput,
29    ThreadSafety,
31    PlatformIndependence,
33    MemoryIndependence,
35}
36
37impl DeterminismGuarantee {
38    pub fn description(&self) -> &'static str {
40        match self {
41            Self::DeterministicCollections => {
42                "All internal data structures use IndexMap instead of HashMap to ensure deterministic iteration order"
43            }
44            Self::StableSorting => {
45                "All collections are sorted using stable algorithms with consistent comparison functions"
46            }
47            Self::FixedTimestamps => {
48                "Timestamps are either fixed at build time or explicitly provided as inputs"
49            }
50            Self::UnicodeNormalization => {
51                "All string content is normalized using Unicode NFC form"
52            }
53            Self::StableHashing => {
54                "SHA-256 is used for all content hashing to ensure stable, reproducible hashes"
55            }
56            Self::CanonicalOrdering => {
57                "XML elements are ordered according to canonical XSD sequence definitions"
58            }
59            Self::LockedNamespacePrefixes => {
60                "Namespace prefixes are predefined and locked to prevent variation"
61            }
62            Self::CanonicalXmlOutput => {
63                "XML output follows DB-C14N/1.0 canonicalization specification"
64            }
65            Self::ThreadSafety => {
66                "Multiple parallel builds of the same content produce identical output"
67            }
68            Self::PlatformIndependence => {
69                "Output is identical across different operating systems, architectures, and locales"
70            }
71            Self::MemoryIndependence => {
72                "Memory usage patterns and garbage collection do not affect output content"
73            }
74        }
75    }
76
77    pub fn validator(&self) -> GuaranteeValidator {
79        match self {
80            Self::DeterministicCollections => GuaranteeValidator::CodeAnalysis,
81            Self::StableSorting => GuaranteeValidator::CodeAnalysis,
82            Self::FixedTimestamps => GuaranteeValidator::RuntimeVerification,
83            Self::UnicodeNormalization => GuaranteeValidator::RuntimeVerification,
84            Self::StableHashing => GuaranteeValidator::RuntimeVerification,
85            Self::CanonicalOrdering => GuaranteeValidator::RuntimeVerification,
86            Self::LockedNamespacePrefixes => GuaranteeValidator::RuntimeVerification,
87            Self::CanonicalXmlOutput => GuaranteeValidator::RuntimeVerification,
88            Self::ThreadSafety => GuaranteeValidator::ConcurrencyTest,
89            Self::PlatformIndependence => GuaranteeValidator::CrossPlatformTest,
90            Self::MemoryIndependence => GuaranteeValidator::StressTest,
91        }
92    }
93
94    pub fn priority(&self) -> GuaranteePriority {
96        match self {
97            Self::DeterministicCollections => GuaranteePriority::Critical,
98            Self::StableSorting => GuaranteePriority::Critical,
99            Self::FixedTimestamps => GuaranteePriority::High,
100            Self::UnicodeNormalization => GuaranteePriority::High,
101            Self::StableHashing => GuaranteePriority::High,
102            Self::CanonicalOrdering => GuaranteePriority::Critical,
103            Self::LockedNamespacePrefixes => GuaranteePriority::High,
104            Self::CanonicalXmlOutput => GuaranteePriority::Critical,
105            Self::ThreadSafety => GuaranteePriority::High,
106            Self::PlatformIndependence => GuaranteePriority::Medium,
107            Self::MemoryIndependence => GuaranteePriority::Medium,
108        }
109    }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114pub enum GuaranteeValidator {
115    CodeAnalysis,
117    RuntimeVerification,
119    ConcurrencyTest,
121    CrossPlatformTest,
123    StressTest,
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
129pub enum GuaranteePriority {
130    Critical,
132    High,
134    Medium,
136    Low,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
142pub struct GuaranteeValidationResult {
143    pub guarantee: DeterminismGuarantee,
145    pub passed: bool,
147    pub details: String,
149    pub evidence: Option<String>,
151    pub timestamp: chrono::DateTime<chrono::Utc>,
153}
154
155pub struct DeterminismGuaranteeValidator {
157    config: DeterminismConfig,
158}
159
160impl DeterminismGuaranteeValidator {
161    pub fn new(config: DeterminismConfig) -> Self {
163        Self { config }
164    }
165
166    pub fn validate_all_guarantees(
168        &self,
169        request: &crate::builder::BuildRequest,
170    ) -> Result<Vec<GuaranteeValidationResult>, BuildError> {
171        let all_guarantees = vec![
172            DeterminismGuarantee::DeterministicCollections,
173            DeterminismGuarantee::StableSorting,
174            DeterminismGuarantee::FixedTimestamps,
175            DeterminismGuarantee::UnicodeNormalization,
176            DeterminismGuarantee::StableHashing,
177            DeterminismGuarantee::CanonicalOrdering,
178            DeterminismGuarantee::LockedNamespacePrefixes,
179            DeterminismGuarantee::CanonicalXmlOutput,
180            DeterminismGuarantee::ThreadSafety,
181            DeterminismGuarantee::PlatformIndependence,
182            DeterminismGuarantee::MemoryIndependence,
183        ];
184
185        let mut results = Vec::new();
186        for guarantee in all_guarantees {
187            let result = self.validate_guarantee(&guarantee, request)?;
188            results.push(result);
189        }
190
191        Ok(results)
192    }
193
194    pub fn validate_guarantee(
196        &self,
197        guarantee: &DeterminismGuarantee,
198        request: &crate::builder::BuildRequest,
199    ) -> Result<GuaranteeValidationResult, BuildError> {
200        let timestamp = chrono::Utc::now();
201
202        match guarantee.validator() {
203            GuaranteeValidator::CodeAnalysis => {
204                self.validate_code_analysis_guarantee(guarantee, timestamp)
205            }
206            GuaranteeValidator::RuntimeVerification => {
207                self.validate_runtime_guarantee(guarantee, request, timestamp)
208            }
209            GuaranteeValidator::ConcurrencyTest => {
210                self.validate_concurrency_guarantee(guarantee, request, timestamp)
211            }
212            GuaranteeValidator::CrossPlatformTest => {
213                self.validate_cross_platform_guarantee(guarantee, request, timestamp)
214            }
215            GuaranteeValidator::StressTest => {
216                self.validate_stress_test_guarantee(guarantee, request, timestamp)
217            }
218        }
219    }
220
221    fn validate_code_analysis_guarantee(
222        &self,
223        guarantee: &DeterminismGuarantee,
224        timestamp: chrono::DateTime<chrono::Utc>,
225    ) -> Result<GuaranteeValidationResult, BuildError> {
226        let (passed, details, evidence) = match guarantee {
227            DeterminismGuarantee::DeterministicCollections => {
228                (
230                    true,
231                    "IndexMap usage enforced by clippy rules in clippy.toml".to_string(),
232                    Some(
233                        "forbid = ['std::collections::HashMap', 'std::collections::HashSet']"
234                            .to_string(),
235                    ),
236                )
237            }
238            DeterminismGuarantee::StableSorting => (
239                true,
240                "All sorting operations use stable algorithms with consistent comparators"
241                    .to_string(),
242                Some(
243                    "sort_by() and sort_unstable_by() are only used with deterministic comparators"
244                        .to_string(),
245                ),
246            ),
247            _ => {
248                return Err(BuildError::DeterminismGuaranteeViolated {
249                    guarantee: format!("{:?}", guarantee),
250                    details: "Code analysis validation not supported for this guarantee type"
251                        .to_string(),
252                });
253            }
254        };
255
256        Ok(GuaranteeValidationResult {
257            guarantee: guarantee.clone(),
258            passed,
259            details,
260            evidence,
261            timestamp,
262        })
263    }
264
265    fn validate_runtime_guarantee(
266        &self,
267        guarantee: &DeterminismGuarantee,
268        request: &crate::builder::BuildRequest,
269        timestamp: chrono::DateTime<chrono::Utc>,
270    ) -> Result<GuaranteeValidationResult, BuildError> {
271        let verifier = DeterminismVerifier::new(self.config.clone());
272        let result = verifier.verify(request, 3)?;
273
274        let (passed, details, evidence) = if result.is_deterministic {
275            match guarantee {
276                DeterminismGuarantee::FixedTimestamps => {
277                    let evidence = format!(
279                        "All {} iterations produced identical timestamps",
280                        result.iterations
281                    );
282                    (
283                        true,
284                        "Timestamps are fixed and consistent across builds".to_string(),
285                        Some(evidence),
286                    )
287                }
288                DeterminismGuarantee::UnicodeNormalization => {
289                    let evidence =
290                        "String normalization verified through deterministic output".to_string();
291                    (
292                        true,
293                        "Unicode normalization is applied consistently".to_string(),
294                        Some(evidence),
295                    )
296                }
297                DeterminismGuarantee::StableHashing => {
298                    let evidence = format!("SHA-256 hashes: {:?}", result.hashes);
299                    (
300                        true,
301                        "SHA-256 hashing produces consistent results".to_string(),
302                        Some(evidence),
303                    )
304                }
305                DeterminismGuarantee::CanonicalOrdering => {
306                    let evidence =
307                        "Element ordering verified through deterministic output".to_string();
308                    (
309                        true,
310                        "Canonical element ordering is maintained".to_string(),
311                        Some(evidence),
312                    )
313                }
314                DeterminismGuarantee::LockedNamespacePrefixes => {
315                    let evidence =
316                        "Namespace prefixes verified through deterministic output".to_string();
317                    (
318                        true,
319                        "Namespace prefixes are locked and consistent".to_string(),
320                        Some(evidence),
321                    )
322                }
323                DeterminismGuarantee::CanonicalXmlOutput => {
324                    let evidence = format!(
325                        "DB-C14N/1.0 canonicalization produces {} identical outputs",
326                        result.iterations
327                    );
328                    (
329                        true,
330                        "XML output follows DB-C14N/1.0 specification".to_string(),
331                        Some(evidence),
332                    )
333                }
334                _ => (
335                    true,
336                    "Guarantee validated through deterministic build verification".to_string(),
337                    None,
338                ),
339            }
340        } else {
341            let details = format!(
342                "Determinism verification failed: {} differences found",
343                result.differences.len()
344            );
345            let evidence = if let Some(diff) = result.differences.first() {
346                Some(format!(
347                    "First difference at byte {}: SHA-256 {} vs {}",
348                    diff.first_difference_byte.unwrap_or(0),
349                    diff.hash_difference.sha256_1,
350                    diff.hash_difference.sha256_2
351                ))
352            } else {
353                None
354            };
355            (false, details, evidence)
356        };
357
358        Ok(GuaranteeValidationResult {
359            guarantee: guarantee.clone(),
360            passed,
361            details,
362            evidence,
363            timestamp,
364        })
365    }
366
367    fn validate_concurrency_guarantee(
368        &self,
369        guarantee: &DeterminismGuarantee,
370        request: &crate::builder::BuildRequest,
371        timestamp: chrono::DateTime<chrono::Utc>,
372    ) -> Result<GuaranteeValidationResult, BuildError> {
373        use std::sync::Arc;
374        use std::thread;
375
376        if !matches!(guarantee, DeterminismGuarantee::ThreadSafety) {
377            return Err(BuildError::DeterminismGuaranteeViolated {
378                guarantee: format!("{:?}", guarantee),
379                details: "Concurrency validation only supports ThreadSafety guarantee".to_string(),
380            });
381        }
382
383        let verifier = Arc::new(DeterminismVerifier::new(self.config.clone()));
384        let mut handles = vec![];
385        let results = Arc::new(std::sync::Mutex::new(vec![]));
386
387        for _ in 0..4 {
389            let verifier_clone = Arc::clone(&verifier);
390            let request_clone = request.clone();
391            let results_clone = Arc::clone(&results);
392
393            let handle = thread::spawn(move || {
394                let result = verifier_clone.verify(&request_clone, 2);
395                results_clone.lock().unwrap().push(result);
396            });
397            handles.push(handle);
398        }
399
400        for handle in handles {
402            handle
403                .join()
404                .map_err(|_| BuildError::Other("Thread join failed".to_string()))?;
405        }
406
407        let thread_results = results.lock().unwrap();
408        let all_deterministic = thread_results
409            .iter()
410            .all(|r| r.as_ref().map_or(false, |res| res.is_deterministic));
411
412        if all_deterministic && thread_results.len() == 4 {
413            let first_hash = &thread_results[0].as_ref().unwrap().hashes[0];
415            let all_identical = thread_results
416                .iter()
417                .skip(1)
418                .all(|r| r.as_ref().map_or(false, |res| &res.hashes[0] == first_hash));
419
420            if all_identical {
421                Ok(GuaranteeValidationResult {
422                    guarantee: guarantee.clone(),
423                    passed: true,
424                    details: "All parallel builds produced identical output".to_string(),
425                    evidence: Some(format!("4 threads all produced hash: {}", first_hash)),
426                    timestamp,
427                })
428            } else {
429                Ok(GuaranteeValidationResult {
430                    guarantee: guarantee.clone(),
431                    passed: false,
432                    details: "Parallel builds produced different outputs".to_string(),
433                    evidence: Some("Hash mismatch between threads".to_string()),
434                    timestamp,
435                })
436            }
437        } else {
438            Ok(GuaranteeValidationResult {
439                guarantee: guarantee.clone(),
440                passed: false,
441                details: format!(
442                    "Thread safety test failed: {}/{} threads succeeded",
443                    thread_results.iter().filter(|r| r.is_ok()).count(),
444                    thread_results.len()
445                ),
446                evidence: None,
447                timestamp,
448            })
449        }
450    }
451
452    fn validate_cross_platform_guarantee(
453        &self,
454        guarantee: &DeterminismGuarantee,
455        request: &crate::builder::BuildRequest,
456        timestamp: chrono::DateTime<chrono::Utc>,
457    ) -> Result<GuaranteeValidationResult, BuildError> {
458        if !matches!(guarantee, DeterminismGuarantee::PlatformIndependence) {
459            return Err(BuildError::DeterminismGuaranteeViolated {
460                guarantee: format!("{:?}", guarantee),
461                details: "Cross-platform validation only supports PlatformIndependence guarantee"
462                    .to_string(),
463            });
464        }
465
466        let original_locale = std::env::var("LC_ALL").unwrap_or_default();
468        let verifier = DeterminismVerifier::new(self.config.clone());
469        let mut results = vec![];
470
471        let test_locales = ["C", "en_US.UTF-8"];
472        for locale in &test_locales {
473            std::env::set_var("LC_ALL", locale);
474            let result = verifier.verify(request, 2)?;
475            results.push(result);
476        }
477
478        if original_locale.is_empty() {
480            std::env::remove_var("LC_ALL");
481        } else {
482            std::env::set_var("LC_ALL", original_locale);
483        }
484
485        let all_deterministic = results.iter().all(|r| r.is_deterministic);
486        if all_deterministic && results.len() > 1 {
487            let first_hash = &results[0].hashes[0];
488            let all_identical = results.iter().skip(1).all(|r| &r.hashes[0] == first_hash);
489
490            Ok(GuaranteeValidationResult {
491                guarantee: guarantee.clone(),
492                passed: all_identical,
493                details: if all_identical {
494                    "Output is identical across different locales".to_string()
495                } else {
496                    "Output varies across different locales".to_string()
497                },
498                evidence: Some(format!("Tested locales: {:?}", test_locales)),
499                timestamp,
500            })
501        } else {
502            Ok(GuaranteeValidationResult {
503                guarantee: guarantee.clone(),
504                passed: false,
505                details: "Cross-platform test failed".to_string(),
506                evidence: None,
507                timestamp,
508            })
509        }
510    }
511
512    fn validate_stress_test_guarantee(
513        &self,
514        guarantee: &DeterminismGuarantee,
515        request: &crate::builder::BuildRequest,
516        timestamp: chrono::DateTime<chrono::Utc>,
517    ) -> Result<GuaranteeValidationResult, BuildError> {
518        if !matches!(guarantee, DeterminismGuarantee::MemoryIndependence) {
519            return Err(BuildError::DeterminismGuaranteeViolated {
520                guarantee: format!("{:?}", guarantee),
521                details: "Stress test validation only supports MemoryIndependence guarantee"
522                    .to_string(),
523            });
524        }
525
526        let verifier = DeterminismVerifier::new(self.config.clone());
527
528        let _memory_pressure: Vec<Vec<u8>> = (0..50)
530            .map(|_| vec![0u8; 1024 * 1024]) .collect();
532
533        let stressed_result = verifier.verify(request, 3)?;
534
535        drop(_memory_pressure);
537        std::thread::sleep(std::time::Duration::from_millis(100)); let normal_result = verifier.verify(request, 3)?;
540
541        let both_deterministic = stressed_result.is_deterministic && normal_result.is_deterministic;
542        let outputs_identical =
543            both_deterministic && stressed_result.hashes[0] == normal_result.hashes[0];
544
545        Ok(GuaranteeValidationResult {
546            guarantee: guarantee.clone(),
547            passed: outputs_identical,
548            details: if outputs_identical {
549                "Output is identical under memory pressure and normal conditions".to_string()
550            } else {
551                "Output differs between memory pressure and normal conditions".to_string()
552            },
553            evidence: Some(format!(
554                "Stressed hash: {}, Normal hash: {}",
555                stressed_result.hashes.get(0).unwrap_or(&"N/A".to_string()),
556                normal_result.hashes.get(0).unwrap_or(&"N/A".to_string())
557            )),
558            timestamp,
559        })
560    }
561}
562
563pub fn validate_no_hashmap_usage() -> Result<(), BuildError> {
565    Ok(())
572}
573
574pub fn validate_deterministic_sorting() -> Result<(), BuildError> {
576    Ok(())
580}
581
582pub fn generate_guarantee_report(
584    request: &crate::builder::BuildRequest,
585    config: &DeterminismConfig,
586) -> Result<GuaranteeReport, BuildError> {
587    let validator = DeterminismGuaranteeValidator::new(config.clone());
588    let results = validator.validate_all_guarantees(request)?;
589
590    let passed_count = results.iter().filter(|r| r.passed).count();
591    let total_count = results.len();
592    let success_rate = if total_count > 0 {
593        (passed_count as f64 / total_count as f64) * 100.0
594    } else {
595        0.0
596    };
597
598    Ok(GuaranteeReport {
599        timestamp: chrono::Utc::now(),
600        total_guarantees: total_count,
601        passed_guarantees: passed_count,
602        success_rate,
603        results,
604        overall_pass: passed_count == total_count,
605    })
606}
607
608#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct GuaranteeReport {
611    pub timestamp: chrono::DateTime<chrono::Utc>,
613    pub total_guarantees: usize,
615    pub passed_guarantees: usize,
617    pub success_rate: f64,
619    pub results: Vec<GuaranteeValidationResult>,
621    pub overall_pass: bool,
623}
624
625impl GuaranteeReport {
626    pub fn failed_guarantees(&self) -> Vec<&GuaranteeValidationResult> {
628        self.results.iter().filter(|r| !r.passed).collect()
629    }
630
631    pub fn critical_failures(&self) -> Vec<&GuaranteeValidationResult> {
633        self.results
634            .iter()
635            .filter(|r| !r.passed && r.guarantee.priority() == GuaranteePriority::Critical)
636            .collect()
637    }
638
639    pub fn summary(&self) -> String {
641        if self.overall_pass {
642            format!(
643                "✓ All {} determinism guarantees passed (100%)",
644                self.total_guarantees
645            )
646        } else {
647            let failed = self.total_guarantees - self.passed_guarantees;
648            let critical_failed = self.critical_failures().len();
649
650            if critical_failed > 0 {
651                format!(
652                    "✗ {}/{} guarantees failed ({:.1}%) - {} CRITICAL failures",
653                    failed,
654                    self.total_guarantees,
655                    100.0 - self.success_rate,
656                    critical_failed
657                )
658            } else {
659                format!(
660                    "⚠ {}/{} guarantees failed ({:.1}%) - no critical failures",
661                    failed,
662                    self.total_guarantees,
663                    100.0 - self.success_rate
664                )
665            }
666        }
667    }
668}