Make use of errors in the UDL

master
Lonami Exo 2022-10-27 19:33:35 +02:00
parent 004a921299
commit d11a00d062
2 changed files with 92 additions and 66 deletions

View File

@ -11,6 +11,7 @@ use log;
use log::{error, info, Level}; use log::{error, info, Level};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use std::future::Future; use std::future::Future;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
@ -21,8 +22,6 @@ use tokio::runtime::Runtime;
include!(concat!(env!("OUT_DIR"), "/talaria.uniffi.rs")); include!(concat!(env!("OUT_DIR"), "/talaria.uniffi.rs"));
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
const LOG_MIN_LEVEL: Level = Level::Trace; const LOG_MIN_LEVEL: Level = Level::Trace;
const LOG_TAG: &str = ".native.talari"; const LOG_TAG: &str = ".native.talari";
const API_ID: i32 = { const API_ID: i32 = {
@ -46,6 +45,25 @@ static RUNTIME: OnceCell<Runtime> = OnceCell::new();
static CLIENT: OnceCell<Client> = OnceCell::new(); static CLIENT: OnceCell<Client> = OnceCell::new();
static DATABASE: Mutex<Option<sqlite::Connection>> = Mutex::new(None); static DATABASE: Mutex<Option<sqlite::Connection>> = Mutex::new(None);
#[derive(Debug, Clone, Copy)]
pub enum NativeError {
Initialization,
Database,
Network,
}
impl fmt::Display for NativeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Initialization => write!(f, "a resource could not be initialized correctly"),
Self::Database => write!(f, "a query to the local database failed"),
Self::Network => write!(f, "a network request could not complete successfully"),
}
}
}
impl std::error::Error for NativeError {}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum MessageAck { pub enum MessageAck {
Received, Received,
@ -69,6 +87,8 @@ pub struct Dialog {
pinned: bool, pinned: bool,
} }
type Result<T> = std::result::Result<T, NativeError>;
fn block_on<F: Future>(future: F) -> F::Output { fn block_on<F: Future>(future: F) -> F::Output {
if RUNTIME.get().is_none() { if RUNTIME.get().is_none() {
RUNTIME RUNTIME
@ -100,7 +120,8 @@ async fn init_client() -> Result<()> {
let conn = match guard.as_ref() { let conn = match guard.as_ref() {
Some(c) => c, Some(c) => c,
None => { None => {
return Err("Database was not initialized".into()); error!("Database was not initialized");
return Err(NativeError::Initialization);
} }
}; };
@ -108,7 +129,7 @@ async fn init_client() -> Result<()> {
let session = Session::new(); let session = Session::new();
let sessions = db::get_sessions(conn)?; let sessions = db::get_sessions(conn).map_err(|_| NativeError::Database)?;
if let Some(s) = sessions.get(0) { if let Some(s) = sessions.get(0) {
match (s.user_id, s.dc_id, s.bot) { match (s.user_id, s.dc_id, s.bot) {
(Some(id), Some(dc), Some(bot)) => session.set_user(id, dc, bot), (Some(id), Some(dc), Some(bot)) => session.set_user(id, dc, bot),
@ -147,41 +168,51 @@ async fn init_client() -> Result<()> {
..Default::default() ..Default::default()
}, },
}) })
.await?; .await
.map_err(|_| NativeError::Network)?;
info!("Connected!"); info!("Connected!");
CLIENT CLIENT
.set(client) .set(client)
.map_err(|_| "Client was already initialized")?; .map_err(|_| NativeError::Initialization)?;
Ok(()) Ok(())
} }
async fn need_login() -> Result<bool> { async fn need_login() -> Result<bool> {
let client = CLIENT.get().ok_or("Client not initialized")?; let client = CLIENT.get().ok_or(NativeError::Initialization)?;
Ok(client.is_authorized().await?) client
.is_authorized()
.await
.map_err(|_| NativeError::Network)
} }
async fn request_login_code(phone: &str) -> Result<LoginToken> { async fn request_login_code(phone: &str) -> Result<LoginToken> {
let client = CLIENT.get().ok_or("Client not initialized")?; let client = CLIENT.get().ok_or(NativeError::Initialization)?;
let token = client.request_login_code(&phone, API_ID, API_HASH).await?; client
Ok(token) .request_login_code(&phone, API_ID, API_HASH)
.await
.map_err(|_| NativeError::Network)
} }
async fn sign_in(token: LoginToken, code: &str) -> Result<()> { async fn sign_in(token: LoginToken, code: &str) -> Result<()> {
let client = CLIENT.get().ok_or("Client not initialized")?; let client = CLIENT.get().ok_or(NativeError::Initialization)?;
client.sign_in(&token, &code).await?; client
.sign_in(&token, &code)
.await
.map_err(|_| NativeError::Network)?;
let guard = DATABASE.lock().unwrap(); let guard = DATABASE.lock().unwrap();
let conn = match guard.as_ref() { let conn = match guard.as_ref() {
Some(c) => c, Some(c) => c,
None => { None => {
return Err("Database was not initialized".into()); error!("Database was not initialized");
return Err(NativeError::Initialization);
} }
}; };
let mut session = db::create_session(conn)?; let mut session = db::create_session(conn).map_err(|_| NativeError::Database)?;
let s = client.session(); let s = client.session();
if let Some(user) = s.get_user() { if let Some(user) = s.get_user() {
session.user_id = Some(user.id); session.user_id = Some(user.id);
@ -211,79 +242,67 @@ async fn sign_in(token: LoginToken, code: &str) -> Result<()> {
} }
} }
db::update_session(conn, &session)?; db::update_session(conn, &session).map_err(|_| NativeError::Database)?;
Ok(()) Ok(())
} }
async fn get_dialogs() -> Result<Vec<OgDialog>> { async fn get_dialogs() -> Result<Vec<OgDialog>> {
let client = CLIENT.get().ok_or("Client not initialized")?; let client = CLIENT.get().ok_or(NativeError::Initialization)?;
let mut result = Vec::new(); let mut result = Vec::new();
let mut dialogs = client.iter_dialogs(); let mut dialogs = client.iter_dialogs();
while let Some(dialog) = dialogs.next().await? { while let Some(dialog) = dialogs.next().await.map_err(|_| NativeError::Network)? {
result.push(dialog); result.push(dialog);
} }
Ok(result) Ok(result)
} }
async fn send_message(chat: PackedChat, text: &str) -> Result<()> { async fn send_message(chat: PackedChat, text: &str) -> Result<()> {
let client = CLIENT.get().ok_or("Client not initialized")?; let client = CLIENT.get().ok_or(NativeError::Initialization)?;
client.send_message(chat, text).await?; client
.send_message(chat, text)
.await
.map_err(|_| NativeError::Network)?;
Ok(()) Ok(())
} }
pub fn initDatabase(path: String) { pub fn initDatabase(path: String) -> Result<()> {
let mut guard = DATABASE.lock().unwrap(); let mut guard = DATABASE.lock().unwrap();
if guard.is_some() { if guard.is_some() {
info!("Database is already initialized"); info!("Database is already initialized");
} }
match db::init_connection(&path) { match db::init_connection(&path) {
Ok(conn) => *guard = Some(conn), Ok(conn) => {
Err(e) => error!("Failed to initialize database: {}", e), *guard = Some(conn);
} Ok(())
}
pub fn initClient() {
match block_on(init_client()) {
Ok(_) => info!("Client initialized"),
Err(e) => error!("Failed to initialize client: {}", e),
}
}
pub fn needLogin() -> bool {
match block_on(need_login()) {
Ok(login) => login.into(),
Err(e) => {
error!("Failed to check if user is authorized: {}", e);
false.into()
} }
Err(e) => Err(NativeError::Database),
} }
} }
pub fn requestLoginCode(phone: String) -> u64 { pub fn initClient() -> Result<()> {
match block_on(request_login_code(&phone)) { block_on(init_client())
Ok(token) => Box::into_raw(Box::new(token)) as u64,
Err(e) => {
error!("Failed to request login code: {}", e);
0 as u64
}
}
} }
pub fn signIn(token_ptr: u64, code: String) { pub fn needLogin() -> Result<bool> {
block_on(need_login())
}
pub fn requestLoginCode(phone: String) -> Result<u64> {
block_on(request_login_code(&phone)).map(|token| Box::into_raw(Box::new(token)) as u64)
}
pub fn signIn(token_ptr: u64, code: String) -> Result<()> {
let token = unsafe { *Box::from_raw(token_ptr as *mut LoginToken) }; let token = unsafe { *Box::from_raw(token_ptr as *mut LoginToken) };
match block_on(sign_in(token, &code)) { block_on(sign_in(token, &code))
Ok(_) => info!("Sign in success"),
Err(e) => error!("Failed to sign in: {}", e),
}
} }
pub fn getDialogs() -> Vec<Dialog> { pub fn getDialogs() -> Result<Vec<Dialog>> {
match block_on(get_dialogs()) { block_on(get_dialogs()).map(|dialogs| {
Ok(dialogs) => dialogs dialogs
.into_iter() .into_iter()
.map(|d| Dialog { .map(|d| Dialog {
id: d.chat().pack().to_hex(), id: d.chat().pack().to_hex(),
@ -316,18 +335,11 @@ pub fn getDialogs() -> Vec<Dialog> {
tl::enums::Dialog::Folder(f) => f.pinned, tl::enums::Dialog::Folder(f) => f.pinned,
}, },
}) })
.collect(), .collect()
Err(e) => { })
error!("Failed to get dialogs: {}", e);
Vec::new()
}
}
} }
pub fn sendMessage(packed: String, text: String) { pub fn sendMessage(packed: String, text: String) -> Result<()> {
let packed = PackedChat::from_hex(&packed).unwrap(); let packed = PackedChat::from_hex(&packed).unwrap();
match block_on(send_message(packed, &text)) { block_on(send_message(packed, &text))
Ok(_) => info!("Message sent"),
Err(e) => error!("Failed to send message: {}", e),
}
} }

View File

@ -1,3 +1,10 @@
[Error]
enum NativeError {
"Initialization",
"Database",
"Network",
};
enum MessageAck { enum MessageAck {
"Received", "Received",
"Seen", "Seen",
@ -19,11 +26,18 @@ dictionary Dialog {
}; };
namespace talaria { namespace talaria {
[Throws=NativeError]
void initDatabase(string path); void initDatabase(string path);
[Throws=NativeError]
void initClient(); void initClient();
[Throws=NativeError]
boolean needLogin(); boolean needLogin();
[Throws=NativeError]
u64 requestLoginCode(string phone); u64 requestLoginCode(string phone);
[Throws=NativeError]
void signIn(u64 tokenPtr, string code); void signIn(u64 tokenPtr, string code);
[Throws=NativeError]
sequence<Dialog> getDialogs(); sequence<Dialog> getDialogs();
[Throws=NativeError]
void sendMessage(string packed, string text); void sendMessage(string packed, string text);
}; };