subscribe-list/src/config.rs
2024-04-08 01:50:25 -07:00

97 lines
2.7 KiB
Rust

use clap::builder::{TryMapValueParser, TypedValueParser, ValueParserFactory};
use clio::CachedInput;
use eyre::Context;
use futures::future::try_join_all;
use indexmap::IndexMap;
use openidconnect::{
core::{CoreClient, CoreProviderMetadata},
ClientId, ClientSecret, IssuerUrl, Scope,
};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Config {
pub oidc: IndexMap<String, OIDCProvider>,
}
impl ValueParserFactory for Config {
type Parser = TryMapValueParser<
<CachedInput as ValueParserFactory>::Parser,
fn(CachedInput) -> eyre::Result<Config>,
>;
fn value_parser() -> Self::Parser {
CachedInput::value_parser().try_map(Config::load)
}
}
impl Config {
pub const EXAMPLE: &'static str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/config.toml.sample"));
pub fn load_str(input: &str, path: impl ToString) -> eyre::Result<Config> {
toml::from_str(input).wrap_err_with(|| path.to_string())
}
pub fn load(input: CachedInput) -> eyre::Result<Config> {
let s = std::str::from_utf8(input.get_data()).wrap_err_with(|| input.path().to_string())?;
toml::from_str(s).wrap_err_with(|| input.path().to_string())
}
pub async fn resolve(&mut self) -> eyre::Result<()> {
try_join_all(self.oidc.iter_mut().map(|(_, provider)| provider.resolve())).await?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct OIDCProviderState {
pub client: CoreClient,
pub provider_metadata: CoreProviderMetadata,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct OIDCProvider {
pub pretty_name: String,
pub issuer_url: IssuerUrl,
pub client_id: ClientId,
pub secret: ClientSecret,
pub scopes: Vec<Scope>,
#[serde(skip)]
state: Option<OIDCProviderState>,
}
impl OIDCProvider {
pub fn state(&self) -> &OIDCProviderState {
self.state.as_ref().expect("resolve called by main")
}
pub async fn resolve(&mut self) -> eyre::Result<()> {
let provider_metadata = CoreProviderMetadata::discover_async(
self.issuer_url.clone(),
openidconnect::reqwest::async_http_client,
)
.await?;
let client = CoreClient::from_provider_metadata(
provider_metadata.clone(),
self.client_id.clone(),
Some(self.secret.clone()),
);
let client = client.disable_openid_scope();
self.state = Some(OIDCProviderState {
client,
provider_metadata,
});
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_example() -> eyre::Result<()> {
Config::load_str(Config::EXAMPLE, "config.toml.sample")?;
Ok(())
}
}