use crate::Error;
use serde_json::json;
use serde_json::Value;
use std::fmt::Display;
#[derive(Debug, Clone, Eq)]
pub struct Disclosure {
pub salt: String,
pub claim_name: Option<String>,
pub claim_value: Value,
unparsed: String,
}
impl AsRef<str> for Disclosure {
fn as_ref(&self) -> &str {
&self.unparsed
}
}
impl Disclosure {
pub(crate) fn new(salt: String, claim_name: Option<String>, claim_value: Value) -> Self {
let string_encoded = {
let json_input = if let Some(name) = claim_name.as_deref() {
json!([salt, name, claim_value])
} else {
json!([salt, claim_value])
};
multibase::Base::Base64Url.encode(json_input.to_string())
};
Self {
salt,
claim_name,
claim_value,
unparsed: string_encoded,
}
}
pub fn parse(disclosure: &str) -> Result<Self, Error> {
let decoded: Vec<Value> = multibase::Base::Base64Url
.decode(disclosure)
.map_err(|_e| {
Error::InvalidDisclosure(format!(
"Base64 decoding of the disclosure was not possible {}",
disclosure
))
})
.and_then(|data| {
serde_json::from_slice(&data).map_err(|_e| {
Error::InvalidDisclosure(format!(
"decoded disclosure could not be serialized as an array {}",
disclosure
))
})
})?;
if decoded.len() == 2 {
Ok(Self {
salt: decoded
.first()
.ok_or(Error::InvalidDisclosure("invalid salt".to_string()))?
.as_str()
.ok_or(Error::InvalidDisclosure(
"salt could not be parsed as a string".to_string(),
))?
.to_owned(),
claim_name: None,
claim_value: decoded
.get(1)
.ok_or(Error::InvalidDisclosure("invalid claim name".to_string()))?
.clone(),
unparsed: disclosure.to_string(),
})
} else if decoded.len() == 3 {
Ok(Self {
salt: decoded
.first()
.ok_or(Error::InvalidDisclosure("invalid salt".to_string()))?
.as_str()
.ok_or(Error::InvalidDisclosure(
"salt could not be parsed as a string".to_string(),
))?
.to_owned(),
claim_name: Some(
decoded
.get(1)
.ok_or(Error::InvalidDisclosure("invalid claim name".to_string()))?
.as_str()
.ok_or(Error::InvalidDisclosure(
"claim name could not be parsed as a string".to_string(),
))?
.to_owned(),
),
claim_value: decoded
.get(2)
.ok_or(Error::InvalidDisclosure("invalid claim name".to_string()))?
.clone(),
unparsed: disclosure.to_string(),
})
} else {
Err(Error::InvalidDisclosure(format!(
"deserialized array has an invalid length of {}",
decoded.len()
)))
}
}
pub fn as_str(&self) -> &str {
self.as_ref()
}
}
impl Display for Disclosure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.unparsed)
}
}
impl PartialEq for Disclosure {
fn eq(&self, other: &Self) -> bool {
self.claim_name == other.claim_name && self.salt == other.salt && self.claim_value == other.claim_value
}
}
#[cfg(test)]
mod test {
use super::Disclosure;
#[test]
fn test_parsing() {
let disclosure = Disclosure::new(
"2GLC42sKQveCfGfryNRN9w".to_string(),
Some("time".to_owned()),
"2012-04-23T18:25Z".to_owned().into(),
);
let parsed =
Disclosure::parse("WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgInRpbWUiLCAiMjAxMi0wNC0yM1QxODoyNVoiXQ").unwrap();
assert_eq!(parsed, disclosure);
}
}