Skip to main content

chimera_opencode/
backend.rs

1use bon::Builder;
2
3use chimera_core::*;
4
5use crate::config::{OpenCodeConfig, OpenCodeProvider};
6use crate::session::OpenCodeSession;
7
8#[derive(Debug, Builder)]
9pub struct OpenCodeBackend {
10    #[builder(into)]
11    pub(crate) api_key: Option<String>,
12
13    pub(crate) provider: Option<OpenCodeProvider>,
14}
15
16impl OpenCodeBackend {
17    /// Create a backend, reading API key from OPENCODE_ZEN_KEY or OPENCODE_API_KEY.
18    pub fn from_env() -> Self {
19        let api_key = std::env::var("OPENCODE_ZEN_KEY")
20            .ok()
21            .or_else(|| std::env::var("OPENCODE_API_KEY").ok());
22        Self {
23            api_key,
24            provider: None,
25        }
26    }
27}
28
29impl Backend for OpenCodeBackend {
30    type Config = OpenCodeConfig;
31    type Session = OpenCodeSession;
32
33    fn name(&self) -> &'static str {
34        "opencode"
35    }
36
37    async fn session(&self, config: SessionConfig<OpenCodeConfig>) -> Result<OpenCodeSession> {
38        let api_key = config
39            .backend
40            .api_key
41            .clone()
42            .or_else(|| self.api_key.clone());
43        let provider = self
44            .provider
45            .clone()
46            .unwrap_or_else(|| config.backend.provider.clone());
47
48        Ok(OpenCodeSession::new(api_key, provider, config))
49    }
50
51    async fn resume(
52        &self,
53        session_id: &str,
54        config: SessionConfig<OpenCodeConfig>,
55    ) -> Result<OpenCodeSession> {
56        let api_key = config
57            .backend
58            .api_key
59            .clone()
60            .or_else(|| self.api_key.clone());
61        let provider = self
62            .provider
63            .clone()
64            .unwrap_or_else(|| config.backend.provider.clone());
65
66        Ok(OpenCodeSession::new(api_key, provider, config).with_session_id(session_id.to_owned()))
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn backend_name() {
76        let backend = OpenCodeBackend::builder().build();
77        assert_eq!(backend.name(), "opencode");
78    }
79
80    #[tokio::test]
81    async fn session_merges_api_key() {
82        let backend = OpenCodeBackend::builder().api_key("backend-key").build();
83        let config = SessionConfig::builder()
84            .backend(OpenCodeConfig::default())
85            .build();
86
87        let session = backend.session(config).await.unwrap();
88        assert_eq!(session.api_key(), Some("backend-key"));
89    }
90
91    #[tokio::test]
92    async fn session_config_overrides_backend_key() {
93        let backend = OpenCodeBackend::builder().api_key("backend-key").build();
94        let config = SessionConfig::builder()
95            .backend(OpenCodeConfig::builder().api_key("session-key").build())
96            .build();
97
98        let session = backend.session(config).await.unwrap();
99        assert_eq!(session.api_key(), Some("session-key"));
100    }
101
102    #[tokio::test]
103    async fn resume_sets_session_id() {
104        let backend = OpenCodeBackend::builder().build();
105        let config = SessionConfig::builder()
106            .backend(OpenCodeConfig::default())
107            .build();
108
109        let session = backend.resume("sess-abc", config).await.unwrap();
110        assert_eq!(session.session_id(), Some("sess-abc"));
111    }
112}