Skip to main content

gobby_code/
lib.rs

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