1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, Default)]
6#[serde(rename_all = "camelCase")]
7pub struct Notebook {
8 pub name: Option<String>,
9 pub title: String,
10 #[serde(rename = "notebookId", skip_serializing_if = "Option::is_none")]
11 pub notebook_id: Option<String>,
12 #[serde(flatten)]
13 pub extra: HashMap<String, serde_json::Value>,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct NotebookRef {
19 pub notebook_id: String,
20 pub name: String,
21}
22
23#[derive(Debug, Clone, Serialize)]
24#[serde(rename_all = "camelCase")]
25pub struct CreateNotebookRequest {
26 pub title: String,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, Default)]
39pub struct BatchDeleteNotebooksRequest {
40 pub names: Vec<String>,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, Default)]
44pub struct BatchDeleteNotebooksResponse {
45 #[serde(flatten)]
47 pub extra: HashMap<String, serde_json::Value>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, Default)]
51#[serde(rename_all = "camelCase")]
52pub struct BatchCreateSourcesRequest {
53 #[serde(rename = "userContents")]
54 pub user_contents: Vec<UserContent>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, Default)]
58pub struct BatchDeleteSourcesRequest {
59 pub names: Vec<String>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, Default)]
63pub struct BatchDeleteSourcesResponse {
64 #[serde(flatten)]
66 pub extra: HashMap<String, serde_json::Value>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
70#[serde(untagged)]
71pub enum UserContent {
72 Web {
73 #[serde(rename = "webContent")]
74 web_content: WebContent,
75 },
76 Text {
77 #[serde(rename = "textContent")]
78 text_content: TextContent,
79 },
80 GoogleDrive {
81 #[serde(rename = "googleDriveContent")]
82 google_drive_content: GoogleDriveContent,
83 },
84 Video {
85 #[serde(rename = "videoContent")]
86 video_content: VideoContent,
87 },
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, Default)]
91#[serde(rename_all = "camelCase")]
92pub struct WebContent {
93 pub url: String,
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub source_name: Option<String>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, Default)]
99#[serde(rename_all = "camelCase")]
100pub struct TextContent {
101 pub content: String,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub source_name: Option<String>,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, Default)]
116#[serde(rename_all = "camelCase")]
117pub struct GoogleDriveContent {
118 pub document_id: String,
119 pub mime_type: String,
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub source_name: Option<String>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize, Default)]
125pub struct VideoContent {
126 #[serde(rename = "youtubeUrl")]
127 pub url: String,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize, Default)]
131#[serde(rename_all = "camelCase")]
132pub struct BatchCreateSourcesResponse {
133 #[serde(default)]
134 pub sources: Vec<SourceResult>,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub error_count: Option<i32>,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize, Default)]
140#[serde(rename_all = "camelCase")]
141pub struct SourceResult {
142 pub url: Option<String>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub name: Option<String>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub status: Option<String>,
147 #[serde(flatten)]
148 pub extra: HashMap<String, serde_json::Value>,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize, Default)]
152#[serde(rename_all = "camelCase")]
153pub struct ShareRequest {
154 pub account_and_roles: Vec<AccountRole>,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize, Default)]
158#[serde(rename_all = "camelCase")]
159pub struct AccountRole {
160 pub email: String,
161 pub role: ProjectRole,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, Default)]
165#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
166pub enum ProjectRole {
167 ProjectRoleOwner,
168 ProjectRoleWriter,
169 #[default]
170 ProjectRoleReader,
171 ProjectRoleNotShared,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize, Default)]
175#[serde(rename_all = "camelCase")]
176pub struct ShareResponse {
177 #[serde(default)]
178 pub granted: Option<i32>,
179 #[serde(flatten)]
180 pub extra: HashMap<String, serde_json::Value>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize, Default)]
189#[serde(rename_all = "camelCase")]
190pub struct ListRecentlyViewedResponse {
191 #[serde(default)]
192 pub notebooks: Vec<serde_json::Value>,
193 #[serde(skip_serializing_if = "Option::is_none")]
195 pub next_page_token: Option<String>,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize, Default)]
210pub struct AudioOverviewRequest {
211 }
219
220#[derive(Debug, Clone, Serialize, Deserialize, Default)]
227#[serde(rename_all = "camelCase")]
228pub struct AudioOverviewResponse {
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub name: Option<String>,
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub state: Option<String>,
233 #[serde(flatten)]
234 pub extra: HashMap<String, serde_json::Value>,
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn user_content_untagged_web() {
243 let json = r#"{"webContent":{"url":"https://example.com"}}"#;
244 let content: UserContent = serde_json::from_str(json).unwrap();
245 match content {
246 UserContent::Web { web_content } => {
247 assert_eq!(web_content.url, "https://example.com");
248 }
249 _ => panic!("expected Web variant"),
250 }
251 }
252
253 #[test]
254 fn user_content_untagged_text() {
255 let json = r#"{"textContent":{"content":"sample text"}}"#;
256 let content: UserContent = serde_json::from_str(json).unwrap();
257 match content {
258 UserContent::Text { text_content } => {
259 assert_eq!(text_content.content, "sample text");
260 }
261 _ => panic!("expected Text variant"),
262 }
263 }
264
265 #[test]
266 fn user_content_untagged_google_drive() {
267 let json = r#"{"googleDriveContent":{"documentId":"123","mimeType":"application/vnd.google-apps.document"}}"#;
268 let content: UserContent = serde_json::from_str(json).unwrap();
269 match content {
270 UserContent::GoogleDrive {
271 google_drive_content,
272 } => {
273 assert_eq!(google_drive_content.document_id, "123");
274 assert_eq!(
275 google_drive_content.mime_type,
276 "application/vnd.google-apps.document"
277 );
278 }
279 _ => panic!("expected GoogleDrive variant"),
280 }
281 }
282
283 #[test]
284 fn user_content_untagged_video() {
285 let json = r#"{"videoContent":{"youtubeUrl":"https://youtube.com/watch?v=123"}}"#;
286 let content: UserContent = serde_json::from_str(json).unwrap();
287 match content {
288 UserContent::Video { video_content } => {
289 assert_eq!(video_content.url, "https://youtube.com/watch?v=123");
290 }
291 _ => panic!("expected Video variant"),
292 }
293 }
294
295 #[test]
296 fn user_content_video_serializes_correctly() {
297 let content = UserContent::Video {
298 video_content: VideoContent {
299 url: "https://youtube.com/watch?v=123".to_string(),
300 },
301 };
302 let json = serde_json::to_string(&content).unwrap();
303 assert!(
304 json.contains("videoContent"),
305 "JSON should contain videoContent, got: {}",
306 json
307 );
308 assert!(
309 json.contains(r#""youtubeUrl":"https://youtube.com/watch?v=123""#),
310 "JSON should contain youtubeUrl field, got: {}",
311 json
312 );
313 }
314
315 #[test]
316 fn notebook_skips_notebook_id_when_none() {
317 let notebook = Notebook {
318 name: Some("test".to_string()),
319 title: "Test Notebook".to_string(),
320 notebook_id: None,
321 extra: Default::default(),
322 };
323 let json = serde_json::to_string(¬ebook).unwrap();
324 assert!(!json.contains("notebookId"));
325 }
326
327 #[test]
328 fn notebook_includes_notebook_id_when_some() {
329 let notebook = Notebook {
330 name: Some("test".to_string()),
331 title: "Test Notebook".to_string(),
332 notebook_id: Some("nb123".to_string()),
333 extra: Default::default(),
334 };
335 let json = serde_json::to_string(¬ebook).unwrap();
336 assert!(json.contains("notebookId"));
337 assert!(json.contains("nb123"));
338 }
339}