Skip to main content

scud/opencode/
types.rs

1//! OpenCode Server API types
2//!
3//! Type definitions matching the OpenCode Server REST API schema.
4
5use serde::{Deserialize, Serialize};
6
7/// Session creation request
8#[derive(Debug, Serialize)]
9pub struct CreateSessionRequest {
10    pub title: String,
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub system_prompt: Option<String>,
13}
14
15/// Session response from server
16#[derive(Debug, Deserialize, Clone)]
17pub struct Session {
18    pub id: String,
19    pub title: String,
20    #[serde(default)]
21    pub created_at: String,
22    #[serde(default)]
23    pub message_count: usize,
24}
25
26/// Message/prompt request
27#[derive(Debug, Serialize)]
28pub struct MessageRequest {
29    pub parts: Vec<MessagePart>,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub model: Option<ModelSpec>,
32}
33
34/// Part of a message (currently only text supported)
35#[derive(Debug, Serialize)]
36#[serde(tag = "type", rename_all = "snake_case")]
37pub enum MessagePart {
38    Text { text: String },
39}
40
41/// Model specification for a message
42#[derive(Debug, Serialize)]
43pub struct ModelSpec {
44    #[serde(rename = "providerID")]
45    pub provider_id: String,
46    #[serde(rename = "modelID")]
47    pub model_id: String,
48}
49
50/// Session status response
51#[derive(Debug, Deserialize)]
52pub struct SessionStatus {
53    pub id: String,
54    pub status: SessionState,
55    #[serde(default)]
56    pub active_message: Option<String>,
57}
58
59/// State of a session
60#[derive(Debug, Deserialize, PartialEq, Clone)]
61#[serde(rename_all = "snake_case")]
62pub enum SessionState {
63    Idle,
64    Running,
65    Completed,
66    Error,
67}
68
69/// Server info response
70#[derive(Debug, Deserialize)]
71pub struct ServerInfo {
72    #[serde(default)]
73    pub version: String,
74    #[serde(default)]
75    pub ready: bool,
76}
77
78/// Error response from server
79#[derive(Debug, Deserialize)]
80pub struct ErrorResponse {
81    pub error: String,
82    #[serde(default)]
83    pub details: Option<String>,
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_create_session_request_serialization() {
92        let request = CreateSessionRequest {
93            title: "Test Session".to_string(),
94            system_prompt: None,
95        };
96
97        let json = serde_json::to_string(&request).unwrap();
98        assert!(json.contains("\"title\":\"Test Session\""));
99        // system_prompt should be omitted when None
100        assert!(!json.contains("system_prompt"));
101    }
102
103    #[test]
104    fn test_create_session_request_with_system_prompt() {
105        let request = CreateSessionRequest {
106            title: "Test".to_string(),
107            system_prompt: Some("You are helpful".to_string()),
108        };
109
110        let json = serde_json::to_string(&request).unwrap();
111        assert!(json.contains("system_prompt"));
112        assert!(json.contains("You are helpful"));
113    }
114
115    #[test]
116    fn test_message_part_serialization() {
117        let part = MessagePart::Text {
118            text: "Hello".to_string(),
119        };
120
121        let json = serde_json::to_string(&part).unwrap();
122        assert!(json.contains("\"type\":\"text\""));
123        assert!(json.contains("\"text\":\"Hello\""));
124    }
125
126    #[test]
127    fn test_model_spec_serialization() {
128        let spec = ModelSpec {
129            provider_id: "xai".to_string(),
130            model_id: "grok-3".to_string(),
131        };
132
133        let json = serde_json::to_string(&spec).unwrap();
134        assert!(json.contains("\"providerID\":\"xai\""));
135        assert!(json.contains("\"modelID\":\"grok-3\""));
136    }
137
138    #[test]
139    fn test_session_deserialization() {
140        let json = r#"{"id": "abc123", "title": "Test", "created_at": "2024-01-01", "message_count": 5}"#;
141        let session: Session = serde_json::from_str(json).unwrap();
142
143        assert_eq!(session.id, "abc123");
144        assert_eq!(session.title, "Test");
145        assert_eq!(session.message_count, 5);
146    }
147
148    #[test]
149    fn test_session_deserialization_minimal() {
150        // Server might not always include all fields
151        let json = r#"{"id": "abc", "title": "Test"}"#;
152        let session: Session = serde_json::from_str(json).unwrap();
153
154        assert_eq!(session.id, "abc");
155        assert_eq!(session.message_count, 0); // default
156    }
157
158    #[test]
159    fn test_session_state_deserialization() {
160        let json = r#""running""#;
161        let state: SessionState = serde_json::from_str(json).unwrap();
162        assert_eq!(state, SessionState::Running);
163
164        let json = r#""idle""#;
165        let state: SessionState = serde_json::from_str(json).unwrap();
166        assert_eq!(state, SessionState::Idle);
167    }
168}