My personal project and infrastructure archive
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nomicon/apps/unionbot/src/config.rs

177 lines
4.4 KiB

use crate::fatal;
use elefren::{
data::Data, http_send::HttpSender, registration::Registered, scopes::Scopes, Mastodon,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{
collections::BTreeMap,
fs::{File, OpenOptions},
io::{self, Read, Write},
path::{Path, PathBuf},
};
pub type Location = String;
pub type Name = String;
type InnerMap = BTreeMap<Location, BTreeMap<Name, BTreeMap<String, String>>>;
#[derive(Debug, Deserialize, Serialize)]
pub struct UnionMap {
#[serde(flatten)]
inner: InnerMap,
#[serde(skip)]
path: PathBuf,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Config {
/// The instance URL (example: `botsin.space`)
pub instance: String,
/// The username (example: `bot@beep.boop`)
pub username: String,
/// The required login secret
#[serde(flatten)]
pub secret: Option<Secret>,
/// Union suggestion map
pub unions: Unions,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Unions {
/// The configuration contains a path
Path(PathBuf),
/// Which is then turned into the union map
Map(UnionMap),
}
impl Unions {
fn close(&mut self) {
let path = match self {
Unions::Path(ref path) => path,
Unions::Map(ref map) => &map.path,
};
*self = Unions::Path(path.clone());
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Secret {
pub id: String,
pub secret: String,
pub token: String,
}
impl<'d> From<&'d Data> for Secret {
fn from(d: &'d Data) -> Self {
Self {
id: d.client_id.to_string(),
secret: d.client_secret.to_string(),
token: d.token.to_string(),
}
}
}
fn load_path(p: &Path) -> String {
let mut f = File::open(p)
.unwrap_or_else(|e| fatal!("Failed to open path `{}`: {}", p.to_str().unwrap(), e));
let mut buf = String::new();
f.read_to_string(&mut buf)
.unwrap_or_else(|e| fatal!("Failed to read file: {}", e));
info!("Loaded path {}", p.to_str().unwrap());
buf
}
fn parse_toml<T: DeserializeOwned>(s: String) -> T {
toml::from_str(s.as_str()).unwrap_or_else(|e| fatal!("Failed to parse toml: {}", e))
}
impl Config {
pub fn load(path: &Path) -> Self {
let cfg_buf = load_path(path);
let mut cfg: Config = parse_toml(cfg_buf);
dbg!(&cfg);
// Load the union map from the provided path
cfg.load_unions();
// Return
cfg
}
/// Load the unions from the path
pub fn load_unions(&mut self) {
let union_buf = load_path(self.unions_path());
let inner: InnerMap = parse_toml(union_buf);
// Swap the path for the map
self.unions = Unions::Map(UnionMap {
inner,
path: self.unions_path().to_path_buf(),
});
}
pub fn registration(&self) -> Mastodon<HttpSender> {
let sec = self.assume_secret().clone();
Data {
base: (&self.instance).clone().into(),
client_id: sec.id.into(),
client_secret: sec.secret.into(),
token: sec.token.into(),
redirect: "".into(),
}
.into()
}
pub fn save(&mut self, path: &Path) -> io::Result<()> {
let mut f = OpenOptions::new()
.write(true)
.create(false)
.truncate(true)
.open(path)?;
let string = self.as_toml();
f.write_all(string.as_bytes())?;
Ok(())
}
pub fn as_toml(&mut self) -> String {
self.unions.close();
let string = toml::to_string(self).unwrap();
self.load_unions();
string
}
fn unions_path(&self) -> &Path {
match self.unions {
Unions::Path(ref pathbuf) => pathbuf.as_path(),
_ => unreachable!(),
}
}
fn assume_secret(&self) -> &Secret {
self.secret.as_ref().unwrap()
}
pub fn unions(&self, loc: Location) -> &BTreeMap<Name, BTreeMap<String, String>> {
match self.unions {
Unions::Map(ref map) => map.inner.get(&loc).unwrap(),
_ => unreachable!(),
}
}
/// Update the secret in the configuration
pub fn update_secret<S: Into<Secret>>(&mut self, secret: S) {
self.secret = Some(secret.into());
}
pub fn secret(&self) -> Option<Secret> {
self.secret.clone()
}
}