use crate::error::{Error, Result};
use crate::host::{OS, Os};
use std::sync::OnceLock;
pub(crate) const OPENED: bool = cfg!(any(test, pitboard_unreleased_windows));
static VERSION: OnceLock<&'static str> = OnceLock::new();
pub fn set_version(version: &'static str) {
let _ = VERSION.set(version);
}
#[cfg(any(test, feature = "test-support"))]
#[doc(hidden)]
pub fn version() -> Option<&'static str> {
VERSION.get().copied()
}
pub(crate) fn pitboard_version() -> &'static str {
VERSION.get().copied().unwrap_or(env!("CARGO_PKG_VERSION"))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Decision {
Runs,
NotReleased,
}
pub(crate) fn decision(os: Os, version: &str, opened: bool) -> Decision {
let major = version
.split('.')
.next()
.and_then(|major| major.parse::<u64>().ok())
.unwrap_or(0);
match os {
Os::MacOs | Os::Linux => Decision::Runs,
Os::Windows if major >= 1 || opened => Decision::Runs,
Os::Windows => Decision::NotReleased,
}
}
fn decided(os: Os, opened: bool) -> Decision {
decision(os, VERSION.get().copied().unwrap_or_default(), opened)
}
pub fn check() -> Result<()> {
match decided(OS, OPENED) {
Decision::Runs => Ok(()),
Decision::NotReleased => Err(Error::WindowsNotReleased),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_a_windows_build_of_a_release_before_1_0_refuses() {
for version in ["0.7.0", "0.8.0", "0.99.1", "not a version", ""] {
assert_eq!(
decision(Os::Windows, version, false),
Decision::NotReleased,
"{version}"
);
assert_eq!(
decision(Os::Windows, version, true),
Decision::Runs,
"{version}"
);
}
for version in ["1.0.0-beta.1", "1.0.0-beta.12", "1.0.0", "1.4.2", "2.0.0"] {
for opened in [false, true] {
assert_eq!(
decision(Os::Windows, version, opened),
Decision::Runs,
"{version}, opened {opened}"
);
}
}
for os in [Os::MacOs, Os::Linux] {
for version in ["0.7.0", "0.8.0", "1.0.0-beta.1", "1.0.0", "not a version"] {
for opened in [false, true] {
assert_eq!(
decision(os, version, opened),
Decision::Runs,
"{os:?}, {version}, opened {opened}"
);
}
}
}
}
#[test]
fn this_build_answers_for_its_own_system_and_version() {
const { assert!(OPENED) };
assert_eq!(check().is_ok(), decided(OS, OPENED) == Decision::Runs);
assert!(check().is_ok());
let refused = Error::WindowsNotReleased;
assert_eq!(refused.code(), "windows_not_released");
assert_eq!(
refused.to_string(),
"Pitboard for Windows is not released yet. This build changes nothing."
);
assert_eq!(refused.exit_code(), 1);
}
fn user_agent_sent() -> String {
use std::io::{BufRead, BufReader, Write};
let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("a port on loopback");
let url = format!("http://{}/", listener.local_addr().expect("its address"));
let host = std::thread::spawn(move || {
let (mut client, _) = listener.accept().expect("a request");
let head = BufReader::new(client.try_clone().expect("a second handle"));
let agent = head
.lines()
.map_while(std::result::Result::ok)
.take_while(|line| !line.is_empty())
.find_map(|line| {
let (name, value) = line.split_once(':')?;
name.eq_ignore_ascii_case("user-agent")
.then(|| value.trim().to_owned())
});
let _ = client.write_all(
b"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\nConnection: close\r\n\r\n",
);
agent
});
let ctx = crate::context::Context::for_unit_test();
crate::api::agent(&ctx).get(&url).call().expect("an answer");
host.join().expect("the host").expect("a User-Agent")
}
#[test]
fn the_gate_and_every_request_read_the_version_the_front_end_names() {
assert_eq!(decided(Os::Windows, false), Decision::NotReleased);
assert_eq!(
user_agent_sent(),
concat!("pitboard/", env!("CARGO_PKG_VERSION"))
);
set_version("1.0.0-beta.1");
set_version("0.15.0");
assert_eq!(version(), Some("1.0.0-beta.1"));
assert_eq!(decided(Os::Windows, false), Decision::Runs);
assert_eq!(user_agent_sent(), "pitboard/1.0.0-beta.1");
}
}