xtask/
codegen.rs

1use std::{env, fs};
2
3use anyhow::Result;
4
5use crate::schema::Group;
6
7mod js_lists;
8mod js_pkgs;
9mod parser_lists;
10mod parsers_dep;
11mod parsers_git;
12mod parsers_gitdep;
13mod queries;
14mod theme_list;
15
16const TOML_AUTOGEN_HEADER: &str = "
17###########################################
18### All following code is autogenerated ###
19### by running `cargo xtask codegen` in ###
20### the syntastica workspace. #############
21###########################################
22";
23const TOML_FEATURES_HEAD: &str = r##"
24[features]
25#! ### Features
26default = []
27
28#! Every supported language has a feature with the same name as the respective public function.
29#! Additionally the three feature groups
30#! <span class="stab portability"><code>some</code></span>,
31#! <span class="stab portability"><code>most</code></span>, and
32#! <span class="stab portability"><code>all</code></span>
33#! are available.
34
35## Include parsers for the most widely known supported languages.
36"##;
37const TOML_FEATURES_MOST: &str = r##"
38## Implies <span class="stab portability"><code>some</code></span>.
39## Include parsers for most common languages.
40"##;
41const TOML_FEATURES_ALL: &str = r##"
42## Implies <span class="stab portability"><code>most</code></span>.
43## Include parsers for all supported languages.
44"##;
45const TOML_FEATURES_DOCS: &str = r##"
46## Meant to be enabled when building docs
47docs = ["dep:document-features", "dep:rustc_version"]
48
49"##;
50
51fn is_arg(test: &str) -> bool {
52    env::args().nth(2).map_or(true, |arg| arg == test)
53}
54
55pub fn run() -> Result<()> {
56    if is_arg("queries") {
57        let mut queries_lib_rs = r###"
58#![doc = include_str!("../README.md")]
59#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
60#![cfg_attr(rustfmt, rustfmt_skip)]
61"###
62        .trim_start()
63        .to_owned();
64
65        let queries_dir = crate::WORKSPACE_DIR.join("syntastica-queries/generated_queries");
66        let _ = fs::remove_dir_all(&queries_dir);
67        fs::create_dir_all(&queries_dir)?;
68        fs::write(
69            queries_dir.join("README.md"),
70            include_str!("./codegen/generated_queries_readme.md"),
71        )?;
72
73        for (
74            ref name,
75            [highlights, injections, locals, highlights_crates_io, injections_crates_io, locals_crates_io],
76        ) in queries::make_queries()?
77        {
78            let lang_dir = queries_dir.join(name);
79            fs::create_dir(&lang_dir)?;
80
81            fs::write(lang_dir.join("highlights.scm"), highlights)?;
82            fs::write(lang_dir.join("injections.scm"), injections)?;
83            fs::write(lang_dir.join("locals.scm"), locals)?;
84            fs::write(
85                lang_dir.join("highlights_crates_io.scm"),
86                highlights_crates_io,
87            )?;
88            fs::write(
89                lang_dir.join("injections_crates_io.scm"),
90                injections_crates_io,
91            )?;
92            fs::write(lang_dir.join("locals_crates_io.scm"), locals_crates_io)?;
93
94            queries_lib_rs += &format!(
95                r###"
96pub const {lang}_HIGHLIGHTS: &str = include_str!("../generated_queries/{name}/highlights.scm");
97pub const {lang}_INJECTIONS: &str = include_str!("../generated_queries/{name}/injections.scm");
98pub const {lang}_LOCALS: &str = include_str!("../generated_queries/{name}/locals.scm");
99pub const {lang}_HIGHLIGHTS_CRATES_IO: &str = include_str!("../generated_queries/{name}/highlights_crates_io.scm");
100pub const {lang}_INJECTIONS_CRATES_IO: &str = include_str!("../generated_queries/{name}/injections_crates_io.scm");
101pub const {lang}_LOCALS_CRATES_IO: &str = include_str!("../generated_queries/{name}/locals_crates_io.scm");
102"###,
103                lang = name.to_uppercase()
104            )
105        }
106        fs::write(
107            crate::WORKSPACE_DIR.join("syntastica-queries/src/lib.rs"),
108            queries_lib_rs,
109        )?;
110    }
111
112    if is_arg("parsers-dep") {
113        parsers_dep::write()?;
114    }
115
116    if is_arg("parsers-gitdep") {
117        parsers_gitdep::write()?;
118    }
119
120    if is_arg("parsers-git") {
121        parsers_git::write()?;
122    }
123
124    if is_arg("parser-lists") {
125        parser_lists::write()?;
126    }
127
128    if is_arg("js-list") {
129        js_lists::write()?;
130    }
131
132    if is_arg("js-pkgs") {
133        js_pkgs::write()?;
134    }
135
136    if is_arg("theme-list") {
137        theme_list::write()?;
138    }
139
140    Ok(())
141}
142
143#[derive(PartialEq, Eq)]
144enum ParserCollection {
145    Git,
146    GitDep,
147    Dep,
148}
149
150fn parsers_toml_feature(group: Group) -> String {
151    let mut feature_str = format!("{group} = [\n");
152    if let Some(group) = group.next_smaller() {
153        feature_str += &format!("    \"{group}\",\n");
154    }
155    for lang in crate::LANGUAGE_CONFIG
156        .languages
157        .iter()
158        .filter(|lang| lang.group == group)
159    {
160        feature_str += "    \"";
161        feature_str += &lang.name;
162        feature_str += "\",\n";
163    }
164    feature_str + "]\n"
165}
166
167fn parsers_toml_lang_features(collection: ParserCollection) -> String {
168    let mut out = String::new();
169    for lang in &crate::LANGUAGE_CONFIG.languages {
170        out += &lang.name;
171        out += " = [";
172        if collection != ParserCollection::Git
173            && lang.parser.supports(collection == ParserCollection::GitDep)
174        {
175            out += "\"dep:";
176            out += &lang.parser.package;
177            out += "\"";
178        }
179        out += "]\n";
180    }
181    out
182}
183
184fn parsers_toml_deps(toml: &mut String, git: bool) {
185    let mut added_packages = vec![];
186    for lang in crate::LANGUAGE_CONFIG
187        .languages
188        .iter()
189        .filter(|lang| lang.parser.supports(git))
190    {
191        let package = &lang.parser.package;
192        let url = &lang.parser.git.url;
193        let rev = &lang.parser.git.rev;
194
195        if added_packages.contains(&package) {
196            continue;
197        }
198        added_packages.push(package);
199
200        let dep_str = if git {
201            format!(
202                r##"
203[dependencies.{package}]
204optional = true
205git = "{url}"
206rev = "{rev}"
207"##
208            )
209        } else {
210            format!(
211                r##"
212[dependencies.{package}]
213optional = true
214version = "={version}"
215"##,
216                version = lang
217                    .parser
218                    .crates_io
219                    .as_ref()
220                    .expect("`None` is filtered above if `git` is `false`")
221            )
222        };
223        *toml += &dep_str;
224    }
225}