use crate::provider::ProviderId;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Platform {
MacOs,
Linux,
}
impl Platform {
pub const ALL: &'static [Platform] = &[Platform::MacOs, Platform::Linux];
pub fn code(self) -> &'static str {
match self {
Platform::MacOs => "macos",
Platform::Linux => "linux",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Assumption {
pub name: &'static str,
pub fact: &'static str,
pub read_from: &'static str,
pub verified_against: &'static str,
pub depends: &'static str,
pub probe: &'static [&'static str],
pub absent: &'static [&'static str],
}
pub fn of(provider: ProviderId) -> &'static [Assumption] {
match provider {
ProviderId::Claude => crate::provider::claude::assumptions::ASSUMPTIONS,
ProviderId::Codex => crate::provider::codex::assumptions::ASSUMPTIONS,
}
}
pub fn read_on(provider: ProviderId, name: &str) -> &'static [Platform] {
match provider {
ProviderId::Claude => crate::provider::claude::assumptions::read_on(name),
ProviderId::Codex => Platform::ALL,
}
}
pub fn verified_against(provider: ProviderId) -> &'static str {
match provider {
ProviderId::Claude => crate::provider::claude::assumptions::VERIFIED_AGAINST,
ProviderId::Codex => crate::provider::codex::assumptions::VERIFIED_AGAINST,
}
}
pub fn all() -> Vec<&'static Assumption> {
ProviderId::ALL.iter().flat_map(|&p| of(p)).collect()
}
pub fn named(name: &str) -> Option<&'static Assumption> {
all().into_iter().find(|a| a.name == name)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Reading {
Holds,
NotReadable,
Moved(Vec<&'static str>),
Appeared(Vec<&'static str>),
}
pub fn read_from_build(assumption: &Assumption, strings: &str) -> Reading {
let arrived: Vec<&'static str> = assumption
.absent
.iter()
.filter(|needle| strings.contains(**needle))
.copied()
.collect();
if !arrived.is_empty() {
return Reading::Appeared(arrived);
}
if assumption.probe.is_empty() {
return if assumption.absent.is_empty() {
Reading::NotReadable
} else {
Reading::Holds
};
}
let gone: Vec<&'static str> = assumption
.probe
.iter()
.filter(|needle| !strings.contains(**needle))
.copied()
.collect();
if gone.is_empty() {
Reading::Holds
} else {
Reading::Moved(gone)
}
}
pub fn printable_runs(bytes: &[u8], least: usize) -> String {
let mut out = String::new();
let mut run = Vec::new();
for &b in bytes {
if (0x20..0x7f).contains(&b) || b == b'\t' {
run.push(b);
continue;
}
if run.len() >= least {
out.push_str(&String::from_utf8_lossy(&run));
out.push('\n');
}
run.clear();
}
if run.len() >= least {
out.push_str(&String::from_utf8_lossy(&run));
out.push('\n');
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_keyring_arriving_where_there_was_none_is_reported() {
let no_keyring = named("no_keyring_off_macos").unwrap();
let backends = r#"tengu_windows_credman CLAUDE_CODE_FORCE_WINDOWS_CREDMAN ["keychain","plaintext","windows-credman"]"#;
assert_eq!(read_from_build(no_keyring, backends), Reading::Holds);
assert_eq!(
read_from_build(no_keyring, &format!("{backends} Bun.secrets.get")),
Reading::Appeared(vec!["Bun.secrets"])
);
}
#[test]
fn the_bundled_runtime_is_not_taken_for_a_keyring() {
let no_keyring = named("no_keyring_off_macos").unwrap();
assert!(!no_keyring.absent.contains(&"libsecret"));
let backends = r#"tengu_windows_credman CLAUDE_CODE_FORCE_WINDOWS_CREDMAN ["keychain","plaintext","windows-credman"]"#;
assert_eq!(
read_from_build(
no_keyring,
&format!("{backends} libsecret not available. libsecret-1.so.0")
),
Reading::Holds
);
}
#[test]
fn every_fact_is_read_from_some_build() {
for &provider in ProviderId::ALL {
for a in of(provider) {
assert!(
!read_on(provider, a.name).is_empty(),
"{} is read from no build",
a.name
);
}
}
}
#[test]
fn each_keychain_fact_is_read_where_the_keychain_code_is() {
for name in ["keychain_write_route", "keychain_absence_codes"] {
assert_eq!(
read_on(ProviderId::Claude, name),
&[Platform::MacOs],
"{name}"
);
}
assert_eq!(
read_on(ProviderId::Claude, "no_keyring_off_macos"),
&[Platform::Linux]
);
}
#[test]
fn nothing_already_in_a_build_is_used_to_rule_a_backend_out() {
for a in all() {
for needle in a.absent {
assert!(
!["secret-tool", "kwallet-query", "keytar", "keyring"].contains(needle),
"{}: `{needle}` is in the build for other reasons",
a.name
);
assert!(
needle.len() >= 8,
"{}: `{needle}` is too short to mean one thing",
a.name
);
}
}
}
#[test]
fn a_fact_that_is_only_an_absence_still_reads() {
let only_absent = Assumption {
name: "x",
fact: "x",
read_from: "x",
verified_against: "9.9.9",
depends: "x",
probe: &[],
absent: &["a_thing_that_should_not_be_here"],
};
assert_eq!(
read_from_build(&only_absent, "nothing to see"),
Reading::Holds
);
assert_eq!(
read_from_build(&only_absent, "a_thing_that_should_not_be_here"),
Reading::Appeared(vec!["a_thing_that_should_not_be_here"])
);
}
#[test]
fn every_assumption_is_named_once_and_says_all_four_things() {
let mut names: Vec<&str> = all().iter().map(|a| a.name).collect();
let before = names.len();
names.sort_unstable();
names.dedup();
assert_eq!(names.len(), before, "two assumptions share a name");
for a in all() {
assert!(!a.fact.is_empty(), "{} says nothing", a.name);
assert!(!a.read_from.is_empty(), "{} says nowhere", a.name);
assert!(!a.depends.is_empty(), "{} costs nothing", a.name);
assert!(
a.verified_against.split('.').count() == 3,
"{} is dated against `{}`, which is not a version",
a.name,
a.verified_against
);
assert!(
a.name
.bytes()
.all(|b| b.is_ascii_lowercase() || b == b'_' || b.is_ascii_digit()),
"{} is not a stable code",
a.name
);
}
}
#[test]
fn a_probe_reads_what_is_there_and_names_what_is_not() {
let write_lock = named("write_lock").expect("listed");
let whole = write_lock.probe.join(" and also ");
assert_eq!(read_from_build(write_lock, &whole), Reading::Holds);
let moved = read_from_build(write_lock, "nothing of the sort");
assert_eq!(moved, Reading::Moved(write_lock.probe.to_vec()));
let cache = named("credential_cache").expect("listed");
assert_eq!(read_from_build(cache, ""), Reading::NotReadable);
}
#[test]
fn printable_runs_finds_the_strings_and_nothing_else() {
let bytes = b"\x00\x01hello there\x00\x02tiny\x00wide load\xff";
let found = printable_runs(bytes, 6);
assert!(found.contains("hello there"));
assert!(found.contains("wide load"));
assert!(
!found.contains("tiny"),
"a run shorter than asked for is not a string"
);
}
#[test]
fn an_assumption_can_be_looked_up_by_name() {
assert_eq!(
named("write_lock").expect("it is listed").name,
"write_lock"
);
assert_eq!(named("nothing_like_this"), None);
}
}