1 //! `pest_derive` crate has large dependency tree, and, as a build dependency,
2 //! it imposes these deps onto our consumers.
3 //!
4 //! To avoid that, let's just dump generated code to string into this
5 //! repository, and add a test that checks that the code is fresh.
6 use std::{
7     fs,
8     io::Write,
9     process::{Command, Stdio},
10     time::Instant,
11 };
12 
13 const PREAMBLE: &str = "\
14 //! This is @generated code, do not edit by hand.
15 //! See `semver.pest` and `genpest.rs`.
16 #![allow(unused_attributes)]
17 use super::SemverParser;
18 ";
19 
20 #[test]
generated_code_is_fresh()21 fn generated_code_is_fresh() {
22     let t = Instant::now();
23 
24     let token_stream = {
25         let grammar = include_str!("../src/semver.pest");
26         let input = format!(
27             r###"
28 #[derive(Parser)]
29 #[grammar_inline = r#"{}"#]
30 struct SemverParser;
31 "###,
32             grammar
33         )
34         .parse::<proc_macro2::TokenStream>()
35         .unwrap();
36 
37         let ts = pest_generator::derive_parser(input.into(), true);
38         eprintln!("Generated code in {:02?}", t.elapsed());
39         ts
40     };
41 
42     let current = {
43         let current = fs::read("./src/generated.rs").unwrap_or_default();
44         String::from_utf8(current).unwrap()
45     };
46 
47     let is_up_to_date = {
48         let current = normalize(&current[PREAMBLE.len()..]);
49         let generated = normalize(&token_stream.to_string());
50         current == generated
51     };
52 
53     // Rustfmt takes ages on this input, so fast-path skip it for unchanged
54     // code.
55     if is_up_to_date {
56         return;
57     }
58 
59     let code = {
60         eprintln!("Reformatting (this will take couple of minutes)");
61         let t = Instant::now();
62         let code = reformat(&token_stream.to_string());
63         let code = format!("{}\n{}", PREAMBLE, code);
64         eprintln!("Reformatted in {:02?}", t.elapsed());
65         code
66     };
67 
68     fs::write("./src/generated.rs", code).unwrap();
69     panic!("Generated code in the repository is outdated, updating...");
70 }
71 
reformat(code: &str) -> String72 fn reformat(code: &str) -> String {
73     let mut cmd = Command::new("rustfmt")
74         .args(&["--config", "tab_spaces=2"])
75         .stdin(Stdio::piped())
76         .stdout(Stdio::piped())
77         .spawn()
78         .unwrap();
79 
80     cmd.stdin
81         .take()
82         .unwrap()
83         .write_all(code.as_bytes())
84         .unwrap();
85     let output = cmd.wait_with_output().unwrap();
86     assert!(output.status.success());
87     String::from_utf8(output.stdout).unwrap()
88 }
89 
normalize(code: &str) -> String90 fn normalize(code: &str) -> String {
91     code.replace(|c: char| c.is_ascii_whitespace() || "{},".contains(c), "")
92 }
93