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() && suffix.chars().all(|c| c.is_ascii_digit())
45 && let Ok(v) = suffix.parse::<u32>() {
46 let name = message[..idx].to_string();
47 return Self { code, name, value: Some(v) };
48 }
49 }
50 Self { code, name: message.to_string(), value: None }
51 }
52
53 pub fn is(&self, pattern: &str) -> bool {
60 if let Some(prefix) = pattern.strip_suffix('*') {
61 self.name.starts_with(prefix)
62 } else if let Some(suffix) = pattern.strip_prefix('*') {
63 self.name.ends_with(suffix)
64 } else {
65 self.name == pattern
66 }
67 }
68
69 pub fn flood_wait_seconds(&self) -> Option<u64> {
71 if self.code == 420 && self.name == "FLOOD_WAIT" {
72 self.value.map(|v| v as u64)
73 } else {
74 None
75 }
76 }
77}
78
79#[derive(Debug)]
83pub enum InvocationError {
84 Rpc(RpcError),
86 Io(io::Error),
88 Deserialize(String),
90 Dropped,
92 Migrate(i32),
94}
95
96impl fmt::Display for InvocationError {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 match self {
99 Self::Rpc(e) => write!(f, "{e}"),
100 Self::Io(e) => write!(f, "I/O error: {e}"),
101 Self::Deserialize(s) => write!(f, "deserialize error: {s}"),
102 Self::Dropped => write!(f, "request dropped"),
103 Self::Migrate(dc) => write!(f, "DC migration to {dc}"),
104 }
105 }
106}
107
108impl std::error::Error for InvocationError {}
109
110impl From<io::Error> for InvocationError {
111 fn from(e: io::Error) -> Self { Self::Io(e) }
112}
113
114impl From<layer_tl_types::deserialize::Error> for InvocationError {
115 fn from(e: layer_tl_types::deserialize::Error) -> Self { Self::Deserialize(e.to_string()) }
116}
117
118impl InvocationError {
119 pub fn is(&self, pattern: &str) -> bool {
121 match self {
122 Self::Rpc(e) => e.is(pattern),
123 _ => false,
124 }
125 }
126
127 pub fn flood_wait_seconds(&self) -> Option<u64> {
129 match self {
130 Self::Rpc(e) => e.flood_wait_seconds(),
131 _ => None,
132 }
133 }
134}
135
136#[derive(Debug)]
140pub enum SignInError {
141 SignUpRequired,
143 PasswordRequired(Box<PasswordToken>),
145 InvalidCode,
147 Other(InvocationError),
149}
150
151impl fmt::Display for SignInError {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 match self {
154 Self::SignUpRequired => write!(f, "sign up required — use official Telegram app"),
155 Self::PasswordRequired(_) => write!(f, "2FA password required"),
156 Self::InvalidCode => write!(f, "invalid or expired code"),
157 Self::Other(e) => write!(f, "{e}"),
158 }
159 }
160}
161
162impl std::error::Error for SignInError {}
163
164impl From<InvocationError> for SignInError {
165 fn from(e: InvocationError) -> Self { Self::Other(e) }
166}
167
168pub struct PasswordToken {
174 pub(crate) password: layer_tl_types::types::account::Password,
175}
176
177impl PasswordToken {
178 pub fn hint(&self) -> Option<&str> {
180 self.password.hint.as_deref()
181 }
182}
183
184impl fmt::Debug for PasswordToken {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(f, "PasswordToken {{ hint: {:?} }}", self.hint())
187 }
188}
189
190pub struct LoginToken {
196 pub(crate) phone: String,
197 pub(crate) phone_code_hash: String,
198}