1use std::collections::BTreeMap;
6
7use serde::Serialize;
8use serde_json::value::RawValue;
9
10use crate::host::IoFail;
11
12pub mod code {
14 pub const OK: u8 = 0;
15 pub const USAGE: u8 = 2;
16 pub const IO: u8 = 3;
17 pub const INVALID_INPUT: u8 = 4;
18 pub const REFUSED: u8 = 5;
19 pub const REJECTED: u8 = 6;
20 pub const CANCELLED: u8 = 7;
21 pub const EVIDENCE_NOT_DELIVERED: u8 = 8;
22 pub const DIVERGED: u8 = 9;
23 pub const INCOMPARABLE: u8 = 10;
24 pub const FAIL: u8 = 11;
25 pub const UNKNOWN: u8 = 12;
26}
27
28#[derive(Debug, Clone)]
30pub struct Out(BTreeMap<String, Box<RawValue>>);
31
32fn raw_json<T: Serialize + ?Sized>(v: &T) -> Box<RawValue> {
33 serde_json::value::to_raw_value(v).expect("output value serializes")
35}
36
37impl Out {
38 pub fn new(command: &str, status: &str) -> Self {
39 Out(BTreeMap::new())
40 .set("command", command)
41 .set("status", status)
42 }
43
44 pub fn set<T: Serialize + ?Sized>(mut self, key: &str, value: &T) -> Self {
45 self.0.insert(key.into(), raw_json(value));
46 self
47 }
48
49 pub fn raw(mut self, key: &str, bytes: Vec<u8>) -> Self {
52 let value = String::from_utf8(bytes)
53 .ok()
54 .and_then(|s| RawValue::from_string(s).ok())
55 .expect("record canonical bytes are JSON");
56 self.0.insert(key.into(), value);
57 self
58 }
59
60 pub fn status(&self) -> &str {
61 self.0
62 .get("status")
63 .map_or("", |v| v.get().trim_matches('"'))
64 }
65
66 pub fn bytes(&self) -> Vec<u8> {
67 let mut b = serde_json::to_vec(&self.0).expect("output serializes");
68 b.push(b'\n');
69 b
70 }
71}
72
73#[derive(Debug, Clone)]
75pub struct Done {
76 pub code: u8,
77 pub out: Out,
78}
79
80pub type Res = Result<Done, Done>;
82
83impl Done {
84 pub fn new(code: u8, out: Out) -> Self {
85 Done { code, out }
86 }
87
88 pub fn ok(out: Out) -> Self {
89 Done::new(code::OK, out)
90 }
91
92 pub fn io(command: &str, e: IoFail) -> Self {
93 Done::new(
94 code::IO,
95 Out::new(command, "io_error")
96 .set("path", &e.path)
97 .set("detail", &e.detail),
98 )
99 }
100
101 pub fn invalid(command: &str, input: &str, detail: impl Into<String>) -> Self {
103 Done::new(
104 code::INVALID_INPUT,
105 Out::new(command, "invalid_input")
106 .set("input", input)
107 .set("detail", &detail.into()),
108 )
109 }
110}