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.
86 /// # use rustfmt_config::license::TemplateParser;
88 /// TemplateParser::parse(
90 /// // Copyright {\d+} The \} Rust \\ Project \{ Developers. See the {([A-Z]+)}
91 /// // file at the top-level directory of this distribution and at
94 /// // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
95 /// // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
96 /// // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
97 /// // option. This file may not be copied, modified, or distributed
98 /// // except according to those terms.
102 /// // Copyright \d+ The \} Rust \\ Project \{ Developers\. See the ([A-Z]+)
103 /// // file at the top\-level directory of this distribution and at
106 /// // Licensed under the Apache License, Version 2\.0 <LICENSE\-APACHE or
107 /// // http://www\.apache\.org/licenses/LICENSE\-2\.0> or the MIT license
108 /// // <LICENSE\-MIT or http://opensource\.org/licenses/MIT>, at your
109 /// // option\. This file may not be copied, modified, or distributed
110 /// // except according to those terms\.
114 pub fn parse(template: &str) -> Result<String, LicenseError> {
115 let mut parser = Self::new();
116 for chr in template.chars() {
120 parser.state = match parser.state {
121 Lit => parser.trans_from_lit(chr),
122 LitEsc => parser.trans_from_litesc(chr),
123 Re(brace_nesting) => parser.trans_from_re(chr, brace_nesting),
124 ReEsc(brace_nesting) => parser.trans_from_reesc(chr, brace_nesting),
125 Abort(msg) => return Err(LicenseError::Parse(msg)),
128 // check if we've ended parsing in a valid state
130 Abort(msg) => return Err(LicenseError::Parse(msg)),
131 Re(_) | ReEsc(_) => {
132 return Err(LicenseError::Parse(format!(
133 "escape or balance opening brace on l. {}",
134 parser.open_brace_line
138 return Err(LicenseError::Parse(format!(
139 "incomplete escape sequence on l. {}",
145 parser.parsed.push_str(®ex::escape(&parser.buffer));
150 fn trans_from_lit(&mut self, chr: char) -> ParsingState {
153 self.parsed.push_str(®ex::escape(&self.buffer));
155 self.open_brace_line = self.linum;
158 '}' => Abort(format!(
159 "escape or balance closing brace on l. {}",
164 self.buffer.push(chr);
170 fn trans_from_litesc(&mut self, chr: char) -> ParsingState {
171 self.buffer.push(chr);
175 fn trans_from_re(&mut self, chr: char, brace_nesting: u32) -> ParsingState {
178 self.buffer.push(chr);
179 Re(brace_nesting + 1)
182 match brace_nesting {
184 // default regex for empty placeholder {}
185 if self.buffer.is_empty() {
186 self.parsed.push_str(".*?");
188 self.parsed.push_str(&self.buffer);
194 self.buffer.push(chr);
195 Re(brace_nesting - 1)
200 self.buffer.push(chr);
204 self.buffer.push(chr);
210 fn trans_from_reesc(&mut self, chr: char, brace_nesting: u32) -> ParsingState {
211 self.buffer.push(chr);
216 pub fn load_and_compile_template(path: &str) -> Result<Regex, LicenseError> {
217 let mut lt_file = File::open(&path)?;
218 let mut lt_str = String::new();
219 lt_file.read_to_string(&mut lt_str)?;
220 let lt_parsed = TemplateParser::parse(<_str)?;
221 Ok(Regex::new(<_parsed)?)
226 use super::TemplateParser;
229 fn test_parse_license_template() {
231 TemplateParser::parse("literal (.*)").unwrap(),
235 TemplateParser::parse(r"escaping \}").unwrap(),
238 assert!(TemplateParser::parse("unbalanced } without escape").is_err());
240 TemplateParser::parse(r"{\d+} place{-?}holder{s?}").unwrap(),
241 r"^\d+ place-?holders?"
243 assert_eq!(TemplateParser::parse("default {}").unwrap(), "^default .*?");
245 TemplateParser::parse(r"unbalanced nested braces {\{{3}}").unwrap(),
246 r"^unbalanced nested braces \{{3}"
249 &TemplateParser::parse("parsing error }")
252 "parsing failed, escape or balance closing brace on l. 1"
255 &TemplateParser::parse("parsing error {\nsecond line")
258 "parsing failed, escape or balance opening brace on l. 1"
261 &TemplateParser::parse(r"parsing error \")
264 "parsing failed, incomplete escape sequence on l. 1"