Skip to content

Instantly share code, notes, and snippets.

@toyboot4e
Last active January 8, 2021 05:19
Show Gist options
  • Save toyboot4e/c3ebb86ef6d68e53e74376543ad4d493 to your computer and use it in GitHub Desktop.
Save toyboot4e/c3ebb86ef6d68e53e74376543ad4d493 to your computer and use it in GitHub Desktop.
Generate asset path with build script
use std::{
env,
fmt::Write as _,
fs::{self, File},
io::prelude::*,
path::{Path, PathBuf},
};
use convert_case::{Case, Casing};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[derive(Debug)]
struct AssetPrint {
pub asset_root: PathBuf,
pub buf: String,
pub indent: usize,
}
impl AssetPrint {
fn indent(&mut self) {
for _ in 0..self.indent {
write!(&mut self.buf, " ").unwrap();
}
}
fn filter(item: &Path) -> bool {
let name = item.file_name().unwrap().to_str().unwrap();
name.starts_with(".")
}
pub fn file(&mut self, item: &Path) {
if Self::filter(item) {
return;
}
self.indent();
let abs_path = item.canonicalize().unwrap();
let rel_path = abs_path.strip_prefix(&self.asset_root).unwrap();
let name = item.file_stem().unwrap().to_str().unwrap();
let name = name.to_case(Case::UpperSnake);
writeln!(
&mut self.buf,
r#"pub const {}: &str = "{}";"#,
name,
rel_path.display()
)
.unwrap();
}
pub fn push_dir(&mut self, dir: &Path) {
self.indent();
let name = dir.components().rev().next().unwrap();
let name = name.as_os_str().to_str().unwrap();
let name = name.to_case(Case::Snake);
writeln!(&mut self.buf, "pub mod {} {{", name,).unwrap();
self.indent += 1;
}
pub fn pop_dir(&mut self) {
self.indent -= 1;
self.indent();
writeln!(&mut self.buf, "}}").unwrap();
}
}
fn main() -> Result<()> {
let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let asset_root = root.join("assets");
let dst = root.join("src/assets.rs");
// if you prefer to not commit `assets.rs`:
// let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
// let dst = out_dir.join("assets.rs");
// and do this in your source file:
// include!(concat!(env!("OUT_DIR"), "/assets.rs"));
let mut ap = AssetPrint {
asset_root: asset_root.clone(),
buf: String::with_capacity(1024 * 10),
indent: 0,
};
writeln!(&mut ap.buf, "//! Automatically generated with `build.rs`")?;
writeln!(&mut ap.buf, "")?;
self::rec(&mut ap, &asset_root)?;
let mut file = File::create(&dst)?;
file.write_all(ap.buf.as_bytes())?;
Ok(())
}
fn rec(ap: &mut AssetPrint, dir: &Path) -> Result<()> {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
ap.push_dir(&path);
self::rec(ap, &path)?;
ap.pop_dir();
} else if path.is_file() {
ap.file(&path);
} else {
// TODO: handle symlink
eprintln!("symlink found: {}", path.display());
}
}
Ok(())
}
@toyboot4e
Copy link
Author

Example output:

pub mod map {
    pub mod images {
        pub mod nekura_1 {
            pub const M_MURA: &str = "map/images/nekura_1/m_mura.png";
            pub const M_SNOW_02: &str = "map/images/nekura_1/m_snow02.png";
        }
        pub mod denzi {
            pub const DUNGEON_1: &str = "map/images/denzi/dungeon_1.png";
        }
    }
    pub mod tsx {
        pub const NEKURA_1: &str = "map/tsx/nekura_1.tsx";
        pub const NEKURA_SNOW: &str = "map/tsx/nekura_snow.tsx";
        pub const DENZI_1: &str = "map/tsx/denzi_1.tsx";
    }
    pub const SNOW_RL: &str = "map/SnowRL.tiled-session";
    pub const ANF_SAMPLES: &str = "map/anf_samples.tiled-session";
    pub const SNOW_RL: &str = "map/SnowRL.tiled-project";
    pub mod tmx {
        pub const RL_START: &str = "map/tmx/rl_start.tmx";
        pub const RL_DUNGEON: &str = "map/tmx/rl_dungeon.tmx";
        pub const TILES: &str = "map/tmx/tiles.tmx";
        pub const TITLE: &str = "map/tmx/title.tmx";
    }
}
pub const IKA_CHAN: &str = "ika-chan.png";

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment