use include_dir::{Dir, include_dir};
pub use crate::payload_roots::PAYLOAD_ROOTS;
pub static METHOD: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/method");
pub static BINDINGS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/bindings");
pub static RUNBOOKS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/runbooks");
pub static FORGES: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/forges");
pub static SETUP: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/setup");
pub static SNIPPETS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/snippets");
pub static SKILLS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/skills");
pub static SKILL_SHARED: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/skill-shared");
pub static VERSIONS: &str = include_str!("../versions.toml");
pub static LICENSE: &str = include_str!("../LICENSE");
pub static LICENSE_MIT: &str = include_str!("../LICENSE-MIT");
pub static LICENSE_CC_BY: &str = include_str!("../LICENSE-CC-BY-4.0");
pub const SENTINEL: &str = "TODO(release-kit)";
pub(crate) fn walk<'a>(dir: &Dir<'a>) -> Vec<(String, &'a [u8])> {
let mut out = Vec::new();
for file in dir.files() {
out.push((file.path().to_string_lossy().into_owned(), file.contents()));
}
for sub in dir.dirs() {
out.extend(walk(sub));
}
out.sort_by(|a, b| a.0.cmp(&b.0));
out
}
#[must_use]
pub fn root_files(root: &str) -> Option<Vec<(String, &'static [u8])>> {
let dir = match root {
"method" => &METHOD,
"bindings" => &BINDINGS,
"runbooks" => &RUNBOOKS,
"forges" => &FORGES,
"snippets" => &SNIPPETS,
"setup" => &SETUP,
"skills" => &SKILLS,
"skill-shared" => &SKILL_SHARED,
"versions.toml" => return Some(vec![(root.to_owned(), VERSIONS.as_bytes())]),
_ => return None,
};
Some(
walk(dir)
.into_iter()
.map(|(path, bytes)| (format!("{root}/{path}"), bytes))
.collect(),
)
}
#[must_use]
pub fn artifacts() -> Vec<(String, &'static [u8])> {
PAYLOAD_ROOTS
.iter()
.filter_map(|root| root_files(root))
.flatten()
.collect()
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
use super::{PAYLOAD_ROOTS, artifacts, root_files};
#[test]
fn the_inventory_and_the_embed_declare_the_same_roots() {
let source = include_str!("embedded.rs");
let mut embedded: Vec<String> = source
.lines()
.filter_map(|line| {
let (_, rest) = line.split_once("include_dir!(\"$CARGO_MANIFEST_DIR/")?;
let (root, _) = rest.split_once('"')?;
Some(root.to_owned())
})
.collect();
embedded.extend(source.lines().filter_map(|line| {
let (_, rest) = line.split_once("include_str!(\"../")?;
let (name, _) = rest.split_once('"')?;
(!name.starts_with("LICENSE")).then(|| name.to_owned())
}));
embedded.sort();
let mut declared: Vec<String> = PAYLOAD_ROOTS.iter().map(ToString::to_string).collect();
declared.sort();
assert_eq!(
embedded, declared,
"src/embedded.rs and src/payload_roots.rs disagree on the payload roots"
);
}
#[test]
fn every_declared_root_serves_at_least_one_file() {
for root in PAYLOAD_ROOTS {
let files = root_files(root).expect("a declared root resolves");
assert!(!files.is_empty(), "{root}: the root carries no file");
for (path, _) in &files {
assert!(
path == root || path.starts_with(&format!("{root}/")),
"{path}: an artifact path must carry its root"
);
}
}
assert!(root_files("no-such-root").is_none());
}
#[test]
fn the_artifact_list_is_stable_and_complete() {
let listed = artifacts();
let total: usize = PAYLOAD_ROOTS
.iter()
.map(|root| root_files(root).expect("a declared root resolves").len())
.sum();
assert_eq!(listed.len(), total);
assert!(
listed.iter().any(|(path, _)| path == "versions.toml"),
"the single-file root must appear as itself"
);
}
}