Skip to main content

ferogram_fsm/
context.rs

1/*
2 * Copyright (c) 2026 Ankit Chaubey <ankitchaubey.dev@gmail.com>
3 * https://github.com/ankit-chaubey
4 *
5 * Project: ferogram
6 * Website: https://ferogram.dev
7 *
8 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
9 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
10 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
11 * This file may not be copied, modified, or distributed except according
12 * to those terms.
13 */
14
15use std::collections::HashMap;
16use std::fmt;
17use std::sync::Arc;
18
19use serde::Serialize;
20use serde::de::DeserializeOwned;
21
22use crate::FsmState;
23use crate::error::StorageError;
24use crate::key::StateKey;
25use crate::storage::StateStorage;
26
27/// The FSM context injected into state-matched handlers.
28///
29/// Provides typed access to state transitions and arbitrary key-value data
30/// associated with the current conversation slot.
31#[derive(Clone)]
32pub struct StateContext {
33    storage: Arc<dyn StateStorage>,
34    key: StateKey,
35    /// The state key that matched this handler, provided as context.
36    pub current_state: String,
37}
38
39impl StateContext {
40    /// Construct a new `StateContext`. Called internally by the dispatcher.
41    pub fn new(storage: Arc<dyn StateStorage>, key: StateKey, current_state: String) -> Self {
42        Self {
43            storage,
44            key,
45            current_state,
46        }
47    }
48
49    /// Transition to a new state. Overwrites the current state.
50    pub async fn transition(&self, new_state: impl FsmState) -> Result<(), StorageError> {
51        self.storage
52            .set_state(self.key.clone(), new_state.as_key())
53            .await
54    }
55
56    /// Clear the current state (set to `None`). Leaves data intact.
57    pub async fn clear_state(&self) -> Result<(), StorageError> {
58        self.storage.clear_state(self.key.clone()).await
59    }
60
61    /// Set a typed data value for `field`. The value is serialized to JSON.
62    pub async fn set_data<T: Serialize>(&self, field: &str, value: T) -> Result<(), StorageError> {
63        let json = serde_json::to_value(value).map_err(|e| {
64            StorageError::with_source(format!("failed to serialize field `{field}`"), e)
65        })?;
66        self.storage.set_data(self.key.clone(), field, json).await
67    }
68
69    /// Get a typed data value for `field`. Returns `None` if not set.
70    pub async fn get_data<T: DeserializeOwned>(
71        &self,
72        field: &str,
73    ) -> Result<Option<T>, StorageError> {
74        let raw = self.storage.get_data(self.key.clone(), field).await?;
75        match raw {
76            None => Ok(None),
77            Some(val) => {
78                let typed = serde_json::from_value(val).map_err(|e| {
79                    StorageError::with_source(format!("failed to deserialize field `{field}`"), e)
80                })?;
81                Ok(Some(typed))
82            }
83        }
84    }
85
86    /// Return all data fields as a raw JSON map.
87    pub async fn get_all_data(&self) -> Result<HashMap<String, serde_json::Value>, StorageError> {
88        self.storage.get_all_data(self.key.clone()).await
89    }
90
91    /// Remove all data fields. State is unchanged.
92    pub async fn clear_data(&self) -> Result<(), StorageError> {
93        self.storage.clear_data(self.key.clone()).await
94    }
95
96    /// Reset both state and all data (full conversation reset).
97    pub async fn clear_all(&self) -> Result<(), StorageError> {
98        self.storage.clear_all(self.key.clone()).await
99    }
100
101    /// The [`StateKey`] for this conversation slot.
102    pub fn key(&self) -> &StateKey {
103        &self.key
104    }
105}
106
107impl fmt::Debug for StateContext {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        f.debug_struct("StateContext")
110            .field("key", &self.key)
111            .field("current_state", &self.current_state)
112            .finish()
113    }
114}