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

corebreaker / poreader / 178

03 May 2024 05:07AM UTC coverage: 98.197% (-1.6%) from 99.764%
178

push

circleci

web-flow
Merge pull request #13 from corebreaker/circleci-project-setup

Fix coverage check in CI

708 of 721 relevant lines covered (98.2%)

50.92 hits per line

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

97.5
/src/po/parser.rs
1
use super::{line::PoLine, reader::PoReader, unescape::Unescaper};
2
use crate::error::Error;
3
use regex::Regex;
4
use std::{collections::HashMap, io::Read};
5

6
/// Object used for the creation of a PO reader
7
///
8
/// This structure is used for initializing a parser.
9
///
10
/// The reader is created by the method `[PoReader::parse](#method.parse)`.
11
pub struct PoParser {
12
    map_re: Regex,
13
    map_check_re: Regex,
14
    message_re: Regex,
15
    comment_re: Regex,
16
    unescaper: Unescaper,
17
}
18

19
impl PoParser {
20
    pub fn new() -> PoParser {
54✔
21
        PoParser {
54✔
22
            map_re: Regex::new(r"(\S+?)\s*=\s*(.*?)\s*;").unwrap(),
54✔
23
            map_check_re: Regex::new(r"^\s*(\S+?\s*=\s*.*?\s*;\s*)*$").unwrap(),
54✔
24
            message_re: Regex::new(
54✔
25
                r#"^\s*(?:#(~?\|?))?\s*(msgctxt|msgid|msgid_plural|msgstr(?:\[(?:0|[1-9]\d*)\])?)?\s*"(.*)"\s*$"#,
26
            )
27
            .unwrap(),
28
            comment_re: Regex::new(r#"^\s*#(.)?\s*(.*)$"#).unwrap(),
54✔
29
            unescaper: Unescaper::new(),
54✔
30
        }
×
31
    }
54✔
32

33
    pub fn parse<R: Read>(&self, reader: R) -> Result<PoReader<R>, Error> {
34
        PoReader::new(reader, self)
35
    }
36

37
    pub(crate) fn parse_map<'a>(&self, text: &'a str) -> Result<HashMap<&'a str, &'a str>, Error> {
35✔
38
        if self.map_check_re.is_match(text) {
35✔
39
            Ok(self
64✔
40
                .map_re
41
                .captures_iter(text)
32✔
42
                .filter_map(|c| c.get(1).and_then(|v1| c.get(2).map(|v2| (v1.as_str(), v2.as_str()))))
195✔
43
                .collect())
44
        } else {
45
            Err(Error::Unexpected(0, format!("Bad value list definition: `{}`", text)))
3✔
46
        }
47
    }
35✔
48

49
    pub(super) fn parse_line(&self, line: &str, n: usize) -> Result<PoLine, ()> {
136✔
50
        if !line.contains(|c: char| !c.is_whitespace()) {
479✔
51
            Ok(PoLine::Blank)
17✔
52
        } else if let Some(c) = self.message_re.captures(line) {
119✔
53
            let string = self
122✔
54
                .unescaper
55
                .unescape(c.get(3).map(|m| m.as_str()).unwrap_or_default());
122✔
56
            let flags = c.get(1).map(|x| x.as_str().to_string()).unwrap_or_default();
68✔
57

58
            Ok(match c.get(2) {
122✔
59
                None => PoLine::Continuation(n, flags, string),
14✔
60
                Some(m) => {
47✔
61
                    let tag = if flags.ends_with('|') {
48✔
62
                        String::from("|") + m.as_str()
1✔
63
                    } else {
64
                        m.as_str().to_string()
46✔
65
                    };
66

67
                    PoLine::Message(n, flags, tag, string)
47✔
68
                }
47✔
69
            })
70
        } else {
61✔
71
            self.comment_re.captures(line).map_or(Err(()), |c| {
97✔
72
                Ok(PoLine::Comment(
39✔
73
                    n,
39✔
74
                    c.get(1).and_then(|m| m.as_str().chars().next()).unwrap_or(' '),
117✔
75
                    c.get(2).map(|m| m.as_str().to_string()).unwrap_or_default(),
78✔
76
                ))
77
            })
39✔
78
        }
119✔
79
    }
136✔
80
}
81

82
// no-coverage:start
83
#[cfg(test)]
84
mod tests {
85
    use super::*;
86

87
    struct TestCase {
88
        source: &'static str,
89
        target: Result<PoLine, ()>,
90
    }
91

92
    impl TestCase {
93
        fn test(&self, parser: &PoParser) {
94
            assert_eq!(
95
                parser.parse_line(self.source, 123),
96
                self.target,
97
                "Error for source: `{}`",
98
                self.source
99
            );
100
        }
101
    }
102

103
    #[test]
104
    fn test_func_parse_map() {
105
        let parser = PoParser::new();
106

107
        match parser.parse_map("key=value") {
108
            Err(err) => assert_eq!(
109
                format!("{:?}", err),
110
                "Unexpected error: Bad value list definition: `key=value`"
111
            ),
112
            v => panic!("Unexpected result: {:?}", v),
113
        }
114

115
        assert_eq!(
116
            parser.parse_map("     xyz=abc \t ; a=b;c=  d;   e  \t = f;"),
117
            Ok(vec![("xyz", "abc"), ("a", "b"), ("c", "d"), ("e", "f"),]
118
                .into_iter()
119
                .collect::<HashMap<_, _>>())
120
        );
121
    }
122

123
    #[test]
124
    fn test_func_parse_line() {
125
        let parser = PoParser::new();
126
        let cases = vec![
127
            TestCase {
128
                source: "---",
129
                target: Err(()),
130
            },
131
            TestCase {
132
                source: "\"--",
133
                target: Err(()),
134
            },
135
            TestCase {
136
                source: "msgid \"--",
137
                target: Err(()),
138
            },
139
            TestCase {
140
                source: "msgxx \"--\"",
141
                target: Err(()),
142
            },
143
            TestCase {
144
                source: "-# Something",
145
                target: Err(()),
146
            },
147
            TestCase {
148
                source: "",
149
                target: Ok(PoLine::Blank),
150
            },
151
            TestCase {
152
                source: "      ",
153
                target: Ok(PoLine::Blank),
154
            },
155
            TestCase {
156
                source: "   \t\r\n   ",
157
                target: Ok(PoLine::Blank),
158
            },
159
            TestCase {
160
                source: r#"msgid "hello\n\tworld""#,
161
                target: Ok(PoLine::Message(
162
                    123,
163
                    String::new(),
164
                    String::from("msgid"),
165
                    String::from("hello\n\tworld"),
166
                )),
167
            },
168
            TestCase {
169
                source: r#"#| msgstr[3] "hello\n\tworld""#,
170
                target: Ok(PoLine::Message(
171
                    123,
172
                    String::from("|"),
173
                    String::from("|msgstr[3]"),
174
                    String::from("hello\n\tworld"),
175
                )),
176
            },
177
            TestCase {
178
                source: r#""hello\n\tworld""#,
179
                target: Ok(PoLine::Continuation(123, String::new(), String::from("hello\n\tworld"))),
180
            },
181
            TestCase {
182
                source: r#"#| "path: xx\\yy""#,
183
                target: Ok(PoLine::Continuation(
184
                    123,
185
                    String::from("|"),
186
                    String::from("path: xx\\yy"),
187
                )),
188
            },
189
            TestCase {
190
                source: "#, My comment",
191
                target: Ok(PoLine::Comment(123, ',', String::from("My comment"))),
192
            },
193
            TestCase {
194
                source: "# Another comment",
195
                target: Ok(PoLine::Comment(123, ' ', String::from("Another comment"))),
196
            },
197
            TestCase {
198
                source: "#$ Special comment",
199
                target: Ok(PoLine::Comment(123, '$', String::from("Special comment"))),
200
            },
201
        ];
202

203
        for case in cases {
204
            case.test(&parser);
205
        }
206
    }
207
}
208
// no-coverage:stop
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