lean_ctx/core/providers/
mod.rs1pub mod cache;
2pub mod config;
3pub mod config_provider;
4pub mod github;
5pub mod gitlab;
6pub mod init;
7pub mod jira;
8pub mod jira_oauth;
9pub mod mcp_bridge;
10pub mod postgres;
11pub mod provider_trait;
12pub mod registry;
13
14pub use provider_trait::{ContextPacket, ContextProvider, ProviderParams};
15pub use registry::{global_registry, ProviderRegistry};
16
17use serde::{Deserialize, Serialize};
18
19use crate::core::evidence::Claim;
20
21pub(crate) fn intern(s: String) -> &'static str {
26 use std::collections::HashSet;
27 use std::sync::{Mutex, OnceLock};
28 static POOL: OnceLock<Mutex<HashSet<&'static str>>> = OnceLock::new();
29 let pool = POOL.get_or_init(|| Mutex::new(HashSet::new()));
30 let mut guard = pool
31 .lock()
32 .unwrap_or_else(std::sync::PoisonError::into_inner);
33 if let Some(&existing) = guard.get(s.as_str()) {
34 return existing;
35 }
36 let leaked: &'static str = Box::leak(s.into_boxed_str());
37 guard.insert(leaked);
38 leaked
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ProviderResult {
43 pub provider: String,
44 pub resource_type: String,
45 pub items: Vec<ProviderItem>,
46 pub total_count: Option<usize>,
47 pub truncated: bool,
48}
49
50#[derive(Debug, Clone, Default, Serialize, Deserialize)]
51pub struct ProviderItem {
52 pub id: String,
53 pub title: String,
54 pub state: Option<String>,
55 pub author: Option<String>,
56 pub created_at: Option<String>,
57 pub updated_at: Option<String>,
58 pub url: Option<String>,
59 pub labels: Vec<String>,
60 pub body: Option<String>,
61 #[serde(default, skip_serializing_if = "Vec::is_empty")]
64 pub claims: Vec<Claim>,
65}
66
67impl ProviderResult {
68 pub fn format_compact(&self) -> String {
69 let mut out = format!(
70 "{} {} ({}{}):\n",
71 self.provider,
72 self.resource_type,
73 self.items.len(),
74 if self.truncated { "+" } else { "" }
75 );
76 for item in &self.items {
77 let state = item.state.as_deref().unwrap_or("");
78 let labels = if item.labels.is_empty() {
79 String::new()
80 } else {
81 format!(" [{}]", item.labels.join(","))
82 };
83 out.push_str(&format!(
84 " #{} {} ({}){}\n",
85 item.id, item.title, state, labels
86 ));
87 for claim in &item.claims {
88 out.push_str(" ▸ ");
89 out.push_str(&claim.render());
90 out.push('\n');
91 }
92 }
93 out
94 }
95}