pub mod types;
use std::sync::Arc;
use crate::{error::Error, http::HttpClient};
use types::{LoginStart, LoginStatus, TokenIntrospection};
pub struct Passepartout {
http: Arc<HttpClient>,
#[allow(dead_code)]
api_key: String,
}
impl std::fmt::Debug for Passepartout {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Passepartout").finish_non_exhaustive()
}
}
impl Passepartout {
pub fn new(api_key: impl Into<String>) -> Self {
let key = api_key.into();
Self::builder()
.api_key(&key)
.build()
.expect("failed to build Passepartout client")
}
pub fn builder() -> PassepartoutBuilder {
PassepartoutBuilder::default()
}
pub(crate) fn from_http(http: Arc<HttpClient>, api_key: String) -> Self {
Self { http, api_key }
}
pub async fn login_start(&self) -> Result<LoginStart, Error> {
self.http
.post("/v1/passepartout/login/start", &serde_json::json!({}), false)
.await
}
pub async fn login_status(&self, nonce: &str) -> Result<LoginStatus, Error> {
let encoded = urlencode(nonce);
self.http
.get(&format!("/v1/passepartout/login/status?nonce={encoded}"))
.await
}
pub async fn introspect(&self, access_token: &str) -> Result<TokenIntrospection, Error> {
#[derive(serde::Serialize)]
struct IntrospectBody<'a> {
access_token: &'a str,
}
self.http
.post(
"/v1/passepartout/tokens/introspect",
&IntrospectBody { access_token },
false,
)
.await
}
}
fn urlencode(value: &str) -> String {
let mut out = String::with_capacity(value.len());
for byte in value.bytes() {
match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
out.push(byte as char);
}
_ => out.push_str(&format!("%{byte:02X}")),
}
}
out
}
#[derive(Default)]
pub struct PassepartoutBuilder {
api_key: Option<String>,
base_url: Option<String>,
timeout_secs: Option<u64>,
}
impl PassepartoutBuilder {
pub fn api_key(mut self, key: impl Into<String>) -> Self {
self.api_key = Some(key.into());
self
}
pub fn base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = Some(url.into());
self
}
pub fn timeout_secs(mut self, secs: u64) -> Self {
self.timeout_secs = Some(secs);
self
}
pub fn build(self) -> Result<Passepartout, Error> {
let key = self
.api_key
.ok_or_else(|| Error::Config("passepartout API key is required".into()))?;
let http = HttpClient::new(&key, self.base_url, self.timeout_secs)?;
Ok(Passepartout {
http: Arc::new(http),
api_key: key,
})
}
}