Skip to main content

rh_foundation/snapshot/
generator.rs

1use crate::snapshot::error::{SnapshotError, SnapshotResult};
2use crate::snapshot::merger::ElementMerger;
3use crate::snapshot::types::{Snapshot, StructureDefinition};
4use std::cell::RefCell;
5use std::collections::{HashMap, HashSet};
6use tracing::{debug, info};
7
8pub struct SnapshotGenerator {
9    structure_definitions: HashMap<String, StructureDefinition>,
10    snapshot_cache: RefCell<HashMap<String, Snapshot>>,
11}
12
13impl SnapshotGenerator {
14    pub fn new() -> Self {
15        Self {
16            structure_definitions: HashMap::new(),
17            snapshot_cache: RefCell::new(HashMap::new()),
18        }
19    }
20
21    pub fn clear_cache(&self) {
22        self.snapshot_cache.borrow_mut().clear();
23    }
24
25    pub fn cache_size(&self) -> usize {
26        self.snapshot_cache.borrow().len()
27    }
28
29    pub fn load_structure_definition(&mut self, sd: StructureDefinition) {
30        debug!("Loading StructureDefinition: {} ({})", sd.name, sd.url);
31        self.structure_definitions.insert(sd.url.clone(), sd);
32    }
33
34    pub fn load_structure_definitions(&mut self, sds: Vec<StructureDefinition>) {
35        for sd in sds {
36            self.load_structure_definition(sd);
37        }
38    }
39
40    pub fn generate_snapshot(&self, url: &str) -> SnapshotResult<Snapshot> {
41        info!("Generating snapshot for: {}", url);
42        let mut visited = HashSet::new();
43        self.generate_snapshot_internal(url, &mut visited)
44    }
45
46    fn generate_snapshot_internal(
47        &self,
48        url: &str,
49        visited: &mut HashSet<String>,
50    ) -> SnapshotResult<Snapshot> {
51        if visited.contains(url) {
52            return Err(SnapshotError::CircularDependency(format!(
53                "Circular dependency detected: {url}"
54            )));
55        }
56
57        if let Some(cached) = self.snapshot_cache.borrow().get(url) {
58            debug!("Using cached snapshot for {}", url);
59            return Ok(cached.clone());
60        }
61
62        visited.insert(url.to_string());
63
64        let sd = self
65            .structure_definitions
66            .get(url)
67            .ok_or_else(|| SnapshotError::BaseNotFound(url.to_string()))?;
68
69        debug!(
70            "Processing StructureDefinition: {} (type: {})",
71            sd.name, sd.type_
72        );
73
74        if let Some(ref snapshot) = sd.snapshot {
75            debug!(
76                "Using existing snapshot with {} elements",
77                snapshot.element.len()
78            );
79            self.snapshot_cache
80                .borrow_mut()
81                .insert(url.to_string(), snapshot.clone());
82            return Ok(snapshot.clone());
83        }
84
85        let snapshot = if let Some(base_url) = &sd.base_definition {
86            debug!("Resolving base definition: {}", base_url);
87            let base_snapshot = self.generate_snapshot_internal(base_url, visited)?;
88
89            if let Some(differential) = &sd.differential {
90                debug!(
91                    "Merging {} base elements with {} differential elements",
92                    base_snapshot.element.len(),
93                    differential.element.len()
94                );
95                let merged =
96                    ElementMerger::merge_elements(&base_snapshot.element, &differential.element)?;
97                info!("Generated snapshot with {} elements", merged.len());
98                Snapshot { element: merged }
99            } else {
100                debug!("No differential, returning base snapshot");
101                base_snapshot
102            }
103        } else if let Some(differential) = &sd.differential {
104            debug!(
105                "No base definition, using differential as snapshot ({} elements)",
106                differential.element.len()
107            );
108            Snapshot {
109                element: differential.element.clone(),
110            }
111        } else {
112            return Err(SnapshotError::InvalidStructureDefinition(format!(
113                "StructureDefinition {url} has no snapshot, differential, or base definition"
114            )));
115        };
116
117        self.snapshot_cache
118            .borrow_mut()
119            .insert(url.to_string(), snapshot.clone());
120        Ok(snapshot)
121    }
122}
123
124impl Default for SnapshotGenerator {
125    fn default() -> Self {
126        Self::new()
127    }
128}