use std::{
env, fs,
path::{Path, PathBuf},
time::Duration,
};
const RELEASE_URL: &str =
"https://github.com/lazywalker/zukan-assets/releases/latest/download/zukan-assets-bin.tar.gz";
const DOWNLOAD_TIMEOUT: Duration = Duration::from_secs(120);
const PAYLOAD_SUBDIRS: &[&str] = &["data", "icons"];
fn main() {
let manifest_dir =
PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is set by cargo"));
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR is set by cargo"));
let in_tree = manifest_dir.join("assets");
let assets_dir = if has_payload(&in_tree) {
in_tree
} else {
let staged = out_dir.join("zukan-assets");
if !has_payload(&staged) {
download_and_extract(&staged);
require_payload(&staged);
}
staged
};
for sub in PAYLOAD_SUBDIRS {
println!("cargo:rerun-if-changed={}/{}", assets_dir.display(), sub);
}
println!("cargo:rustc-env=ASSETS_DIR={}", assets_dir.display());
}
fn has_payload(dir: &Path) -> bool {
PAYLOAD_SUBDIRS.iter().all(|sub| dir.join(sub).is_dir())
}
fn require_payload(path: &Path) {
if !has_payload(path) {
panic!(
"assets directory ({}) is missing data/ or icons/ after download; \
run `make download`, or check your network connection.",
path.display()
);
}
}
fn download_and_extract(dest: &Path) {
eprintln!("[zukan] downloading assets from {RELEASE_URL}");
fs::create_dir_all(dest).unwrap_or_else(|e| panic!("failed to create {}: {e}", dest.display()));
let tmp = dest.join(".download-partials");
if tmp.exists() {
fs::remove_dir_all(&tmp).ok();
}
fs::create_dir_all(&tmp).unwrap_or_else(|e| panic!("failed to create {}: {e}", tmp.display()));
let config = ureq::Agent::config_builder()
.timeout_global(Some(DOWNLOAD_TIMEOUT))
.build();
let agent = config.new_agent();
let response = agent
.get(RELEASE_URL)
.header("accept-encoding", "gzip")
.call()
.unwrap_or_else(|e| {
fs::remove_dir_all(&tmp).ok();
panic!(
"assets download failed ({RELEASE_URL}): {e}\n\
hint: run `make download` to fetch via curl instead, \
or check your network connection."
)
});
let reader = response.into_body().into_reader();
let gz = flate2::read::GzDecoder::new(reader);
let mut archive = tar::Archive::new(gz);
archive.unpack(&tmp).unwrap_or_else(|e| {
fs::remove_dir_all(&tmp).ok();
panic!("failed to unpack assets into {}: {e}", tmp.display());
});
for sub in PAYLOAD_SUBDIRS {
let from = tmp.join(sub);
let to = dest.join(sub);
if from.is_dir() {
if to.exists() {
fs::remove_dir_all(&to).ok();
}
fs::rename(&from, &to).unwrap_or_else(|e| {
panic!("failed to move {} → {}: {e}", from.display(), to.display())
});
}
}
fs::remove_dir_all(&tmp).ok();
}