quoted_string/
test_utils.rs

1//! provides an example implementation of quoted string spec's
2use std::default::Default;
3
4use spec::{
5    GeneralQSSpec,
6    QuotingClassifier, QuotingClass,
7    ParsingImpl,
8    State,
9    PartialCodePoint,
10    WithoutQuotingValidator
11};
12use error::CoreError;
13
14#[derive(Copy, Clone, Debug)]
15pub struct TestSpec;
16
17impl GeneralQSSpec for TestSpec {
18    type Quoting = Self;
19    type Parsing = TestParsingImpl;
20}
21
22impl QuotingClassifier for TestSpec {
23    fn classify_for_quoting(pcp: PartialCodePoint) -> QuotingClass {
24        if !is_valid_pcp(pcp) {
25            QuotingClass::Invalid
26        } else {
27            match pcp.as_u8() {
28                b'"' | b'\\' => QuotingClass::NeedsQuoting,
29                _ => QuotingClass::QText
30            }
31        }
32    }
33}
34
35fn is_valid_pcp(pcp: PartialCodePoint) -> bool {
36    let bch = pcp.as_u8();
37    b' ' <= bch && bch <= b'~'
38}
39
40/// a parsing implementations which allows non semantic stange thinks in it for testing purpose
41///
42/// basically you can have a non-semantic section starting with `←` ending with `→` which has
43/// a number of `+` followed by the same number of `-`.
44///
45/// E.g. `"some think \n+++---\n"`
46///
47/// This naturally makes no sense, but is a simple way to test if the custom state is used
48/// correctly, there are some quoted string impl which need custom state as they can e.g.
49/// have non semantic soft line brakes (which are slight more complex to implement and less
50/// visible in error messages, so I used this think here)
51#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
52pub enum TestParsingImpl {
53    StrangeInc(usize),
54    StrangeDec(usize)
55}
56
57impl ParsingImpl for TestParsingImpl {
58    fn can_be_quoted(bch: PartialCodePoint) -> bool {
59        is_valid_pcp(bch)
60    }
61    fn handle_normal_state(bch: PartialCodePoint) -> Result<(State<Self>, bool), CoreError> {
62        if bch.as_u8() == b'\n' {
63            Ok((State::Custom(TestParsingImpl::StrangeInc(0)), false))
64        } else if is_valid_pcp(bch) {
65            Ok((State::Normal, true))
66        } else {
67            Err(CoreError::InvalidChar)
68        }
69    }
70
71    fn advance(&self, pcp: PartialCodePoint) -> Result<(State<Self>, bool), CoreError> {
72        use self::TestParsingImpl::*;
73        let bch = pcp.as_u8();
74        match *self {
75            StrangeInc(v) => {
76                if bch == b'\n' {
77                    Ok((State::Normal, false))
78                } else if bch == b'+' {
79                    Ok((State::Custom(StrangeInc(v+1)), false))
80                } else if bch == b'-' && v > 0{
81                    Ok((State::Custom(StrangeDec(v-1)), false))
82                } else {
83                    Err(CoreError::InvalidChar)
84                }
85            },
86            StrangeDec(v) => {
87                if bch == b'-' && v > 0 {
88                    Ok((State::Custom(StrangeDec(v-1)), false))
89                } else if bch == b'\n' && v == 0 {
90                    Ok((State::Normal, false))
91                } else {
92                    Err(CoreError::InvalidChar)
93                }
94            }
95        }
96    }
97}
98
99
100pub struct TestUnquotedValidator {
101    pub count: usize,
102    pub last_was_dot: bool
103}
104impl Default for TestUnquotedValidator {
105    fn default() -> Self {
106        TestUnquotedValidator {
107            count: 0,
108            last_was_dot: true
109        }
110    }
111}
112impl TestUnquotedValidator {
113    pub fn new() -> Self {
114        Default::default()
115    }
116}
117
118impl WithoutQuotingValidator for TestUnquotedValidator {
119    fn next(&mut self, pcp: PartialCodePoint) -> bool {
120        let bch = pcp.as_u8();
121        let lwd = self.last_was_dot;
122        let res = match bch {
123            b'a'...b'z' => {
124                self.last_was_dot = false;
125                true
126            }
127            b'.' if !lwd => {
128                self.last_was_dot = true;
129                true
130            }
131            _ => false
132        };
133        if res {
134            self.count += 1;
135        }
136        res
137    }
138    fn end(&self) -> bool {
139        self.count == 6 && !self.last_was_dot
140    }
141}