Skip to main content

lexicon_api/
baseline.rs

1use std::path::Path;
2
3use crate::error::ApiError;
4use crate::schema::ApiSnapshot;
5
6/// Save an API snapshot as a baseline JSON file.
7pub fn save_baseline(snapshot: &ApiSnapshot, path: &Path) -> Result<(), ApiError> {
8    let json = serde_json::to_string_pretty(snapshot)?;
9    std::fs::write(path, json)?;
10    Ok(())
11}
12
13/// Load an API snapshot baseline from a JSON file.
14pub fn load_baseline(path: &Path) -> Result<ApiSnapshot, ApiError> {
15    if !path.exists() {
16        return Err(ApiError::BaselineNotFound(
17            path.to_string_lossy().to_string(),
18        ));
19    }
20    let contents = std::fs::read_to_string(path)?;
21    let snapshot: ApiSnapshot = serde_json::from_str(&contents)?;
22    Ok(snapshot)
23}
24
25/// Save the current API snapshot as current.json in the given directory.
26pub fn save_current(snapshot: &ApiSnapshot, path: &Path) -> Result<(), ApiError> {
27    save_baseline(snapshot, path)
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33    use crate::schema::{ApiItem, ApiItemKind, Visibility};
34
35    fn sample_snapshot() -> ApiSnapshot {
36        ApiSnapshot {
37            crate_name: "test-crate".into(),
38            version: Some("0.1.0".into()),
39            items: vec![ApiItem {
40                kind: ApiItemKind::Function,
41                name: "hello".into(),
42                module_path: vec![],
43                signature: "fn hello()".into(),
44                visibility: Visibility::Public,
45                trait_associations: vec![],
46                stability: None,
47                doc_summary: None,
48                span_file: None,
49                span_line: None,
50            }],
51            extracted_at: "2026-01-01T00:00:00Z".into(),
52        }
53    }
54
55    #[test]
56    fn save_load_roundtrip() {
57        let dir = tempfile::tempdir().unwrap();
58        let path = dir.path().join("baseline.json");
59        let snap = sample_snapshot();
60        save_baseline(&snap, &path).unwrap();
61        let loaded = load_baseline(&path).unwrap();
62        assert_eq!(loaded.crate_name, snap.crate_name);
63        assert_eq!(loaded.items.len(), 1);
64        assert_eq!(loaded.items[0], snap.items[0]);
65    }
66
67    #[test]
68    fn load_missing_baseline() {
69        let result = load_baseline(Path::new("/nonexistent/baseline.json"));
70        assert!(result.is_err());
71        assert!(matches!(result.unwrap_err(), ApiError::BaselineNotFound(_)));
72    }
73
74    #[test]
75    fn save_current_works() {
76        let dir = tempfile::tempdir().unwrap();
77        let path = dir.path().join("current.json");
78        let snap = sample_snapshot();
79        save_current(&snap, &path).unwrap();
80        let loaded = load_baseline(&path).unwrap();
81        assert_eq!(loaded.crate_name, snap.crate_name);
82    }
83}