Skip to main content

gatehouse_runtime_protocol/
wire.rs

1//! Preserve one encoding for each variant without first buffering into Value,
2//! which would discard duplicate object keys before typed validation.
3use crate::{Capability, Engine, Object, Operation, Outcome, Protocol};
4use serde::de::{self, MapAccess, Visitor};
5use serde::{Deserialize, Deserializer};
6use std::fmt;
7
8#[derive(Deserialize)]
9#[serde(deny_unknown_fields)]
10struct Target {
11    object_id: String,
12}
13
14#[derive(Deserialize)]
15#[serde(deny_unknown_fields)]
16struct Present {
17    object: Object,
18}
19
20#[derive(Deserialize)]
21#[serde(deny_unknown_fields)]
22struct Capabilities {
23    supported: Vec<Capability>,
24}
25
26fn tag<'de, M: MapAccess<'de>>(map: &mut M) -> Result<String, M::Error> {
27    map.next_key()?
28        .ok_or_else(|| de::Error::custom("missing variant"))
29}
30
31fn end<'de, M: MapAccess<'de>>(map: &mut M) -> Result<(), M::Error> {
32    if map.next_key::<de::IgnoredAny>()?.is_some() {
33        return Err(de::Error::custom("multiple variant fields"));
34    }
35    Ok(())
36}
37
38struct OperationVisitor;
39impl<'de> Visitor<'de> for OperationVisitor {
40    type Value = Operation;
41
42    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
43        formatter.write_str("a string unit operation or one tagged operation object")
44    }
45
46    fn visit_str<E: de::Error>(self, value: &str) -> Result<Operation, E> {
47        match value {
48            "capabilities" => Ok(Operation::Capabilities),
49            _ => Err(E::custom("invalid string operation")),
50        }
51    }
52
53    fn visit_map<M: MapAccess<'de>>(self, mut map: M) -> Result<Operation, M::Error> {
54        let operation = match tag(&mut map)?.as_str() {
55            "inspect" => Operation::Inspect {
56                object_id: map.next_value::<Target>()?.object_id,
57            },
58            "remove" => Operation::Remove {
59                object_id: map.next_value::<Target>()?.object_id,
60            },
61            _ => return Err(de::Error::custom("invalid object operation")),
62        };
63        end(&mut map)?;
64        Ok(operation)
65    }
66}
67
68impl<'de> Deserialize<'de> for Operation {
69    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
70        deserializer.deserialize_any(OperationVisitor)
71    }
72}
73
74struct OutcomeVisitor;
75impl<'de> Visitor<'de> for OutcomeVisitor {
76    type Value = Outcome;
77
78    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
79        formatter.write_str("a string unit outcome or one tagged outcome object")
80    }
81
82    fn visit_str<E: de::Error>(self, value: &str) -> Result<Outcome, E> {
83        match value {
84            "absent" => Ok(Outcome::Absent),
85            "unavailable" => Ok(Outcome::Unavailable),
86            "completed" => Ok(Outcome::Completed),
87            "rejected-before-effect" => Ok(Outcome::RejectedBeforeEffect),
88            "unknown" => Ok(Outcome::Unknown),
89            _ => Err(E::custom("invalid string outcome")),
90        }
91    }
92
93    fn visit_map<M: MapAccess<'de>>(self, mut map: M) -> Result<Outcome, M::Error> {
94        let outcome = match tag(&mut map)?.as_str() {
95            "present" => Outcome::Present {
96                object: map.next_value::<Present>()?.object,
97            },
98            "capabilities" => Outcome::Capabilities {
99                supported: map.next_value::<Capabilities>()?.supported,
100            },
101            _ => return Err(de::Error::custom("invalid object outcome")),
102        };
103        end(&mut map)?;
104        Ok(outcome)
105    }
106}
107
108impl<'de> Deserialize<'de> for Outcome {
109    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
110        deserializer.deserialize_any(OutcomeVisitor)
111    }
112}
113
114impl TryFrom<String> for Protocol {
115    type Error = &'static str;
116    fn try_from(value: String) -> Result<Self, Self::Error> {
117        match value.as_str() {
118            "gate-runtime/v1" => Ok(Self::V1),
119            _ => Err("unsupported protocol"),
120        }
121    }
122}
123
124impl TryFrom<String> for Engine {
125    type Error = &'static str;
126    fn try_from(value: String) -> Result<Self, Self::Error> {
127        match value.as_str() {
128            "docker" => Ok(Self::Docker),
129            "podman" => Ok(Self::Podman),
130            _ => Err("unsupported engine"),
131        }
132    }
133}
134
135impl TryFrom<String> for Capability {
136    type Error = &'static str;
137    fn try_from(value: String) -> Result<Self, Self::Error> {
138        match value.as_str() {
139            "inspect" => Ok(Self::Inspect),
140            "remove" => Ok(Self::Remove),
141            _ => Err("unsupported capability"),
142        }
143    }
144}