Skip to main content

gobby_code/
lib.rs

1pub mod commands;
2pub mod config;
3pub mod contract;
4pub mod db;
5pub mod falkor;
6pub mod freshness;
7pub mod git;
8pub mod graph;
9pub mod index;
10pub(crate) mod index_lock;
11pub mod models;
12pub mod output;
13pub mod progress;
14pub mod project;
15pub mod projection;
16pub mod savings;
17pub mod schema;
18pub mod search;
19pub mod secrets;
20pub mod setup;
21pub mod skill;
22pub mod utils;
23pub mod vector;
24pub(crate) mod visibility;
25
26pub use index::api::{
27    IndexDegradation, IndexDurations, IndexOutcome, IndexRequest, UnsupportedFileType, index_files,
28};
29
30#[cfg(test)]
31mod tests {
32    use serde::Serialize;
33    use serde::de::DeserializeOwned;
34
35    fn assert_cli_independent_contract<T>()
36    where
37        T: Serialize + DeserializeOwned,
38    {
39        let type_name = std::any::type_name::<T>();
40        assert!(!type_name.contains("commands::"), "{type_name}");
41        assert!(!type_name.contains("output::"), "{type_name}");
42        assert!(!type_name.contains("clap"), "{type_name}");
43    }
44
45    #[test]
46    fn public_projection_api_is_cli_independent() {
47        let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
48        for rel_path in [
49            "src/index/api.rs",
50            "src/graph/typed_query.rs",
51            "src/graph/code_graph.rs",
52            "src/vector/code_symbols.rs",
53            "src/projection/sync.rs",
54        ] {
55            assert!(
56                manifest_dir.join(rel_path).exists(),
57                "missing projection boundary module {rel_path}"
58            );
59        }
60
61        assert_cli_independent_contract::<crate::index::api::CodeFactWriteRequest>();
62        assert_cli_independent_contract::<crate::index::api::CodeFactWriteSummary>();
63        assert_cli_independent_contract::<crate::index::api::IndexRequest>();
64        assert_cli_independent_contract::<crate::index::api::IndexOutcome>();
65        assert_cli_independent_contract::<crate::index::api::IndexDurations>();
66        assert_cli_independent_contract::<crate::index::api::IndexDegradation>();
67        assert_cli_independent_contract::<crate::graph::code_graph::GraphLifecycleRequest>();
68        assert_cli_independent_contract::<crate::graph::code_graph::GraphLifecycleOutput>();
69        assert_cli_independent_contract::<crate::graph::code_graph::GraphReadRequest>();
70        assert_cli_independent_contract::<crate::vector::code_symbols::CodeSymbolVectorSearchRequest>(
71        );
72        assert_cli_independent_contract::<crate::vector::code_symbols::CodeSymbolVectorSearchHit>();
73        assert_cli_independent_contract::<crate::vector::code_symbols::CodeSymbolVectorPayload>();
74        assert_cli_independent_contract::<crate::projection::sync::ProjectionSyncRequest>();
75        assert_cli_independent_contract::<crate::projection::sync::ProjectionSyncStatus>();
76    }
77
78    #[test]
79    fn falkor_facade_is_available() {
80        let _ = std::any::type_name::<crate::falkor::FalkorClient>();
81
82        let ctx = crate::config::Context {
83            database_url: "postgresql://localhost/nonexistent".to_string(),
84            project_root: std::path::PathBuf::from("/nonexistent"),
85            project_id: "project-1".to_string(),
86            quiet: true,
87            falkordb: None,
88            qdrant: None,
89            embedding: None,
90            code_vectors: crate::config::CodeVectorSettings::default(),
91            daemon_url: None,
92            index_scope: crate::config::ProjectIndexScope::Single,
93        };
94
95        let value = crate::falkor::with_falkor(&ctx, 7usize, |_| Ok(9usize))
96            .expect("missing FalkorDB config degrades to default");
97        assert_eq!(value, 7);
98    }
99
100    #[test]
101    fn foundation_consumer_migration() {
102        let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
103        let cargo = std::fs::read_to_string(manifest_dir.join("Cargo.toml"))
104            .expect("read gobby-code Cargo.toml");
105        for feature in ["postgres", "falkor", "qdrant", "search", "indexing"] {
106            assert!(
107                cargo.contains(feature),
108                "gobby-code must enable gobby-core feature `{feature}`"
109            );
110        }
111
112        let config_root =
113            std::fs::read_to_string(manifest_dir.join("src/config.rs")).expect("read config.rs");
114        let config_services = std::fs::read_to_string(manifest_dir.join("src/config/services.rs"))
115            .expect("read config/services.rs");
116        assert!(config_services.contains("gobby_core::config::{"));
117        assert!(config_services.contains("ConfigSource"));
118        assert!(config_services.contains("StandaloneConfig"));
119        assert!(config_services.contains("impl ConfigSource for PostgresConfigSource"));
120        assert!(config_services.contains("gobby_core::postgres::read_config_value"));
121        assert!(config_services.contains("gobby_core::config::decode_config_value"));
122        assert!(!config_root.contains("fn decode_config_value("));
123
124        let db = std::fs::read_to_string(manifest_dir.join("src/db.rs")).expect("read db.rs");
125        assert!(db.contains("gobby_core::gobby_home"));
126        assert!(db.contains("gobby_core::postgres::connect_readonly"));
127        assert!(db.contains("gobby_core::postgres::connect_readwrite"));
128        assert!(!db.contains("Client::connect(database_url, NoTls)"));
129
130        let graph = [
131            std::fs::read_to_string(manifest_dir.join("src/graph/code_graph.rs"))
132                .expect("read graph/code_graph.rs"),
133            std::fs::read_to_string(manifest_dir.join("src/graph/code_graph/connection.rs"))
134                .expect("read graph/code_graph/connection.rs"),
135            std::fs::read_to_string(manifest_dir.join("src/graph/code_graph/write.rs"))
136                .expect("read graph/code_graph/write.rs"),
137        ]
138        .join("\n");
139        assert!(graph.contains("gobby_core::falkor::with_graph"));
140        assert!(!graph.contains("falkor::with_falkor"));
141
142        let falkor =
143            std::fs::read_to_string(manifest_dir.join("src/falkor.rs")).expect("read falkor.rs");
144        assert!(falkor.contains("gobby_core::falkor::GraphClient"));
145        for token in [
146            ["Falkor", "Client", "Builder"].concat(),
147            ["Falkor", "Connection", "Info"].concat(),
148            ["Sync", "Graph"].concat(),
149            ["Falkor", "Value"].concat(),
150            ["Lazy", "Result", "Set"].concat(),
151            ["Query", "Result"].concat(),
152        ] {
153            assert!(!falkor.contains(&token), "{token} leaked into falkor.rs");
154        }
155
156        let semantic = std::fs::read_to_string(manifest_dir.join("src/search/semantic.rs"))
157            .expect("read search/semantic.rs");
158        assert!(semantic.contains("gobby_core::qdrant::with_qdrant"));
159        assert!(semantic.contains("gobby_core::qdrant::collection_name"));
160        assert!(semantic.contains("gobby_core::qdrant::search"));
161    }
162
163    #[test]
164    fn indexing_search_primitive_migration() {
165        let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
166
167        let walker = std::fs::read_to_string(manifest_dir.join("src/index/walker.rs"))
168            .expect("read index/walker.rs");
169        assert!(walker.contains("gobby_core::indexing::WalkerSettings"));
170        let local_walker_builder = ["WalkBuilder", "::new(root)"].concat();
171        assert!(!walker.contains(&local_walker_builder));
172
173        let hasher = std::fs::read_to_string(manifest_dir.join("src/index/hasher.rs"))
174            .expect("read index/hasher.rs");
175        assert!(hasher.contains("gobby_core::indexing::file_content_hash"));
176        let local_buffer = format!("let mut buf = [0u8; {}]", 64 * 1024);
177        assert!(!hasher.contains(&local_buffer));
178
179        let rrf =
180            std::fs::read_to_string(manifest_dir.join("src/search/rrf.rs")).expect("read rrf.rs");
181        assert!(rrf.contains("gobby_core::search::rrf_merge"));
182        let local_rrf_const = ["const ", "RRF_K"].concat();
183        assert!(!rrf.contains(&local_rrf_const));
184
185        let chunker = std::fs::read_to_string(manifest_dir.join("src/index/chunker.rs"))
186            .expect("read index/chunker.rs");
187        assert!(!chunker.contains("use gobby_core::indexing::Chunk"));
188        assert!(!chunker.contains("use gobby_core::indexing::ChunkIdentity"));
189        assert!(!chunker.contains("use gobby_core::indexing::IndexEvent"));
190        assert!(!chunker.contains("use gobby_core::indexing::index_events_from_hashes"));
191    }
192
193    #[test]
194    fn falkor_facade_uses_core_graph_client_only() {
195        let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
196        let src_dir = manifest_dir.join("src");
197        let mut offenders = Vec::new();
198
199        fn visit(path: &std::path::Path, offenders: &mut Vec<std::path::PathBuf>) {
200            for entry in std::fs::read_dir(path).expect("read source directory") {
201                let entry = entry.expect("source entry");
202                let path = entry.path();
203                if path.is_dir() {
204                    visit(&path, offenders);
205                    continue;
206                }
207                if path.extension().and_then(|ext| ext.to_str()) != Some("rs") {
208                    continue;
209                }
210                let source = std::fs::read_to_string(&path).expect("read source file");
211                let builder = ["Falkor", "Client", "Builder"].concat();
212                if source.contains(&builder) {
213                    offenders.push(path);
214                }
215            }
216        }
217
218        visit(&src_dir, &mut offenders);
219        assert!(
220            offenders.is_empty(),
221            "gobby-code must use gobby_core::falkor::GraphClient instead of direct FalkorDB builders: {offenders:?}"
222        );
223    }
224}