Skip to main content

fallow_engine/
source.rs

1//! Source parsing contracts owned by the engine boundary.
2
3use std::sync::atomic::AtomicBool;
4
5use fallow_types::discover::DiscoveredFile;
6#[cfg(test)]
7pub use fallow_types::extract::{ExportName, MemberKind, VisibilityTag};
8pub use fallow_types::extract::{ModuleInfo, ParseResult, SourceReadFailure};
9
10type CacheStore = fallow_extract::cache::CacheStore;
11
12/// On-demand, transient function extraction for local similar-code inference.
13pub mod similar_code {
14    use std::path::Path;
15
16    pub use fallow_types::similar_code::{
17        ExtractedSimilarCodeFunction, SIMILAR_CODE_EXTRACTION_SEMANTICS_VERSION,
18        SimilarCodeExtraction, SimilarCodeExtractionLimits, SimilarCodeExtractionSkip,
19        SimilarCodeExtractionSkipReason, SimilarCodeFunctionKind, SimilarCodeFunctionLocation,
20        SimilarCodeSideEffectHint, SimilarCodeSourceDigest,
21    };
22
23    /// Extract bounded named functions without touching the normal parse cache.
24    #[must_use]
25    pub fn extract(
26        path: &Path,
27        source: &str,
28        limits: SimilarCodeExtractionLimits,
29    ) -> SimilarCodeExtraction {
30        fallow_extract::extract_similar_code_functions(path, source, limits)
31    }
32}
33
34/// Source inventory walking for coverage and upload surfaces.
35pub mod inventory {
36    use std::path::Path;
37
38    use rustc_hash::FxHashMap;
39
40    /// A single static-inventory entry for one function.
41    ///
42    /// This is the engine-owned inventory contract exposed to CLI upload
43    /// surfaces. The extractor owns AST traversal; the engine owns the public
44    /// shape that downstream crates construct and upload.
45    #[derive(Debug, Clone, PartialEq, Eq)]
46    pub struct InventoryEntry {
47        /// Beacon-compatible function name.
48        pub name: String,
49        /// 1-based source line of the function declaration.
50        pub line: u32,
51        /// 1-indexed UTF-16 column of the function node start.
52        pub start_column: u32,
53        /// 1-based source line where the function node ends.
54        pub end_line: u32,
55        /// 1-indexed UTF-16 column of the function node end.
56        pub end_column: u32,
57        /// Content digest of the function's full-span source slice.
58        pub source_hash: String,
59        /// Whether the name came from the callee of the call this function is
60        /// passed to (`arr.map(cb)` -> `map`) rather than from a declaration,
61        /// a binding, or a property key.
62        pub is_callback: bool,
63    }
64
65    impl From<fallow_extract::inventory::InventoryEntry> for InventoryEntry {
66        fn from(entry: fallow_extract::inventory::InventoryEntry) -> Self {
67            Self {
68                name: entry.name,
69                line: entry.line,
70                start_column: entry.start_column,
71                end_line: entry.end_line,
72                end_column: entry.end_column,
73                source_hash: entry.source_hash,
74                is_callback: entry.is_callback,
75            }
76        }
77    }
78
79    /// Per-function static complexity collected alongside the inventory walk.
80    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
81    pub struct InventoryComplexity {
82        /// `McCabe` cyclomatic complexity (1 + decision points).
83        pub cyclomatic: u16,
84        /// `SonarSource` cognitive complexity (structural + nesting penalty).
85        pub cognitive: u16,
86    }
87
88    impl From<fallow_extract::inventory::InventoryComplexity> for InventoryComplexity {
89        fn from(complexity: fallow_extract::inventory::InventoryComplexity) -> Self {
90            Self {
91                cyclomatic: complexity.cyclomatic,
92                cognitive: complexity.cognitive,
93            }
94        }
95    }
96
97    /// Walk source and emit engine-owned function inventory entries.
98    #[must_use]
99    pub fn walk_source(path: &Path, source: &str) -> Vec<InventoryEntry> {
100        fallow_extract::inventory::walk_source(path, source)
101            .into_iter()
102            .map(InventoryEntry::from)
103            .collect()
104    }
105
106    /// Walk source once and emit inventory entries plus static complexity by source hash.
107    #[must_use]
108    pub fn walk_source_with_complexity(
109        path: &Path,
110        source: &str,
111    ) -> (Vec<InventoryEntry>, FxHashMap<String, InventoryComplexity>) {
112        let (entries, complexity) =
113            fallow_extract::inventory::walk_source_with_complexity(path, source);
114        let entries = entries.into_iter().map(InventoryEntry::from).collect();
115        let complexity = complexity
116            .into_iter()
117            .map(|(hash, metrics)| (hash, InventoryComplexity::from(metrics)))
118            .collect();
119        (entries, complexity)
120    }
121}
122
123/// Parse discovered source files into typed module facts.
124///
125/// Keeping parsing behind the engine boundary lets sessions and future
126/// incremental runners choose cache policy without exposing the extract crate
127/// as the public orchestration layer.
128///
129/// `cancellation`, when set mid-parse, turns every remaining file into a no-op
130/// and truncates the returned modules; see
131/// [`fallow_extract::parse_all_files_cancellable`].
132#[must_use]
133pub(crate) fn parse_all_files(
134    files: &[DiscoveredFile],
135    cache: Option<&CacheStore>,
136    need_complexity: bool,
137    cancellation: Option<&AtomicBool>,
138) -> ParseResult {
139    fallow_extract::parse_all_files_cancellable(files, cache, need_complexity, cancellation)
140}