etcd_txn_parser/
lib.rs

1use crate::compare::Compare;
2use crate::operation::Operation;
3use noa_parser::bytes::matchers::match_pattern;
4use noa_parser::bytes::primitives::whitespace::OptionalWhitespaces;
5use noa_parser::bytes::token::Token;
6use noa_parser::errors::{ParseError, ParseResult};
7use noa_parser::matcher::{Match, MatchSize};
8use noa_parser::peek::{peek, Until, UntilEnd};
9use noa_parser::recognizer::recognize;
10use noa_parser::scanner::Scanner;
11use noa_parser::separated_list::SeparatedList;
12use noa_parser::visitor::Visitor;
13
14pub mod compare;
15pub mod operation;
16
17/// A transactional data structure.
18#[derive(Debug, PartialEq)]
19pub struct TxnData<'a> {
20    /// A list of operations to compare against the current state.
21    pub compares: Vec<Compare<'a>>,
22    /// A list of operations to apply if the compare operations pass.
23    pub success: Vec<Operation<'a>>,
24    /// A list of operations to apply if the compare operations fail.
25    pub failure: Vec<Operation<'a>>,
26}
27
28struct LineFeed;
29
30impl<'a> Visitor<'a, u8> for LineFeed {
31    fn accept(scanner: &mut Scanner<'a, u8>) -> ParseResult<Self> {
32        recognize(Token::Ln, scanner)?;
33        Ok(LineFeed)
34    }
35}
36
37#[derive(Clone)]
38struct SectionEnd;
39
40impl Match<u8> for SectionEnd {
41    fn matcher(&self, data: &[u8]) -> (bool, usize) {
42        match_pattern(b"\n\n", data)
43    }
44}
45
46impl MatchSize for SectionEnd {
47    fn size(&self) -> usize {
48        2
49    }
50}
51
52impl<'a> Visitor<'a, u8> for TxnData<'a> {
53    fn accept(scanner: &mut Scanner<'a, u8>) -> ParseResult<Self> {
54        OptionalWhitespaces::accept(scanner)?;
55
56        // Read the compare section
57        let section_compare =
58            peek(Until::new(SectionEnd), scanner)?.ok_or(ParseError::UnexpectedToken)?;
59        let mut compares = vec![];
60        if !section_compare.data.is_empty() {
61            let mut section_compare_scanner = Scanner::new(section_compare.data);
62            compares =
63                SeparatedList::<u8, Compare, LineFeed>::accept(&mut section_compare_scanner)?
64                    .into_iter()
65                    .collect();
66            scanner.bump_by(section_compare.end_slice);
67        }
68        LineFeed::accept(scanner)?;
69        LineFeed::accept(scanner)?;
70
71        // Read the success section
72        let section_success =
73            peek(Until::new(SectionEnd), scanner)?.ok_or(ParseError::UnexpectedToken)?;
74        let mut success = vec![];
75        if !section_success.data.is_empty() {
76            let mut section_success_scanner = Scanner::new(section_success.data);
77            success =
78                SeparatedList::<u8, Operation, LineFeed>::accept(&mut section_success_scanner)?
79                    .into_iter()
80                    .collect();
81            scanner.bump_by(section_success.end_slice);
82        }
83        LineFeed::accept(scanner)?;
84        LineFeed::accept(scanner)?;
85
86        // Read the failure section
87        let section_failure =
88            peek(UntilEnd::default(), scanner)?.ok_or(ParseError::UnexpectedToken)?;
89        let mut failure = vec![];
90        if !section_failure.data.is_empty() {
91            let mut section_failure_scanner = Scanner::new(section_failure.data);
92            failure =
93                SeparatedList::<u8, Operation, LineFeed>::accept(&mut section_failure_scanner)?
94                    .into_iter()
95                    .collect();
96        }
97
98        Ok(TxnData {
99            compares,
100            success,
101            failure,
102        })
103    }
104}