use std::sync::atomic::{AtomicUsize, Ordering};
use crate::error::AlpineSdkError;
use tracing::info;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(usize)]
pub(crate) enum Phase {
Idle = 0,
Discovery = 1,
Handshake = 2,
}
impl Phase {
fn from_usize(value: usize) -> Self {
match value {
1 => Phase::Discovery,
2 => Phase::Handshake,
_ => Phase::Idle,
}
}
pub(crate) fn label(&self) -> &'static str {
match self {
Phase::Idle => "Idle",
Phase::Discovery => "Discovery",
Phase::Handshake => "Handshake",
}
}
}
static CURRENT_PHASE: AtomicUsize = AtomicUsize::new(Phase::Idle as usize);
pub struct PhaseGuard {
previous: Phase,
active: Phase,
}
impl Drop for PhaseGuard {
fn drop(&mut self) {
CURRENT_PHASE.store(self.previous as usize, Ordering::SeqCst);
info!(
"[ALPINE][PHASE] exiting phase: {} -> {}",
self.active.label(),
self.previous.label()
);
}
}
pub(crate) fn current_phase() -> Phase {
Phase::from_usize(CURRENT_PHASE.load(Ordering::SeqCst))
}
fn enter_phase(desired: Phase) -> Result<PhaseGuard, AlpineSdkError> {
info!(
"[ALPINE][PHASE] attempting to enter phase: {}",
desired.label()
);
match CURRENT_PHASE.compare_exchange(
Phase::Idle as usize,
desired as usize,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => {
info!("[ALPINE][PHASE] entered phase: {}", desired.label());
Ok(PhaseGuard {
previous: Phase::Idle,
active: desired,
})
}
Err(current) => match Phase::from_usize(current) {
Phase::Discovery => {
if desired == Phase::Handshake {
Err(AlpineSdkError::InvalidPhaseTransition(
"discovery already running".into(),
))
} else {
Err(AlpineSdkError::InvalidPhaseTransition(
"discovery already running".into(),
))
}
}
Phase::Handshake => {
if desired == Phase::Discovery {
Err(AlpineSdkError::DiscoveryAfterHandshake)
} else {
Err(AlpineSdkError::HandshakeAlreadyInProgress)
}
}
Phase::Idle => enter_phase(desired),
},
}
}
pub fn claim_discovery() -> Result<PhaseGuard, AlpineSdkError> {
enter_phase(Phase::Discovery)
}
pub fn claim_handshake() -> Result<PhaseGuard, AlpineSdkError> {
enter_phase(Phase::Handshake)
}