• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

projectfluent / fluent-rs / 15149120108

20 May 2025 10:27PM UTC coverage: 89.142% (-0.5%) from 89.654%
15149120108

Pull #382

github

web-flow
Merge 6765dcd8c into 10302d0ad
Pull Request #382: Bump deps and prep for release

5 of 5 new or added lines in 2 files covered. (100.0%)

2 existing lines in 2 files now uncovered.

3604 of 4043 relevant lines covered (89.14%)

3834.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.71
/fluent-syntax/src/parser/pattern.rs
1
use super::errors::{ErrorKind, ParserError};
2
use super::{core::Parser, core::Result, slice::Slice};
3
use crate::ast;
4

5
#[derive(Debug, PartialEq)]
6
enum TextElementTermination {
7
    LineFeed,
8
    Crlf,
9
    PlaceableStart,
10
    Eof,
11
}
12

13
// This enum tracks the placement of the text element in the pattern, which is needed for
14
// dedentation logic.
15
#[derive(Debug, PartialEq)]
16
enum TextElementPosition {
17
    InitialLineStart,
18
    LineStart,
19
    Continuation,
20
}
21

22
// This enum allows us to mark pointers in the source which will later become text elements
23
// but without slicing them out of the source string. This makes the indentation adjustments
24
// cheaper since they'll happen on the pointers, rather than extracted slices.
25
#[derive(Debug)]
26
enum PatternElementPlaceholders<S> {
27
    Placeable(ast::Expression<S>),
28
    // (start, end, indent, position)
29
    TextElement(usize, usize, usize, TextElementPosition),
30
}
31

32
// This enum tracks whether the text element is blank or not.
33
// This is important to identify text elements which should not be taken into account
34
// when calculating common indent.
35
#[derive(Debug, PartialEq)]
36
enum TextElementType {
37
    Blank,
38
    NonBlank,
39
}
40

41
impl<'s, S> Parser<S>
42
where
43
    S: Slice<'s>,
44
{
45
    pub(super) fn get_pattern(&mut self) -> Result<Option<ast::Pattern<S>>> {
31,695✔
46
        let mut elements = vec![];
31,695✔
47
        let mut last_non_blank = None;
31,695✔
48
        let mut common_indent = None;
31,695✔
49

50
        self.skip_blank_inline();
31,695✔
51

52
        let mut text_element_role = if self.skip_eol() {
31,695✔
53
            self.skip_blank_block();
3,447✔
54
            TextElementPosition::LineStart
3,447✔
55
        } else {
56
            TextElementPosition::InitialLineStart
28,248✔
57
        };
58

59
        while self.ptr < self.length {
81,684✔
60
            if self.take_byte_if(b'{') {
78,923✔
61
                if text_element_role == TextElementPosition::LineStart {
17,463✔
62
                    common_indent = Some(0);
30✔
63
                }
17,433✔
64
                let exp = self.get_placeable()?;
17,463✔
65
                last_non_blank = Some(elements.len());
16,935✔
66
                elements.push(PatternElementPlaceholders::Placeable(exp));
16,935✔
67
                text_element_role = TextElementPosition::Continuation;
16,935✔
68
            } else {
69
                let slice_start = self.ptr;
61,460✔
70
                let mut indent = 0;
61,460✔
71
                if text_element_role == TextElementPosition::LineStart {
61,460✔
72
                    indent = self.skip_blank_inline();
31,824✔
73
                    if let Some(b) = get_current_byte!(self) {
31,824✔
74
                        if indent == 0 {
31,759✔
75
                            if b != &b'\r' && b != &b'\n' {
23,115✔
76
                                break;
20,122✔
77
                            }
2,993✔
78
                        } else if !Self::is_byte_pattern_continuation(*b) {
8,644✔
79
                            self.ptr = slice_start;
8,207✔
80
                            break;
8,207✔
81
                        }
437✔
82
                    } else {
83
                        break;
65✔
84
                    }
85
                }
29,636✔
86
                let (start, end, text_element_type, termination_reason) = self.get_text_slice()?;
33,066✔
87
                if start != end {
33,054✔
88
                    if text_element_role == TextElementPosition::LineStart
32,779✔
89
                        && text_element_type == TextElementType::NonBlank
3,155✔
90
                    {
91
                        if let Some(common) = common_indent {
141✔
92
                            if indent < common {
62✔
93
                                common_indent = Some(indent);
10✔
94
                            }
52✔
95
                        } else {
79✔
96
                            common_indent = Some(indent);
79✔
97
                        }
79✔
98
                    }
32,638✔
99
                    if text_element_role != TextElementPosition::LineStart
32,779✔
100
                        || text_element_type == TextElementType::NonBlank
3,155✔
101
                        || termination_reason == TextElementTermination::LineFeed
3,014✔
102
                    {
103
                        if text_element_type == TextElementType::NonBlank {
32,779✔
104
                            last_non_blank = Some(elements.len());
16,579✔
105
                        }
18,307✔
106
                        elements.push(PatternElementPlaceholders::TextElement(
32,779✔
107
                            slice_start,
32,779✔
108
                            end,
32,779✔
109
                            indent,
32,779✔
110
                            text_element_role,
32,779✔
111
                        ));
32,779✔
112
                    }
×
113
                }
275✔
114

115
                text_element_role = match termination_reason {
33,054✔
116
                    TextElementTermination::LineFeed => TextElementPosition::LineStart,
29,004✔
117
                    TextElementTermination::Crlf => TextElementPosition::LineStart,
12✔
118
                    TextElementTermination::PlaceableStart => TextElementPosition::Continuation,
3,736✔
119
                    TextElementTermination::Eof => TextElementPosition::Continuation,
302✔
120
                };
121
            }
122
        }
123

124
        if let Some(last_non_blank) = last_non_blank {
31,155✔
125
            let elements = elements
28,046✔
126
                .into_iter()
28,046✔
127
                .take(last_non_blank + 1)
28,046✔
128
                .enumerate()
28,046✔
129
                .map(|(i, elem)| match elem {
34,706✔
130
                    PatternElementPlaceholders::Placeable(expression) => {
16,931✔
131
                        ast::PatternElement::Placeable { expression }
16,931✔
132
                    }
133
                    PatternElementPlaceholders::TextElement(start, end, indent, role) => {
17,775✔
134
                        let start = if role == TextElementPosition::LineStart {
17,775✔
135
                            common_indent.map_or_else(
191✔
UNCOV
136
                                || start + indent,
×
137
                                |common_indent| start + std::cmp::min(indent, common_indent),
191✔
138
                            )
139
                        } else {
140
                            start
17,584✔
141
                        };
142
                        let mut value = self.source.slice(start..end);
17,775✔
143
                        if last_non_blank == i {
17,775✔
144
                            value.trim();
14,160✔
145
                        }
14,160✔
146
                        ast::PatternElement::TextElement { value }
17,775✔
147
                    }
148
                })
34,706✔
149
                .collect();
28,046✔
150
            return Ok(Some(ast::Pattern { elements }));
28,046✔
151
        }
3,109✔
152

153
        Ok(None)
3,109✔
154
    }
31,695✔
155

156
    fn get_text_slice(
33,066✔
157
        &mut self,
33,066✔
158
    ) -> Result<(usize, usize, TextElementType, TextElementTermination)> {
33,066✔
159
        let start_pos = self.ptr;
33,066✔
160
        let Some(rest) = get_remaining_bytes!(self) else {
33,066✔
161
            return Ok((
×
162
                start_pos,
×
163
                self.ptr,
×
164
                TextElementType::Blank,
×
165
                TextElementTermination::Eof,
×
166
            ));
×
167
        };
168
        let end = memchr::memchr3(b'\n', b'{', b'}', rest);
33,066✔
169
        let element_type = |text: &[u8]| {
33,066✔
170
            if text.iter().any(|&c| c != b' ') {
33,054✔
171
                TextElementType::NonBlank
16,579✔
172
            } else {
173
                TextElementType::Blank
16,475✔
174
            }
175
        };
33,054✔
176
        match end.map(|p| &rest[..=p]) {
33,066✔
177
            Some([text @ .., b'}']) => {
12✔
178
                self.ptr += text.len();
12✔
179
                error!(ErrorKind::UnbalancedClosingBrace, self.ptr)
12✔
180
            }
181
            Some([text @ .., b'\r', b'\n']) => {
12✔
182
                self.ptr += text.len() + 1;
12✔
183
                Ok((
12✔
184
                    start_pos,
12✔
185
                    self.ptr - 1,
12✔
186
                    element_type(text),
12✔
187
                    TextElementTermination::Crlf,
12✔
188
                ))
12✔
189
            }
190
            Some([text @ .., b'\n']) => {
29,004✔
191
                self.ptr += text.len() + 1;
29,004✔
192
                Ok((
29,004✔
193
                    start_pos,
29,004✔
194
                    self.ptr,
29,004✔
195
                    element_type(text),
29,004✔
196
                    TextElementTermination::LineFeed,
29,004✔
197
                ))
29,004✔
198
            }
199
            Some([text @ .., b'{']) => {
3,736✔
200
                self.ptr += text.len();
3,736✔
201
                Ok((
3,736✔
202
                    start_pos,
3,736✔
203
                    self.ptr,
3,736✔
204
                    element_type(text),
3,736✔
205
                    TextElementTermination::PlaceableStart,
3,736✔
206
                ))
3,736✔
207
            }
208
            None => {
209
                self.ptr += rest.len();
302✔
210
                Ok((
302✔
211
                    start_pos,
302✔
212
                    self.ptr,
302✔
213
                    element_type(rest),
302✔
214
                    TextElementTermination::Eof,
302✔
215
                ))
302✔
216
            }
217
            _ => unreachable!(),
×
218
        }
219
    }
33,066✔
220
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc