1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use serde_json::Value;
3use std::fmt;
4
5pub(crate) fn deserialize_content_blocks<'de, D>(
7 deserializer: D,
8) -> Result<Vec<ContentBlock>, D::Error>
9where
10 D: Deserializer<'de>,
11{
12 let value: Value = Value::deserialize(deserializer)?;
13 match value {
14 Value::String(s) => Ok(vec![ContentBlock::Text(TextBlock {
15 text: s,
16 citations: Vec::new(),
17 })]),
18 Value::Array(_) => serde_json::from_value(value).map_err(serde::de::Error::custom),
19 _ => Err(serde::de::Error::custom(
20 "content must be a string or array",
21 )),
22 }
23}
24
25#[derive(Debug, Clone)]
30pub enum ContentBlock {
31 Text(TextBlock),
32 Image(ImageBlock),
33 Thinking(ThinkingBlock),
34 ToolUse(ToolUseBlock),
35 ToolResult(ToolResultBlock),
36 ServerToolUse(ServerToolUseBlock),
38 WebSearchToolResult(WebSearchToolResultBlock),
40 CodeExecutionToolResult(CodeExecutionToolResultBlock),
42 McpToolUse(McpToolUseBlock),
44 McpToolResult(McpToolResultBlock),
46 ContainerUpload(ContainerUploadBlock),
48 Fallback(FallbackBlock),
51 Unknown(Value),
54}
55
56impl ContentBlock {
57 pub fn block_type(&self) -> &str {
59 match self {
60 Self::Text(_) => "text",
61 Self::Image(_) => "image",
62 Self::Thinking(_) => "thinking",
63 Self::ToolUse(_) => "tool_use",
64 Self::ToolResult(_) => "tool_result",
65 Self::ServerToolUse(_) => "server_tool_use",
66 Self::WebSearchToolResult(_) => "web_search_tool_result",
67 Self::CodeExecutionToolResult(_) => "code_execution_tool_result",
68 Self::McpToolUse(_) => "mcp_tool_use",
69 Self::McpToolResult(_) => "mcp_tool_result",
70 Self::ContainerUpload(_) => "container_upload",
71 Self::Fallback(_) => "fallback",
72 Self::Unknown(v) => v.get("type").and_then(|t| t.as_str()).unwrap_or("unknown"),
73 }
74 }
75
76 pub fn is_unknown(&self) -> bool {
78 matches!(self, Self::Unknown(_))
79 }
80}
81
82impl Serialize for ContentBlock {
83 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
84 match self {
85 Self::Text(v) => serialize_tagged("text", v, serializer),
86 Self::Image(v) => serialize_tagged("image", v, serializer),
87 Self::Thinking(v) => serialize_tagged("thinking", v, serializer),
88 Self::ToolUse(v) => serialize_tagged("tool_use", v, serializer),
89 Self::ToolResult(v) => serialize_tagged("tool_result", v, serializer),
90 Self::ServerToolUse(v) => serialize_tagged("server_tool_use", v, serializer),
91 Self::WebSearchToolResult(v) => {
92 serialize_tagged("web_search_tool_result", v, serializer)
93 }
94 Self::CodeExecutionToolResult(v) => {
95 serialize_tagged("code_execution_tool_result", v, serializer)
96 }
97 Self::McpToolUse(v) => serialize_tagged("mcp_tool_use", v, serializer),
98 Self::McpToolResult(v) => serialize_tagged("mcp_tool_result", v, serializer),
99 Self::ContainerUpload(v) => serialize_tagged("container_upload", v, serializer),
100 Self::Fallback(v) => serialize_tagged("fallback", v, serializer),
101 Self::Unknown(v) => v.serialize(serializer),
102 }
103 }
104}
105
106impl<'de> Deserialize<'de> for ContentBlock {
107 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
108 let value = Value::deserialize(deserializer)?;
109 let type_str = value
110 .get("type")
111 .and_then(|v| v.as_str())
112 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
113
114 match type_str {
115 "text" => serde_json::from_value(value)
116 .map(ContentBlock::Text)
117 .map_err(serde::de::Error::custom),
118 "image" => serde_json::from_value(value)
119 .map(ContentBlock::Image)
120 .map_err(serde::de::Error::custom),
121 "thinking" => serde_json::from_value(value)
122 .map(ContentBlock::Thinking)
123 .map_err(serde::de::Error::custom),
124 "tool_use" => serde_json::from_value(value)
125 .map(ContentBlock::ToolUse)
126 .map_err(serde::de::Error::custom),
127 "tool_result" => serde_json::from_value(value)
128 .map(ContentBlock::ToolResult)
129 .map_err(serde::de::Error::custom),
130 "server_tool_use" => serde_json::from_value(value)
131 .map(ContentBlock::ServerToolUse)
132 .map_err(serde::de::Error::custom),
133 "web_search_tool_result" => serde_json::from_value(value)
134 .map(ContentBlock::WebSearchToolResult)
135 .map_err(serde::de::Error::custom),
136 "code_execution_tool_result" => serde_json::from_value(value)
137 .map(ContentBlock::CodeExecutionToolResult)
138 .map_err(serde::de::Error::custom),
139 "mcp_tool_use" => serde_json::from_value(value)
140 .map(ContentBlock::McpToolUse)
141 .map_err(serde::de::Error::custom),
142 "mcp_tool_result" => serde_json::from_value(value)
143 .map(ContentBlock::McpToolResult)
144 .map_err(serde::de::Error::custom),
145 "container_upload" => serde_json::from_value(value)
146 .map(ContentBlock::ContainerUpload)
147 .map_err(serde::de::Error::custom),
148 "fallback" => serde_json::from_value(value)
149 .map(ContentBlock::Fallback)
150 .map_err(serde::de::Error::custom),
151 _ => Ok(ContentBlock::Unknown(value)),
152 }
153 }
154}
155
156fn serialize_tagged<S: Serializer, T: Serialize>(
158 tag: &str,
159 value: &T,
160 serializer: S,
161) -> Result<S::Ok, S::Error> {
162 let mut map = serde_json::to_value(value).map_err(serde::ser::Error::custom)?;
163 if let Some(obj) = map.as_object_mut() {
164 obj.insert("type".to_string(), Value::String(tag.to_string()));
165 }
166 map.serialize(serializer)
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct TextBlock {
172 pub text: String,
173 #[serde(default, skip_serializing_if = "Vec::is_empty")]
176 pub citations: Vec<Citation>,
177}
178
179#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
189pub struct Citation {
190 #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
192 pub citation_type: Option<String>,
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub url: Option<String>,
196 #[serde(default, skip_serializing_if = "Option::is_none")]
198 pub title: Option<String>,
199 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub cited_text: Option<String>,
202 #[serde(default, skip_serializing_if = "Option::is_none")]
204 pub document_index: Option<u32>,
205 #[serde(default, skip_serializing_if = "Option::is_none")]
207 pub document_title: Option<String>,
208 #[serde(flatten)]
210 pub extra: serde_json::Map<String, Value>,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct ImageBlock {
216 pub source: ImageSource,
217}
218
219#[derive(Debug, Clone, PartialEq, Eq, Hash)]
221pub enum ImageSourceType {
222 Base64,
224 Unknown(String),
226}
227
228impl ImageSourceType {
229 pub fn as_str(&self) -> &str {
230 match self {
231 Self::Base64 => "base64",
232 Self::Unknown(s) => s.as_str(),
233 }
234 }
235}
236
237impl fmt::Display for ImageSourceType {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 f.write_str(self.as_str())
240 }
241}
242
243impl From<&str> for ImageSourceType {
244 fn from(s: &str) -> Self {
245 match s {
246 "base64" => Self::Base64,
247 other => Self::Unknown(other.to_string()),
248 }
249 }
250}
251
252impl Serialize for ImageSourceType {
253 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
254 serializer.serialize_str(self.as_str())
255 }
256}
257
258impl<'de> Deserialize<'de> for ImageSourceType {
259 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
260 let s = String::deserialize(deserializer)?;
261 Ok(Self::from(s.as_str()))
262 }
263}
264
265#[derive(Debug, Clone, PartialEq, Eq, Hash)]
267pub enum MediaType {
268 Jpeg,
270 Png,
272 Gif,
274 Webp,
276 Unknown(String),
278}
279
280impl MediaType {
281 pub fn as_str(&self) -> &str {
282 match self {
283 Self::Jpeg => "image/jpeg",
284 Self::Png => "image/png",
285 Self::Gif => "image/gif",
286 Self::Webp => "image/webp",
287 Self::Unknown(s) => s.as_str(),
288 }
289 }
290}
291
292impl fmt::Display for MediaType {
293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 f.write_str(self.as_str())
295 }
296}
297
298impl From<&str> for MediaType {
299 fn from(s: &str) -> Self {
300 match s {
301 "image/jpeg" => Self::Jpeg,
302 "image/png" => Self::Png,
303 "image/gif" => Self::Gif,
304 "image/webp" => Self::Webp,
305 other => Self::Unknown(other.to_string()),
306 }
307 }
308}
309
310impl Serialize for MediaType {
311 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
312 serializer.serialize_str(self.as_str())
313 }
314}
315
316impl<'de> Deserialize<'de> for MediaType {
317 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
318 let s = String::deserialize(deserializer)?;
319 Ok(Self::from(s.as_str()))
320 }
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize)]
325pub struct ImageSource {
326 #[serde(rename = "type")]
327 pub source_type: ImageSourceType,
328 pub media_type: MediaType,
329 pub data: String,
330}
331
332#[derive(Debug, Clone, Serialize, Deserialize)]
334pub struct ThinkingBlock {
335 pub thinking: String,
336 pub signature: String,
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize)]
341pub struct ToolUseBlock {
342 pub id: String,
343 pub name: String,
344 pub input: Value,
345 #[serde(default, skip_serializing_if = "Option::is_none")]
349 pub caller: Option<ToolCaller>,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
354pub struct ToolCaller {
355 #[serde(rename = "type")]
357 pub caller_type: String,
358 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
360 pub extra: serde_json::Map<String, Value>,
361}
362
363impl ToolUseBlock {
364 pub fn typed_input(&self) -> Option<crate::tool_inputs::ToolInput> {
387 serde_json::from_value(self.input.clone()).ok()
388 }
389
390 pub fn try_typed_input(&self) -> Result<crate::tool_inputs::ToolInput, serde_json::Error> {
394 serde_json::from_value(self.input.clone())
395 }
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
400pub struct ToolResultBlock {
401 pub tool_use_id: String,
402 #[serde(skip_serializing_if = "Option::is_none")]
403 pub content: Option<ToolResultContent>,
404 #[serde(skip_serializing_if = "Option::is_none")]
405 pub is_error: Option<bool>,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410#[serde(untagged)]
411pub enum ToolResultContent {
412 Text(String),
413 Structured(Vec<Value>),
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
420pub struct ServerToolUseBlock {
421 pub id: String,
422 pub name: String,
423 #[serde(default)]
424 pub input: Value,
425}
426
427#[derive(Debug, Clone, Serialize, Deserialize)]
429pub struct WebSearchToolResultBlock {
430 pub tool_use_id: String,
431 #[serde(default)]
432 pub content: Value,
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize)]
437pub struct CodeExecutionToolResultBlock {
438 pub tool_use_id: String,
439 #[serde(default)]
440 pub content: Value,
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
447pub struct McpToolUseBlock {
448 pub id: String,
449 pub name: String,
450 #[serde(skip_serializing_if = "Option::is_none")]
451 pub server_name: Option<String>,
452 #[serde(default)]
453 pub input: Value,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
458pub struct McpToolResultBlock {
459 pub tool_use_id: String,
460 #[serde(default)]
461 pub content: Value,
462 #[serde(skip_serializing_if = "Option::is_none")]
463 pub is_error: Option<bool>,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct ContainerUploadBlock {
469 #[serde(flatten)]
470 pub data: Value,
471}
472
473#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
479pub struct FallbackBlock {
480 pub from: FallbackModel,
482 pub to: FallbackModel,
484}
485
486#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
488pub struct FallbackModel {
489 pub model: String,
490}
491
492#[cfg(test)]
493mod tests {
494 use super::*;
495 use serde_json::json;
496
497 #[test]
498 fn test_unknown_content_block_deserializes() {
499 let json = json!({
500 "type": "some_future_block_type",
501 "data": "arbitrary"
502 });
503
504 let block: ContentBlock = serde_json::from_value(json.clone()).unwrap();
505 assert!(block.is_unknown());
506 assert_eq!(block.block_type(), "some_future_block_type");
507 if let ContentBlock::Unknown(v) = &block {
508 assert_eq!(v["data"], "arbitrary");
509 } else {
510 panic!("Expected Unknown variant");
511 }
512 }
513
514 #[test]
515 fn test_unknown_block_roundtrips() {
516 let json = json!({
517 "type": "some_future_type",
518 "tool_use_id": "x",
519 "content": [{"nested": true}]
520 });
521
522 let block: ContentBlock = serde_json::from_value(json.clone()).unwrap();
523 let reserialized = serde_json::to_value(&block).unwrap();
524 assert_eq!(json, reserialized);
525 }
526
527 #[test]
528 fn test_server_tool_use_deserializes() {
529 let json = json!({
530 "type": "server_tool_use",
531 "id": "srvtu_1",
532 "name": "web_search",
533 "input": {"query": "rust serde"}
534 });
535
536 let block: ContentBlock = serde_json::from_value(json).unwrap();
537 assert!(!block.is_unknown());
538 assert_eq!(block.block_type(), "server_tool_use");
539 if let ContentBlock::ServerToolUse(b) = &block {
540 assert_eq!(b.id, "srvtu_1");
541 assert_eq!(b.name, "web_search");
542 assert_eq!(b.input["query"], "rust serde");
543 } else {
544 panic!("Expected ServerToolUse variant");
545 }
546 }
547
548 #[test]
549 fn test_web_search_tool_result_deserializes() {
550 let json = json!({
551 "type": "web_search_tool_result",
552 "tool_use_id": "srvtu_1",
553 "content": [{"type": "web_search_result", "url": "https://example.com"}]
554 });
555
556 let block: ContentBlock = serde_json::from_value(json.clone()).unwrap();
557 assert_eq!(block.block_type(), "web_search_tool_result");
558 if let ContentBlock::WebSearchToolResult(b) = &block {
559 assert_eq!(b.tool_use_id, "srvtu_1");
560 } else {
561 panic!("Expected WebSearchToolResult variant");
562 }
563 let reserialized = serde_json::to_value(&block).unwrap();
565 assert_eq!(json, reserialized);
566 }
567
568 #[test]
569 fn test_code_execution_tool_result_deserializes() {
570 let json = json!({
571 "type": "code_execution_tool_result",
572 "tool_use_id": "exec_1",
573 "content": {"stdout": "hello", "exit_code": 0}
574 });
575
576 let block: ContentBlock = serde_json::from_value(json).unwrap();
577 assert_eq!(block.block_type(), "code_execution_tool_result");
578 assert!(matches!(block, ContentBlock::CodeExecutionToolResult(_)));
579 }
580
581 #[test]
582 fn test_mcp_tool_use_deserializes() {
583 let json = json!({
584 "type": "mcp_tool_use",
585 "id": "mcp_tu_1",
586 "name": "custom_tool",
587 "server_name": "my-mcp-server",
588 "input": {"arg": "value"}
589 });
590
591 let block: ContentBlock = serde_json::from_value(json).unwrap();
592 assert_eq!(block.block_type(), "mcp_tool_use");
593 if let ContentBlock::McpToolUse(b) = &block {
594 assert_eq!(b.id, "mcp_tu_1");
595 assert_eq!(b.name, "custom_tool");
596 assert_eq!(b.server_name.as_deref(), Some("my-mcp-server"));
597 } else {
598 panic!("Expected McpToolUse variant");
599 }
600 }
601
602 #[test]
603 fn test_mcp_tool_result_deserializes() {
604 let json = json!({
605 "type": "mcp_tool_result",
606 "tool_use_id": "mcp_tu_1",
607 "content": "tool output text",
608 "is_error": false
609 });
610
611 let block: ContentBlock = serde_json::from_value(json).unwrap();
612 assert_eq!(block.block_type(), "mcp_tool_result");
613 if let ContentBlock::McpToolResult(b) = &block {
614 assert_eq!(b.tool_use_id, "mcp_tu_1");
615 assert_eq!(b.is_error, Some(false));
616 } else {
617 panic!("Expected McpToolResult variant");
618 }
619 }
620
621 #[test]
622 fn test_container_upload_deserializes() {
623 let json = json!({
624 "type": "container_upload",
625 "file_name": "output.csv",
626 "url": "https://storage.example.com/file"
627 });
628
629 let block: ContentBlock = serde_json::from_value(json).unwrap();
630 assert_eq!(block.block_type(), "container_upload");
631 assert!(matches!(block, ContentBlock::ContainerUpload(_)));
632 }
633
634 #[test]
635 fn test_fallback_block_deserializes() {
636 let json = json!({
638 "from": { "model": "claude-fable-5" },
639 "to": { "model": "claude-opus-4-8" },
640 "type": "fallback"
641 });
642
643 let block: ContentBlock = serde_json::from_value(json.clone()).unwrap();
644 assert!(!block.is_unknown());
645 assert_eq!(block.block_type(), "fallback");
646 if let ContentBlock::Fallback(b) = &block {
647 assert_eq!(b.from.model, "claude-fable-5");
648 assert_eq!(b.to.model, "claude-opus-4-8");
649 } else {
650 panic!("Expected Fallback variant");
651 }
652 let reserialized = serde_json::to_value(&block).unwrap();
654 assert_eq!(json, reserialized);
655 }
656
657 #[test]
658 fn test_known_blocks_still_work() {
659 let text_json = json!({"type": "text", "text": "hello"});
660 let block: ContentBlock = serde_json::from_value(text_json).unwrap();
661 assert!(!block.is_unknown());
662 assert_eq!(block.block_type(), "text");
663 assert!(matches!(block, ContentBlock::Text(TextBlock { text, .. }) if text == "hello"));
664
665 let tool_json =
666 json!({"type": "tool_use", "id": "tu_1", "name": "Bash", "input": {"command": "ls"}});
667 let block: ContentBlock = serde_json::from_value(tool_json).unwrap();
668 assert_eq!(block.block_type(), "tool_use");
669 assert!(matches!(block, ContentBlock::ToolUse(_)));
670 }
671
672 #[test]
673 fn test_known_blocks_roundtrip() {
674 let text_json = json!({"type": "text", "text": "hello world"});
675 let block: ContentBlock = serde_json::from_value(text_json.clone()).unwrap();
676 let reserialized = serde_json::to_value(&block).unwrap();
677 assert_eq!(text_json, reserialized);
678 }
679
680 #[test]
681 fn test_assistant_message_with_server_tool_use() {
682 let json = r#"{
683 "type": "assistant",
684 "message": {
685 "id": "msg_1",
686 "role": "assistant",
687 "model": "claude-3",
688 "content": [
689 {"type": "text", "text": "Let me search for that."},
690 {"type": "server_tool_use", "id": "srvtu_1", "name": "web_search", "input": {"query": "test"}},
691 {"type": "tool_use", "id": "tu_1", "name": "Bash", "input": {"command": "ls"}}
692 ]
693 },
694 "session_id": "abc"
695 }"#;
696
697 let output: crate::io::ClaudeOutput = serde_json::from_str(json).unwrap();
698 assert!(output.is_assistant_message());
699 let assistant = output.as_assistant().unwrap();
700 assert_eq!(assistant.message.content.len(), 3);
701 assert!(matches!(
702 &assistant.message.content[0],
703 ContentBlock::Text(_)
704 ));
705 assert!(matches!(
706 &assistant.message.content[1],
707 ContentBlock::ServerToolUse(_)
708 ));
709 assert!(matches!(
710 &assistant.message.content[2],
711 ContentBlock::ToolUse(_)
712 ));
713
714 assert_eq!(
716 output.text_content(),
717 Some("Let me search for that.".to_string())
718 );
719 assert_eq!(output.tool_uses().count(), 1);
721 }
722
723 #[test]
724 fn test_text_block_with_citations() {
725 let json = json!({
726 "type": "text",
727 "text": "According to the documentation...",
728 "citations": [
729 {"type": "web_search_result_location", "url": "https://example.com", "title": "Example"}
730 ]
731 });
732
733 let block: ContentBlock = serde_json::from_value(json.clone()).unwrap();
734 if let ContentBlock::Text(t) = &block {
735 assert_eq!(t.text, "According to the documentation...");
736 assert_eq!(t.citations.len(), 1);
737 let cite = &t.citations[0];
738 assert_eq!(
739 cite.citation_type.as_deref(),
740 Some("web_search_result_location")
741 );
742 assert_eq!(cite.url.as_deref(), Some("https://example.com"));
743 assert_eq!(cite.title.as_deref(), Some("Example"));
744 } else {
745 panic!("Expected Text variant");
746 }
747 let reserialized = serde_json::to_value(&block).unwrap();
749 assert_eq!(json, reserialized);
750 }
751
752 #[test]
753 fn test_citation_preserves_unmodeled_location_fields() {
754 let json = json!({
757 "type": "char_location",
758 "cited_text": "the quick brown fox",
759 "document_index": 0,
760 "document_title": "Doc A",
761 "start_char_index": 12,
762 "end_char_index": 31
763 });
764 let cite: Citation = serde_json::from_value(json.clone()).unwrap();
765 assert_eq!(cite.citation_type.as_deref(), Some("char_location"));
766 assert_eq!(cite.cited_text.as_deref(), Some("the quick brown fox"));
767 assert_eq!(cite.document_index, Some(0));
768 assert_eq!(
769 cite.extra.get("start_char_index").and_then(|v| v.as_u64()),
770 Some(12)
771 );
772 assert_eq!(
773 cite.extra.get("end_char_index").and_then(|v| v.as_u64()),
774 Some(31)
775 );
776 assert_eq!(serde_json::to_value(&cite).unwrap(), json);
778 }
779
780 #[test]
781 fn test_text_block_without_citations_defaults_empty() {
782 let json = json!({"type": "text", "text": "no citations"});
783 let block: ContentBlock = serde_json::from_value(json).unwrap();
784 if let ContentBlock::Text(t) = &block {
785 assert!(t.citations.is_empty());
786 } else {
787 panic!("Expected Text variant");
788 }
789 let reserialized = serde_json::to_value(&block).unwrap();
791 assert!(reserialized.get("citations").is_none());
792 }
793
794 #[test]
795 fn test_missing_type_field_errors() {
796 let json = json!({"text": "no type field"});
797 let result = serde_json::from_value::<ContentBlock>(json);
798 assert!(result.is_err());
799 }
800}