use std::borrow::Cow;
use std::io;
use std::time::SystemTime;
use glacier_ui::AssetSource;
use include_dir::{Dir, File, include_dir};
static VIEWS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/views");
static ICONS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/assets/icons");
static FONTS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/assets/fonts");
static BLUEPRINTS: Dir<'static> = include_dir!("$OUT_DIR/blueprint_logos");
pub(crate) fn luau_sources() -> Vec<(String, &'static str)> {
fn recolhe(dir: &'static Dir<'static>, out: &mut Vec<(String, &'static str)>) {
for f in dir.files() {
let caminho = f.path().to_string_lossy().to_string();
if caminho.ends_with(".luau") {
if let Some(texto) = f.contents_utf8() {
out.push((caminho, texto));
}
}
}
for sub in dir.dirs() {
recolhe(sub, out);
}
}
let mut out = Vec::new();
if let Some(scripts) = VIEWS.get_dir("scripts") {
recolhe(scripts, &mut out);
}
out
}
fn route(path: &str) -> Option<&'static File<'static>> {
let norm = path.replace('\\', "/");
let norm = norm.strip_prefix("./").unwrap_or(&norm);
const ROUTES: &[(&str, &Dir<'static>)] = &[
("views/", &VIEWS),
("assets/icons/", &ICONS),
("assets/fonts/", &FONTS),
("assets/blueprint-logos/", &BLUEPRINTS),
];
for (prefix, dir) in ROUTES {
if let Some(rest) = norm.strip_prefix(prefix) {
return dir.get_file(rest);
}
}
None
}
fn not_found(path: &str) -> io::Error {
io::Error::new(
io::ErrorKind::NotFound,
format!("asset embutido não encontrado: {path}"),
)
}
#[derive(Debug, Default, Clone, Copy)]
pub struct EmbeddedAssets;
impl AssetSource for EmbeddedAssets {
fn read_bytes(&self, path: &str) -> io::Result<Cow<'static, [u8]>> {
route(path)
.map(|f| Cow::Borrowed(f.contents()))
.ok_or_else(|| not_found(path))
}
fn read_to_string(&self, path: &str) -> io::Result<Cow<'static, str>> {
let file = route(path).ok_or_else(|| not_found(path))?;
file.contents_utf8().map(Cow::Borrowed).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("asset embutido não é UTF-8 válido: {path}"),
)
})
}
fn exists(&self, path: &str) -> bool {
route(path).is_some()
}
fn modified(&self, _path: &str) -> Option<SystemTime> {
None
}
fn supports_reload(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resolve_os_assets_referenciados() {
let a = EmbeddedAssets;
for p in [
"views/app.gvb",
"views/styles/app.gss",
"views/styles/theme.json",
"views/scripts/app.luau",
"views/scripts/handlers/connection.luau",
] {
assert!(a.exists(p), "faltou embutir (texto): {p}");
assert!(a.read_to_string(p).is_ok(), "não leu (texto): {p}");
}
for p in [
"assets/icons/terminal.svg",
"assets/fonts/JetBrainsMono-Regular.ttf",
"assets/blueprint-logos/ackee/logo.png",
] {
assert!(a.exists(p), "faltou embutir (binário): {p}");
assert!(!a.read_bytes(p).unwrap().is_empty(), "vazio: {p}");
}
assert!(!a.exists("views/nao_existe.gvb"));
assert!(
a.read_to_string("views/nao_existe.gvb")
.is_err()
);
}
#[test]
fn motor_sobe_o_app_so_do_binario() {
use std::sync::Arc;
let mut motor = glacier_ui::GlacierUI::new().with_asset_source(Arc::new(EmbeddedAssets));
motor
.register_app("views/app.gvb")
.expect("registrar o app a partir dos assets embutidos");
assert!(
motor.render_current().is_ok(),
"render do app embutido falhou"
);
}
}