egui-test/src/matrix.rs

137 lines
3.7 KiB
Rust

//! Utility functions for working matrix-related tasks
use std::{
fs::File,
path::{Path, PathBuf},
};
use matrix_sdk::{
config::ClientConfig,
reqwest::Url,
ruma::{DeviceIdBox, UserId},
Client,
};
use ron::ser::PrettyConfig;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
/// The homeserver URL
pub homeserver: Url,
/// Access token for authentication
pub access_token: String,
/// The user's mxid.
pub user_id: UserId,
/// The user's device ID.
pub device_id: DeviceIdBox,
}
impl Session {
/// Read a `Session` from the filesystem.
pub fn from_fs() -> Result<Self, LoginError> {
let path = path()?.join("session.ron");
let file = File::open(&path)?;
let session = ron::de::from_reader(file)?;
Ok(session)
}
/// Create a matrix client and restore this session.
pub fn restore(self) -> Result<Client, LoginError> {
let session = matrix_sdk::Session {
access_token: self.access_token,
user_id: self.user_id,
device_id: self.device_id,
};
let client = Client::new(self.homeserver)?;
let runtime = tokio::runtime::Runtime::new().unwrap();
runtime.block_on(client.restore_login(session))?;
Ok(client)
}
}
/// Create a matrix client and log it in to the server at the given URL with the
/// given credentials.
pub fn login(url: &str, user: &str, password: &str) -> Result<Client, LoginError> {
let url: Url = if !url.contains("://") {
format!("https://{}", url).parse()
} else {
url.parse()
}?;
let client = Client::new_with_config(url.clone(), config()?)?;
let rt = tokio::runtime::Runtime::new().unwrap();
let response = rt.block_on(client.login(user, password, None, None))?;
let session = Session {
homeserver: url,
access_token: response.access_token,
user_id: response.user_id,
device_id: response.device_id,
};
let file = File::create(path()?.join("session.ron"))?;
ron::ser::to_writer_pretty(file, &session, PrettyConfig::new())?;
Ok(client)
}
/// Errors that can happen when logging in
#[derive(Debug)]
pub enum LoginError {
/// Invalid URL
Url(url::ParseError),
/// Matrix SDK error
Sdk(matrix_sdk::Error),
/// I/O error
Io(std::io::Error),
/// Serialization error
Ron(ron::Error),
}
impl std::fmt::Display for LoginError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LoginError::Url(_) => write!(f, "Invalid homeserver address"),
LoginError::Sdk(e) => write!(f, "{}", e),
LoginError::Io(e) => write!(f, "Filesystem error: {}", e),
LoginError::Ron(e) => write!(f, "Serialization error: {}", e),
}
}
}
impl std::error::Error for LoginError {}
impl From<url::ParseError> for LoginError {
fn from(e: url::ParseError) -> Self {
LoginError::Url(e)
}
}
impl From<matrix_sdk::Error> for LoginError {
fn from(e: matrix_sdk::Error) -> Self {
LoginError::Sdk(e)
}
}
impl From<std::io::Error> for LoginError {
fn from(e: std::io::Error) -> Self {
LoginError::Io(e)
}
}
impl From<ron::Error> for LoginError {
fn from(e: ron::Error) -> Self {
LoginError::Ron(e)
}
}
/// Configuration for `Clients`.
fn config() -> Result<ClientConfig, std::io::Error> {
Ok(ClientConfig::new().store_path(&path()?))
}
/// The path the the sdk store should be put in.
fn path() -> Result<PathBuf, std::io::Error> {
let path = Path::new(&std::env::var_os("HOME").unwrap())
.join(".config")
.join("retrix");
std::fs::create_dir_all(&path)?;
Ok(path)
}