kernex_adapter_core/
lib.rs1#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
7
8use std::sync::Arc;
9
10use thiserror::Error;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
14#[non_exhaustive]
15pub enum AdapterId {
16 ClaudeCode,
17 CodexCli,
18 OpenCode,
19 Cursor,
20 Cline,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
27#[non_exhaustive]
28pub enum Capability {
29 Skills,
30 Memory,
31 Mcp,
32 OutputStyle,
33}
34
35#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
37#[non_exhaustive]
38pub struct Detection {
39 pub installed: bool,
40 pub config_root: Option<std::path::PathBuf>,
41 pub version: Option<String>,
42}
43
44#[derive(Debug, Error)]
46#[non_exhaustive]
47pub enum AdapterError {
48 #[error("adapter id {0:?} is not supported in this build")]
49 Unsupported(AdapterId),
50
51 #[error("config root unreadable: {0}")]
52 ConfigRootUnreadable(std::path::PathBuf),
53
54 #[error("io: {0}")]
55 Io(#[from] std::io::Error),
56
57 #[error("serialization: {0}")]
58 Serde(#[from] serde_json::Error),
59}
60
61#[async_trait::async_trait]
63pub trait Adapter: Send + Sync {
64 fn id(&self) -> AdapterId;
65
66 fn supports(&self, _cap: Capability) -> bool {
67 false
68 }
69
70 async fn detect(&self) -> Result<Detection, AdapterError>;
71
72 async fn install_command(&self) -> Result<String, AdapterError>;
73}
74
75pub const DEFAULT_ADAPTER_IDS: &[AdapterId] = &[];
78
79pub fn new_adapter(id: AdapterId) -> Result<Arc<dyn Adapter>, AdapterError> {
82 match id {
83 AdapterId::ClaudeCode
84 | AdapterId::CodexCli
85 | AdapterId::OpenCode
86 | AdapterId::Cursor
87 | AdapterId::Cline => Err(AdapterError::Unsupported(id)),
88 }
89}
90
91#[derive(Default)]
93pub struct AdapterRegistry {
94 inner: std::collections::HashMap<AdapterId, Arc<dyn Adapter>>,
95}
96
97impl AdapterRegistry {
98 pub fn new() -> Self {
99 Self::default()
100 }
101
102 pub fn register(&mut self, adapter: Arc<dyn Adapter>) -> Option<Arc<dyn Adapter>> {
107 self.inner.insert(adapter.id(), adapter)
108 }
109
110 pub fn get(&self, id: AdapterId) -> Option<Arc<dyn Adapter>> {
111 self.inner.get(&id).cloned()
112 }
113}
114
115pub fn default_registry() -> Result<AdapterRegistry, AdapterError> {
118 let mut registry = AdapterRegistry::new();
119 for id in DEFAULT_ADAPTER_IDS {
120 let _ = registry.register(new_adapter(*id)?);
121 }
122 Ok(registry)
123}