# 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. If you're building a bot, you probably don't need to depend on this directly. For installation instructions see the [ferogram README](https://github.com/ankit-chaubey/ferogram).
## 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` (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) {
let item = ctx.get_data("item").await.unwrap_or_default();
ctx.set_data("item", "Widget").await.ok();
ctx.transition(Order::Quantity).await.ok();
ctx.finish().await.ok(); // clear everything, exit the FSM
}
```
## Custom storage
Implement `StateStorage` for Redis, SQL, or any other backend:
```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)