#![allow(dead_code)]
use std::path::{Path, PathBuf};
use std::process::Command;
pub(crate) fn repository() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../..")
.canonicalize()
.expect("the repository root resolves")
}
pub(crate) struct Root {
pub(crate) at: PathBuf,
}
impl Root {
pub(crate) fn new(label: &str) -> Root {
Root::shaped(label, |_| {})
}
pub(crate) fn founding(label: &str) -> Root {
Root::shaped(label, |at| {
write_bundle(at, "zz-a", FOUNDS);
write_bundle(at, "zz-b", DECLARES);
let declaration = at.join(".headwater/taxonomy.yml");
let text = std::fs::read_to_string(&declaration).expect("the declaration reads");
let from = " bundles: [design-spec";
assert!(text.contains(from), "the declaration lists its bundles");
std::fs::write(
&declaration,
text.replacen(from, " bundles: [zz-a, zz-b, design-spec", 1),
)
.expect("the declaration writes");
})
}
pub(crate) fn shaped(label: &str, prepare: impl FnOnce(&Path)) -> Root {
let at =
std::env::temp_dir().join(format!("headwater-cli-root-{}-{label}", std::process::id()));
let _ = std::fs::remove_dir_all(&at);
std::fs::create_dir_all(&at).expect("the root is made");
let repository = repository();
copy(
&repository.join("taxonomy-source/headwater-standard"),
&at.join(".headwater/packages/headwater-standard"),
);
repoint_bundles(&at.join(".headwater/packages/headwater-standard"));
copy(
&repository.join("docs/taxonomies"),
&at.join("docs/taxonomies"),
);
copy(
&repository.join("engine/crates/cli/fixtures/change/docs"),
&at.join("docs"),
);
for name in ["taxonomy.yml", "overlay.yml"] {
let to = at.join(".headwater").join(name);
std::fs::create_dir_all(to.parent().expect("it has a parent"))
.expect("the declaration directory is there");
std::fs::copy(repository.join(".headwater").join(name), to)
.expect("the declaration copies");
}
for entry in [
"engine/crates/stub/src/lib.rs",
".githooks/stub",
".claude/hooks/stub",
".claude/agents/stub",
".claude/skills/stub",
"tools/stub",
"site/stub",
] {
let to = at.join(entry);
std::fs::create_dir_all(to.parent().expect("it has a parent"))
.expect("the scope directory is there");
std::fs::write(to, "").expect("the scope entry writes");
}
prepare(&at);
pin(&at, "1.0.0");
let root = Root { at };
let resolved = root.run(&["taxonomy", "resolve"]);
assert_eq!(resolved.code, Some(0), "the fixture resolves: {resolved:?}");
root
}
pub(crate) fn run(&self, arguments: &[&str]) -> Ran {
let output = Command::new(env!("CARGO_BIN_EXE_headwater"))
.args(arguments)
.arg("--root")
.arg(&self.at)
.output()
.expect("the binary runs");
Ran {
code: output.status.code(),
out: String::from_utf8_lossy(&output.stdout).into_owned(),
err: String::from_utf8_lossy(&output.stderr).into_owned(),
}
}
}
pub(crate) const FOUNDS: &str = "add:\n kinds.zz_thing.voice: declarative\n";
pub(crate) const DECLARES: &str = "add:\n kinds.zz_thing:\n is_a: governed_document\n \
purpose: behavior\n lifecycle: standard\n \
shelves.zz_things:\n title: Zz Things\n path: \
docs/zz/**\n homogeneous: true\n kind: zz_thing\n";
pub(crate) fn write_bundle(at: &Path, name: &str, add: &str) {
let directory = at.join("docs/taxonomies").join(name);
std::fs::create_dir_all(&directory).expect("the bundle directory is made");
std::fs::write(
directory.join("bundle.yml"),
format!(
"# SPDX-License-Identifier: Apache-2.0\n\nbundle: {name}\nextends: \
headwater/standard@4.1.0\nrequires: []\n\n{add}"
),
)
.expect("the bundle writes");
}
impl Drop for Root {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.at);
}
}
#[derive(Debug)]
pub(crate) struct Ran {
pub(crate) code: Option<i32>,
pub(crate) out: String,
pub(crate) err: String,
}
pub(crate) fn copy(from: &Path, to: &Path) {
std::fs::create_dir_all(to).expect("the directory is there");
for entry in std::fs::read_dir(from).expect("the fixture directory reads") {
let entry = entry.expect("the entry reads");
let target = to.join(entry.file_name());
match entry.file_type().expect("the file type reads").is_dir() {
true => copy(&entry.path(), &target),
false => {
std::fs::copy(entry.path(), &target).expect("the fixture copies");
}
}
}
}
pub(crate) fn pin(at: &Path, version: &str) {
let manifest = at.join(".headwater/packages/headwater-standard/package.yml");
let text = std::fs::read_to_string(&manifest).expect("the manifest reads");
let found = text
.lines()
.find_map(|line| line.strip_prefix("version: "))
.expect("the manifest declares a version")
.to_string();
for (relative, indent) in [
(".headwater/packages/headwater-standard/package.yml", ""),
(".headwater/packages/headwater-standard/taxonomy.yml", ""),
(".headwater/taxonomy.yml", " "),
] {
let path = at.join(relative);
let text = std::fs::read_to_string(&path).expect("the source reads");
let from = format!("\n{indent}version: {found}\n");
assert!(
text.contains(&from),
"{relative} does not declare version {found}"
);
let to = format!("\n{indent}version: {version}\n");
std::fs::write(&path, text.replacen(&from, &to, 1)).expect("the source writes");
}
}
fn repoint_bundles(package: &std::path::Path) {
let up = "../".repeat(headwater_resolve::package::PACKAGES.split('/').count() + 1);
let manifest = package.join(headwater_resolve::package::MANIFEST);
let text = std::fs::read_to_string(&manifest).expect("the scratch manifest reads");
let from = " bundles: ../../docs/taxonomies";
assert!(text.contains(from), "the authored manifest states `{from}`");
let to = format!(" bundles: {up}docs/taxonomies");
std::fs::write(&manifest, text.replace(from, &to)).expect("the scratch manifest writes");
}