Improve error handling with anyhow

This commit is contained in:
Jez Cope 2021-07-03 16:49:54 +01:00
parent 6c2f601300
commit 33eb0c71f8
5 changed files with 51 additions and 17 deletions

7
Cargo.lock generated
View File

@ -63,6 +63,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "anyhow"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61"
[[package]] [[package]]
name = "assign" name = "assign"
version = "1.1.1" version = "1.1.1"
@ -1218,6 +1224,7 @@ dependencies = [
name = "mxadm" name = "mxadm"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"clap", "clap",
"directories", "directories",
"lazy_static", "lazy_static",

View File

@ -15,3 +15,4 @@ serde = "1.0"
serde-lexpr = "0.1.0" serde-lexpr = "0.1.0"
directories = "3.0" directories = "3.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
anyhow = "1.0.41"

View File

@ -1,3 +1,4 @@
use anyhow::{Context, Result};
use matrix_sdk::{ use matrix_sdk::{
ruma::{api::client::r0::alias, RoomAliasId, RoomId, UserId}, ruma::{api::client::r0::alias, RoomAliasId, RoomId, UserId},
Client, Session, SyncSettings, Client, Session, SyncSettings,
@ -8,7 +9,7 @@ use std::io::{self, Write};
use crate::session::{build_client_config, restore_session, save_session}; use crate::session::{build_client_config, restore_session, save_session};
type CommandResult = Result<(), Box<dyn std::error::Error>>; type CommandResult = Result<()>;
macro_rules! room_fmt { macro_rules! room_fmt {
() => { () => {
@ -18,7 +19,8 @@ macro_rules! room_fmt {
pub async fn login(username: Option<&str>) -> CommandResult { pub async fn login(username: Option<&str>) -> CommandResult {
let user_id = match username { let user_id = match username {
Some(s) => UserId::try_from(s)?, Some(s) => UserId::try_from(s)
.with_context(|| format!("Failed to parse '{}' as User ID", username))?,
None => { None => {
let mut s = String::new(); let mut s = String::new();
@ -27,17 +29,20 @@ pub async fn login(username: Option<&str>) -> CommandResult {
let stdin = io::stdin(); let stdin = io::stdin();
stdin.read_line(&mut s)?; stdin.read_line(&mut s)?;
UserId::try_from(s.trim_end())? UserId::try_from(s.trim_end())
.with_context(|| format!("Failed to parse '{}' as User ID", s))?
} }
}; };
let password = prompt_password_stderr("Password: ")?; let password = prompt_password_stderr("Password: ")?;
let client = let client = Client::new_from_user_id_with_config(user_id.clone(), build_client_config())
Client::new_from_user_id_with_config(user_id.clone(), build_client_config()).await?; .await
.context("Unable to initialise client")?;
let response = client let response = client
.login(user_id.localpart(), &password, None, Some("mxadm")) .login(user_id.localpart(), &password, None, Some("mxadm"))
.await?; .await
.context("Login failed")?;
println!( println!(
"{} logged in? {}", "{} logged in? {}",
client.homeserver().await, client.homeserver().await,
@ -81,7 +86,10 @@ pub async fn list_rooms() -> CommandResult {
print!("Syncing..."); print!("Syncing...");
io::stderr().flush().unwrap(); io::stderr().flush().unwrap();
client.sync_once(sync_settings).await?; client
.sync_once(sync_settings)
.await
.context("Sync failed")?;
println!(" done"); println!(" done");
println!("Joined rooms:"); println!("Joined rooms:");
@ -100,8 +108,10 @@ pub async fn list_rooms() -> CommandResult {
} }
pub async fn add_alias(room_id: &str, alias: &str) -> CommandResult { pub async fn add_alias(room_id: &str, alias: &str) -> CommandResult {
let room_id = RoomId::try_from(room_id)?; let room_id = RoomId::try_from(room_id)
let alias_id = RoomAliasId::try_from(alias)?; .with_context(|| format!("Failed to parse '{}' as room ID", room_id))?;
let alias_id = RoomAliasId::try_from(alias)
.with_context(|| format!("Failed to parse '{}' as room alias", alias))?;
let client = restore_session().await?; let client = restore_session().await?;
let request = alias::create_alias::Request::new(&alias_id, &room_id); let request = alias::create_alias::Request::new(&alias_id, &room_id);
@ -111,11 +121,15 @@ pub async fn add_alias(room_id: &str, alias: &str) -> CommandResult {
} }
pub async fn del_alias(alias: &str) -> CommandResult { pub async fn del_alias(alias: &str) -> CommandResult {
let alias_id = RoomAliasId::try_from(alias)?; let alias_id = RoomAliasId::try_from(alias)
.with_context(|| format!("Failed to parse '{}' as room alias", alias))?;
let client = restore_session().await?; let client = restore_session().await?;
let request = alias::delete_alias::Request::new(&alias_id); let request = alias::delete_alias::Request::new(&alias_id);
client.send(request, None).await?; client
.send(request, None)
.await
.with_context(|| format!("Failed to delete alias '{}'", alias))?;
Ok(()) Ok(())
} }

View File

@ -2,14 +2,17 @@
extern crate clap; extern crate clap;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
#[macro_use]
extern crate anyhow;
mod commands; mod commands;
mod session; mod session;
use anyhow::Result;
use clap::{App, SubCommand}; use clap::{App, SubCommand};
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<()> {
let matches = App::new(crate_name!()) let matches = App::new(crate_name!())
.version(crate_version!()) .version(crate_version!())
.about(crate_description!()) .about(crate_description!())
@ -55,7 +58,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
("del-alias", Some(submatches)) => { ("del-alias", Some(submatches)) => {
commands::del_alias(submatches.value_of("alias").unwrap()).await? commands::del_alias(submatches.value_of("alias").unwrap()).await?
} }
("", None) => eprintln!("No subcommand given"), ("", None) => bail!("No subcommand given"),
(c, _) => { (c, _) => {
todo!("Subcommand '{}' not implemented yet!", c); todo!("Subcommand '{}' not implemented yet!", c);
} }

View File

@ -1,7 +1,9 @@
use anyhow::{Context, Result};
use directories::ProjectDirs; use directories::ProjectDirs;
use matrix_sdk::{Client, ClientConfig, Session}; use matrix_sdk::{Client, ClientConfig, Session};
use serde_lexpr; use serde_lexpr;
use std::fs::{create_dir_all, File}; use std::fs::{create_dir_all, File};
use std::io::ErrorKind;
lazy_static! { lazy_static! {
static ref PROJECT_DIRS: ProjectDirs = ProjectDirs::from("me", "petrichor", "mxadm") static ref PROJECT_DIRS: ProjectDirs = ProjectDirs::from("me", "petrichor", "mxadm")
@ -13,18 +15,25 @@ pub fn build_client_config() -> ClientConfig {
ClientConfig::new().store_path(PROJECT_DIRS.cache_dir().join("store")) ClientConfig::new().store_path(PROJECT_DIRS.cache_dir().join("store"))
} }
pub fn save_session(session: Session) -> Result<(), Box<dyn std::error::Error>> { pub fn save_session(session: Session) -> Result<()> {
let cache_dir = PROJECT_DIRS.cache_dir(); let cache_dir = PROJECT_DIRS.cache_dir();
if !cache_dir.exists() { if !cache_dir.exists() {
create_dir_all(cache_dir)?; create_dir_all(cache_dir)
.with_context(|| format!("Failed to create cache directory {}", cache_dir.display()))?;
} }
let mut session_file = File::create(cache_dir.join(SESSION_FILE))?; let mut session_file = File::create(cache_dir.join(SESSION_FILE))?;
serde_lexpr::to_writer(&mut session_file, &session)?; serde_lexpr::to_writer(&mut session_file, &session)?;
Ok(()) Ok(())
} }
pub async fn restore_session() -> Result<Client, Box<dyn std::error::Error>> { pub async fn restore_session() -> Result<Client> {
let session_file = File::open(PROJECT_DIRS.cache_dir().join(SESSION_FILE))?; let session_file = match File::open(PROJECT_DIRS.cache_dir().join(SESSION_FILE)) {
Err(e) => match e.kind() {
ErrorKind::NotFound => bail!("Session file not found: try `mxadm login` first"),
_ => return Err(e).context("Unable to open session file"),
},
Ok(f) => f,
};
let session_info: Session = serde_lexpr::from_reader(session_file)?; let session_info: Session = serde_lexpr::from_reader(session_file)?;
let client = let client =