Skip to content

Instantly share code, notes, and snippets.

@doitian
Created November 17, 2025 11:26
Show Gist options
  • Select an option

  • Save doitian/e4a9f74d79f533617bb7c491dd809571 to your computer and use it in GitHub Desktop.

Select an option

Save doitian/e4a9f74d79f533617bb7c491dd809571 to your computer and use it in GitHub Desktop.
Ractor FSM
#[derive(Debug, Clone)]
enum ConnectionState {
Disconnected,
Connecting { attempts: u32 },
Connected { session_id: String },
Reconnecting { last_session: String },
}
enum FSMMessage {
Connect,
ConnectionSucceeded(String),
ConnectionFailed,
Disconnect,
GetState(RpcReplyPort<ConnectionState>),
}
struct ConnectionFSM;
#[cfg_attr(feature = "async-trait", ractor::async_trait)]
impl Actor for ConnectionFSM {
type Msg = FSMMessage;
type State = ConnectionState;
type Arguments = ();
async fn pre_start(
&self,
_myself: ActorRef<Self::Msg>,
_args: (),
) -> Result<Self::State, ActorProcessingErr> {
Ok(ConnectionState::Disconnected)
}
async fn handle(
&self,
myself: ActorRef<Self::Msg>,
message: Self::Msg,
state: &mut Self::State,
) -> Result<(), ActorProcessingErr> {
let new_state = match (&*state, message) {
// Transitions from Disconnected
(ConnectionState::Disconnected, FSMMessage::Connect) => {
// Entry action: start connection attempt
tracing::info!("Starting connection...");
Some(ConnectionState::Connecting { attempts: 1 })
}
// Transitions from Connecting
(ConnectionState::Connecting { attempts }, FSMMessage::ConnectionSucceeded(session_id)) => {
tracing::info!("Connected with session: {}", session_id);
Some(ConnectionState::Connected { session_id })
}
(ConnectionState::Connecting { attempts }, FSMMessage::ConnectionFailed) if *attempts < 3 => {
tracing::warn!("Connection failed, retrying... (attempt {})", attempts + 1);
Some(ConnectionState::Connecting { attempts: attempts + 1 })
}
(ConnectionState::Connecting { .. }, FSMMessage::ConnectionFailed) => {
tracing::error!("Connection failed after max attempts");
Some(ConnectionState::Disconnected)
}
// Transitions from Connected
(ConnectionState::Connected { session_id }, FSMMessage::Disconnect) => {
tracing::info!("Disconnecting session: {}", session_id);
Some(ConnectionState::Disconnected)
}
// Query handling (no state change)
(current, FSMMessage::GetState(reply)) => {
let _ = reply.send(current.clone());
None // No state change
}
// Invalid transitions
_ => {
tracing::warn!("Invalid state transition attempted");
None
}
};
if let Some(next_state) = new_state {
*state = next_state;
}
Ok(())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment