rh-foundation 0.1.0-beta.1

Foundation crate providing common utilities, error handling, and shared functionality
Documentation
use crate::snapshot::error::{SnapshotError, SnapshotResult};
use crate::snapshot::merger::ElementMerger;
use crate::snapshot::types::{Snapshot, StructureDefinition};
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use tracing::{debug, info};

pub struct SnapshotGenerator {
    structure_definitions: HashMap<String, StructureDefinition>,
    snapshot_cache: RefCell<HashMap<String, Snapshot>>,
}

impl SnapshotGenerator {
    pub fn new() -> Self {
        Self {
            structure_definitions: HashMap::new(),
            snapshot_cache: RefCell::new(HashMap::new()),
        }
    }

    pub fn clear_cache(&self) {
        self.snapshot_cache.borrow_mut().clear();
    }

    pub fn cache_size(&self) -> usize {
        self.snapshot_cache.borrow().len()
    }

    pub fn load_structure_definition(&mut self, sd: StructureDefinition) {
        debug!("Loading StructureDefinition: {} ({})", sd.name, sd.url);
        self.structure_definitions.insert(sd.url.clone(), sd);
    }

    pub fn load_structure_definitions(&mut self, sds: Vec<StructureDefinition>) {
        for sd in sds {
            self.load_structure_definition(sd);
        }
    }

    pub fn generate_snapshot(&self, url: &str) -> SnapshotResult<Snapshot> {
        info!("Generating snapshot for: {}", url);
        let mut visited = HashSet::new();
        self.generate_snapshot_internal(url, &mut visited)
    }

    fn generate_snapshot_internal(
        &self,
        url: &str,
        visited: &mut HashSet<String>,
    ) -> SnapshotResult<Snapshot> {
        if visited.contains(url) {
            return Err(SnapshotError::CircularDependency(format!(
                "Circular dependency detected: {url}"
            )));
        }

        if let Some(cached) = self.snapshot_cache.borrow().get(url) {
            debug!("Using cached snapshot for {}", url);
            return Ok(cached.clone());
        }

        visited.insert(url.to_string());

        let sd = self
            .structure_definitions
            .get(url)
            .ok_or_else(|| SnapshotError::BaseNotFound(url.to_string()))?;

        debug!(
            "Processing StructureDefinition: {} (type: {})",
            sd.name, sd.type_
        );

        if let Some(ref snapshot) = sd.snapshot {
            debug!(
                "Using existing snapshot with {} elements",
                snapshot.element.len()
            );
            self.snapshot_cache
                .borrow_mut()
                .insert(url.to_string(), snapshot.clone());
            return Ok(snapshot.clone());
        }

        let snapshot = if let Some(base_url) = &sd.base_definition {
            debug!("Resolving base definition: {}", base_url);
            let base_snapshot = self.generate_snapshot_internal(base_url, visited)?;

            if let Some(differential) = &sd.differential {
                debug!(
                    "Merging {} base elements with {} differential elements",
                    base_snapshot.element.len(),
                    differential.element.len()
                );
                let merged =
                    ElementMerger::merge_elements(&base_snapshot.element, &differential.element)?;
                info!("Generated snapshot with {} elements", merged.len());
                Snapshot { element: merged }
            } else {
                debug!("No differential, returning base snapshot");
                base_snapshot
            }
        } else if let Some(differential) = &sd.differential {
            debug!(
                "No base definition, using differential as snapshot ({} elements)",
                differential.element.len()
            );
            Snapshot {
                element: differential.element.clone(),
            }
        } else {
            return Err(SnapshotError::InvalidStructureDefinition(format!(
                "StructureDefinition {url} has no snapshot, differential, or base definition"
            )));
        };

        self.snapshot_cache
            .borrow_mut()
            .insert(url.to_string(), snapshot.clone());
        Ok(snapshot)
    }
}

impl Default for SnapshotGenerator {
    fn default() -> Self {
        Self::new()
    }
}