Skip to main content

structured_cms/
counter.rs

1//! Lazy direct countersignatures without a recursive traversal or new parser.
2
3use crate::signer::SignerInfo;
4use crate::{Error, ErrorCode, Result};
5use structured_asn1::Reader;
6use structured_asn1::memory::Work;
7
8mod walk;
9pub use walk::{Visit, WalkError, walk};
10
11const FIELD: &str = "signerInfo.unsignedAttrs.countersignature";
12
13/// Direct unsigned countersignature values in original encoded order.
14///
15/// RFC5652 ยง11.4 permits repeated instances, multiple nonempty values and
16/// nesting. This iterator decodes one direct value at a time. Malformed values
17/// do not erase later siblings; a resource refusal is yielded once then stops.
18/// <https://www.rfc-editor.org/rfc/rfc5652#section-11.4>
19pub struct Countersignatures<'s, 'a> {
20    signer: &'s SignerInfo<'a>,
21    parent: &'s Reader<'a>,
22    cursor: Cursor,
23}
24
25// Numeric state can move with its owner without references into that owner.
26#[derive(Default)]
27struct Cursor {
28    attribute: usize,
29    active: Option<usize>,
30    value: usize,
31    stopped: bool,
32}
33
34impl<'s, 'a> Countersignatures<'s, 'a> {
35    pub(crate) fn new(signer: &'s SignerInfo<'a>, parent: &'s Reader<'a>) -> Self {
36        Self {
37            signer,
38            parent,
39            cursor: Cursor::default(),
40        }
41    }
42}
43
44impl Cursor {
45    fn next<'a>(
46        &mut self,
47        signer: &SignerInfo<'a>,
48        parent: &Reader<'a>,
49    ) -> Option<Result<(SignerInfo<'a>, Reader<'a>)>> {
50        if self.stopped {
51            return None;
52        }
53        if let Err(error) = signer.require_session(parent, FIELD) {
54            self.stopped = true;
55            return Some(Err(error));
56        }
57        let Some(attributes) = signer.unsigned_attributes() else {
58            self.stopped = true;
59            return None;
60        };
61        loop {
62            let at = self.active.map_or(signer.span().start, |index| {
63                attributes.items()[index].span().start
64            });
65            if let Err(reason) = parent.execution().charge_work(1) {
66                self.stopped = true;
67                return Some(Err(Error::resource(reason, FIELD, at)));
68            }
69            if let Some(index) = self.active {
70                let attribute = &attributes.items()[index];
71                if self.value < attribute.values().len() {
72                    let index = self.value;
73                    self.value += 1;
74                    let value = (|| {
75                        let Some(mut reader) = attribute.value_reader_in(index, parent, FIELD)?
76                        else {
77                            return Err(Error::schema(ErrorCode::MissingField, FIELD, at));
78                        };
79                        let element = crate::signer::required(&mut reader, FIELD)?;
80                        let signer = SignerInfo::read(&reader, &element)?;
81                        Ok((signer, reader))
82                    })();
83                    if value.as_ref().is_err_and(Error::is_terminal) {
84                        self.stopped = true;
85                    }
86                    return Some(value);
87                }
88                self.active = None;
89            }
90            let attribute = match attributes.next_matching(
91                crate::oid::COUNTERSIGNATURE,
92                &parent.execution(),
93                &mut self.attribute,
94                &mut self.stopped,
95            ) {
96                Some(Ok(attribute)) => attribute,
97                Some(Err(error)) => return Some(Err(error)),
98                None => return None,
99            };
100            if attribute.values().is_empty() {
101                return Some(Err(Error::schema(
102                    ErrorCode::AttributeCardinality,
103                    FIELD,
104                    attribute.span().start,
105                )));
106            }
107            self.active = Some(self.attribute - 1);
108            self.value = 0;
109        }
110    }
111}
112
113impl<'a> Iterator for Countersignatures<'_, 'a> {
114    type Item = Result<SignerInfo<'a>>;
115    fn next(&mut self) -> Option<Self::Item> {
116        self.cursor
117            .next(self.signer, self.parent)
118            .map(|reading| reading.map(|(signer, _reader)| signer))
119    }
120}
121impl core::iter::FusedIterator for Countersignatures<'_, '_> {}