Skip to main content

mur_core/
membership.rs

1//! Feature limits based on membership plan.
2
3use crate::auth::Plan;
4use serde::{Deserialize, Serialize};
5
6/// Feature limits per plan.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct PlanLimits {
9    pub max_schedules: usize,
10    pub max_models: usize,
11    pub max_workflows: usize,
12    pub max_skills: usize,
13    pub audit_retention_days: u32,
14    pub cloud_execution: bool,
15    pub team_sharing: bool,
16    pub autocomplete: bool,
17    pub live_streaming: bool,
18    pub auto_fix: bool,
19    pub nl_workflow: bool,
20}
21
22impl PlanLimits {
23    /// Get limits for a plan.
24    pub fn for_plan(plan: &Plan) -> Self {
25        match plan {
26            Plan::Free => Self {
27                max_schedules: 3,
28                max_models: 2,
29                max_workflows: 10,
30                max_skills: 5,
31                audit_retention_days: 7,
32                cloud_execution: false,
33                team_sharing: false,
34                autocomplete: false,
35                live_streaming: true,
36                auto_fix: false,
37                nl_workflow: false,
38            },
39            Plan::Pro => Self {
40                max_schedules: 50,
41                max_models: 5,
42                max_workflows: 100,
43                max_skills: 50,
44                audit_retention_days: 90,
45                cloud_execution: true,
46                team_sharing: false,
47                autocomplete: false,
48                live_streaming: true,
49                auto_fix: true,
50                nl_workflow: true,
51            },
52            Plan::Team => Self {
53                max_schedules: 500,
54                max_models: 5,
55                max_workflows: 1000,
56                max_skills: 500,
57                audit_retention_days: 365,
58                cloud_execution: true,
59                team_sharing: true,
60                autocomplete: true,
61                live_streaming: true,
62                auto_fix: true,
63                nl_workflow: true,
64            },
65        }
66    }
67
68    /// Check if a feature is available.
69    pub fn check_schedule_limit(&self, current: usize) -> bool {
70        current < self.max_schedules
71    }
72
73    pub fn check_workflow_limit(&self, current: usize) -> bool {
74        current < self.max_workflows
75    }
76
77    pub fn check_skill_limit(&self, current: usize) -> bool {
78        current < self.max_skills
79    }
80}
81
82/// Format plan comparison for upgrade prompts.
83pub fn plan_comparison() -> String {
84    r#"
85┌─────────────────┬──────────┬──────────┬──────────┐
86│ Feature         │ Free     │ Pro $9/m │ Team $49 │
87├─────────────────┼──────────┼──────────┼──────────┤
88│ Workflows       │ 10       │ 100      │ 1000     │
89│ Schedules       │ 3        │ 50       │ 500      │
90│ Models          │ 2        │ 5        │ 5        │
91│ Audit Retention │ 7 days   │ 90 days  │ 365 days │
92│ Cloud Execution │ ✗        │ ✓        │ ✓        │
93│ Auto-fix        │ ✗        │ ✓        │ ✓        │
94│ NL → Workflow   │ ✗        │ ✓        │ ✓        │
95│ Team Sharing    │ ✗        │ ✗        │ ✓        │
96│ Autocomplete    │ ✗        │ ✗        │ ✓        │
97└─────────────────┴──────────┴──────────┴──────────┘
98"#
99    .to_string()
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_free_limits() {
108        let limits = PlanLimits::for_plan(&Plan::Free);
109        assert_eq!(limits.max_schedules, 3);
110        assert!(!limits.cloud_execution);
111        assert!(!limits.auto_fix);
112    }
113
114    #[test]
115    fn test_pro_limits() {
116        let limits = PlanLimits::for_plan(&Plan::Pro);
117        assert_eq!(limits.max_schedules, 50);
118        assert!(limits.cloud_execution);
119        assert!(limits.auto_fix);
120        assert!(!limits.team_sharing);
121    }
122
123    #[test]
124    fn test_team_limits() {
125        let limits = PlanLimits::for_plan(&Plan::Team);
126        assert!(limits.team_sharing);
127        assert!(limits.autocomplete);
128        assert_eq!(limits.audit_retention_days, 365);
129    }
130
131    #[test]
132    fn test_schedule_limit_check() {
133        let limits = PlanLimits::for_plan(&Plan::Free);
134        assert!(limits.check_schedule_limit(2));
135        assert!(!limits.check_schedule_limit(3));
136    }
137}