use crate::Error;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use std::fmt::Display;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Disclosure {
pub salt: String,
pub claim_name: Option<String>,
pub claim_value: Value,
pub disclosure: String,
}
impl Disclosure {
pub fn new(salt: String, claim_name: Option<String>, claim_value: Value) -> Self {
let input = if let Some(name) = &claim_name {
format!("[\"{}\", \"{}\", {}]", &salt, &name, &claim_value.to_string())
} else {
format!("[\"{}\", {}]", &salt, &claim_value.to_string())
};
let encoded = multibase::Base::Base64Url.encode(input);
Self {
salt,
claim_name,
claim_value,
disclosure: encoded,
}
}
pub fn parse(disclosure: String) -> 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(),
disclosure,
})
} 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(),
disclosure,
})
} else {
Err(Error::InvalidDisclosure(format!(
"deserialized array has an invalid length of {}",
decoded.len()
)))
}
}
pub fn as_str(&self) -> &str {
&self.disclosure
}
pub fn into_string(self) -> String {
self.disclosure
}
}
impl Display for Disclosure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.disclosure)
}
}
#[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".to_owned());
assert_eq!(parsed.unwrap(), disclosure);
}
#[test]
fn test_creating() {
let disclosure = Disclosure::new("lklxF5jMYlGTPUovMNIvCA".to_owned(), None, "US".to_owned().into());
assert_eq!(
"WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIlVTIl0".to_owned(),
disclosure.to_string()
);
}
}