]> git.lizzy.rs Git - rust.git/blob - crates/tools/src/lib.rs
Speedup fmt
[rust.git] / crates / tools / src / lib.rs
1 extern crate failure;
2 extern crate itertools;
3 extern crate teraron;
4
5 use std::{
6     path::{Path, PathBuf},
7     process::{Command, Stdio},
8 };
9
10 use failure::bail;
11 use itertools::Itertools;
12
13 pub use teraron::{Mode, Overwrite, Verify};
14
15 pub type Result<T> = ::std::result::Result<T, failure::Error>;
16
17 pub const GRAMMAR: &str = "crates/ra_syntax/src/grammar.ron";
18 pub const SYNTAX_KINDS: &str = "crates/ra_syntax/src/syntax_kinds/generated.rs.tera";
19 pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs.tera";
20 const TOOLCHAIN: &str = "beta-2018-10-30";
21
22 #[derive(Debug)]
23 pub struct Test {
24     pub name: String,
25     pub text: String,
26 }
27
28 pub fn collect_tests(s: &str) -> Vec<(usize, Test)> {
29     let mut res = vec![];
30     let prefix = "// ";
31     let comment_blocks = s
32         .lines()
33         .map(str::trim_left)
34         .enumerate()
35         .group_by(|(_idx, line)| line.starts_with(prefix));
36
37     'outer: for (is_comment, block) in comment_blocks.into_iter() {
38         if !is_comment {
39             continue;
40         }
41         let mut block = block.map(|(idx, line)| (idx, &line[prefix.len()..]));
42
43         let (start_line, name) = loop {
44             match block.next() {
45                 Some((idx, line)) if line.starts_with("test ") => {
46                     break (idx, line["test ".len()..].to_string())
47                 }
48                 Some(_) => (),
49                 None => continue 'outer,
50             }
51         };
52         let text: String = itertools::join(
53             block.map(|(_, line)| line).chain(::std::iter::once("")),
54             "\n",
55         );
56         assert!(!text.trim().is_empty() && text.ends_with('\n'));
57         res.push((start_line, Test { name, text }))
58     }
59     res
60 }
61
62 pub fn generate(mode: Mode) -> Result<()> {
63     let grammar = project_root().join(GRAMMAR);
64     let syntax_kinds = project_root().join(SYNTAX_KINDS);
65     let ast = project_root().join(AST);
66     teraron::generate(&syntax_kinds, &grammar, mode)?;
67     teraron::generate(&ast, &grammar, mode)?;
68     Ok(())
69 }
70
71 pub fn project_root() -> PathBuf {
72     Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap())
73         .ancestors()
74         .nth(2)
75         .unwrap()
76         .to_path_buf()
77 }
78
79 pub fn run(cmdline: &str, dir: &str) -> Result<()> {
80     eprintln!("\nwill run: {}", cmdline);
81     let project_dir = project_root().join(dir);
82     let mut args = cmdline.split_whitespace();
83     let exec = args.next().unwrap();
84     let status = Command::new(exec)
85         .args(args)
86         .current_dir(project_dir)
87         .status()?;
88     if !status.success() {
89         bail!("`{}` exited with {}", cmdline, status);
90     }
91     Ok(())
92 }
93
94 pub fn run_rustfmt(mode: Mode) -> Result<()> {
95     match Command::new("rustup")
96         .args(&["run", TOOLCHAIN, "--", "cargo", "fmt", "--version"])
97         .stderr(Stdio::null())
98         .stdout(Stdio::null())
99         .status()
100     {
101         Ok(status) if status.success() => (),
102         _ => install_rustfmt()?,
103     };
104
105     if mode == Verify {
106         run(
107             &format!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN),
108             ".",
109         )?;
110     } else {
111         run(&format!("rustup run {} -- cargo fmt", TOOLCHAIN), ".")?;
112     }
113     Ok(())
114 }
115
116 fn install_rustfmt() -> Result<()> {
117     run(&format!("rustup install {}", TOOLCHAIN), ".")?;
118     run(
119         &format!(
120             "rustup component add rustfmt-preview --toolchain {}",
121             TOOLCHAIN
122         ),
123         ".",
124     )
125 }