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.
178 lines
4.4 KiB
178 lines
4.4 KiB
3 years ago
|
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()
|
||
|
}
|
||
|
}
|