1use std::collections::HashMap;
2
3pub type InviteHeaders = HashMap<String, Vec<(String, String)>>;
6
7#[derive(Debug, Clone)]
8pub enum AppEvent {
9 Registering {
10 account: String,
11 },
12 RegisterOk {
13 account: String,
14 },
15 RegisterFailed {
16 reason: String,
17 },
18 Unregistered {
19 account: String,
20 },
21 CallIncoming {
22 call_id: String,
23 number: String,
24 display_name: Option<String>,
25 },
26 CallOutgoing {
27 call_id: String,
28 number: String,
29 },
30 CallRinging {
31 call_id: String,
32 },
33 CallEstablished {
34 call_id: String,
35 },
36 CallClosed {
37 call_id: String,
38 reason: String,
39 error: bool,
40 },
41 VoicemailStatus {
42 waiting: bool,
43 new_count: u32,
44 },
45 Response {
46 ok: bool,
47 data: String,
48 },
49 Unknown {
50 class: String,
51 type_: String,
52 },
53 BackendConnectFailed {
54 reason: String,
55 },
56}
57
58pub fn is_error_reason(reason: &str) -> bool {
61 if reason.is_empty() {
62 return false;
63 }
64 const NORMAL: &[&str] = &[
65 "Connection reset by peer",
66 "Connection closed",
67 "Rejected by user",
68 "Call transfered",
69 ];
70 !NORMAL
71 .iter()
72 .any(|n| reason.to_lowercase().starts_with(&n.to_lowercase()))
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
82 fn empty_reason_is_not_error() {
83 assert!(!is_error_reason(""));
84 }
85
86 #[test]
87 fn connection_reset_is_not_error() {
88 assert!(!is_error_reason("Connection reset by peer"));
89 }
90
91 #[test]
92 fn connection_reset_with_errno_is_not_error() {
93 assert!(!is_error_reason("Connection reset by peer [104]"));
94 }
95
96 #[test]
97 fn connection_closed_is_not_error() {
98 assert!(!is_error_reason("Connection closed"));
99 }
100
101 #[test]
102 fn rejected_by_user_is_not_error() {
103 assert!(!is_error_reason("Rejected by user"));
104 }
105
106 #[test]
107 fn sip_busy_is_error() {
108 assert!(is_error_reason("486 Busy Here"));
109 }
110
111 #[test]
112 fn sip_not_found_is_error() {
113 assert!(is_error_reason("404 Not Found"));
114 }
115}