Skip to main content

cougr_core/accounts/
session_builder.rs

1//! Fluent builder pattern for creating session keys with scoped permissions.
2//!
3//! # Example
4//! ```no_run
5//! use cougr_core::accounts::SessionBuilder;
6//! use soroban_sdk::{symbol_short, Env};
7//!
8//! let env = Env::default();
9//! let scope = SessionBuilder::new(&env)
10//!     .allow_action(symbol_short!("move"))
11//!     .allow_action(symbol_short!("attack"))
12//!     .max_operations(100_u32)
13//!     .expires_at(3_600_u64)
14//!     .build_scope();
15//! assert_eq!(scope.allowed_actions.len(), 2);
16//! ```
17
18use soroban_sdk::{Env, Symbol, Vec};
19
20use super::error::AccountError;
21use super::traits::SessionKeyProvider;
22use super::types::{SessionKey, SessionScope};
23
24/// Builder for creating sessions with scoped permissions.
25pub struct SessionBuilder<'a> {
26    env: &'a Env,
27    allowed_actions: Vec<Symbol>,
28    max_operations: u32,
29    expires_at: u64,
30}
31
32impl<'a> SessionBuilder<'a> {
33    /// Create a new session builder.
34    pub fn new(env: &'a Env) -> Self {
35        Self {
36            env,
37            allowed_actions: Vec::new(env),
38            max_operations: 0,
39            expires_at: 0,
40        }
41    }
42
43    /// Allow a specific game action (e.g., `symbol_short!("move")`).
44    pub fn allow_action(mut self, action: Symbol) -> Self {
45        self.allowed_actions.push_back(action);
46        self
47    }
48
49    /// Set maximum number of operations for this session.
50    pub fn max_operations(mut self, count: u32) -> Self {
51        self.max_operations = count;
52        self
53    }
54
55    /// Set expiration timestamp (ledger timestamp).
56    pub fn expires_at(mut self, timestamp: u64) -> Self {
57        self.expires_at = timestamp;
58        self
59    }
60
61    /// Build the SessionScope.
62    pub fn build_scope(self) -> SessionScope {
63        SessionScope {
64            allowed_actions: self.allowed_actions,
65            max_operations: self.max_operations,
66            expires_at: self.expires_at,
67        }
68    }
69
70    /// Build and create the session key on a provider.
71    pub fn create<P: SessionKeyProvider>(
72        self,
73        provider: &mut P,
74    ) -> Result<SessionKey, AccountError> {
75        let env = self.env;
76        let scope = self.build_scope();
77        provider.create_session(env, scope)
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use soroban_sdk::{symbol_short, Env};
85
86    #[test]
87    fn test_session_builder_new() {
88        let env = Env::default();
89        let builder = SessionBuilder::new(&env);
90        let scope = builder.build_scope();
91        assert_eq!(scope.allowed_actions.len(), 0);
92        assert_eq!(scope.max_operations, 0);
93        assert_eq!(scope.expires_at, 0);
94    }
95
96    #[test]
97    fn test_session_builder_allow_action() {
98        let env = Env::default();
99        let scope = SessionBuilder::new(&env)
100            .allow_action(symbol_short!("move"))
101            .allow_action(symbol_short!("attack"))
102            .build_scope();
103        assert_eq!(scope.allowed_actions.len(), 2);
104    }
105
106    #[test]
107    fn test_session_builder_max_operations() {
108        let env = Env::default();
109        let scope = SessionBuilder::new(&env).max_operations(100).build_scope();
110        assert_eq!(scope.max_operations, 100);
111    }
112
113    #[test]
114    fn test_session_builder_expires_at() {
115        let env = Env::default();
116        let scope = SessionBuilder::new(&env).expires_at(5000).build_scope();
117        assert_eq!(scope.expires_at, 5000);
118    }
119
120    #[test]
121    fn test_session_builder_full_chain() {
122        let env = Env::default();
123        let scope = SessionBuilder::new(&env)
124            .allow_action(symbol_short!("move"))
125            .allow_action(symbol_short!("attack"))
126            .allow_action(symbol_short!("trade"))
127            .max_operations(200)
128            .expires_at(10000)
129            .build_scope();
130
131        assert_eq!(scope.allowed_actions.len(), 3);
132        assert_eq!(scope.max_operations, 200);
133        assert_eq!(scope.expires_at, 10000);
134        assert_eq!(scope.allowed_actions.get(0).unwrap(), symbol_short!("move"));
135    }
136}