1use std::{fmt, io};
6
7#[derive(Clone, Debug, PartialEq)]
16pub struct RpcError {
17 pub code: i32,
19 pub name: String,
21 pub value: Option<u32>,
23}
24
25impl fmt::Display for RpcError {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 write!(f, "RPC {}: {}", self.code, self.name)?;
28 if let Some(v) = self.value {
29 write!(f, " (value: {v})")?;
30 }
31 Ok(())
32 }
33}
34
35impl std::error::Error for RpcError {}
36
37impl RpcError {
38 pub fn from_telegram(code: i32, message: &str) -> Self {
40 if let Some(idx) = message.rfind('_') {
43 let suffix = &message[idx + 1..];
44 if !suffix.is_empty()
45 && suffix.chars().all(|c| c.is_ascii_digit())
46 && let Ok(v) = suffix.parse::<u32>()
47 {
48 let name = message[..idx].to_string();
49 return Self {
50 code,
51 name,
52 value: Some(v),
53 };
54 }
55 }
56 Self {
57 code,
58 name: message.to_string(),
59 value: None,
60 }
61 }
62
63 pub fn is(&self, pattern: &str) -> bool {
70 if let Some(prefix) = pattern.strip_suffix('*') {
71 self.name.starts_with(prefix)
72 } else if let Some(suffix) = pattern.strip_prefix('*') {
73 self.name.ends_with(suffix)
74 } else {
75 self.name == pattern
76 }
77 }
78
79 pub fn flood_wait_seconds(&self) -> Option<u64> {
81 if self.code == 420 && self.name == "FLOOD_WAIT" {
82 self.value.map(|v| v as u64)
83 } else {
84 None
85 }
86 }
87}
88
89#[derive(Debug)]
93#[non_exhaustive]
94pub enum InvocationError {
95 Rpc(RpcError),
97 Io(io::Error),
99 Deserialize(String),
101 Dropped,
103 #[doc(hidden)]
106 Migrate(i32),
107}
108
109impl fmt::Display for InvocationError {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 match self {
112 Self::Rpc(e) => write!(f, "{e}"),
113 Self::Io(e) => write!(f, "I/O error: {e}"),
114 Self::Deserialize(s) => write!(f, "deserialize error: {s}"),
115 Self::Dropped => write!(f, "request dropped"),
116 Self::Migrate(dc) => write!(f, "DC migration to {dc}"),
117 }
118 }
119}
120
121impl std::error::Error for InvocationError {}
122
123impl From<io::Error> for InvocationError {
124 fn from(e: io::Error) -> Self {
125 Self::Io(e)
126 }
127}
128
129impl From<layer_tl_types::deserialize::Error> for InvocationError {
130 fn from(e: layer_tl_types::deserialize::Error) -> Self {
131 Self::Deserialize(e.to_string())
132 }
133}
134
135impl InvocationError {
136 pub fn is(&self, pattern: &str) -> bool {
138 match self {
139 Self::Rpc(e) => e.is(pattern),
140 _ => false,
141 }
142 }
143
144 pub fn flood_wait_seconds(&self) -> Option<u64> {
146 match self {
147 Self::Rpc(e) => e.flood_wait_seconds(),
148 _ => None,
149 }
150 }
151}
152
153#[derive(Debug)]
157pub enum SignInError {
158 SignUpRequired,
160 PasswordRequired(Box<PasswordToken>),
162 InvalidCode,
164 Other(InvocationError),
166}
167
168impl fmt::Display for SignInError {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 match self {
171 Self::SignUpRequired => write!(f, "sign up required: use official Telegram app"),
172 Self::PasswordRequired(_) => write!(f, "2FA password required"),
173 Self::InvalidCode => write!(f, "invalid or expired code"),
174 Self::Other(e) => write!(f, "{e}"),
175 }
176 }
177}
178
179impl std::error::Error for SignInError {}
180
181impl From<InvocationError> for SignInError {
182 fn from(e: InvocationError) -> Self {
183 Self::Other(e)
184 }
185}
186
187pub struct PasswordToken {
193 pub(crate) password: layer_tl_types::types::account::Password,
194}
195
196impl PasswordToken {
197 pub fn hint(&self) -> Option<&str> {
199 self.password.hint.as_deref()
200 }
201}
202
203impl fmt::Debug for PasswordToken {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 write!(f, "PasswordToken {{ hint: {:?} }}", self.hint())
206 }
207}
208
209pub struct LoginToken {
215 pub(crate) phone: String,
216 pub(crate) phone_code_hash: String,
217}