diff options
author | Aleksey Kladov <[email protected]> | 2018-08-10 20:33:29 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-10 20:33:29 +0100 |
commit | 7c67612b8a894187fa3b64725531a5459f9211bf (patch) | |
tree | 9e2a536efa0c880d921fd8d4d74423afc9451fd4 /crates/tools | |
parent | 26262aaf05983c5b7f41cc438e287523268fe1eb (diff) |
organizize
Diffstat (limited to 'crates/tools')
-rw-r--r-- | crates/tools/Cargo.toml | 14 | ||||
-rw-r--r-- | crates/tools/src/lib.rs | 43 | ||||
-rw-r--r-- | crates/tools/src/main.rs | 216 |
3 files changed, 273 insertions, 0 deletions
diff --git a/crates/tools/Cargo.toml b/crates/tools/Cargo.toml new file mode 100644 index 000000000..f9fee16f9 --- /dev/null +++ b/crates/tools/Cargo.toml | |||
@@ -0,0 +1,14 @@ | |||
1 | [package] | ||
2 | name = "tools" | ||
3 | version = "0.1.0" | ||
4 | authors = ["Aleksey Kladov <[email protected]>"] | ||
5 | publish = false | ||
6 | |||
7 | [dependencies] | ||
8 | ron = "0.1.7" | ||
9 | walkdir = "2.1.3" | ||
10 | itertools = "0.7.8" | ||
11 | tera = "0.11" | ||
12 | clap = "2.32.0" | ||
13 | failure = "0.1.1" | ||
14 | commandspec = "0.10" | ||
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs new file mode 100644 index 000000000..97a56a31f --- /dev/null +++ b/crates/tools/src/lib.rs | |||
@@ -0,0 +1,43 @@ | |||
1 | extern crate itertools; | ||
2 | |||
3 | use itertools::Itertools; | ||
4 | |||
5 | #[derive(Debug)] | ||
6 | pub struct Test { | ||
7 | pub name: String, | ||
8 | pub text: String, | ||
9 | } | ||
10 | |||
11 | pub fn collect_tests(s: &str) -> Vec<(usize, Test)> { | ||
12 | let mut res = vec![]; | ||
13 | let prefix = "// "; | ||
14 | let comment_blocks = s | ||
15 | .lines() | ||
16 | .map(str::trim_left) | ||
17 | .enumerate() | ||
18 | .group_by(|(_idx, line)| line.starts_with(prefix)); | ||
19 | |||
20 | 'outer: for (is_comment, block) in comment_blocks.into_iter() { | ||
21 | if !is_comment { | ||
22 | continue; | ||
23 | } | ||
24 | let mut block = block.map(|(idx, line)| (idx, &line[prefix.len()..])); | ||
25 | |||
26 | let (start_line, name) = loop { | ||
27 | match block.next() { | ||
28 | Some((idx, line)) if line.starts_with("test ") => { | ||
29 | break (idx, line["test ".len()..].to_string()) | ||
30 | } | ||
31 | Some(_) => (), | ||
32 | None => continue 'outer, | ||
33 | } | ||
34 | }; | ||
35 | let text: String = itertools::join( | ||
36 | block.map(|(_, line)| line).chain(::std::iter::once("")), | ||
37 | "\n", | ||
38 | ); | ||
39 | assert!(!text.trim().is_empty() && text.ends_with("\n")); | ||
40 | res.push((start_line, Test { name, text })) | ||
41 | } | ||
42 | res | ||
43 | } | ||
diff --git a/crates/tools/src/main.rs b/crates/tools/src/main.rs new file mode 100644 index 000000000..d42d3ecb7 --- /dev/null +++ b/crates/tools/src/main.rs | |||
@@ -0,0 +1,216 @@ | |||
1 | extern crate clap; | ||
2 | #[macro_use] | ||
3 | extern crate failure; | ||
4 | extern crate ron; | ||
5 | extern crate tera; | ||
6 | extern crate tools; | ||
7 | extern crate walkdir; | ||
8 | #[macro_use] | ||
9 | extern crate commandspec; | ||
10 | |||
11 | use clap::{App, Arg, SubCommand}; | ||
12 | use std::{ | ||
13 | collections::HashMap, | ||
14 | fs, | ||
15 | path::{Path, PathBuf}, | ||
16 | }; | ||
17 | use tools::{collect_tests, Test}; | ||
18 | |||
19 | type Result<T> = ::std::result::Result<T, failure::Error>; | ||
20 | |||
21 | const GRAMMAR_DIR: &str = "./crates/libsyntax2/src/grammar"; | ||
22 | const INLINE_TESTS_DIR: &str = "./crates/libsyntax2/tests/data/parser/inline"; | ||
23 | const GRAMMAR: &str = "./crates/libsyntax2/src/grammar.ron"; | ||
24 | const SYNTAX_KINDS: &str = "./crates/libsyntax2/src/syntax_kinds/generated.rs"; | ||
25 | const SYNTAX_KINDS_TEMPLATE: &str = "./crates/libsyntax2/src/syntax_kinds/generated.rs.tera"; | ||
26 | const AST: &str = "./crates/libsyntax2/src/ast/generated.rs"; | ||
27 | const AST_TEMPLATE: &str = "./crates/libsyntax2/src/ast/generated.rs.tera"; | ||
28 | |||
29 | fn main() -> Result<()> { | ||
30 | let matches = App::new("tasks") | ||
31 | .setting(clap::AppSettings::SubcommandRequiredElseHelp) | ||
32 | .arg( | ||
33 | Arg::with_name("verify") | ||
34 | .long("--verify") | ||
35 | .help("Verify that generated code is up-to-date") | ||
36 | .global(true), | ||
37 | ) | ||
38 | .subcommand(SubCommand::with_name("gen-kinds")) | ||
39 | .subcommand(SubCommand::with_name("gen-tests")) | ||
40 | .subcommand(SubCommand::with_name("install-code")) | ||
41 | .get_matches(); | ||
42 | match matches.subcommand() { | ||
43 | ("install-code", _) => install_code_extension()?, | ||
44 | (name, Some(matches)) => run_gen_command(name, matches.is_present("verify"))?, | ||
45 | _ => unreachable!(), | ||
46 | } | ||
47 | Ok(()) | ||
48 | } | ||
49 | |||
50 | fn run_gen_command(name: &str, verify: bool) -> Result<()> { | ||
51 | match name { | ||
52 | "gen-kinds" => { | ||
53 | update(Path::new(SYNTAX_KINDS), &render_template(SYNTAX_KINDS_TEMPLATE)?, verify)?; | ||
54 | update(Path::new(AST), &render_template(AST_TEMPLATE)?, verify)?; | ||
55 | }, | ||
56 | "gen-tests" => { | ||
57 | gen_tests(verify)? | ||
58 | }, | ||
59 | _ => unreachable!(), | ||
60 | } | ||
61 | Ok(()) | ||
62 | } | ||
63 | |||
64 | fn update(path: &Path, contents: &str, verify: bool) -> Result<()> { | ||
65 | match fs::read_to_string(path) { | ||
66 | Ok(ref old_contents) if old_contents == contents => { | ||
67 | return Ok(()); | ||
68 | } | ||
69 | _ => (), | ||
70 | } | ||
71 | if verify { | ||
72 | bail!("`{}` is not up-to-date", path.display()); | ||
73 | } | ||
74 | eprintln!("updating {}", path.display()); | ||
75 | fs::write(path, contents)?; | ||
76 | Ok(()) | ||
77 | } | ||
78 | |||
79 | fn render_template(template: &str) -> Result<String> { | ||
80 | let grammar: ron::value::Value = { | ||
81 | let text = fs::read_to_string(GRAMMAR)?; | ||
82 | ron::de::from_str(&text)? | ||
83 | }; | ||
84 | let template = fs::read_to_string(template)?; | ||
85 | let mut tera = tera::Tera::default(); | ||
86 | tera.add_raw_template("grammar", &template) | ||
87 | .map_err(|e| format_err!("template error: {:?}", e))?; | ||
88 | tera.register_global_function("concat", Box::new(concat)); | ||
89 | tera.register_filter("camel", |arg, _| { | ||
90 | Ok(arg.as_str().unwrap() | ||
91 | .split("_") | ||
92 | .flat_map(|word| { | ||
93 | word.chars() | ||
94 | .next().unwrap() | ||
95 | .to_uppercase() | ||
96 | .chain( | ||
97 | word.chars().skip(1).flat_map(|c| c.to_lowercase()) | ||
98 | ) | ||
99 | }) | ||
100 | .collect::<String>() | ||
101 | .into()) | ||
102 | }); | ||
103 | let ret = tera | ||
104 | .render("grammar", &grammar) | ||
105 | .map_err(|e| format_err!("template error: {:?}", e))?; | ||
106 | return Ok(ret); | ||
107 | |||
108 | fn concat(args: HashMap<String, tera::Value>) -> tera::Result<tera::Value> { | ||
109 | let mut elements = Vec::new(); | ||
110 | for &key in ["a", "b", "c"].iter() { | ||
111 | let val = match args.get(key) { | ||
112 | Some(val) => val, | ||
113 | None => continue, | ||
114 | }; | ||
115 | let val = val.as_array().unwrap(); | ||
116 | elements.extend(val.iter().cloned()); | ||
117 | } | ||
118 | Ok(tera::Value::Array(elements)) | ||
119 | } | ||
120 | } | ||
121 | |||
122 | fn gen_tests(verify: bool) -> Result<()> { | ||
123 | let tests = tests_from_dir(Path::new(GRAMMAR_DIR))?; | ||
124 | |||
125 | let inline_tests_dir = Path::new(INLINE_TESTS_DIR); | ||
126 | if !inline_tests_dir.is_dir() { | ||
127 | fs::create_dir_all(inline_tests_dir)?; | ||
128 | } | ||
129 | let existing = existing_tests(inline_tests_dir)?; | ||
130 | |||
131 | for t in existing.keys().filter(|&t| !tests.contains_key(t)) { | ||
132 | panic!("Test is deleted: {}", t); | ||
133 | } | ||
134 | |||
135 | let mut new_idx = existing.len() + 2; | ||
136 | for (name, test) in tests { | ||
137 | let path = match existing.get(&name) { | ||
138 | Some((path, _test)) => path.clone(), | ||
139 | None => { | ||
140 | let file_name = format!("{:04}_{}.rs", new_idx, name); | ||
141 | new_idx += 1; | ||
142 | inline_tests_dir.join(file_name) | ||
143 | } | ||
144 | }; | ||
145 | update(&path, &test.text, verify)?; | ||
146 | } | ||
147 | Ok(()) | ||
148 | } | ||
149 | |||
150 | fn tests_from_dir(dir: &Path) -> Result<HashMap<String, Test>> { | ||
151 | let mut res = HashMap::new(); | ||
152 | for entry in ::walkdir::WalkDir::new(dir) { | ||
153 | let entry = entry.unwrap(); | ||
154 | if !entry.file_type().is_file() { | ||
155 | continue; | ||
156 | } | ||
157 | if entry.path().extension().unwrap_or_default() != "rs" { | ||
158 | continue; | ||
159 | } | ||
160 | let text = fs::read_to_string(entry.path())?; | ||
161 | |||
162 | for (_, test) in collect_tests(&text) { | ||
163 | if let Some(old_test) = res.insert(test.name.clone(), test) { | ||
164 | bail!("Duplicate test: {}", old_test.name) | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | Ok(res) | ||
169 | } | ||
170 | |||
171 | fn existing_tests(dir: &Path) -> Result<HashMap<String, (PathBuf, Test)>> { | ||
172 | let mut res = HashMap::new(); | ||
173 | for file in fs::read_dir(dir)? { | ||
174 | let file = file?; | ||
175 | let path = file.path(); | ||
176 | if path.extension().unwrap_or_default() != "rs" { | ||
177 | continue; | ||
178 | } | ||
179 | let name = { | ||
180 | let file_name = path.file_name().unwrap().to_str().unwrap(); | ||
181 | file_name[5..file_name.len() - 3].to_string() | ||
182 | }; | ||
183 | let text = fs::read_to_string(&path)?; | ||
184 | let test = Test { | ||
185 | name: name.clone(), | ||
186 | text, | ||
187 | }; | ||
188 | match res.insert(name, (path, test)) { | ||
189 | Some(old) => println!("Duplicate test: {:?}", old), | ||
190 | None => (), | ||
191 | } | ||
192 | } | ||
193 | Ok(res) | ||
194 | } | ||
195 | |||
196 | fn install_code_extension() -> Result<()> { | ||
197 | execute!( | ||
198 | r" | ||
199 | cd code | ||
200 | npm install | ||
201 | " | ||
202 | )?; | ||
203 | execute!( | ||
204 | r" | ||
205 | cd code | ||
206 | ./node_modules/vsce/out/vsce package | ||
207 | " | ||
208 | )?; | ||
209 | execute!( | ||
210 | r" | ||
211 | cd code | ||
212 | code --install-extension ./libsyntax-rust-0.0.1.vsix | ||
213 | " | ||
214 | )?; | ||
215 | Ok(()) | ||
216 | } | ||