alpine_protocol_sdk/
phase.rs1use std::sync::atomic::{AtomicUsize, Ordering};
2
3use crate::error::AlpineSdkError;
4use tracing::info;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[repr(usize)]
9pub(crate) enum Phase {
10 Idle = 0,
11 Discovery = 1,
12 Handshake = 2,
13}
14
15impl Phase {
16 fn from_usize(value: usize) -> Self {
17 match value {
18 1 => Phase::Discovery,
19 2 => Phase::Handshake,
20 _ => Phase::Idle,
21 }
22 }
23
24 pub(crate) fn label(&self) -> &'static str {
25 match self {
26 Phase::Idle => "Idle",
27 Phase::Discovery => "Discovery",
28 Phase::Handshake => "Handshake",
29 }
30 }
31}
32
33static CURRENT_PHASE: AtomicUsize = AtomicUsize::new(Phase::Idle as usize);
34
35pub struct PhaseGuard {
37 previous: Phase,
38 active: Phase,
39}
40
41impl Drop for PhaseGuard {
42 fn drop(&mut self) {
43 CURRENT_PHASE.store(self.previous as usize, Ordering::SeqCst);
44 info!(
45 "[ALPINE][PHASE] exiting phase: {} -> {}",
46 self.active.label(),
47 self.previous.label()
48 );
49 }
50}
51
52pub(crate) fn current_phase() -> Phase {
53 Phase::from_usize(CURRENT_PHASE.load(Ordering::SeqCst))
54}
55
56fn enter_phase(desired: Phase) -> Result<PhaseGuard, AlpineSdkError> {
57 info!(
58 "[ALPINE][PHASE] attempting to enter phase: {}",
59 desired.label()
60 );
61 match CURRENT_PHASE.compare_exchange(
62 Phase::Idle as usize,
63 desired as usize,
64 Ordering::SeqCst,
65 Ordering::SeqCst,
66 ) {
67 Ok(_) => {
68 info!("[ALPINE][PHASE] entered phase: {}", desired.label());
69 Ok(PhaseGuard {
70 previous: Phase::Idle,
71 active: desired,
72 })
73 }
74 Err(current) => match Phase::from_usize(current) {
75 Phase::Discovery => {
76 if desired == Phase::Handshake {
77 Err(AlpineSdkError::InvalidPhaseTransition(
78 "discovery already running".into(),
79 ))
80 } else {
81 Err(AlpineSdkError::InvalidPhaseTransition(
82 "discovery already running".into(),
83 ))
84 }
85 }
86 Phase::Handshake => {
87 if desired == Phase::Discovery {
88 Err(AlpineSdkError::DiscoveryAfterHandshake)
89 } else {
90 Err(AlpineSdkError::HandshakeAlreadyInProgress)
91 }
92 }
93 Phase::Idle => enter_phase(desired),
94 },
95 }
96}
97
98pub fn claim_discovery() -> Result<PhaseGuard, AlpineSdkError> {
100 enter_phase(Phase::Discovery)
101}
102
103pub fn claim_handshake() -> Result<PhaseGuard, AlpineSdkError> {
105 enter_phase(Phase::Handshake)
106}