Skip to main content

vitaminc_prf/
visitor.rs

1use crate::PrfVisitorError;
2
3/// A fully resolved node supplied to sequence and map visitors.
4///
5/// Visitors may consume each child with a different visitor, allowing a
6/// handwritten record to produce heterogeneous owned output from one batch.
7pub enum ResolvedPrf<Block, Passthrough> {
8    Block(Block),
9    Sequence(Vec<ResolvedPrf<Block, Passthrough>>),
10    Map(Vec<(String, ResolvedPrf<Block, Passthrough>)>),
11    Absent,
12    Passthrough(Passthrough),
13}
14
15impl<Block, Passthrough> ResolvedPrf<Block, Passthrough> {
16    pub fn visit<V>(self, visitor: V) -> Result<V::Value, PrfVisitorError>
17    where
18        V: PrfVisitor<Block, Passthrough>,
19    {
20        match self {
21            Self::Block(block) => visitor.visit_block(block),
22            Self::Sequence(values) => visitor.visit_seq(SeqAccess::new(values)),
23            Self::Map(entries) => visitor.visit_map(MapAccess::new(entries)),
24            Self::Absent => visitor.visit_absent(),
25            Self::Passthrough(value) => visitor.visit_passthrough(value),
26        }
27    }
28}
29
30/// Pull-style access to resolved sequence children.
31pub struct SeqAccess<Block, Passthrough> {
32    values: std::vec::IntoIter<ResolvedPrf<Block, Passthrough>>,
33}
34
35impl<Block, Passthrough> SeqAccess<Block, Passthrough> {
36    pub fn new(values: Vec<ResolvedPrf<Block, Passthrough>>) -> Self {
37        Self {
38            values: values.into_iter(),
39        }
40    }
41
42    pub fn next_node(&mut self) -> Option<ResolvedPrf<Block, Passthrough>> {
43        self.values.next()
44    }
45
46    pub fn len(&self) -> usize {
47        self.values.len()
48    }
49
50    pub fn is_empty(&self) -> bool {
51        self.values.len() == 0
52    }
53}
54
55impl<Block, Passthrough> Iterator for SeqAccess<Block, Passthrough> {
56    type Item = ResolvedPrf<Block, Passthrough>;
57
58    fn next(&mut self) -> Option<Self::Item> {
59        self.next_node()
60    }
61
62    fn size_hint(&self) -> (usize, Option<usize>) {
63        self.values.size_hint()
64    }
65}
66
67impl<Block, Passthrough> ExactSizeIterator for SeqAccess<Block, Passthrough> {}
68
69/// Pull-style access to resolved string-keyed map children.
70pub struct MapAccess<Block, Passthrough> {
71    entries: std::vec::IntoIter<(String, ResolvedPrf<Block, Passthrough>)>,
72}
73
74impl<Block, Passthrough> MapAccess<Block, Passthrough> {
75    pub fn new(entries: Vec<(String, ResolvedPrf<Block, Passthrough>)>) -> Self {
76        Self {
77            entries: entries.into_iter(),
78        }
79    }
80
81    pub fn next_entry(&mut self) -> Option<(String, ResolvedPrf<Block, Passthrough>)> {
82        self.entries.next()
83    }
84
85    pub fn len(&self) -> usize {
86        self.entries.len()
87    }
88
89    pub fn is_empty(&self) -> bool {
90        self.entries.len() == 0
91    }
92}
93
94impl<Block, Passthrough> Iterator for MapAccess<Block, Passthrough> {
95    type Item = (String, ResolvedPrf<Block, Passthrough>);
96
97    fn next(&mut self) -> Option<Self::Item> {
98        self.next_entry()
99    }
100
101    fn size_hint(&self) -> (usize, Option<usize>) {
102        self.entries.size_hint()
103    }
104}
105
106impl<Block, Passthrough> ExactSizeIterator for MapAccess<Block, Passthrough> {}
107
108#[cfg(test)]
109mod access_tests {
110    use super::{MapAccess, ResolvedPrf, SeqAccess};
111
112    #[test]
113    fn sequence_access_reports_its_exact_remaining_length() {
114        let mut seq = SeqAccess::<u8, ()>::new(vec![ResolvedPrf::Block(1), ResolvedPrf::Block(2)]);
115
116        assert_eq!(seq.len(), 2);
117        assert_eq!(seq.size_hint(), (2, Some(2)));
118        assert!(!seq.is_empty());
119        assert!(seq.next().is_some());
120        assert_eq!(seq.len(), 1);
121        assert!(seq.next().is_some());
122        assert_eq!(seq.size_hint(), (0, Some(0)));
123        assert!(seq.is_empty());
124    }
125
126    #[test]
127    fn map_access_reports_its_exact_remaining_length() {
128        let mut map = MapAccess::<u8, ()>::new(vec![
129            (String::from("one"), ResolvedPrf::Block(1)),
130            (String::from("two"), ResolvedPrf::Block(2)),
131        ]);
132
133        assert_eq!(map.len(), 2);
134        assert_eq!(map.size_hint(), (2, Some(2)));
135        assert!(!map.is_empty());
136        assert!(map.next().is_some());
137        assert_eq!(map.len(), 1);
138        assert!(map.next().is_some());
139        assert_eq!(map.size_hint(), (0, Some(0)));
140        assert!(map.is_empty());
141    }
142}
143
144/// Interprets a resolved PRF result.
145pub trait PrfVisitor<Block, Passthrough>: Sized + 'static {
146    type Value: Send + 'static;
147
148    fn visit_block(self, _block: Block) -> Result<Self::Value, PrfVisitorError> {
149        Err(PrfVisitorError::UnexpectedShape)
150    }
151
152    fn visit_seq(
153        self,
154        _seq: SeqAccess<Block, Passthrough>,
155    ) -> Result<Self::Value, PrfVisitorError> {
156        Err(PrfVisitorError::UnexpectedShape)
157    }
158
159    fn visit_map(
160        self,
161        _map: MapAccess<Block, Passthrough>,
162    ) -> Result<Self::Value, PrfVisitorError> {
163        Err(PrfVisitorError::UnexpectedShape)
164    }
165
166    fn visit_absent(self) -> Result<Self::Value, PrfVisitorError> {
167        Err(PrfVisitorError::UnexpectedShape)
168    }
169
170    /// Passthrough is an explicit non-secret channel. It receives no PRF
171    /// protection and must not carry keys, plaintexts, or credentials.
172    fn visit_passthrough(self, _value: Passthrough) -> Result<Self::Value, PrfVisitorError> {
173        Err(PrfVisitorError::UnexpectedShape)
174    }
175}
176
177/// Visitor that returns one raw backend block.
178#[derive(Debug, Clone, Copy, Default)]
179pub struct BlockVisitor;
180
181impl<Block, Passthrough> PrfVisitor<Block, Passthrough> for BlockVisitor
182where
183    Block: Send + 'static,
184{
185    type Value = Block;
186
187    fn visit_block(self, block: Block) -> Result<Self::Value, PrfVisitorError> {
188        Ok(block)
189    }
190}
191
192/// Identity visitor that preserves a resolved node's structural shape.
193///
194/// Backend sequence and map drivers use this visitor to collect child
195/// programs as [`ResolvedPrf`] nodes before the caller's final visitor is
196/// applied.
197impl<Block, Passthrough> PrfVisitor<Block, Passthrough> for ResolvedVisitor
198where
199    Block: Send + 'static,
200    Passthrough: Send + 'static,
201{
202    type Value = ResolvedPrf<Block, Passthrough>;
203
204    fn visit_block(self, block: Block) -> Result<Self::Value, PrfVisitorError> {
205        Ok(ResolvedPrf::Block(block))
206    }
207
208    fn visit_seq(self, seq: SeqAccess<Block, Passthrough>) -> Result<Self::Value, PrfVisitorError> {
209        Ok(ResolvedPrf::Sequence(seq.collect()))
210    }
211
212    fn visit_map(self, map: MapAccess<Block, Passthrough>) -> Result<Self::Value, PrfVisitorError> {
213        Ok(ResolvedPrf::Map(map.collect()))
214    }
215
216    fn visit_absent(self) -> Result<Self::Value, PrfVisitorError> {
217        Ok(ResolvedPrf::Absent)
218    }
219
220    fn visit_passthrough(self, value: Passthrough) -> Result<Self::Value, PrfVisitorError> {
221        Ok(ResolvedPrf::Passthrough(value))
222    }
223}
224
225#[derive(Clone, Copy)]
226pub struct ResolvedVisitor;