use std::sync::Mutex;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Bound {
pub udid: String,
pub port: u16,
}
#[derive(Debug, Default)]
pub struct SessionState {
bound: Mutex<Option<Bound>>,
}
pub const UNBOUND_HINT: &str = "no device is bound to this session — call smix_use with a UDID first \
(smix_devices lists what is available)";
impl SessionState {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn from_env(udid: Option<String>, port: u16) -> Self {
let state = Self::new();
if let Some(udid) = udid {
state.bind(Bound { udid, port });
}
state
}
pub fn bind(&self, next: Bound) -> Option<Bound> {
let mut slot = self.bound.lock().expect("session lock");
slot.replace(next)
}
#[must_use]
pub fn current(&self) -> Option<Bound> {
self.bound.lock().expect("session lock").clone()
}
pub fn release(&self) -> Option<Bound> {
self.bound.lock().expect("session lock").take()
}
pub fn require(&self) -> Result<Bound, &'static str> {
self.current().ok_or(UNBOUND_HINT)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_unbound_session_names_the_tool_that_binds_one() {
let s = SessionState::new();
let err = s.require().expect_err("nothing bound yet");
assert!(err.contains("smix_use"), "got: {err}");
assert!(err.contains("smix_devices"), "got: {err}");
}
#[test]
fn binding_replaces_and_hands_back_the_previous_device() {
let s = SessionState::new();
assert!(
s.bind(Bound {
udid: "AAAA".into(),
port: 22087
})
.is_none()
);
let previous = s.bind(Bound {
udid: "BBBB".into(),
port: 22088,
});
assert_eq!(
previous,
Some(Bound {
udid: "AAAA".into(),
port: 22087
})
);
assert_eq!(s.current().expect("bound").udid, "BBBB");
}
#[test]
fn releasing_returns_to_the_same_error_a_fresh_session_gives() {
let s = SessionState::new();
s.bind(Bound {
udid: "AAAA".into(),
port: 22087,
});
assert_eq!(s.release().expect("was bound").udid, "AAAA");
assert!(s.current().is_none());
assert_eq!(s.require().expect_err("unbound again"), UNBOUND_HINT);
}
#[test]
fn the_environment_is_a_default_not_a_requirement() {
assert!(SessionState::from_env(None, 22087).current().is_none());
let pre = SessionState::from_env(Some("AAAA".into()), 22099);
assert_eq!(
pre.current().expect("bound"),
Bound {
udid: "AAAA".into(),
port: 22099
}
);
}
}