use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use crate::bundle::RuntimeBundle;
const RELEASE_BUNDLE_DIR: &str = "bundle";
const PROJECT_RELEASE_SUFFIX: [&str; 2] = [".phoxal", "release"];
pub(crate) fn open(root: &Path) -> Result<RuntimeBundle> {
RuntimeBundle::open(root).with_context(|| {
format!(
"phoxal-supervisor takes a compiled bundle directory; {} is not one",
root.display()
)
})
}
pub(crate) fn owning_root(bundle_root: &Path) -> PathBuf {
let Some(release_root) = strip_tail(bundle_root, &[RELEASE_BUNDLE_DIR]) else {
return bundle_root.to_path_buf();
};
strip_tail(&release_root, &PROJECT_RELEASE_SUFFIX).unwrap_or(release_root)
}
fn strip_tail(path: &Path, tail: &[&str]) -> Option<PathBuf> {
let mut components = path.components().rev();
let found: Vec<_> = components
.by_ref()
.take(tail.len())
.map(|component| component.as_os_str().to_string_lossy().into_owned())
.collect();
let expected: Vec<_> = tail.iter().rev().map(ToString::to_string).collect();
(found == expected).then(|| components.rev().collect::<PathBuf>())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_directory_without_a_manifest_is_not_a_bundle() {
let dir = tempfile::tempdir().expect("temporary directory");
std::fs::write(dir.path().join("robot.yaml"), "schema: phoxal/robot/v0\n")
.expect("source fixture");
let error = open(dir.path()).expect_err("authored YAML is not a compiled bundle");
assert!(format!("{error:#}").contains("manifest.json"), "{error:#}");
}
#[test]
fn a_bundle_is_owned_by_whatever_owns_the_release_it_sits_in() {
assert_eq!(
owning_root(Path::new("/work/rover/.phoxal/release/bundle")),
Path::new("/work/rover")
);
assert_eq!(
owning_root(Path::new("/var/phoxal/bundle")),
Path::new("/var/phoxal")
);
assert_eq!(
owning_root(Path::new("/var/lib/phoxal/releases/current/bundle")),
Path::new("/var/lib/phoxal/releases/current")
);
assert_eq!(
owning_root(Path::new("/var/lib/phoxal/releases/current")),
Path::new("/var/lib/phoxal/releases/current")
);
}
}