# ferogram-fsm
FSM state management for ferogram bots.
[](https://crates.io/crates/ferogram-fsm)
[](https://t.me/FerogramChat) [](https://t.me/Ferogram)
[](https://docs.rs/ferogram-fsm)
[](#license)
Handles state storage and context for multi-step bot conversations. Used by `ferogram`'s dispatcher when you register FSM handlers.
`ferogram` re-exports everything from here. Existing code needs no changes.
## Installation
```toml
[dependencies]
ferogram-fsm = "0.3.8"
```
## What it does
- `StateStorage` trait so you can plug in any backend
- `MemoryStorage` built-in (in-process, no persistence)
- `StateContext` for reading, writing, and transitioning state per user/chat
- `StateKey` for scoping state by user ID, chat ID, or both
- `FsmState` trait that your state enum implements
## Usage
Define a state enum and implement `FsmState` on it (or use `#[derive(FsmState)]` from `ferogram-derive`):
```rust
use ferogram_fsm::{FsmState, MemoryStorage, StateContext};
use std::sync::Arc;
#[derive(Clone, Debug, PartialEq)]
enum Order {
Item,
Quantity,
Confirm,
}
impl FsmState for Order {
fn as_key(&self) -> String {
match self {
Order::Item => "Item".into(),
Order::Quantity => "Quantity".into(),
Order::Confirm => "Confirm".into(),
}
}
fn from_key(key: &str) -> Option<Self> {
match key {
"Item" => Some(Order::Item),
"Quantity" => Some(Order::Quantity),
"Confirm" => Some(Order::Confirm),
_ => None,
}
}
}
```
Use `StateContext` to read, write, and move between states:
```rust
async fn handle(ctx: StateContext) {
// Read stored data
let item = ctx.get_data("item").await.unwrap_or_default();
// Store data
ctx.set_data("item", "Widget").await.ok();
// Move to next state
ctx.transition(Order::Quantity).await.ok();
// Clear everything and exit the FSM
ctx.finish().await.ok();
}
```
## Custom storage
If you need persistence, implement `StateStorage` for Redis, SQL, or whatever backend you prefer:
```rust
use ferogram_fsm::{StateStorage, StorageError};
struct RedisStorage { /* ... */ }
#[async_trait::async_trait]
impl StateStorage for RedisStorage {
async fn get_state(&self, key: &str) -> Result<Option<String>, StorageError> { todo!() }
async fn set_state(&self, key: &str, state: &str) -> Result<(), StorageError> { todo!() }
async fn del_state(&self, key: &str) -> Result<(), StorageError> { todo!() }
async fn get_data(&self, key: &str, field: &str) -> Result<Option<String>, StorageError> { todo!() }
async fn set_data(&self, key: &str, field: &str, value: &str) -> Result<(), StorageError> { todo!() }
async fn clear(&self, key: &str) -> Result<(), StorageError> { todo!() }
}
```
## Stack position
```
ferogram
└ ferogram-fsm <-- here
```
## License
MIT or Apache-2.0, at your option. See [LICENSE-MIT](../LICENSE-MIT) and [LICENSE-APACHE](../LICENSE-APACHE).
**Ankit Chaubey** - [github.com/ankit-chaubey](https://github.com/ankit-chaubey)