Skip to main content

aperture_cli/engine/
loader.rs

1use crate::cache::metadata::CacheMetadataManager;
2use crate::cache::models::{CachedSpec, CACHE_FORMAT_VERSION};
3use crate::error::Error;
4use crate::fs::OsFileSystem;
5use std::fs;
6use std::path::Path;
7
8/// Loads a cached `OpenAPI` specification from the binary cache with optimized version checking.
9///
10/// This function uses a global cache metadata file for fast version checking before
11/// loading the full specification, significantly improving performance.
12///
13/// # Arguments
14/// * `cache_dir` - The directory containing cached spec files
15/// * `spec_name` - The name of the spec to load (without binary extension)
16///
17/// # Returns
18/// * `Ok(CachedSpec)` - The loaded and deserialized specification
19/// * `Err(Error)` - If the file doesn't exist or deserialization fails
20///
21/// # Errors
22/// Returns an error if the cache file doesn't exist or if deserialization fails
23pub fn load_cached_spec<P: AsRef<Path>>(
24    cache_dir: P,
25    spec_name: &str,
26) -> Result<CachedSpec, Error> {
27    // Fast version check using metadata
28    let fs = OsFileSystem;
29    let metadata_manager = CacheMetadataManager::new(&fs);
30
31    // Check if spec exists and version is compatible
32    match metadata_manager.check_spec_version(&cache_dir, spec_name) {
33        Ok(true) => {
34            // Version is compatible, load spec directly (no version check needed)
35            load_cached_spec_without_version_check(&cache_dir, spec_name)
36        }
37        Ok(false) => {
38            // Version mismatch or spec not in metadata, fall back to legacy method
39            load_cached_spec_with_version_check(&cache_dir, spec_name)
40        }
41        Err(_) => {
42            // Metadata loading failed, fall back to legacy method
43            load_cached_spec_with_version_check(&cache_dir, spec_name)
44        }
45    }
46}
47
48/// Load cached spec without version checking (optimized path)
49fn load_cached_spec_without_version_check<P: AsRef<Path>>(
50    cache_dir: P,
51    spec_name: &str,
52) -> Result<CachedSpec, Error> {
53    let cache_path = cache_dir
54        .as_ref()
55        .join(format!("{spec_name}{}", crate::constants::FILE_EXT_BIN));
56
57    if !cache_path.exists() {
58        return Err(Error::cached_spec_not_found(spec_name));
59    }
60
61    let cache_data = fs::read(&cache_path)
62        .map_err(|e| Error::io_error(format!("Failed to read cache file: {e}")))?;
63    bincode::deserialize(&cache_data)
64        .map_err(|e| Error::cached_spec_corrupted(spec_name, e.to_string()))
65}
66
67/// Load cached spec with embedded version checking (legacy/fallback path)
68fn load_cached_spec_with_version_check<P: AsRef<Path>>(
69    cache_dir: P,
70    spec_name: &str,
71) -> Result<CachedSpec, Error> {
72    let cache_path = cache_dir
73        .as_ref()
74        .join(format!("{spec_name}{}", crate::constants::FILE_EXT_BIN));
75
76    if !cache_path.exists() {
77        return Err(Error::cached_spec_not_found(spec_name));
78    }
79
80    let cache_data = fs::read(&cache_path)
81        .map_err(|e| Error::io_error(format!("Failed to read cache file: {e}")))?;
82    let cached_spec: CachedSpec = bincode::deserialize(&cache_data)
83        .map_err(|e| Error::cached_spec_corrupted(spec_name, e.to_string()))?;
84
85    // Check cache format version
86    if cached_spec.cache_format_version != CACHE_FORMAT_VERSION {
87        return Err(Error::cache_version_mismatch(
88            spec_name,
89            cached_spec.cache_format_version,
90            CACHE_FORMAT_VERSION,
91        ));
92    }
93
94    Ok(cached_spec)
95}