etcd_txn_parser/
lib.rs

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