pub mod label {
pub const CHILD: &str = "gv/v1/child";
pub const OWNER_SIGN: &str = "gv/v1/owner-sign";
pub const OWNER_BOX: &str = "gv/v1/owner-box";
pub const TOKEN_AUTH: &str = "gv/v1/token-auth";
pub const TOKEN_BOX: &str = "gv/v1/token-box";
pub const NAME_INDEX: &str = "gv/v1/name-index";
pub const NAME_ENC: &str = "gv/v1/name-enc";
pub const NAME_AAD: &str = "gv/v1/name";
pub const VAULT_ID: &str = "gv/v1/vault-id";
pub const DESCRIPTOR: &str = "gv/v1/descriptor";
pub const BUNDLE: &str = "gv/v1/bundle";
pub const RECORD: &str = "gv/v1/record";
pub const CHILDREN: &str = "gv/v1/children";
pub const REPORT: &str = "gv/v1/report";
pub const SIG: &str = "gv/v1/sig";
pub const ALL: [&str; 15] = [
CHILD, OWNER_SIGN, OWNER_BOX, TOKEN_AUTH, TOKEN_BOX, NAME_INDEX, NAME_ENC, NAME_AAD,
VAULT_ID, DESCRIPTOR, BUNDLE, RECORD, CHILDREN, REPORT, SIG,
];
}
pub fn frame(label: &str, parts: &[&[u8]]) -> Vec<u8> {
let len = label.len() + 1 + parts.iter().map(|p| p.len()).sum::<usize>();
let mut out = Vec::with_capacity(len);
out.extend_from_slice(label.as_bytes());
out.push(0);
for part in parts {
out.extend_from_slice(part);
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn labels_are_distinct_and_nul_free() {
let mut seen = std::collections::BTreeSet::new();
for l in label::ALL {
assert!(l.starts_with("gv/v1/"), "{l}");
assert!(!l.as_bytes().contains(&0), "{l}");
assert!(seen.insert(l), "{l} is used twice");
}
}
#[test]
fn frame_layout() {
assert_eq!(frame("gv/v1/x", &[b"ab", b"c"]), b"gv/v1/x\0abc");
assert_eq!(frame("gv/v1/x", &[]), b"gv/v1/x\0");
assert_ne!(frame(label::CHILD, &[b"x"]), frame("gv/v1/childx", &[]));
}
}