bebot/src/config.rs

131 lines
3.7 KiB
Rust

use std::{collections::HashMap, fmt, fs::File, io::BufReader};
use anyhow::Context;
use matrix_sdk::ruma::{OwnedRoomOrAliasId, OwnedUserId, RoomOrAliasId, UserId};
use serde::de;
#[derive(Deserialize)]
pub struct RepoConfig {
pub token: String,
#[serde(default)]
#[serde(deserialize_with = "deser_optional_room_or_alias_id")]
pub room: Option<OwnedRoomOrAliasId>,
}
#[derive(Deserialize)]
pub struct Config {
pub bind_address: Option<String>,
pub bind_port: Option<u16>,
pub url_prefix: Option<String>,
#[serde(deserialize_with = "deser_user_id")]
pub user_id: OwnedUserId,
pub password: String,
#[serde(default)]
#[serde(deserialize_with = "deser_optional_room_or_alias_id")]
pub default_room: Option<OwnedRoomOrAliasId>,
pub repo_configs: HashMap<String, RepoConfig>, // key is repo url without scheme; e.g.
// gitlab.xfce.org/xfce/xfdesktop
}
fn deser_user_id<'de, D>(deserializer: D) -> Result<OwnedUserId, D::Error>
where
D: de::Deserializer<'de>,
{
struct UserIdVisitor;
impl<'de> de::Visitor<'de> for UserIdVisitor {
type Value = OwnedUserId;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a matrix user ID")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
UserId::parse(v).map_err(E::custom)
}
}
deserializer.deserialize_any(UserIdVisitor)
}
fn deser_room_or_alias_id<'de, D>(deserializer: D) -> Result<OwnedRoomOrAliasId, D::Error>
where
D: de::Deserializer<'de>,
{
struct RoomOrAliasIdVisitor;
impl<'de> de::Visitor<'de> for RoomOrAliasIdVisitor {
type Value = OwnedRoomOrAliasId;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a matrix room ID")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
RoomOrAliasId::parse(v).map_err(E::custom)
}
}
deserializer.deserialize_any(RoomOrAliasIdVisitor)
}
fn deser_optional_room_or_alias_id<'de, D>(deserializer: D) -> Result<Option<OwnedRoomOrAliasId>, D::Error>
where
D: de::Deserializer<'de>,
{
struct OptionalRoomOrAliasIdVisitor;
impl<'de> de::Visitor<'de> for OptionalRoomOrAliasIdVisitor {
type Value = Option<OwnedRoomOrAliasId>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("null or matrix room ID")
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(None)
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Some(deser_room_or_alias_id(deserializer)?))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
RoomOrAliasId::parse(v).map(Some).map_err(E::custom)
}
}
deserializer.deserialize_any(OptionalRoomOrAliasIdVisitor)
}
fn load_blocking(path: &String) -> anyhow::Result<Config> {
let f = File::open(path)?;
let r = BufReader::new(f);
let config: Config = serde_yaml::from_reader(r)?;
Ok(config)
}
pub async fn load<S: AsRef<str>>(path: S) -> anyhow::Result<Config> {
let p = String::from(path.as_ref());
let config = tokio::task::spawn_blocking(move || {
load_blocking(&p).with_context(|| format!("Failed to load config from {}", p))
})
.await??;
Ok(config)
}