use std::{fmt, io};
#[derive(Clone, Debug, PartialEq)]
pub struct RpcError {
pub code: i32,
pub name: String,
pub value: Option<u32>,
}
impl fmt::Display for RpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RPC {}: {}", self.code, self.name)?;
if let Some(v) = self.value {
write!(f, " (value: {v})")?;
}
Ok(())
}
}
impl std::error::Error for RpcError {}
impl RpcError {
pub fn from_telegram(code: i32, message: &str) -> Self {
if let Some(idx) = message.rfind('_') {
let suffix = &message[idx + 1..];
if !suffix.is_empty()
&& suffix.chars().all(|c| c.is_ascii_digit())
&& let Ok(v) = suffix.parse::<u32>()
{
let name = message[..idx].to_string();
return Self {
code,
name,
value: Some(v),
};
}
}
Self {
code,
name: message.to_string(),
value: None,
}
}
pub fn is(&self, pattern: &str) -> bool {
if let Some(prefix) = pattern.strip_suffix('*') {
self.name.starts_with(prefix)
} else if let Some(suffix) = pattern.strip_prefix('*') {
self.name.ends_with(suffix)
} else {
self.name == pattern
}
}
pub fn flood_wait_seconds(&self) -> Option<u64> {
if self.code == 420 && self.name == "FLOOD_WAIT" {
self.value.map(|v| v as u64)
} else {
None
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum InvocationError {
Rpc(RpcError),
Io(io::Error),
Deserialize(String),
Dropped,
#[doc(hidden)]
Migrate(i32),
}
impl fmt::Display for InvocationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Rpc(e) => write!(f, "{e}"),
Self::Io(e) => write!(f, "I/O error: {e}"),
Self::Deserialize(s) => write!(f, "deserialize error: {s}"),
Self::Dropped => write!(f, "request dropped"),
Self::Migrate(dc) => write!(f, "DC migration to {dc}"),
}
}
}
impl std::error::Error for InvocationError {}
impl From<io::Error> for InvocationError {
fn from(e: io::Error) -> Self {
Self::Io(e)
}
}
impl From<layer_tl_types::deserialize::Error> for InvocationError {
fn from(e: layer_tl_types::deserialize::Error) -> Self {
Self::Deserialize(e.to_string())
}
}
impl InvocationError {
pub fn is(&self, pattern: &str) -> bool {
match self {
Self::Rpc(e) => e.is(pattern),
_ => false,
}
}
pub fn flood_wait_seconds(&self) -> Option<u64> {
match self {
Self::Rpc(e) => e.flood_wait_seconds(),
_ => None,
}
}
}
#[derive(Debug)]
pub enum SignInError {
SignUpRequired,
PasswordRequired(Box<PasswordToken>),
InvalidCode,
Other(InvocationError),
}
impl fmt::Display for SignInError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SignUpRequired => write!(f, "sign up required: use official Telegram app"),
Self::PasswordRequired(_) => write!(f, "2FA password required"),
Self::InvalidCode => write!(f, "invalid or expired code"),
Self::Other(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for SignInError {}
impl From<InvocationError> for SignInError {
fn from(e: InvocationError) -> Self {
Self::Other(e)
}
}
pub struct PasswordToken {
pub(crate) password: layer_tl_types::types::account::Password,
}
impl PasswordToken {
pub fn hint(&self) -> Option<&str> {
self.password.hint.as_deref()
}
}
impl fmt::Debug for PasswordToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PasswordToken {{ hint: {:?} }}", self.hint())
}
}
pub struct LoginToken {
pub(crate) phone: String,
pub(crate) phone_code_hash: String,
}