1use serde::{Deserialize, Serialize};
9
10use crate::result::ExecResult;
11
12#[non_exhaustive]
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
22#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
23#[serde(rename_all = "lowercase")]
24pub enum EntryType {
25 #[default]
27 Text,
28 File,
30 Directory,
32 Executable,
34 Symlink,
36}
37
38#[non_exhaustive]
54#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
55#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
56#[serde(default)]
57pub struct OutputNode {
58 pub name: String,
60 pub entry_type: EntryType,
62 pub text: Option<String>,
71 pub cells: Vec<String>,
73 pub children: Vec<OutputNode>,
75 pub line: Option<u64>,
86}
87
88impl OutputNode {
89 pub fn new(name: impl Into<String>) -> Self {
91 Self {
92 name: name.into(),
93 ..Default::default()
94 }
95 }
96
97 pub fn text(content: impl Into<String>) -> Self {
99 Self {
100 text: Some(content.into()),
101 ..Default::default()
102 }
103 }
104
105 pub fn with_entry_type(mut self, entry_type: EntryType) -> Self {
107 self.entry_type = entry_type;
108 self
109 }
110
111 pub fn at_line(mut self, line: u64) -> Self {
122 self.line = Some(line);
123 self
124 }
125
126 pub fn with_cells(mut self, cells: Vec<String>) -> Self {
128 self.cells = cells;
129 self
130 }
131
132 pub fn with_children(mut self, children: Vec<OutputNode>) -> Self {
134 self.children = children;
135 self
136 }
137
138 pub fn with_text(mut self, text: impl Into<String>) -> Self {
140 self.text = Some(text.into());
141 self
142 }
143
144 pub fn is_text_only(&self) -> bool {
146 self.text.is_some() && self.name.is_empty() && self.cells.is_empty() && self.children.is_empty()
147 }
148
149 pub fn has_children(&self) -> bool {
151 !self.children.is_empty()
152 }
153
154 pub fn estimated_byte_size(&self) -> usize {
156 if self.children.is_empty() {
157 self.name.len() + self.text.as_ref().map_or(0, |t| t.len())
158 } else {
159 let mut size = self.name.len() + 2; for (i, child) in self.children.iter().enumerate() {
162 if i > 0 {
163 size += 1; }
165 size += child.estimated_byte_size();
166 }
167 size + 1 }
169 }
170
171 pub fn write_canonical(&self, w: &mut dyn std::io::Write, budget: usize) -> std::io::Result<usize> {
173 if self.children.is_empty() {
174 w.write_all(self.name.as_bytes())?;
175 return Ok(self.name.len());
176 }
177 let mut written = 0;
178 w.write_all(self.name.as_bytes())?;
179 written += self.name.len();
180 if written >= budget {
181 return Ok(written);
182 }
183 w.write_all(b"/{")?;
184 written += 2;
185 for (i, child) in self.children.iter().enumerate() {
186 if written >= budget {
187 break;
188 }
189 if i > 0 {
190 w.write_all(b",")?;
191 written += 1;
192 }
193 written += child.write_canonical(w, budget.saturating_sub(written))?;
194 }
195 w.write_all(b"}")?;
196 written += 1;
197 Ok(written)
198 }
199
200 pub fn display_name(&self) -> &str {
202 if self.name.is_empty() {
203 self.text.as_deref().unwrap_or("")
204 } else {
205 &self.name
206 }
207 }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
228#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
229#[serde(default)]
230#[non_exhaustive]
231pub struct OutputData {
232 pub headers: Option<Vec<String>>,
234 pub root: Vec<OutputNode>,
236 #[serde(default, skip_serializing_if = "Option::is_none")]
253 pub rich_json: Option<serde_json::Value>,
254}
255
256impl OutputData {
257 pub fn new() -> Self {
259 Self::default()
260 }
261
262 pub fn text(content: impl Into<String>) -> Self {
266 Self {
267 headers: None,
268 root: vec![OutputNode::text(content)],
269 rich_json: None,
270 }
271 }
272
273 pub fn nodes(nodes: Vec<OutputNode>) -> Self {
275 Self {
276 headers: None,
277 root: nodes,
278 rich_json: None,
279 }
280 }
281
282 pub fn table(headers: Vec<String>, nodes: Vec<OutputNode>) -> Self {
284 Self {
285 headers: Some(headers),
286 root: nodes,
287 rich_json: None,
288 }
289 }
290
291 pub fn with_headers(mut self, headers: Vec<String>) -> Self {
293 self.headers = Some(headers);
294 self
295 }
296
297 pub fn with_rich_json(mut self, value: serde_json::Value) -> Self {
299 self.rich_json = Some(value);
300 self
301 }
302
303 pub fn is_simple_text(&self) -> bool {
305 self.root.len() == 1 && self.root[0].is_text_only()
306 }
307
308 pub fn is_flat(&self) -> bool {
310 self.root.iter().all(|n| !n.has_children())
311 }
312
313 pub fn is_tabular(&self) -> bool {
315 self.root.iter().any(|n| !n.cells.is_empty())
316 }
317
318 pub fn as_text(&self) -> Option<&str> {
320 if self.is_simple_text() {
321 self.root[0].text.as_deref()
322 } else {
323 None
324 }
325 }
326
327 pub fn into_text(mut self) -> Result<String, Self> {
331 if self.root.len() == 1 && self.root[0].is_text_only() {
332 Ok(self.root.pop().and_then(|n| n.text).unwrap_or_default())
333 } else {
334 Err(self)
335 }
336 }
337
338 pub fn estimated_byte_size(&self) -> usize {
343 if self.root.len() == 1 && self.root[0].is_text_only() {
344 return self.root[0].text.as_ref().map_or(0, |t| t.len());
345 }
346
347 if self.is_flat() {
348 let mut size = 0;
349 for (i, n) in self.root.iter().enumerate() {
350 if i > 0 {
351 size += 1; }
353 size += n.display_name().len();
354 for cell in &n.cells {
355 size += 1 + cell.len(); }
357 }
358 return size;
359 }
360
361 let mut size = 0;
363 for (i, n) in self.root.iter().enumerate() {
364 if i > 0 {
365 size += 1; }
367 size += n.estimated_byte_size();
368 }
369 size
370 }
371
372 pub fn write_canonical(&self, w: &mut dyn std::io::Write, budget: Option<usize>) -> std::io::Result<usize> {
378 let mut written = 0usize;
379 let budget = budget.unwrap_or(usize::MAX);
380
381 if self.root.len() == 1 && self.root[0].is_text_only() {
382 if let Some(ref text) = self.root[0].text {
383 w.write_all(text.as_bytes())?;
384 return Ok(text.len());
385 }
386 return Ok(0);
387 }
388
389 if self.is_flat() {
390 for (i, n) in self.root.iter().enumerate() {
391 if i > 0 {
392 w.write_all(b"\n")?;
393 written += 1;
394 }
395 let name = n.display_name();
396 w.write_all(name.as_bytes())?;
397 written += name.len();
398 for cell in &n.cells {
399 w.write_all(b"\t")?;
400 w.write_all(cell.as_bytes())?;
401 written += 1 + cell.len();
402 }
403 if written > budget {
404 return Ok(written);
405 }
406 }
407 return Ok(written);
408 }
409
410 for (i, n) in self.root.iter().enumerate() {
412 if i > 0 {
413 w.write_all(b"\n")?;
414 written += 1;
415 }
416 written += n.write_canonical(w, budget.saturating_sub(written))?;
417 if written > budget {
418 return Ok(written);
419 }
420 }
421 Ok(written)
422 }
423
424 pub fn to_canonical_string(&self) -> String {
433 if let Some(text) = self.as_text() {
434 return text.to_string();
435 }
436
437 if self.is_flat() {
439 return self.root.iter()
440 .map(|n| {
441 if n.cells.is_empty() {
442 n.display_name().to_string()
443 } else {
444 let mut parts = vec![n.display_name().to_string()];
446 parts.extend(n.cells.iter().cloned());
447 parts.join("\t")
448 }
449 })
450 .collect::<Vec<_>>()
451 .join("\n");
452 }
453
454 fn format_node(node: &OutputNode) -> String {
456 if node.children.is_empty() {
457 node.name.clone()
458 } else {
459 let children: Vec<String> = node.children.iter()
460 .map(format_node)
461 .collect();
462 format!("{}/{{{}}}", node.name, children.join(","))
463 }
464 }
465
466 self.root.iter()
467 .map(format_node)
468 .collect::<Vec<_>>()
469 .join("\n")
470 }
471
472 pub fn to_json(&self) -> serde_json::Value {
483 if let Some(rich) = &self.rich_json {
486 return rich.clone();
487 }
488 if let Some(text) = self.as_text() {
490 return serde_json::Value::String(text.to_string());
491 }
492
493 if let Some(ref headers) = self.headers {
498 fn row_to_json(node: &OutputNode, headers: &[String]) -> serde_json::Value {
499 let mut map = serde_json::Map::new();
500 if let Some(first) = headers.first() {
502 map.insert(first.clone(), serde_json::Value::String(node.name.clone()));
503 }
504 for (header, cell) in headers.iter().skip(1).zip(node.cells.iter()) {
506 map.insert(header.clone(), serde_json::Value::String(cell.clone()));
507 }
508 if let Some(line) = node.line {
513 map.insert("line".to_string(), serde_json::Value::from(line));
514 }
515 if !node.children.is_empty() {
516 let children: Vec<serde_json::Value> = node
517 .children
518 .iter()
519 .map(|child| row_to_json(child, headers))
520 .collect();
521 map.insert("children".to_string(), serde_json::Value::Array(children));
522 }
523 serde_json::Value::Object(map)
524 }
525 let rows: Vec<serde_json::Value> = self
526 .root
527 .iter()
528 .map(|node| row_to_json(node, headers))
529 .collect();
530 return serde_json::Value::Array(rows);
531 }
532
533 if !self.is_flat() {
535 fn node_to_json(node: &OutputNode) -> serde_json::Value {
536 if node.children.is_empty() {
537 serde_json::Value::Null
538 } else {
539 let mut map = serde_json::Map::new();
540 for child in &node.children {
541 map.insert(child.name.clone(), node_to_json(child));
542 }
543 serde_json::Value::Object(map)
544 }
545 }
546
547 if self.root.len() == 1 {
549 return node_to_json(&self.root[0]);
550 }
551 let mut map = serde_json::Map::new();
553 for node in &self.root {
554 map.insert(node.name.clone(), node_to_json(node));
555 }
556 return serde_json::Value::Object(map);
557 }
558
559 let items: Vec<serde_json::Value> = self.root.iter()
561 .map(|n| serde_json::Value::String(n.display_name().to_string()))
562 .collect();
563 serde_json::Value::Array(items)
564 }
565}
566
567#[non_exhaustive]
573#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574pub enum OutputFormat {
575 Json,
577}
578
579pub fn apply_output_format(mut result: ExecResult, format: OutputFormat) -> ExecResult {
585 if result.is_bytes() {
588 let envelope = crate::bytes::bytes_to_envelope(result.out_bytes().unwrap_or(&[]));
589 match format {
590 OutputFormat::Json => {
591 result.set_out(
592 serde_json::to_string(&envelope).unwrap_or_else(|_| "null".to_string()),
593 );
594 result.data = Some(crate::result::json_to_value(envelope));
595 result.set_output(None);
596 }
597 }
598 return result;
599 }
600 if !result.has_output() && result.text_out().is_empty() {
601 if !result.ok() && !result.err.is_empty() {
607 match format {
608 OutputFormat::Json => {
609 let mut obj = serde_json::json!({
612 "error": result.err.trim_end_matches('\n'),
613 "code": result.code,
614 });
615 if let Some(data) = &result.data {
620 obj["data"] = crate::result::value_to_json(data);
621 }
622 let out =
623 serde_json::to_string(&obj).unwrap_or_else(|_| "null".to_string());
624 result.set_out(out);
625 result.data = Some(crate::result::json_to_value(obj));
626 }
627 }
628 }
629 return result;
630 }
631 match format {
632 OutputFormat::Json => {
633 if let Some(output) = result.output() {
634 let json_value = output.to_json();
635 result.set_out(serde_json::to_string(&json_value)
637 .unwrap_or_else(|_| "null".to_string()));
638 result.data = Some(crate::result::json_to_value(json_value));
639 } else if let Some(data) = &result.data {
640 let json_out = serde_json::to_string(&crate::result::value_to_json(data))
645 .unwrap_or_else(|_| "null".to_string());
646 result.set_out(json_out);
647 } else {
648 let text = result.text_out().into_owned();
650 let json_out = serde_json::to_string(&text)
651 .unwrap_or_else(|_| "null".to_string());
652 result.data = Some(crate::value::Value::String(text));
653 result.set_out(json_out);
654 }
655 result.set_output(None);
657 result
658 }
659 }
660}
661
662#[cfg(test)]
663mod tests {
664 use super::*;
665
666 #[test]
667 fn entry_type_variants() {
668 assert_ne!(EntryType::File, EntryType::Directory);
669 assert_ne!(EntryType::Directory, EntryType::Executable);
670 assert_ne!(EntryType::Executable, EntryType::Symlink);
671 }
672
673 #[test]
674 fn to_json_simple_text() {
675 let output = OutputData::text("hello world");
676 assert_eq!(output.to_json(), serde_json::json!("hello world"));
677 }
678
679 #[test]
680 fn to_json_flat_list() {
681 let output = OutputData::nodes(vec![
682 OutputNode::new("file1"),
683 OutputNode::new("file2"),
684 OutputNode::new("file3"),
685 ]);
686 assert_eq!(output.to_json(), serde_json::json!(["file1", "file2", "file3"]));
687 }
688
689 #[test]
690 fn to_json_table() {
691 let output = OutputData::table(
692 vec!["NAME".into(), "SIZE".into(), "TYPE".into()],
693 vec![
694 OutputNode::new("foo.rs").with_cells(vec!["1024".into(), "file".into()]),
695 OutputNode::new("bar/").with_cells(vec!["4096".into(), "dir".into()]),
696 ],
697 );
698 assert_eq!(output.to_json(), serde_json::json!([
699 {"NAME": "foo.rs", "SIZE": "1024", "TYPE": "file"},
700 {"NAME": "bar/", "SIZE": "4096", "TYPE": "dir"},
701 ]));
702 }
703
704 #[test]
705 fn to_json_table_with_children() {
706 let output = OutputData::table(
711 vec!["NAME".into(), "TYPE".into(), "SIZE".into()],
712 vec![
713 OutputNode::new(".")
714 .with_entry_type(EntryType::Directory)
715 .with_children(vec![
716 OutputNode::new("top.txt").with_cells(vec!["-".into(), "6".into()]),
717 OutputNode::new("a").with_cells(vec!["d".into(), "60".into()]),
718 ]),
719 OutputNode::new("a")
720 .with_entry_type(EntryType::Directory)
721 .with_children(vec![
722 OutputNode::new("mid.txt").with_cells(vec!["-".into(), "3".into()]),
723 ]),
724 ],
725 );
726 assert_eq!(output.to_json(), serde_json::json!([
727 {"NAME": ".", "children": [
728 {"NAME": "top.txt", "TYPE": "-", "SIZE": "6"},
729 {"NAME": "a", "TYPE": "d", "SIZE": "60"},
730 ]},
731 {"NAME": "a", "children": [
732 {"NAME": "mid.txt", "TYPE": "-", "SIZE": "3"},
733 ]},
734 ]));
735 }
736
737 #[test]
738 fn to_json_tree() {
739 let child1 = OutputNode::new("main.rs").with_entry_type(EntryType::File);
740 let child2 = OutputNode::new("utils.rs").with_entry_type(EntryType::File);
741 let subdir = OutputNode::new("lib")
742 .with_entry_type(EntryType::Directory)
743 .with_children(vec![child2]);
744 let root = OutputNode::new("src")
745 .with_entry_type(EntryType::Directory)
746 .with_children(vec![child1, subdir]);
747
748 let output = OutputData::nodes(vec![root]);
749 assert_eq!(output.to_json(), serde_json::json!({
750 "main.rs": null,
751 "lib": {"utils.rs": null},
752 }));
753 }
754
755 #[test]
756 fn to_json_tree_multiple_roots() {
757 let root1 = OutputNode::new("src")
758 .with_entry_type(EntryType::Directory)
759 .with_children(vec![OutputNode::new("main.rs")]);
760 let root2 = OutputNode::new("docs")
761 .with_entry_type(EntryType::Directory)
762 .with_children(vec![OutputNode::new("README.md")]);
763
764 let output = OutputData::nodes(vec![root1, root2]);
765 assert_eq!(output.to_json(), serde_json::json!({
766 "src": {"main.rs": null},
767 "docs": {"README.md": null},
768 }));
769 }
770
771 #[test]
772 fn to_json_empty() {
773 let output = OutputData::new();
774 assert_eq!(output.to_json(), serde_json::json!([]));
775 }
776
777 #[test]
778 fn rich_json_round_trips_through_serde() {
779 let rich = serde_json::json!({"matches": [{"line": 1, "text": "hi"}]});
784 let output = OutputData::new().with_rich_json(rich.clone());
785
786 let encoded = serde_json::to_string(&output).unwrap();
787 let decoded: OutputData = serde_json::from_str(&encoded).unwrap();
788 assert_eq!(
789 decoded.rich_json,
790 Some(rich),
791 "rich_json must survive a serde round trip"
792 );
793 }
794
795 #[test]
796 fn rich_json_none_stays_off_the_wire() {
797 let output = OutputData::nodes(vec![OutputNode::new("f")]);
801 let encoded = serde_json::to_string(&output).unwrap();
802 assert!(
803 !encoded.contains("rich_json"),
804 "a None rich_json must not serialize: {encoded}"
805 );
806 let decoded: OutputData = serde_json::from_str(&encoded).unwrap();
807 assert_eq!(decoded.rich_json, None);
808 }
809
810 #[test]
811 fn apply_output_format_clears_sentinel() {
812 let output = OutputData::table(
813 vec!["NAME".into()],
814 vec![OutputNode::new("test")],
815 );
816 let result = ExecResult::with_output(output);
817 assert!(result.has_output(), "before: sentinel present");
818
819 let formatted = apply_output_format(result, OutputFormat::Json);
820 assert!(!formatted.has_output(), "after Json: sentinel cleared");
821 }
822
823 #[test]
824 fn apply_output_format_no_double_encoding() {
825 let output = OutputData::nodes(vec![
826 OutputNode::new("file1"),
827 OutputNode::new("file2"),
828 ]);
829 let result = ExecResult::with_output(output);
830
831 let after_json = apply_output_format(result, OutputFormat::Json);
832 let json_out = after_json.text_out().into_owned();
833 assert!(!after_json.has_output(), "sentinel cleared by Json");
834
835 let parsed: serde_json::Value = serde_json::from_str(&json_out).expect("valid JSON");
836 assert_eq!(parsed, serde_json::json!(["file1", "file2"]));
837 }
838
839 #[test]
840 fn apply_output_format_populates_data() {
841 let output = OutputData::nodes(vec![
842 OutputNode::new("file1"),
843 OutputNode::new("file2"),
844 ]);
845 let result = ExecResult::with_output(output);
846 assert!(result.data.is_none(), "before: no data on non-text output");
847
848 let formatted = apply_output_format(result, OutputFormat::Json);
849 assert!(formatted.data.is_some(), "after Json: data populated");
850
851 let data = formatted.data.unwrap();
853 assert!(matches!(data, crate::value::Value::Json(_)), "data should be Json variant");
854 if let crate::value::Value::Json(json) = data {
855 assert_eq!(json, serde_json::json!(["file1", "file2"]));
856 }
857 }
858
859 #[test]
860 fn apply_output_format_prefers_structured_data_over_text() {
861 use crate::value::Value;
865 let result = ExecResult::success_with_data("1", Value::Int(1));
866 assert!(!result.has_output(), "no OutputData sentinel on this path");
867
868 let formatted = apply_output_format(result, OutputFormat::Json);
869 let json_out = formatted.text_out().into_owned();
870 let parsed: serde_json::Value = serde_json::from_str(&json_out).expect("valid JSON");
871 assert_eq!(parsed, serde_json::json!(1), "number, not the string \"1\"");
872 assert_eq!(formatted.data, Some(Value::Int(1)));
874 }
875
876 #[test]
877 fn apply_output_format_compact_json() {
878 let output = OutputData::nodes(vec![
879 OutputNode::new("file1"),
880 OutputNode::new("file2"),
881 ]);
882 let result = ExecResult::with_output(output);
883
884 let formatted = apply_output_format(result, OutputFormat::Json);
885 let out = formatted.text_out();
887 assert!(!out.contains('\n'), "should be compact JSON, got: {}", out);
888 assert_eq!(&*out, r#"["file1","file2"]"#);
889 }
890
891 #[test]
892 fn apply_output_format_emits_json_error_object_on_failure() {
893 let result = ExecResult::failure(2, "grep: unknown flag --bogus-flag");
897 assert!(!result.has_output());
898 assert!(result.text_out().is_empty());
899
900 let formatted = apply_output_format(result, OutputFormat::Json);
901 let out = formatted.text_out().into_owned();
902 let parsed: serde_json::Value = serde_json::from_str(&out).expect("valid JSON");
903 assert_eq!(
904 parsed,
905 serde_json::json!({"error": "grep: unknown flag --bogus-flag", "code": 2})
906 );
907 assert!(matches!(formatted.data, Some(crate::value::Value::Json(_))));
909 }
910
911 #[test]
912 fn apply_output_format_preserves_structured_data_on_error() {
913 let mut result = ExecResult::failure(2, "rm: refusing without --recursive");
918 result.data = Some(crate::value::Value::Json(serde_json::json!({
919 "operation": "fs.remove",
920 "paths": ["important.dat"],
921 })));
922
923 let formatted = apply_output_format(result, OutputFormat::Json);
924 let out = formatted.text_out().into_owned();
925 let parsed: serde_json::Value = serde_json::from_str(&out).expect("valid JSON");
926 assert_eq!(parsed["error"], "rm: refusing without --recursive");
927 assert_eq!(parsed["code"], 2);
928 assert_eq!(parsed["data"]["operation"], "fs.remove");
929 match &formatted.data {
931 Some(crate::value::Value::Json(v)) => assert_eq!(v["data"]["operation"], "fs.remove"),
932 other => panic!("expected nested JSON data, got {other:?}"),
933 }
934 }
935
936 #[test]
937 fn apply_output_format_leaves_clean_no_match_empty() {
938 let result = ExecResult::failure(1, "");
941 let formatted = apply_output_format(result, OutputFormat::Json);
942 assert!(formatted.text_out().is_empty());
943 assert!(formatted.data.is_none());
944 }
945
946 #[test]
947 fn apply_output_format_empty_success_stays_empty() {
948 let result = ExecResult::success("");
949 let formatted = apply_output_format(result, OutputFormat::Json);
950 assert!(formatted.text_out().is_empty());
951 assert!(formatted.data.is_none());
952 }
953
954 #[test]
955 fn estimated_byte_size_text_only_node() {
956 let node = OutputNode::text("hello world");
957 assert_eq!(node.estimated_byte_size(), 11);
959 }
960
961 #[test]
962 fn estimated_byte_size_named_node() {
963 let node = OutputNode::new("file.txt");
964 assert_eq!(node.estimated_byte_size(), 8);
965 }
966
967 #[test]
968 fn write_canonical_respects_budget() {
969 let parent = OutputNode::new("root")
970 .with_children(vec![
971 OutputNode::new("aaaa"),
972 OutputNode::new("bbbb"),
973 OutputNode::new("cccc"),
974 ]);
975 let mut buf = Vec::new();
977 let written = parent.write_canonical(&mut buf, 8).unwrap();
978 let output = String::from_utf8(buf).unwrap();
979 assert!(written <= 16, "should respect budget, wrote {} bytes: {}", written, output);
981 assert!(output.starts_with("root"), "should start with root: {}", output);
983 }
984
985 #[test]
986 fn into_text_simple() {
987 let data = OutputData::text("hello");
988 assert_eq!(data.into_text(), Ok("hello".to_string()));
989 }
990
991 #[test]
992 fn into_text_non_simple() {
993 let data = OutputData::nodes(vec![OutputNode::new("a"), OutputNode::new("b")]);
994 assert!(data.into_text().is_err());
995 }
996
997 #[test]
998 fn into_text_empty() {
999 let data = OutputData::text("");
1000 assert_eq!(data.into_text(), Ok("".to_string()));
1001 }
1002}