Skip to main content

fallow_engine/
source.rs

1//! Source parsing contracts owned by the engine boundary.
2
3use fallow_types::discover::DiscoveredFile;
4#[cfg(test)]
5pub use fallow_types::extract::{ExportName, MemberKind, VisibilityTag};
6pub use fallow_types::extract::{ModuleInfo, ParseResult};
7
8type CacheStore = fallow_extract::cache::CacheStore;
9
10/// Source inventory walking for coverage and upload surfaces.
11pub mod inventory {
12    use std::path::Path;
13
14    use rustc_hash::FxHashMap;
15
16    /// A single static-inventory entry for one function.
17    ///
18    /// This is the engine-owned inventory contract exposed to CLI upload
19    /// surfaces. The extractor owns AST traversal; the engine owns the public
20    /// shape that downstream crates construct and upload.
21    #[derive(Debug, Clone, PartialEq, Eq)]
22    pub struct InventoryEntry {
23        /// Beacon-compatible function name.
24        pub name: String,
25        /// 1-based source line of the function declaration.
26        pub line: u32,
27        /// 1-indexed UTF-16 column of the function node start.
28        pub start_column: u32,
29        /// 1-based source line where the function node ends.
30        pub end_line: u32,
31        /// 1-indexed UTF-16 column of the function node end.
32        pub end_column: u32,
33        /// Content digest of the function's full-span source slice.
34        pub source_hash: String,
35    }
36
37    impl From<fallow_extract::inventory::InventoryEntry> for InventoryEntry {
38        fn from(entry: fallow_extract::inventory::InventoryEntry) -> Self {
39            Self {
40                name: entry.name,
41                line: entry.line,
42                start_column: entry.start_column,
43                end_line: entry.end_line,
44                end_column: entry.end_column,
45                source_hash: entry.source_hash,
46            }
47        }
48    }
49
50    /// Per-function static complexity collected alongside the inventory walk.
51    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
52    pub struct InventoryComplexity {
53        /// `McCabe` cyclomatic complexity (1 + decision points).
54        pub cyclomatic: u16,
55        /// `SonarSource` cognitive complexity (structural + nesting penalty).
56        pub cognitive: u16,
57    }
58
59    impl From<fallow_extract::inventory::InventoryComplexity> for InventoryComplexity {
60        fn from(complexity: fallow_extract::inventory::InventoryComplexity) -> Self {
61            Self {
62                cyclomatic: complexity.cyclomatic,
63                cognitive: complexity.cognitive,
64            }
65        }
66    }
67
68    /// Walk source and emit engine-owned function inventory entries.
69    #[must_use]
70    pub fn walk_source(path: &Path, source: &str) -> Vec<InventoryEntry> {
71        fallow_extract::inventory::walk_source(path, source)
72            .into_iter()
73            .map(InventoryEntry::from)
74            .collect()
75    }
76
77    /// Walk source once and emit inventory entries plus static complexity by source hash.
78    #[must_use]
79    pub fn walk_source_with_complexity(
80        path: &Path,
81        source: &str,
82    ) -> (Vec<InventoryEntry>, FxHashMap<String, InventoryComplexity>) {
83        let (entries, complexity) =
84            fallow_extract::inventory::walk_source_with_complexity(path, source);
85        let entries = entries.into_iter().map(InventoryEntry::from).collect();
86        let complexity = complexity
87            .into_iter()
88            .map(|(hash, metrics)| (hash, InventoryComplexity::from(metrics)))
89            .collect();
90        (entries, complexity)
91    }
92}
93
94/// Parse discovered source files into typed module facts.
95///
96/// Keeping parsing behind the engine boundary lets sessions and future
97/// incremental runners choose cache policy without exposing the extract crate
98/// as the public orchestration layer.
99#[must_use]
100pub fn parse_all_files(
101    files: &[DiscoveredFile],
102    cache: Option<&CacheStore>,
103    need_complexity: bool,
104) -> ParseResult {
105    fallow_extract::parse_all_files(files, cache, need_complexity)
106}