use serde::Serialize;
use std::collections::HashMap;
#[derive(Debug, Clone, Default, Serialize)]
pub struct Claims {
#[serde(skip_serializing_if = "Option::is_none")]
pub iss: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sub: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aud: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exp: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub iat: Option<u64>,
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
impl Claims {
pub fn builder() -> ClaimsBuilder {
ClaimsBuilder::default()
}
}
#[derive(Debug, Default)]
pub struct ClaimsBuilder {
claims: Claims,
}
impl ClaimsBuilder {
#[deprecated(note = "use `issuer` instead")]
pub fn iss(self, value: &str) -> Self {
self.issuer(value)
}
pub fn issuer(mut self, value: &str) -> Self {
self.claims.iss = Some(value.into());
self
}
#[deprecated(note = "use `subject` instead")]
#[allow(clippy::should_implement_trait)]
pub fn sub(self, value: &str) -> Self {
self.subject(value)
}
pub fn subject(mut self, value: &str) -> Self {
self.claims.sub = Some(value.into());
self
}
#[deprecated(note = "use `audience` instead")]
pub fn aud(self, value: &str) -> Self {
self.audience(value)
}
pub fn audience(mut self, value: &str) -> Self {
self.claims.aud = Some(value.into());
self
}
#[deprecated(note = "use `expires_at` instead")]
pub fn exp(self, value: u64) -> Self {
self.expires_at(value)
}
pub fn expires_at(mut self, value: u64) -> Self {
self.claims.exp = Some(value);
self
}
#[deprecated(note = "use `issued_at` instead")]
pub fn iat(self, value: u64) -> Self {
self.issued_at(value)
}
pub fn issued_at(mut self, value: u64) -> Self {
self.claims.iat = Some(value);
self
}
pub fn extra(mut self, key: &str, value: impl Into<serde_json::Value>) -> Self {
self.claims.extra.insert(key.into(), value.into());
self
}
pub fn build(self) -> Claims {
self.claims
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serialize_omits_none_fields() {
let claims = Claims {
iss: Some("test".into()),
exp: Some(1234567890),
..Default::default()
};
let json = serde_json::to_value(&claims).unwrap();
assert_eq!(json["iss"], "test");
assert_eq!(json["exp"], 1234567890);
assert!(json.get("sub").is_none());
assert!(json.get("aud").is_none());
assert!(json.get("iat").is_none());
}
#[test]
fn serialize_flattens_extra() {
let mut claims = Claims::default();
claims.extra.insert(
"scope".into(),
serde_json::Value::String("read write".into()),
);
let json = serde_json::to_value(&claims).unwrap();
assert_eq!(json["scope"], "read write");
}
}