From 73119e7e8e72536c2dddfdb0ab007529b674cedb Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Mon, 11 Jul 2022 22:23:54 +0200 Subject: [PATCH] :art: Cleaned up build.rs --- stackline/build.rs | 94 ++++++++++++++++++++++++++------------- stackline/src/tile/mod.rs | 1 + 2 files changed, 64 insertions(+), 31 deletions(-) diff --git a/stackline/build.rs b/stackline/build.rs index 8bfeed9..e43df79 100644 --- a/stackline/build.rs +++ b/stackline/build.rs @@ -1,7 +1,7 @@ use std::env; use std::fs; use std::path::{Path, PathBuf}; -use syn::{Item, Type, ItemImpl}; +use syn::{Item, ItemImpl, Type}; // This script reads the contents of any rust file in the `tiles/` directory, // and gathers any type that implements `Tile`. These types are then put into @@ -13,20 +13,7 @@ use syn::{Item, Type, ItemImpl}; // - only impls in the format "impl Tile for X" are accepted (X must not contain any "::") // TODO: generate a kind of Reflection API for AnyTile - -fn parse_impl_tile(item: &ItemImpl) -> Option { - let (_, trait_, _) = item.trait_.as_ref()?; - let ident = trait_.get_ident()?; - - if ident.to_string() == "Tile" { - if let Type::Path(path) = &*item.self_ty { - let name = path.path.get_ident().map(|i| i.to_string())?; - return Some(name); - } - } - - None -} +// - reading and writing can now be done through serde fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); @@ -37,16 +24,16 @@ fn main() { // Read and parse the contents of every .rs file in tiles/ - for entry in fs::read_dir("tiles/").expect("Error while reading tiles/") { - let entry = entry.expect("Error while reading tiles/"); - let src_path = entry.path(); + for src_path in list_files("tiles/") { if let Some("rs") = src_path.extension().and_then(|x| x.to_str()) { let contents = fs::read_to_string(src_path.clone()) - .expect(&format!("Couldn't read {:?}", src_path)); + .unwrap_or_else(|err| panic!("Couldn't read {:?}: {}", src_path, err)); let mut local_names: Vec = Vec::new(); + // TODO: don't throw an error when a parsing error occured; + // Instead, include the file so that rustc can give a helpful error let syntax = syn::parse_file(&contents) - .expect(&format!("Unable to parse file {:?}", src_path)); + .unwrap_or_else(|err| panic!("Unable to parse file {:?}: {}", src_path, err)); for item in syntax.items.iter() { match item { @@ -70,23 +57,67 @@ fn main() { } } - // Generate code + // == Generate code == + + let res = generate_code(files, names); + + fs::write(dest_path.clone(), &res) + .unwrap_or_else(|err| panic!("Couldn't write to {:?}: {}", dest_path, err)); +} + +/// Helper function to recognize `impl Tile for XYZ` +fn parse_impl_tile(item: &ItemImpl) -> Option { + let (_, trait_, _) = item.trait_.as_ref()?; + let ident = trait_.get_ident()?; + + if ident.to_string() == "Tile" { + if let Type::Path(path) = &*item.self_ty { + let name = path.path.get_ident().map(|i| i.to_string())?; + return Some(name); + } + } + + None +} - let mut res = String::from("use enum_dispatch::enum_dispatch;\n\n"); +// TODO: recursively list files in `path` (right now only the top-level files are listed). +// The method can return a `Vec` instead if necessary. +fn list_files(path: impl AsRef) -> impl Iterator { + let iter = fs::read_dir(path.as_ref()) + .unwrap_or_else(|err| panic!("Error while reading {}: {}", path.as_ref().display(), err)); - for file in files { - let mod_name = file.0.as_path().file_stem().map(|x| x.to_str()).flatten().expect(&format!("Couldn't extract valid UTF-8 filename from path {:?}", file)); - let path = file.0.as_path().to_str().expect("Invalid UTF-8 path"); + iter.filter_map(|entry| match entry { + Ok(entry) => Some(entry.path()), + Err(_) => None, + }) +} - res += &format!("#[path = \"{}\"]\nmod {};\n", path, mod_name); - res += &format!("pub use {}::{{", mod_name); - for name in file.1 { +fn generate_code(files: Vec<(PathBuf, Vec)>, names: Vec) -> String { + let mut res = String::new(); + + // TODO: use a HashMap to prevent duplicate module names + for (file, names) in files { + let module_name = file + .as_path() + .file_stem() + .map(|x| x.to_str()) + .flatten() + .expect(&format!( + "Couldn't extract valid UTF-8 filename from path {:?}", + file + )); + let path = file.as_path().to_str().expect("Invalid UTF-8 path"); + + res += &format!("#[path = \"{}\"]\nmod {};\n", path, module_name); + res += &format!("pub use {}::{{", module_name); + for name in names { res += &format!("{}, ", name); } res += "};\n\n"; } - res += &fs::read_to_string("src/tile/anytile.doc.rs").expect("Couldn't read src/tile/anytile.doc.rs"); + res += &fs::read_to_string("src/tile/anytile.doc.rs") + .expect("Couldn't read src/tile/anytile.doc.rs"); res += "#[derive(Clone, Debug, Serialize, Deserialize)]\n"; res += "#[enum_dispatch]\n"; res += "pub enum AnyTile {\n"; @@ -96,10 +127,10 @@ fn main() { } res += "}\n"; - // impl TryInto<&T> for &AnyTile res += "\n"; for name in names { + // impl TryInto<&T> for &AnyTile res += &format!( concat!( "impl<'a> TryInto<&'a {0}> for &'a AnyTile {{\n", @@ -115,6 +146,7 @@ fn main() { name ); + // impl TryInto<&mut T> for &mut AnyTile res += &format!( concat!( "impl<'a> TryInto<&'a mut {0}> for &'a mut AnyTile {{\n", @@ -131,5 +163,5 @@ fn main() { ); } - fs::write(dest_path.clone(), &res).expect(&format!("Couldn't write to {:?}", dest_path)); + res } diff --git a/stackline/src/tile/mod.rs b/stackline/src/tile/mod.rs index dd4b50e..e9ad43f 100644 --- a/stackline/src/tile/mod.rs +++ b/stackline/src/tile/mod.rs @@ -5,6 +5,7 @@ * See [its documentation](AnyTile) for more information on the discovery process. */ use super::*; +use enum_dispatch::enum_dispatch; use serde::{Serialize, Deserialize}; mod full;