zai_rs/tool/file_parser_create/
response.rs1use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
4
5#[derive(Debug, Clone, Serialize)]
7pub struct FileParserCreateResponse {
8 #[serde(skip_serializing_if = "Option::is_none")]
10 pub success: Option<bool>,
11 #[serde(skip_serializing_if = "Option::is_none")]
13 pub message: Option<String>,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub task_id: Option<String>,
17}
18
19#[derive(Deserialize)]
20struct FileParserCreateResponseWire {
21 success: Option<bool>,
22 message: Option<String>,
23 task_id: Option<String>,
24}
25
26impl<'de> Deserialize<'de> for FileParserCreateResponse {
27 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
28 where
29 D: Deserializer<'de>,
30 {
31 let wire = FileParserCreateResponseWire::deserialize(deserializer)?;
32 if wire.success.is_none() && wire.message.is_none() && wire.task_id.is_none() {
33 return Err(D::Error::custom(
34 "file-parser response contained no documented non-null fields",
35 ));
36 }
37 Ok(Self {
38 success: wire.success,
39 message: wire.message,
40 task_id: wire.task_id,
41 })
42 }
43}
44
45impl FileParserCreateResponse {
46 pub fn is_success(&self) -> bool {
48 self.success != Some(false)
49 && self
50 .task_id
51 .as_deref()
52 .is_some_and(|task_id| !task_id.trim().is_empty())
53 }
54
55 pub fn task_id(&self) -> Option<&str> {
57 self.task_id.as_deref()
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn accepts_optional_schema_fields_but_rejects_empty_success() {
67 assert!(serde_json::from_str::<FileParserCreateResponse>("{}").is_err());
68 assert!(serde_json::from_str::<FileParserCreateResponse>(r#"{"success":null}"#).is_err());
69 let response: FileParserCreateResponse =
70 serde_json::from_str(r#"{"success":true,"task_id":"task-1"}"#).unwrap();
71 assert!(response.is_success());
72 assert_eq!(response.task_id(), Some("task-1"));
73 }
74}