#![forbid(unsafe_code)]
use std::path::{Path, PathBuf};
pub const CONNECTIVITY_CONSENT_FILE: &str = "connectivity-consent";
const STANDING_ONLINE_TOKEN: &str = "standing-online";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectivityConsent {
Unrecorded,
StandingOnline,
}
fn consent_path(data_dir: &Path) -> PathBuf {
data_dir.join(CONNECTIVITY_CONSENT_FILE)
}
pub fn load_connectivity_consent(data_dir: &Path) -> ConnectivityConsent {
match std::fs::read_to_string(consent_path(data_dir)) {
Ok(content) if content.trim() == STANDING_ONLINE_TOKEN => {
ConnectivityConsent::StandingOnline
}
Ok(_) | Err(_) => ConnectivityConsent::Unrecorded,
}
}
pub fn store_standing_online(data_dir: &Path) -> std::io::Result<()> {
std::fs::create_dir_all(data_dir)?;
std::fs::write(consent_path(data_dir), format!("{STANDING_ONLINE_TOKEN}\n"))
}
pub fn forget_connectivity_consent(data_dir: &Path) -> std::io::Result<()> {
match std::fs::remove_file(consent_path(data_dir)) {
Err(error) if error.kind() != std::io::ErrorKind::NotFound => Err(error),
Ok(()) | Err(_) => Ok(()),
}
}
#[cfg(test)]
mod wire_contract {
use super::*;
fn scratch_dir() -> tempfile::TempDir {
tempfile::tempdir().expect("a scratch directory")
}
#[test]
fn the_minted_tokens_are_pinned() {
assert_eq!(CONNECTIVITY_CONSENT_FILE, "connectivity-consent");
assert_eq!(STANDING_ONLINE_TOKEN, "standing-online");
}
#[test]
fn an_absent_record_is_unrecorded() {
let dir = scratch_dir();
assert_eq!(
load_connectivity_consent(dir.path()),
ConnectivityConsent::Unrecorded
);
}
#[test]
fn store_and_load_round_trip() {
let dir = scratch_dir();
store_standing_online(dir.path()).expect("the store writes");
assert_eq!(
load_connectivity_consent(dir.path()),
ConnectivityConsent::StandingOnline
);
let written = std::fs::read_to_string(dir.path().join(CONNECTIVITY_CONSENT_FILE))
.expect("the record exists");
assert_eq!(written.trim(), STANDING_ONLINE_TOKEN);
}
#[test]
fn a_corrupt_record_is_unrecorded() {
let dir = scratch_dir();
for corrupt in [
"",
"online",
"STANDING-ONLINE",
"standing-online extra",
"yes",
] {
std::fs::write(dir.path().join(CONNECTIVITY_CONSENT_FILE), corrupt)
.expect("the corrupt record writes");
assert_eq!(
load_connectivity_consent(dir.path()),
ConnectivityConsent::Unrecorded,
"content {corrupt:?} must not grant consent"
);
}
}
#[test]
fn forget_returns_the_store_to_unrecorded_and_is_idempotent() {
let dir = scratch_dir();
store_standing_online(dir.path()).expect("the store writes");
forget_connectivity_consent(dir.path()).expect("the forget removes");
assert_eq!(
load_connectivity_consent(dir.path()),
ConnectivityConsent::Unrecorded
);
forget_connectivity_consent(dir.path()).expect("forgetting nothing succeeds");
}
#[test]
fn store_creates_the_data_directory() {
let dir = scratch_dir();
let nested = dir.path().join("wallets");
store_standing_online(&nested).expect("the store creates the directory");
assert_eq!(
load_connectivity_consent(&nested),
ConnectivityConsent::StandingOnline
);
}
}