diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-10-16 13:24:26 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-10-16 13:24:26 +0100 |
commit | 39cb6c6d3f78b193f5873c3492e530bbd24d5dd2 (patch) | |
tree | 3b588576a9645d958178c39925f22c291d8a0360 /crates/tools | |
parent | 8c74fd96192679525f985b9f6f85e9bdc011aa09 (diff) | |
parent | 2c4cfb297ec59625310023796b65e3dd48f1e76a (diff) |
Merge #128
128: Add a test to verify if the generated codes are up-to-date. r=matklad a=mominul
This test checks if the generated codes are up-to-date every time during `cargo test`.
I have confirmed that the test works by manually editing the `grammar.ron` file.
Closes #126
Thanks!
Co-authored-by: Muhammad Mominul Huque <[email protected]>
Diffstat (limited to 'crates/tools')
-rw-r--r-- | crates/tools/src/lib.rs | 77 | ||||
-rw-r--r-- | crates/tools/src/main.rs | 69 | ||||
-rw-r--r-- | crates/tools/tests/cli.rs | 13 |
3 files changed, 93 insertions, 66 deletions
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs index 97a56a31f..9a1b12a16 100644 --- a/crates/tools/src/lib.rs +++ b/crates/tools/src/lib.rs | |||
@@ -1,6 +1,25 @@ | |||
1 | extern crate itertools; | 1 | extern crate itertools; |
2 | #[macro_use] | ||
3 | extern crate failure; | ||
4 | extern crate ron; | ||
5 | extern crate tera; | ||
6 | extern crate heck; | ||
2 | 7 | ||
8 | use std::{ | ||
9 | collections::HashMap, | ||
10 | fs, | ||
11 | path::{Path, PathBuf}, | ||
12 | }; | ||
3 | use itertools::Itertools; | 13 | use itertools::Itertools; |
14 | use heck::{CamelCase, ShoutySnakeCase, SnakeCase}; | ||
15 | |||
16 | pub type Result<T> = ::std::result::Result<T, failure::Error>; | ||
17 | |||
18 | const GRAMMAR: &str = "ra_syntax/src/grammar.ron"; | ||
19 | pub const SYNTAX_KINDS: &str = "ra_syntax/src/syntax_kinds/generated.rs"; | ||
20 | pub const SYNTAX_KINDS_TEMPLATE: &str = "ra_syntax/src/syntax_kinds/generated.rs.tera"; | ||
21 | pub const AST: &str = "ra_syntax/src/ast/generated.rs"; | ||
22 | pub const AST_TEMPLATE: &str = "ra_syntax/src/ast/generated.rs.tera"; | ||
4 | 23 | ||
5 | #[derive(Debug)] | 24 | #[derive(Debug)] |
6 | pub struct Test { | 25 | pub struct Test { |
@@ -41,3 +60,61 @@ pub fn collect_tests(s: &str) -> Vec<(usize, Test)> { | |||
41 | } | 60 | } |
42 | res | 61 | res |
43 | } | 62 | } |
63 | |||
64 | |||
65 | pub fn update(path: &Path, contents: &str, verify: bool) -> Result<()> { | ||
66 | match fs::read_to_string(path) { | ||
67 | Ok(ref old_contents) if old_contents == contents => { | ||
68 | return Ok(()); | ||
69 | } | ||
70 | _ => (), | ||
71 | } | ||
72 | if verify { | ||
73 | bail!("`{}` is not up-to-date", path.display()); | ||
74 | } | ||
75 | eprintln!("updating {}", path.display()); | ||
76 | fs::write(path, contents)?; | ||
77 | Ok(()) | ||
78 | } | ||
79 | |||
80 | pub fn render_template(template: &Path) -> Result<String> { | ||
81 | let grammar: ron::value::Value = { | ||
82 | let text = fs::read_to_string(project_root().join(GRAMMAR))?; | ||
83 | ron::de::from_str(&text)? | ||
84 | }; | ||
85 | let template = fs::read_to_string(template)?; | ||
86 | let mut tera = tera::Tera::default(); | ||
87 | tera.add_raw_template("grammar", &template) | ||
88 | .map_err(|e| format_err!("template error: {:?}", e))?; | ||
89 | tera.register_function("concat", Box::new(concat)); | ||
90 | tera.register_filter("camel", |arg, _| { | ||
91 | Ok(arg.as_str().unwrap().to_camel_case().into()) | ||
92 | }); | ||
93 | tera.register_filter("snake", |arg, _| { | ||
94 | Ok(arg.as_str().unwrap().to_snake_case().into()) | ||
95 | }); | ||
96 | tera.register_filter("SCREAM", |arg, _| { | ||
97 | Ok(arg.as_str().unwrap().to_shouty_snake_case().into()) | ||
98 | }); | ||
99 | let ret = tera | ||
100 | .render("grammar", &grammar) | ||
101 | .map_err(|e| format_err!("template error: {:?}", e))?; | ||
102 | return Ok(ret); | ||
103 | |||
104 | fn concat(args: HashMap<String, tera::Value>) -> tera::Result<tera::Value> { | ||
105 | let mut elements = Vec::new(); | ||
106 | for &key in ["a", "b", "c"].iter() { | ||
107 | let val = match args.get(key) { | ||
108 | Some(val) => val, | ||
109 | None => continue, | ||
110 | }; | ||
111 | let val = val.as_array().unwrap(); | ||
112 | elements.extend(val.iter().cloned()); | ||
113 | } | ||
114 | Ok(tera::Value::Array(elements)) | ||
115 | } | ||
116 | } | ||
117 | |||
118 | pub fn project_root() -> PathBuf { | ||
119 | Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).parent().unwrap().to_path_buf() | ||
120 | } | ||
diff --git a/crates/tools/src/main.rs b/crates/tools/src/main.rs index ee900553c..6eacfc190 100644 --- a/crates/tools/src/main.rs +++ b/crates/tools/src/main.rs | |||
@@ -1,31 +1,20 @@ | |||
1 | extern crate clap; | 1 | extern crate clap; |
2 | #[macro_use] | 2 | #[macro_use] |
3 | extern crate failure; | 3 | extern crate failure; |
4 | extern crate ron; | ||
5 | extern crate tera; | ||
6 | extern crate tools; | 4 | extern crate tools; |
7 | extern crate walkdir; | 5 | extern crate walkdir; |
8 | extern crate heck; | ||
9 | 6 | ||
10 | use clap::{App, Arg, SubCommand}; | 7 | use clap::{App, Arg, SubCommand}; |
11 | use heck::{CamelCase, ShoutySnakeCase, SnakeCase}; | ||
12 | use std::{ | 8 | use std::{ |
13 | collections::HashMap, | 9 | collections::HashMap, |
14 | fs, | 10 | fs, |
15 | path::{Path, PathBuf}, | 11 | path::{Path, PathBuf}, |
16 | process::Command, | 12 | process::Command, |
17 | }; | 13 | }; |
18 | use tools::{collect_tests, Test}; | 14 | use tools::{AST, AST_TEMPLATE, Result, SYNTAX_KINDS, SYNTAX_KINDS_TEMPLATE, Test, collect_tests, render_template, update, project_root}; |
19 | |||
20 | type Result<T> = ::std::result::Result<T, failure::Error>; | ||
21 | 15 | ||
22 | const GRAMMAR_DIR: &str = "./crates/ra_syntax/src/grammar"; | 16 | const GRAMMAR_DIR: &str = "./crates/ra_syntax/src/grammar"; |
23 | const INLINE_TESTS_DIR: &str = "./crates/ra_syntax/tests/data/parser/inline"; | 17 | const INLINE_TESTS_DIR: &str = "./crates/ra_syntax/tests/data/parser/inline"; |
24 | const GRAMMAR: &str = "./crates/ra_syntax/src/grammar.ron"; | ||
25 | const SYNTAX_KINDS: &str = "./crates/ra_syntax/src/syntax_kinds/generated.rs"; | ||
26 | const SYNTAX_KINDS_TEMPLATE: &str = "./crates/ra_syntax/src/syntax_kinds/generated.rs.tera"; | ||
27 | const AST: &str = "./crates/ra_syntax/src/ast/generated.rs"; | ||
28 | const AST_TEMPLATE: &str = "./crates/ra_syntax/src/ast/generated.rs.tera"; | ||
29 | 18 | ||
30 | fn main() -> Result<()> { | 19 | fn main() -> Result<()> { |
31 | let matches = App::new("tasks") | 20 | let matches = App::new("tasks") |
@@ -51,8 +40,8 @@ fn main() -> Result<()> { | |||
51 | fn run_gen_command(name: &str, verify: bool) -> Result<()> { | 40 | fn run_gen_command(name: &str, verify: bool) -> Result<()> { |
52 | match name { | 41 | match name { |
53 | "gen-kinds" => { | 42 | "gen-kinds" => { |
54 | update(Path::new(SYNTAX_KINDS), &render_template(SYNTAX_KINDS_TEMPLATE)?, verify)?; | 43 | update(&project_root().join(SYNTAX_KINDS), &render_template(&project_root().join(SYNTAX_KINDS_TEMPLATE))?, verify)?; |
55 | update(Path::new(AST), &render_template(AST_TEMPLATE)?, verify)?; | 44 | update(&project_root().join(AST), &render_template(&project_root().join(AST_TEMPLATE))?, verify)?; |
56 | }, | 45 | }, |
57 | "gen-tests" => { | 46 | "gen-tests" => { |
58 | gen_tests(verify)? | 47 | gen_tests(verify)? |
@@ -62,58 +51,6 @@ fn run_gen_command(name: &str, verify: bool) -> Result<()> { | |||
62 | Ok(()) | 51 | Ok(()) |
63 | } | 52 | } |
64 | 53 | ||
65 | fn update(path: &Path, contents: &str, verify: bool) -> Result<()> { | ||
66 | match fs::read_to_string(path) { | ||
67 | Ok(ref old_contents) if old_contents == contents => { | ||
68 | return Ok(()); | ||
69 | } | ||
70 | _ => (), | ||
71 | } | ||
72 | if verify { | ||
73 | bail!("`{}` is not up-to-date", path.display()); | ||
74 | } | ||
75 | eprintln!("updating {}", path.display()); | ||
76 | fs::write(path, contents)?; | ||
77 | Ok(()) | ||
78 | } | ||
79 | |||
80 | fn render_template(template: &str) -> Result<String> { | ||
81 | let grammar: ron::value::Value = { | ||
82 | let text = fs::read_to_string(GRAMMAR)?; | ||
83 | ron::de::from_str(&text)? | ||
84 | }; | ||
85 | let template = fs::read_to_string(template)?; | ||
86 | let mut tera = tera::Tera::default(); | ||
87 | tera.add_raw_template("grammar", &template) | ||
88 | .map_err(|e| format_err!("template error: {:?}", e))?; | ||
89 | tera.register_function("concat", Box::new(concat)); | ||
90 | tera.register_filter("camel", |arg, _| { | ||
91 | Ok(arg.as_str().unwrap().to_camel_case().into()) | ||
92 | }); | ||
93 | tera.register_filter("snake", |arg, _| { | ||
94 | Ok(arg.as_str().unwrap().to_snake_case().into()) | ||
95 | }); | ||
96 | tera.register_filter("SCREAM", |arg, _| { | ||
97 | Ok(arg.as_str().unwrap().to_shouty_snake_case().into()) | ||
98 | }); | ||
99 | let ret = tera | ||
100 | .render("grammar", &grammar) | ||
101 | .map_err(|e| format_err!("template error: {:?}", e))?; | ||
102 | return Ok(ret); | ||
103 | |||
104 | fn concat(args: HashMap<String, tera::Value>) -> tera::Result<tera::Value> { | ||
105 | let mut elements = Vec::new(); | ||
106 | for &key in ["a", "b", "c"].iter() { | ||
107 | let val = match args.get(key) { | ||
108 | Some(val) => val, | ||
109 | None => continue, | ||
110 | }; | ||
111 | let val = val.as_array().unwrap(); | ||
112 | elements.extend(val.iter().cloned()); | ||
113 | } | ||
114 | Ok(tera::Value::Array(elements)) | ||
115 | } | ||
116 | } | ||
117 | 54 | ||
118 | fn gen_tests(verify: bool) -> Result<()> { | 55 | fn gen_tests(verify: bool) -> Result<()> { |
119 | let tests = tests_from_dir(Path::new(GRAMMAR_DIR))?; | 56 | let tests = tests_from_dir(Path::new(GRAMMAR_DIR))?; |
diff --git a/crates/tools/tests/cli.rs b/crates/tools/tests/cli.rs new file mode 100644 index 000000000..f507d80a2 --- /dev/null +++ b/crates/tools/tests/cli.rs | |||
@@ -0,0 +1,13 @@ | |||
1 | extern crate tools; | ||
2 | |||
3 | use tools::{AST, AST_TEMPLATE, SYNTAX_KINDS, SYNTAX_KINDS_TEMPLATE, render_template, update, project_root}; | ||
4 | |||
5 | #[test] | ||
6 | fn verify_template_generation() { | ||
7 | if let Err(error) = update(&project_root().join(SYNTAX_KINDS), &render_template(&project_root().join(SYNTAX_KINDS_TEMPLATE)).unwrap(), true) { | ||
8 | panic!("{}. Please update it by running `cargo gen-kinds`", error); | ||
9 | } | ||
10 | if let Err(error) = update(&project_root().join(AST), &render_template(&project_root().join(AST_TEMPLATE)).unwrap(), true) { | ||
11 | panic!("{}. Please update it by running `cargo gen-kinds`", error); | ||
12 | } | ||
13 | } | ||