Skip to main content

semver_analyzer_core/
lib.rs

1//! Core types, traits, and diff engine for the semver-analyzer.
2//!
3//! This crate contains language-agnostic components:
4//! - API surface types (`ApiSurface`, `Symbol`, etc.)
5//! - Report types (`AnalysisReport`, `StructuralChange`, etc.)
6//! - Traits for language-pluggable analysis (`Language`, `LanguageSemantics`, etc.)
7//! - The structural diff engine (`diff_surfaces_with_semantics`)
8
9pub mod cli;
10pub mod diagnostics;
11pub mod diff;
12pub mod error;
13pub mod git;
14pub mod shared;
15pub mod traits;
16pub mod types;
17
18pub use shared::*;
19pub use traits::*;
20pub use types::*;
21
22/// Shared test utilities for the core crate.
23///
24/// Provides a minimal `TestLang` implementation of the `Language` trait
25/// for use in unit tests across core modules.
26#[cfg(test)]
27pub(crate) mod test_support {
28    use std::collections::HashMap;
29    use std::path::{Path, PathBuf};
30
31    use crate::traits::{Language, LanguageSemantics, MessageFormatter};
32    use crate::types::{
33        AnalysisMetadata, AnalysisReport, AnalysisResult, ApiSurface, Caller, ChangedFunction,
34        Comparison, ManifestChange, Reference, StructuralChange, Summary, Symbol, TestDiff,
35        TestFile, Visibility,
36    };
37
38    /// Minimal Language implementation for tests.
39    #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
40    pub struct TestLang;
41
42    impl<M: Default + Clone + PartialEq> LanguageSemantics<M> for TestLang {
43        fn is_member_addition_breaking(&self, _c: &Symbol<M>, _m: &Symbol<M>) -> bool {
44            false
45        }
46        fn same_family(&self, _a: &Symbol<M>, _b: &Symbol<M>) -> bool {
47            false
48        }
49        fn same_identity(&self, _a: &Symbol<M>, _b: &Symbol<M>) -> bool {
50            false
51        }
52        fn visibility_rank(&self, _v: Visibility) -> u8 {
53            0
54        }
55    }
56
57    impl MessageFormatter for TestLang {
58        fn describe(&self, _c: &StructuralChange) -> String {
59            String::new()
60        }
61    }
62
63    impl Language for TestLang {
64        type SymbolData = ();
65        type Category = String;
66        type ManifestChangeType = String;
67        type Evidence = String;
68        type ReportData = String;
69        type AnalysisExtensions = crate::types::EmptyExtensions;
70        const RENAMEABLE_SYMBOL_KINDS: &'static [crate::types::SymbolKind] = &[];
71        const NAME: &'static str = "test";
72        const MANIFEST_FILES: &'static [&'static str] = &[];
73        const SOURCE_FILE_PATTERNS: &'static [&'static str] = &[];
74
75        fn extract(
76            &self,
77            _repo: &Path,
78            _git_ref: &str,
79            _degradation: Option<&crate::diagnostics::DegradationTracker>,
80        ) -> anyhow::Result<ApiSurface> {
81            Ok(ApiSurface::default())
82        }
83        fn parse_changed_functions(
84            &self,
85            _repo: &Path,
86            _from_ref: &str,
87            _to_ref: &str,
88        ) -> anyhow::Result<Vec<ChangedFunction>> {
89            Ok(vec![])
90        }
91        fn find_callers(&self, _file: &Path, _symbol_name: &str) -> anyhow::Result<Vec<Caller>> {
92            Ok(vec![])
93        }
94        fn find_references(
95            &self,
96            _file: &Path,
97            _symbol_name: &str,
98        ) -> anyhow::Result<Vec<Reference>> {
99            Ok(vec![])
100        }
101        fn find_tests(&self, _repo: &Path, _source_file: &Path) -> anyhow::Result<Vec<TestFile>> {
102            Ok(vec![])
103        }
104        fn diff_test_assertions(
105            &self,
106            _repo: &Path,
107            _test_file: &TestFile,
108            _from_ref: &str,
109            _to_ref: &str,
110        ) -> anyhow::Result<TestDiff> {
111            Ok(TestDiff {
112                test_file: PathBuf::new(),
113                removed_assertions: vec![],
114                added_assertions: vec![],
115                has_assertion_changes: false,
116                full_diff: String::new(),
117            })
118        }
119
120        fn diff_manifest_content(_old: &str, _new: &str) -> Vec<ManifestChange<Self>> {
121            vec![]
122        }
123        fn should_exclude_from_analysis(_path: &Path) -> bool {
124            false
125        }
126        fn build_report(
127            &self,
128            _results: &AnalysisResult<Self>,
129            _repo: &Path,
130            _from_ref: &str,
131            _to_ref: &str,
132        ) -> AnalysisReport<Self> {
133            AnalysisReport {
134                repository: PathBuf::new(),
135                comparison: Comparison {
136                    from_ref: String::new(),
137                    to_ref: String::new(),
138                    from_sha: String::new(),
139                    to_sha: String::new(),
140                    commit_count: 0,
141                    analysis_timestamp: String::new(),
142                },
143                summary: Summary {
144                    total_breaking_changes: 0,
145                    breaking_api_changes: 0,
146                    breaking_behavioral_changes: 0,
147                    files_with_breaking_changes: 0,
148                },
149                changes: vec![],
150                manifest_changes: vec![],
151                added_files: vec![],
152                packages: vec![],
153                member_renames: HashMap::new(),
154                inferred_rename_patterns: None,
155                extensions: crate::types::EmptyExtensions {},
156                metadata: AnalysisMetadata {
157                    call_graph_analysis: String::new(),
158                    tool_version: String::new(),
159                    llm_usage: None,
160                },
161            }
162        }
163    }
164}