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
44impl Detection {
45 pub fn new(
51 installed: bool,
52 config_root: Option<std::path::PathBuf>,
53 version: Option<String>,
54 ) -> Self {
55 Self {
56 installed,
57 config_root,
58 version,
59 }
60 }
61}
62
63#[derive(Debug, Error)]
65#[non_exhaustive]
66pub enum AdapterError {
67 #[error("adapter id {0:?} is not supported in this build")]
68 Unsupported(AdapterId),
69
70 #[error("config root unreadable: {0}")]
71 ConfigRootUnreadable(std::path::PathBuf),
72
73 #[error("io: {0}")]
74 Io(#[from] std::io::Error),
75
76 #[error("serialization: {0}")]
77 Serde(#[from] serde_json::Error),
78}
79
80#[async_trait::async_trait]
82pub trait Adapter: Send + Sync {
83 fn id(&self) -> AdapterId;
84
85 fn supports(&self, _cap: Capability) -> bool {
86 false
87 }
88
89 async fn detect(&self) -> Result<Detection, AdapterError>;
90
91 async fn install_command(&self) -> Result<String, AdapterError>;
92}
93
94pub const DEFAULT_ADAPTER_IDS: &[AdapterId] = &[];
97
98pub fn new_adapter(id: AdapterId) -> Result<Arc<dyn Adapter>, AdapterError> {
101 match id {
102 AdapterId::ClaudeCode
103 | AdapterId::CodexCli
104 | AdapterId::OpenCode
105 | AdapterId::Cursor
106 | AdapterId::Cline => Err(AdapterError::Unsupported(id)),
107 }
108}
109
110#[derive(Default)]
112pub struct AdapterRegistry {
113 inner: std::collections::HashMap<AdapterId, Arc<dyn Adapter>>,
114}
115
116impl AdapterRegistry {
117 pub fn new() -> Self {
118 Self::default()
119 }
120
121 pub fn register(&mut self, adapter: Arc<dyn Adapter>) -> Option<Arc<dyn Adapter>> {
126 self.inner.insert(adapter.id(), adapter)
127 }
128
129 pub fn get(&self, id: AdapterId) -> Option<Arc<dyn Adapter>> {
130 self.inner.get(&id).cloned()
131 }
132}
133
134pub fn default_registry() -> Result<AdapterRegistry, AdapterError> {
137 let mut registry = AdapterRegistry::new();
138 for id in DEFAULT_ADAPTER_IDS {
139 let _ = registry.register(new_adapter(*id)?);
140 }
141 Ok(registry)
142}