10 pub enum LicenseError {
16 impl fmt::Display for LicenseError {
17 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
19 LicenseError::IO(ref err) => err.fmt(f),
20 LicenseError::Regex(ref err) => err.fmt(f),
21 LicenseError::Parse(ref err) => write!(f, "parsing failed, {}", err),
26 impl From<io::Error> for LicenseError {
27 fn from(err: io::Error) -> LicenseError {
32 impl From<regex::Error> for LicenseError {
33 fn from(err: regex::Error) -> LicenseError {
34 LicenseError::Regex(err)
38 // the template is parsed using a state machine
42 // the u32 keeps track of brace nesting
48 use self::ParsingState::*;
50 pub struct TemplateParser {
61 parsed: "^".to_owned(),
62 buffer: String::new(),
65 // keeps track of last line on which a regex placeholder was started
70 /// Convert a license template into a string which can be turned into a regex.
72 /// The license template could use regex syntax directly, but that would require a lot of manual
73 /// escaping, which is inconvenient. It is therefore literal by default, with optional regex
74 /// subparts delimited by `{` and `}`. Additionally:
76 /// - to insert literal `{`, `}` or `\`, escape it with `\`
77 /// - an empty regex placeholder (`{}`) is shorthand for `{.*?}`
79 /// This function parses this input format and builds a properly escaped *string* representation
80 /// of the equivalent regular expression. It **does not** however guarantee that the returned
81 /// string is a syntactically valid regular expression.
87 /// TemplateParser::parse(
89 /// // Copyright {\d+} The \} Rust \\ Project \{ Developers. See the {([A-Z]+)}
90 /// // file at the top-level directory of this distribution and at
93 /// // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
94 /// // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
95 /// // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
96 /// // option. This file may not be copied, modified, or distributed
97 /// // except according to those terms.
101 /// // Copyright \d+ The \} Rust \\ Project \{ Developers\. See the ([A-Z]+)
102 /// // file at the top\-level directory of this distribution and at
105 /// // Licensed under the Apache License, Version 2\.0 <LICENSE\-APACHE or
106 /// // http://www\.apache\.org/licenses/LICENSE\-2\.0> or the MIT license
107 /// // <LICENSE\-MIT or http://opensource\.org/licenses/MIT>, at your
108 /// // option\. This file may not be copied, modified, or distributed
109 /// // except according to those terms\.
113 pub fn parse(template: &str) -> Result<String, LicenseError> {
114 let mut parser = Self::new();
115 for chr in template.chars() {
119 parser.state = match parser.state {
120 Lit => parser.trans_from_lit(chr),
121 LitEsc => parser.trans_from_litesc(chr),
122 Re(brace_nesting) => parser.trans_from_re(chr, brace_nesting),
123 ReEsc(brace_nesting) => parser.trans_from_reesc(chr, brace_nesting),
124 Abort(msg) => return Err(LicenseError::Parse(msg)),
127 // check if we've ended parsing in a valid state
129 Abort(msg) => return Err(LicenseError::Parse(msg)),
130 Re(_) | ReEsc(_) => {
131 return Err(LicenseError::Parse(format!(
132 "escape or balance opening brace on l. {}",
133 parser.open_brace_line
137 return Err(LicenseError::Parse(format!(
138 "incomplete escape sequence on l. {}",
144 parser.parsed.push_str(®ex::escape(&parser.buffer));
149 fn trans_from_lit(&mut self, chr: char) -> ParsingState {
152 self.parsed.push_str(®ex::escape(&self.buffer));
154 self.open_brace_line = self.linum;
157 '}' => Abort(format!(
158 "escape or balance closing brace on l. {}",
163 self.buffer.push(chr);
169 fn trans_from_litesc(&mut self, chr: char) -> ParsingState {
170 self.buffer.push(chr);
174 fn trans_from_re(&mut self, chr: char, brace_nesting: u32) -> ParsingState {
177 self.buffer.push(chr);
178 Re(brace_nesting + 1)
181 match brace_nesting {
183 // default regex for empty placeholder {}
184 if self.buffer.is_empty() {
185 self.parsed.push_str(".*?");
187 self.parsed.push_str(&self.buffer);
193 self.buffer.push(chr);
194 Re(brace_nesting - 1)
199 self.buffer.push(chr);
203 self.buffer.push(chr);
209 fn trans_from_reesc(&mut self, chr: char, brace_nesting: u32) -> ParsingState {
210 self.buffer.push(chr);
215 pub fn load_and_compile_template(path: &str) -> Result<Regex, LicenseError> {
216 let mut lt_file = File::open(&path)?;
217 let mut lt_str = String::new();
218 lt_file.read_to_string(&mut lt_str)?;
219 let lt_parsed = TemplateParser::parse(<_str)?;
220 Ok(Regex::new(<_parsed)?)
225 use super::TemplateParser;
228 fn test_parse_license_template() {
230 TemplateParser::parse("literal (.*)").unwrap(),
234 TemplateParser::parse(r"escaping \}").unwrap(),
237 assert!(TemplateParser::parse("unbalanced } without escape").is_err());
239 TemplateParser::parse(r"{\d+} place{-?}holder{s?}").unwrap(),
240 r"^\d+ place-?holders?"
242 assert_eq!(TemplateParser::parse("default {}").unwrap(), "^default .*?");
244 TemplateParser::parse(r"unbalanced nested braces {\{{3}}").unwrap(),
245 r"^unbalanced nested braces \{{3}"
248 &TemplateParser::parse("parsing error }")
251 "parsing failed, escape or balance closing brace on l. 1"
254 &TemplateParser::parse("parsing error {\nsecond line")
257 "parsing failed, escape or balance opening brace on l. 1"
260 &TemplateParser::parse(r"parsing error \")
263 "parsing failed, incomplete escape sequence on l. 1"