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 .bin 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.as_ref().join(format!("{spec_name}.bin"));
54
55    if !cache_path.exists() {
56        return Err(Error::CachedSpecNotFound {
57            name: spec_name.to_string(),
58        });
59    }
60
61    let cache_data = fs::read(&cache_path).map_err(Error::Io)?;
62    bincode::deserialize(&cache_data).map_err(|e| Error::CachedSpecCorrupted {
63        name: spec_name.to_string(),
64        reason: e.to_string(),
65    })
66}
67
68/// Load cached spec with embedded version checking (legacy/fallback path)
69fn load_cached_spec_with_version_check<P: AsRef<Path>>(
70    cache_dir: P,
71    spec_name: &str,
72) -> Result<CachedSpec, Error> {
73    let cache_path = cache_dir.as_ref().join(format!("{spec_name}.bin"));
74
75    if !cache_path.exists() {
76        return Err(Error::CachedSpecNotFound {
77            name: spec_name.to_string(),
78        });
79    }
80
81    let cache_data = fs::read(&cache_path).map_err(Error::Io)?;
82    let cached_spec: CachedSpec =
83        bincode::deserialize(&cache_data).map_err(|e| Error::CachedSpecCorrupted {
84            name: spec_name.to_string(),
85            reason: e.to_string(),
86        })?;
87
88    // Check cache format version
89    if cached_spec.cache_format_version != CACHE_FORMAT_VERSION {
90        return Err(Error::CacheVersionMismatch {
91            name: spec_name.to_string(),
92            found: cached_spec.cache_format_version,
93            expected: CACHE_FORMAT_VERSION,
94        });
95    }
96
97    Ok(cached_spec)
98}