1use crate::types::permission::Ruleset;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub struct Session {
10 pub id: String,
12 #[serde(default, skip_serializing_if = "Option::is_none")]
14 pub project_id: Option<String>,
15 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub directory: Option<String>,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
20 pub parent_id: Option<String>,
21 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub summary: Option<SessionSummary>,
24 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub share: Option<ShareInfo>,
27 #[serde(default)]
29 pub title: String,
30 #[serde(default)]
32 pub version: String,
33 #[serde(default, skip_serializing_if = "Option::is_none")]
35 pub time: Option<SessionTime>,
36 #[serde(default, skip_serializing_if = "Option::is_none")]
38 pub permission: Option<Ruleset>,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub revert: Option<RevertInfo>,
42 #[serde(flatten)]
44 pub extra: serde_json::Value,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct SessionSummary {
50 pub additions: u64,
52 pub deletions: u64,
54 pub files: u64,
56 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub diffs: Option<Vec<FileDiffLite>>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63#[serde(rename_all = "camelCase")]
64pub struct FileDiffLite {
65 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub file: Option<String>,
68 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub before: Option<String>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub after: Option<String>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub additions: Option<u64>,
77 #[serde(default, skip_serializing_if = "Option::is_none")]
79 pub deletions: Option<u64>,
80 #[serde(flatten)]
82 pub extra: serde_json::Value,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct ShareInfo {
88 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub secret: Option<String>,
91 pub url: String,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct SessionTime {
98 pub created: i64,
100 pub updated: i64,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
104 pub compacting: Option<i64>,
105 #[serde(default, skip_serializing_if = "Option::is_none")]
107 pub archived: Option<i64>,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
112#[serde(rename_all = "camelCase")]
113pub struct RevertInfo {
114 pub message_id: String,
116 #[serde(default, skip_serializing_if = "Option::is_none")]
118 pub part_id: Option<String>,
119 #[serde(default, skip_serializing_if = "Option::is_none")]
121 pub snapshot: Option<String>,
122 #[serde(default, skip_serializing_if = "Option::is_none")]
124 pub diff: Option<String>,
125}
126
127#[derive(Debug, Clone, Default, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct CreateSessionRequest {
131 #[serde(rename = "parentID", default, skip_serializing_if = "Option::is_none")]
135 pub parent_id: Option<String>,
136 #[serde(default, skip_serializing_if = "Option::is_none")]
138 pub title: Option<String>,
139 #[serde(default, skip_serializing_if = "Option::is_none")]
141 pub permission: Option<Ruleset>,
142 #[serde(skip)]
147 pub directory: Option<String>,
148}
149
150#[derive(Debug, Clone, Default)]
152pub struct SessionCreateOptions {
153 pub parent_id: Option<String>,
155 pub title: Option<String>,
157 pub directory: Option<String>,
159 pub permission: Option<Ruleset>,
161}
162
163impl SessionCreateOptions {
164 pub fn new() -> Self {
166 Self::default()
167 }
168
169 pub fn with_parent_id(mut self, parent_id: impl Into<String>) -> Self {
171 self.parent_id = Some(parent_id.into());
172 self
173 }
174
175 pub fn with_title(mut self, title: impl Into<String>) -> Self {
177 self.title = Some(title.into());
178 self
179 }
180
181 pub fn with_directory(mut self, directory: impl Into<String>) -> Self {
183 self.directory = Some(directory.into());
184 self
185 }
186
187 pub fn with_permission(mut self, permission: Ruleset) -> Self {
189 self.permission = Some(permission);
190 self
191 }
192}
193
194impl From<SessionCreateOptions> for CreateSessionRequest {
195 fn from(value: SessionCreateOptions) -> Self {
196 Self {
197 parent_id: value.parent_id,
198 title: value.title,
199 permission: value.permission,
200 directory: value.directory,
201 }
202 }
203}
204
205#[derive(Debug, Clone, Default, Serialize, Deserialize)]
207#[serde(rename_all = "camelCase")]
208pub struct UpdateSessionRequest {
209 #[serde(default, skip_serializing_if = "Option::is_none")]
211 pub title: Option<String>,
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
216#[serde(rename_all = "camelCase")]
217pub struct SummarizeRequest {
218 pub provider_id: String,
220 pub model_id: String,
222 #[serde(default, skip_serializing_if = "Option::is_none")]
224 pub auto: Option<bool>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229#[serde(rename_all = "camelCase")]
230pub struct RevertRequest {
231 pub message_id: String,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
235 pub part_id: Option<String>,
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
240#[serde(rename_all = "camelCase")]
241pub struct SessionStatus {
242 #[serde(default, skip_serializing_if = "Option::is_none")]
244 pub active_session_id: Option<String>,
245 #[serde(default)]
247 pub busy: bool,
248}
249
250#[derive(Debug, Clone, Default, Serialize, Deserialize)]
252#[serde(rename_all = "camelCase")]
253pub struct SessionDiff {
254 #[serde(default)]
256 pub diff: String,
257 #[serde(default)]
259 pub files: Vec<String>,
260 #[serde(flatten)]
262 pub extra: serde_json::Value,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
267#[serde(rename_all = "camelCase")]
268pub struct TodoItem {
269 pub id: String,
271 pub content: String,
273 #[serde(default)]
275 pub completed: bool,
276 #[serde(default, skip_serializing_if = "Option::is_none")]
278 pub priority: Option<String>,
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284
285 #[test]
286 fn test_session_deserialize() {
287 let json = r#"{
288 "id": "s1",
289 "projectId": "p1",
290 "directory": "/path/to/project",
291 "title": "Test Session",
292 "version": "1.0",
293 "time": {"created": 1234567890, "updated": 1234567890}
294 }"#;
295 let session: Session = serde_json::from_str(json).unwrap();
296 assert_eq!(session.id, "s1");
297 assert_eq!(session.title, "Test Session");
298 }
299
300 #[test]
301 fn test_session_minimal() {
302 let json = r#"{"id": "s1"}"#;
304 let session: Session = serde_json::from_str(json).unwrap();
305 assert_eq!(session.id, "s1");
306 assert!(session.project_id.is_none());
307 }
308
309 #[test]
310 fn test_session_with_optional_fields() {
311 let json = r#"{
312 "id": "s1",
313 "projectId": "p1",
314 "directory": "/path",
315 "title": "Test",
316 "version": "1.0",
317 "time": {"created": 1234567890, "updated": 1234567890},
318 "parentId": "s0",
319 "share": {"url": "https://example.com/share/s1"}
320 }"#;
321 let session: Session = serde_json::from_str(json).unwrap();
322 assert_eq!(session.parent_id, Some("s0".to_string()));
323 assert!(session.share.is_some());
324 }
325}