diff options
author | cyqsimon <28627918+cyqsimon@users.noreply.github.com> | 2023-11-02 16:27:26 +0800 |
---|---|---|
committer | cyqsimon <28627918+cyqsimon@users.noreply.github.com> | 2023-12-11 10:20:10 +0800 |
commit | d1bc0ef0d452101b7b488cd18dc373a0c784bf97 (patch) | |
tree | 019df38ec1e832443480e608f30f4dd3e81c2b19 /build | |
parent | 52f94b4623d01bae8a8b2ba538f8b3518eeb98c9 (diff) |
Build script codegen initial impl
Diffstat (limited to 'build')
-rw-r--r-- | build/main.rs | 3 | ||||
-rw-r--r-- | build/syntax_mapping.rs | 106 |
2 files changed, 109 insertions, 0 deletions
diff --git a/build/main.rs b/build/main.rs index 416d90d5..8966ee52 100644 --- a/build/main.rs +++ b/build/main.rs @@ -1,5 +1,6 @@ #[cfg(feature = "application")] mod application; +mod syntax_mapping; mod util; fn main() -> anyhow::Result<()> { @@ -7,6 +8,8 @@ fn main() -> anyhow::Result<()> { // see: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed println!("cargo:rerun-if-changed=build/"); + syntax_mapping::build_static_mappings()?; + #[cfg(feature = "application")] application::gen_man_and_comp()?; diff --git a/build/syntax_mapping.rs b/build/syntax_mapping.rs new file mode 100644 index 00000000..850b25b4 --- /dev/null +++ b/build/syntax_mapping.rs @@ -0,0 +1,106 @@ +use std::{convert::Infallible, env, fs, path::Path, str::FromStr}; + +use anyhow::anyhow; +use indexmap::IndexMap; +use serde::Deserialize; +use serde_with::DeserializeFromStr; +use walkdir::WalkDir; + +/// Known mapping targets. +/// +/// Corresponds to `syntax_mapping::MappingTarget`. +#[derive(Clone, Debug, Eq, PartialEq, Hash, DeserializeFromStr)] +pub enum MappingTarget { + MapTo(String), + MapToUnknown, + MapExtensionToUnknown, +} +impl FromStr for MappingTarget { + type Err = Infallible; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "MappingTarget::MapToUnknown" => Ok(Self::MapToUnknown), + "MappingTarget::MapExtensionToUnknown" => Ok(Self::MapExtensionToUnknown), + syntax => Ok(Self::MapTo(syntax.into())), + } + } +} +impl MappingTarget { + fn codegen(&self) -> String { + match self { + Self::MapTo(syntax) => format!(r###"MappingTarget::MapTo(r#"{syntax}"#)"###), + Self::MapToUnknown => "MappingTarget::MapToUnknown".into(), + Self::MapExtensionToUnknown => "MappingTarget::MapExtensionToUnknown".into(), + } + } +} + +/// A struct that models a single .toml file in /src/syntax_mapping/builtins/. +#[derive(Clone, Debug, Deserialize)] +struct MappingDefModel { + mappings: IndexMap<MappingTarget, Vec<String>>, +} +impl MappingDefModel { + fn into_mapping_list(self) -> Vec<(String, MappingTarget)> { + self.mappings + .into_iter() + .flat_map(|(target, matcher)| { + matcher + .into_iter() + .map(|rule| (rule, target.clone())) + .collect::<Vec<_>>() + }) + .collect() + } +} + +#[derive(Clone, Debug)] +struct MappingList(Vec<(String, MappingTarget)>); +impl MappingList { + fn codegen(&self) -> String { + let array_items: Vec<_> = self + .0 + .iter() + .map(|(matcher, target)| format!(r###"(r#"{matcher}"#, {t})"###, t = target.codegen())) + .collect(); + let len = array_items.len(); + + format!( + "static STATIC_RULES: [(&'static str, &'static str); {len}] = [\n{items}\n];", + items = array_items.join(",\n") + ) + } +} + +fn read_all_mappings() -> anyhow::Result<MappingList> { + let mut all_mappings = vec![]; + + for entry in WalkDir::new("src/syntax_mapping/builtins") + .into_iter() + .map(|entry| entry.unwrap_or_else(|err| panic!("failed to visit a file: {err}"))) + .filter(|entry| { + let path = entry.path(); + path.is_file() && path.extension().map(|ext| ext == "toml").unwrap_or(false) + }) + { + let toml_string = fs::read_to_string(entry.path())?; + let mappings = toml::from_str::<MappingDefModel>(&toml_string)?.into_mapping_list(); + all_mappings.extend(mappings); + } + + Ok(MappingList(all_mappings)) +} + +/// Build the static syntax mappings defined in /src/syntax_mapping/builtins/ +/// into a .rs source file, which is to be inserted with `include!`. +pub fn build_static_mappings() -> anyhow::Result<()> { + let mappings = read_all_mappings()?; + + let codegen_path = Path::new(&env::var_os("OUT_DIR").ok_or(anyhow!("OUT_DIR is unset"))?) + .join("codegen_static_syntax_mappings.rs"); + + fs::write(codegen_path, mappings.codegen())?; + + Ok(()) +} |