Skip to main content

eth_id/privacy/
virtualizer.rs

1use crate::error::{Result, EthIdError};
2use crate::parser::ParsedDocument;
3use crate::claims::DateClaim;
4use chrono::{NaiveDate, Utc};
5
6pub struct DataVirtualizer;
7
8impl DataVirtualizer {
9    pub fn new() -> Self {
10        Self
11    }
12    
13    pub fn virtualize_date_check(
14        &self,
15        document: &ParsedDocument,
16        date_claim: &DateClaim,
17    ) -> Result<bool> {
18        if let Some(age_threshold) = date_claim.age_threshold {
19            return self.check_age_threshold(document, age_threshold);
20        }
21        
22        if let Some(days_threshold) = date_claim.days_threshold {
23            return self.check_days_threshold(document, days_threshold);
24        }
25        
26        Ok(false)
27    }
28    
29    fn check_age_threshold(&self, document: &ParsedDocument, threshold: u32) -> Result<bool> {
30        let dob_str = document.get_field("date_of_birth")
31            .ok_or_else(|| EthIdError::PrivacyFilter("Date of birth not found".to_string()))?;
32        
33        let dob = self.parse_date(dob_str)?;
34        let today = Utc::now().naive_utc().date();
35        
36        let age_days = (today - dob).num_days();
37        let age_years = age_days / 365;
38        
39        tracing::debug!("Calculated age: {} years (threshold: {})", age_years, threshold);
40        
41        Ok(age_years >= threshold as i64)
42    }
43    
44    fn check_days_threshold(&self, document: &ParsedDocument, threshold: i64) -> Result<bool> {
45        let date_fields = ["date_of_birth", "expiry_date", "date_0", "date_1"];
46        
47        for field in &date_fields {
48            if let Some(date_str) = document.get_field(*field) {
49                if let Ok(date) = self.parse_date(date_str) {
50                    let today = Utc::now().naive_utc().date();
51                    let days_diff = (today - date).num_days().abs();
52                    
53                    tracing::debug!("Days difference for {}: {} (threshold: {})", field, days_diff, threshold);
54                    
55                    if days_diff <= threshold {
56                        return Ok(true);
57                    }
58                }
59            }
60        }
61        
62        Ok(false)
63    }
64    
65    fn parse_date(&self, date_str: &str) -> Result<NaiveDate> {
66        let formats = [
67            "%d/%m/%Y",
68            "%Y-%m-%d",
69            "%d-%m-%Y",
70            "%m/%d/%Y",
71        ];
72        
73        for format in &formats {
74            if let Ok(date) = NaiveDate::parse_from_str(date_str.trim(), format) {
75                return Ok(date);
76            }
77        }
78        
79        Err(EthIdError::PrivacyFilter(
80            format!("Unable to parse date: {}", date_str)
81        ))
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use crate::parser::DocumentType;
89
90    #[test]
91    fn test_parse_date_formats() {
92        let virtualizer = DataVirtualizer::new();
93        
94        assert!(virtualizer.parse_date("15/03/1990").is_ok());
95        assert!(virtualizer.parse_date("1990-03-15").is_ok());
96        assert!(virtualizer.parse_date("15-03-1990").is_ok());
97    }
98}