Skip to main content

astmap_core/
port.rs

1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3
4use crate::error::{DbError, DiffError, ScanError};
5use crate::model::{
6    Config, DepLevel, DepType, Dependency, File, ImpactEntry, ParseResult, ParseStatus, PathStep,
7    Symbol, SymbolKind, SymbolResult, SymbolWithParent, VcsFileChange,
8};
9
10// ── Storage Ports ──
11
12/// Shared config operations used by multiple modules.
13pub trait ConfigStore {
14    fn config_get(&self, key: &str) -> Result<Option<String>, DbError>;
15    fn config_set(&self, key: &str, value: &str) -> Result<(), DbError>;
16    fn config_list(&self) -> Result<Vec<Config>, DbError>;
17}
18
19/// Shared read-only symbol/file lookup operations.
20pub trait SymbolLookup {
21    fn get_file_id(&self, path: &str) -> Result<Option<i64>, DbError>;
22    fn get_symbol_by_id(&self, symbol_id: i64) -> Result<Option<Symbol>, DbError>;
23    fn search_symbols_by_name(&self, name: &str, limit: i64) -> Result<Vec<SymbolResult>, DbError>;
24    fn get_symbols_for_file(&self, file_id: i64) -> Result<Vec<Symbol>, DbError>;
25    fn get_children(&self, symbol_id: i64) -> Result<Vec<Symbol>, DbError>;
26    fn impact_analysis(&self, symbol_id: i64, max_depth: i64) -> Result<Vec<ImpactEntry>, DbError>;
27    fn stats(&self) -> Result<(i64, i64, i64), DbError>;
28}
29
30/// Storage operations needed by the scan pipeline.
31#[allow(clippy::too_many_arguments)]
32pub trait ScanStore: ConfigStore + SymbolLookup {
33    fn begin_scan(&self) -> Result<i64, DbError>;
34    fn complete_scan(
35        &self,
36        scan_id: i64,
37        file_count: i64,
38        symbol_count: i64,
39        dep_count: i64,
40    ) -> Result<(), DbError>;
41    fn upsert_file(
42        &self,
43        scan_id: i64,
44        path: &str,
45        language: &str,
46        size_bytes: i64,
47        content_hash: &str,
48        last_modified: u64,
49        parse_status: ParseStatus,
50    ) -> Result<i64, DbError>;
51    fn get_file_hash(&self, path: &str) -> Result<Option<String>, DbError>;
52    fn get_all_file_paths(&self) -> Result<HashMap<String, i64>, DbError>;
53    fn delete_file(&self, file_id: i64) -> Result<(), DbError>;
54    fn insert_symbol(
55        &self,
56        file_id: i64,
57        parent_id: Option<i64>,
58        name: &str,
59        kind: SymbolKind,
60        signature: Option<&str>,
61        start_line: i64,
62        end_line: i64,
63        start_byte: i64,
64        end_byte: i64,
65    ) -> Result<i64, DbError>;
66    fn delete_symbols_for_file(&self, file_id: i64) -> Result<(), DbError>;
67    fn insert_dependency(
68        &self,
69        source_file_id: i64,
70        source_symbol_id: Option<i64>,
71        target_file_id: Option<i64>,
72        target_symbol_id: Option<i64>,
73        dep_type: DepType,
74        level: DepLevel,
75        raw_import: Option<&str>,
76    ) -> Result<i64, DbError>;
77    fn delete_deps_for_file(&self, file_id: i64) -> Result<(), DbError>;
78}
79
80/// Read-only operations for querying the index.
81pub trait QueryStore: ConfigStore + SymbolLookup {
82    fn search_symbols(&self, query: &str, limit: i64) -> Result<Vec<SymbolResult>, DbError>;
83    fn find_path(
84        &self,
85        from_symbol_id: i64,
86        to_symbol_id: i64,
87        max_depth: i64,
88    ) -> Result<Vec<PathStep>, DbError>;
89}
90
91/// Operations needed by the diff module (working.rs, since.rs, session.rs).
92pub trait DiffStore: ConfigStore + SymbolLookup {
93    fn symbols_in_line_range(
94        &self,
95        file_path: &str,
96        start_line: i64,
97        end_line: i64,
98    ) -> Result<Vec<SymbolResult>, DbError>;
99    fn all_symbols_for_file(&self, file_path: &str) -> Result<Vec<SymbolWithParent>, DbError>;
100}
101
102/// Operations needed by the export module.
103pub trait ExportStore: SymbolLookup {
104    fn list_files(&self, pattern: Option<&str>) -> Result<Vec<File>, DbError>;
105    fn get_deps_for_file(&self, file_id: i64) -> Result<Vec<Dependency>, DbError>;
106}
107
108// ── VCS Port ──
109
110/// Abstraction over VCS operations needed by the diff module.
111pub trait VcsProvider {
112    /// Get changed files between working tree and a base ref.
113    fn working_tree_changes(&self, base_ref: Option<&str>)
114        -> Result<Vec<VcsFileChange>, DiffError>;
115    /// Get changed files between two refs (from_ref..to_ref).
116    fn changes_between_refs(
117        &self,
118        from_ref: &str,
119        to_ref: &str,
120    ) -> Result<Vec<VcsFileChange>, DiffError>;
121    /// Read file content at a specific ref. Returns None if file doesn't exist or is binary.
122    fn read_file_at_ref(&self, path: &str, git_ref: &str) -> Result<Option<String>, DiffError>;
123    /// Count commits between two refs.
124    fn commit_count(&self, from_ref: &str, to_ref: &str) -> Result<usize, DiffError>;
125    /// Check if a ref exists.
126    fn resolve_ref(&self, refspec: &str) -> Result<bool, DiffError>;
127    /// Get HEAD commit SHA.
128    fn head_sha(&self) -> Result<String, DiffError>;
129    /// Find the root commit SHA (first commit with no parents).
130    fn root_commit_sha(&self) -> Result<Option<String>, DiffError>;
131}
132
133// ── File Discovery Port ──
134
135/// Abstraction over file discovery (directory walking with ignore rules).
136pub trait FileDiscovery: Send + Sync {
137    fn discover_files(
138        &self,
139        project_dir: &Path,
140        supported_ext: &[&str],
141    ) -> Result<Vec<PathBuf>, ScanError>;
142}
143
144// ── Language Ports ──
145
146/// Trait for language-specific parsing.
147pub trait LanguageParser: Send + Sync {
148    fn parse(&self, source: &str, file_path: &Path) -> ParseResult;
149    fn language_name(&self) -> &str;
150}
151
152/// Trait for language-specific import/module resolution.
153pub trait LanguageResolver: Send + Sync {
154    fn resolve_import(
155        &self,
156        import_path: &str,
157        source_file: &str,
158        project_root: &Path,
159    ) -> Option<PathBuf>;
160}
161
162/// Describes how to find sibling files that belong to the same logical module/package.
163/// Used by import resolution to expand candidate search scope.
164pub struct SiblingExpansion {
165    /// Directory prefix to match (e.g., "pkg/" or "" for root-level).
166    pub prefix: String,
167    /// File extension to match (e.g., "go", "py"). Empty means any extension.
168    pub extension: String,
169    /// If true, only match files without directory separators (root-level only).
170    pub root_only: bool,
171}
172
173/// Registry that provides language parsers and resolvers.
174pub trait LanguageRegistry: Send + Sync {
175    fn parser_for(&self, ext: &str) -> Option<&dyn LanguageParser>;
176    fn resolver_for(&self, ext: &str, root: &Path) -> Option<Box<dyn LanguageResolver>>;
177    fn supported_extensions(&self) -> &[&str];
178    fn config_files(&self) -> &[&str];
179    fn sibling_expansion(&self, rel_path: &str) -> Option<SiblingExpansion>;
180}