lean_ctx/core/providers/
init.rs1use std::path::Path;
12use std::sync::Arc;
13
14use super::config_provider::ConfigProvider;
15use super::config_provider::discovery::discover_configs;
16use super::emspace::EmspaceProvider;
17use super::github::GitHubProvider;
18use super::gitlab::GitLabProvider;
19use super::jira::JiraProvider;
20use super::mcp_bridge::McpBridgeProvider;
21use super::postgres::PostgresProvider;
22use super::provider_trait::ContextProvider;
23use super::registry::{ProviderRegistry, global_registry};
24
25pub fn init_builtin_providers() {
29 init_with_project_root(None);
30}
31
32pub fn init_with_project_root(project_root: Option<&Path>) {
34 populate(global_registry(), project_root);
35}
36
37pub fn populate(registry: &ProviderRegistry, project_root: Option<&Path>) {
42 let cfg = crate::core::config::Config::load();
43
44 if !cfg.providers.enabled {
45 tracing::debug!("[providers] subsystem disabled via config");
46 return;
47 }
48
49 if cfg.providers.github.enabled {
51 registry.register(Arc::new(GitHubProvider::new()));
52 }
53
54 if cfg.providers.gitlab.enabled {
55 registry.register(Arc::new(GitLabProvider::new()));
56 }
57
58 registry.register(Arc::new(JiraProvider::new()));
59 registry.register(Arc::new(PostgresProvider::new()));
60
61 if let Some(root) = project_root {
63 registry.register(Arc::new(EmspaceProvider::new(root)));
64 }
65
66 for (name, entry) in &cfg.providers.mcp_bridges {
68 if let Some(url) = &entry.url
69 && !url.is_empty()
70 {
71 registry.register(Arc::new(McpBridgeProvider::new(name, url)));
72 continue;
73 }
74 if let Some(command) = &entry.command
75 && !command.is_empty()
76 {
77 registry.register(Arc::new(McpBridgeProvider::new_stdio(
78 name,
79 command,
80 &entry.args,
81 )));
82 continue;
83 }
84 tracing::warn!("[providers] MCP bridge '{name}' has neither url nor command — skipping");
85 }
86
87 let discovered = discover_configs(project_root);
89 let mut config_count = 0;
90 for entry in discovered {
91 match ConfigProvider::from_config(entry.config) {
92 Ok(provider) => {
93 tracing::info!(
94 "[providers] registered config provider '{}' from {}",
95 provider.id(),
96 entry.source_path.display()
97 );
98 registry.register(Arc::new(provider));
99 config_count += 1;
100 }
101 Err(e) => {
102 tracing::warn!("[providers] skipping {}: {e}", entry.source_path.display());
103 }
104 }
105 }
106
107 #[cfg(feature = "wasm")]
111 if let Ok(dir) = std::env::var("LEAN_CTX_WASM_DIR") {
112 let ids = crate::core::wasm_ext::register_providers_from_dir(registry, &dir);
113 if !ids.is_empty() {
114 tracing::info!(
115 "[providers] registered {} WASM provider(s): {ids:?}",
116 ids.len()
117 );
118 }
119 }
120
121 tracing::debug!(
122 "[providers] initialized {} provider(s) ({} config-based), {} available",
123 registry.provider_count(),
124 config_count,
125 registry.available_provider_ids().len(),
126 );
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn init_registers_github_when_enabled() {
135 init_builtin_providers();
136 let reg = global_registry();
137 assert!(reg.get("github").is_some());
138 }
139}