use std::{convert::Infallible, fmt, path::PathBuf};
use crate::{ProfileSource, Region, SsoFlow, VerificationPrompt, CLIENT_NAME};
#[derive(Clone)]
#[allow(clippy::module_name_repetitions)]
pub struct SsoFlowBuilder<S = ProfileSource, V = Infallible> {
cache_dir: Option<PathBuf>,
config_source: S,
verification_prompt: Option<V>,
}
impl SsoFlowBuilder<ProfileSource, Infallible> {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl Default for SsoFlowBuilder<ProfileSource, Infallible> {
fn default() -> Self {
Self {
cache_dir: None,
config_source: ProfileSource::default(),
verification_prompt: None,
}
}
}
impl<S: fmt::Debug, V> fmt::Debug for SsoFlowBuilder<S, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SsoFlowBuilder")
.field("cache_dir", &self.cache_dir)
.field("config_source", &self.config_source)
.field(
"verification_prompt",
if self.verification_prompt.is_some() {
&"Some(_)"
} else {
&"None"
},
)
.finish()
}
}
impl<S, V> SsoFlowBuilder<S, V> {
#[must_use]
pub fn cache_dir(self, path: impl Into<PathBuf>) -> Self {
Self {
cache_dir: Some(path.into()),
..self
}
}
#[must_use]
pub fn config<NewS>(self, config_source: NewS) -> SsoFlowBuilder<NewS, V>
where
S: SsoConfigSource,
{
SsoFlowBuilder {
cache_dir: self.cache_dir,
config_source,
verification_prompt: self.verification_prompt,
}
}
#[must_use]
pub fn verification_prompt<NewV>(self, verification_prompt: NewV) -> SsoFlowBuilder<S, NewV>
where
NewV: VerificationPrompt,
{
SsoFlowBuilder {
cache_dir: self.cache_dir,
config_source: self.config_source,
verification_prompt: Some(verification_prompt),
}
}
}
impl<S, V> SsoFlowBuilder<S, V>
where
S: SsoConfigSource,
V: VerificationPrompt,
{
pub async fn build(self) -> Result<SsoFlow<V>, S::Error> {
let config = self.config_source.load().await?;
let verification_prompt = self
.verification_prompt
.expect("verification_prompt must be set");
Ok(SsoFlow::build(
self.cache_dir.or_else(Self::default_cache_dir),
config,
verification_prompt,
))
}
fn default_cache_dir() -> Option<PathBuf> {
dirs_next::cache_dir().map(|mut path| {
path.push(CLIENT_NAME);
path
})
}
}
pub trait SsoConfigSource {
type Future: std::future::Future<Output = Result<SsoConfig, Self::Error>>;
type Error: std::error::Error + Send + Sync + 'static;
fn load(self) -> Self::Future;
}
#[derive(Clone, Debug, Hash)]
pub struct SsoConfig {
pub region: Region,
pub start_url: String,
pub account_id: String,
pub role_name: String,
}
impl SsoConfigSource for SsoConfig {
type Future = std::future::Ready<Result<Self, Self::Error>>;
type Error = Infallible;
fn load(self) -> Self::Future {
std::future::ready(Ok(self))
}
}