Skip to main content

scirs2_core/enterprise/
support.rs

1//! Support channel configuration and contact information.
2//!
3//! Provides types describing how users and enterprise customers can obtain
4//! support for SciRS2, including issue trackers, documentation portals,
5//! email contacts, and response-time expectations.
6//!
7//! # Example
8//!
9//! ```rust
10//! use scirs2_core::enterprise::support::{support_info, SupportTier};
11//!
12//! let config = support_info();
13//! assert_eq!(config.tier, SupportTier::Community);
14//! assert!(!config.github_issues_url.is_empty());
15//! ```
16
17/// Support tier level.
18#[derive(Debug, Clone, PartialEq, Eq)]
19#[non_exhaustive]
20pub enum SupportTier {
21    /// Community support via GitHub issues and discussions.
22    Community,
23    /// Standard commercial support with guaranteed response times.
24    Standard,
25    /// Premium support with dedicated engineering contact.
26    Premium,
27    /// Enterprise support with SLA-backed response and resolution times.
28    Enterprise,
29}
30
31impl core::fmt::Display for SupportTier {
32    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33        match self {
34            Self::Community => write!(f, "Community"),
35            Self::Standard => write!(f, "Standard"),
36            Self::Premium => write!(f, "Premium"),
37            Self::Enterprise => write!(f, "Enterprise"),
38            #[allow(unreachable_patterns)]
39            _ => write!(f, "Unknown"),
40        }
41    }
42}
43
44/// Support channel configuration.
45///
46/// Contains all the information needed for users and customers to reach
47/// the SciRS2 support team.
48#[derive(Debug, Clone)]
49pub struct SupportConfig {
50    /// GitHub issues URL for bug reports and feature requests.
51    pub github_issues_url: &'static str,
52    /// GitHub discussions URL for Q&A.
53    pub github_discussions_url: &'static str,
54    /// Documentation website URL.
55    pub documentation_url: &'static str,
56    /// API reference URL.
57    pub api_reference_url: &'static str,
58    /// Support email address.
59    pub email: &'static str,
60    /// Target response time in hours for the current tier.
61    pub response_time_hours: u32,
62    /// Target resolution time in hours for the current tier.
63    pub resolution_time_hours: Option<u32>,
64    /// Current support tier.
65    pub tier: SupportTier,
66    /// Security vulnerability reporting email.
67    pub security_email: &'static str,
68    /// Changelog / release notes URL.
69    pub changelog_url: &'static str,
70}
71
72impl Default for SupportConfig {
73    fn default() -> Self {
74        Self {
75            github_issues_url: "https://github.com/cool-japan/scirs/issues",
76            github_discussions_url: "https://github.com/cool-japan/scirs/discussions",
77            documentation_url: "https://cool-japan.github.io/scirs/",
78            api_reference_url: "https://docs.rs/scirs2-core/latest/scirs2_core/",
79            email: "support@cooljapan.dev",
80            response_time_hours: 48,
81            resolution_time_hours: None,
82            tier: SupportTier::Community,
83            security_email: "security@cooljapan.dev",
84            changelog_url: "https://github.com/cool-japan/scirs/blob/master/CHANGELOG.md",
85        }
86    }
87}
88
89impl SupportConfig {
90    /// Returns a support configuration for the given tier.
91    pub fn for_tier(tier: SupportTier) -> Self {
92        match tier {
93            SupportTier::Community => Self::default(),
94            SupportTier::Standard => Self {
95                response_time_hours: 24,
96                resolution_time_hours: Some(72),
97                tier: SupportTier::Standard,
98                ..Self::default()
99            },
100            SupportTier::Premium => Self {
101                response_time_hours: 8,
102                resolution_time_hours: Some(48),
103                tier: SupportTier::Premium,
104                ..Self::default()
105            },
106            SupportTier::Enterprise => Self {
107                response_time_hours: 4,
108                resolution_time_hours: Some(24),
109                tier: SupportTier::Enterprise,
110                ..Self::default()
111            },
112            #[allow(unreachable_patterns)]
113            _ => Self::default(),
114        }
115    }
116
117    /// Returns a formatted summary of the support configuration.
118    pub fn summary(&self) -> String {
119        let mut lines = Vec::new();
120        lines.push(format!("SciRS2 Support Configuration ({})", self.tier));
121        lines.push(format!("  Issues:        {}", self.github_issues_url));
122        lines.push(format!("  Discussions:   {}", self.github_discussions_url));
123        lines.push(format!("  Documentation: {}", self.documentation_url));
124        lines.push(format!("  API Reference: {}", self.api_reference_url));
125        lines.push(format!("  Email:         {}", self.email));
126        lines.push(format!("  Security:      {}", self.security_email));
127        lines.push(format!(
128            "  Response time: {} hours",
129            self.response_time_hours
130        ));
131        if let Some(resolution) = self.resolution_time_hours {
132            lines.push(format!("  Resolution:    {} hours", resolution));
133        }
134        lines.push(format!("  Changelog:     {}", self.changelog_url));
135        lines.join("\n")
136    }
137}
138
139/// Returns the default (community) support configuration.
140pub fn support_info() -> SupportConfig {
141    SupportConfig::default()
142}
143
144/// Escalation path for support issues.
145#[derive(Debug, Clone)]
146pub struct EscalationPath {
147    /// Steps in the escalation path, from least to most urgent.
148    pub steps: Vec<EscalationStep>,
149}
150
151/// A single step in the escalation path.
152#[derive(Debug, Clone)]
153pub struct EscalationStep {
154    /// Step number (1-indexed).
155    pub step: u32,
156    /// Description of the action.
157    pub action: String,
158    /// Channel to use (e.g. "GitHub Issues", "Email", "Phone").
159    pub channel: String,
160    /// Expected wait time in hours before escalating to the next step.
161    pub wait_hours: u32,
162}
163
164/// Returns the default escalation path for support issues.
165pub fn default_escalation_path() -> EscalationPath {
166    EscalationPath {
167        steps: vec![
168            EscalationStep {
169                step: 1,
170                action: "Search existing GitHub issues and documentation".into(),
171                channel: "Self-service".into(),
172                wait_hours: 0,
173            },
174            EscalationStep {
175                step: 2,
176                action: "Open a new GitHub issue with reproduction steps".into(),
177                channel: "GitHub Issues".into(),
178                wait_hours: 48,
179            },
180            EscalationStep {
181                step: 3,
182                action: "Post in GitHub Discussions for community help".into(),
183                channel: "GitHub Discussions".into(),
184                wait_hours: 24,
185            },
186            EscalationStep {
187                step: 4,
188                action: "Email support team with issue link".into(),
189                channel: "Email".into(),
190                wait_hours: 24,
191            },
192            EscalationStep {
193                step: 5,
194                action: "For security issues, email security team directly".into(),
195                channel: "Security Email".into(),
196                wait_hours: 0,
197            },
198        ],
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    #[test]
207    fn test_support_config_default() {
208        let config = SupportConfig::default();
209        assert!(!config.github_issues_url.is_empty());
210        assert!(!config.documentation_url.is_empty());
211        assert!(!config.email.is_empty());
212        assert!(config.response_time_hours > 0);
213        assert_eq!(config.tier, SupportTier::Community);
214    }
215
216    #[test]
217    fn test_support_info_returns_community() {
218        let config = support_info();
219        assert_eq!(config.tier, SupportTier::Community);
220    }
221
222    #[test]
223    fn test_support_tiers_response_times() {
224        let community = SupportConfig::for_tier(SupportTier::Community);
225        let standard = SupportConfig::for_tier(SupportTier::Standard);
226        let premium = SupportConfig::for_tier(SupportTier::Premium);
227        let enterprise = SupportConfig::for_tier(SupportTier::Enterprise);
228
229        assert!(community.response_time_hours >= standard.response_time_hours);
230        assert!(standard.response_time_hours >= premium.response_time_hours);
231        assert!(premium.response_time_hours >= enterprise.response_time_hours);
232    }
233
234    #[test]
235    fn test_support_tier_display() {
236        assert_eq!(SupportTier::Community.to_string(), "Community");
237        assert_eq!(SupportTier::Standard.to_string(), "Standard");
238        assert_eq!(SupportTier::Premium.to_string(), "Premium");
239        assert_eq!(SupportTier::Enterprise.to_string(), "Enterprise");
240    }
241
242    #[test]
243    fn test_support_config_summary() {
244        let config = SupportConfig::for_tier(SupportTier::Premium);
245        let summary = config.summary();
246        assert!(summary.contains("Premium"));
247        assert!(summary.contains("8 hours"));
248        assert!(summary.contains("48 hours"));
249        assert!(summary.contains("github.com"));
250    }
251
252    #[test]
253    fn test_escalation_path() {
254        let path = default_escalation_path();
255        assert!(
256            path.steps.len() >= 4,
257            "Expected at least 4 escalation steps"
258        );
259        for (i, step) in path.steps.iter().enumerate() {
260            assert_eq!(step.step as usize, i + 1);
261            assert!(!step.action.is_empty());
262            assert!(!step.channel.is_empty());
263        }
264    }
265
266    #[test]
267    fn test_enterprise_tier_has_resolution_time() {
268        let config = SupportConfig::for_tier(SupportTier::Enterprise);
269        assert!(
270            config.resolution_time_hours.is_some(),
271            "Enterprise tier must have resolution time"
272        );
273    }
274
275    #[test]
276    fn test_security_email_present() {
277        let config = support_info();
278        assert!(
279            config.security_email.contains('@'),
280            "Security email must be a valid email"
281        );
282    }
283}