use core::fmt::Display;
use core::fmt::Formatter;
use serde::Deserialize;
use serde::Serialize;
use identity_core::common::Context;
use identity_core::common::Object;
use identity_core::common::OneOrMany;
use identity_core::common::Url;
use identity_core::convert::FmtJson;
use identity_core::convert::ToJson;
use crate::credential::Credential;
use crate::credential::Policy;
use crate::credential::Proof;
use crate::credential::RefreshService;
use crate::error::Error;
use crate::error::Result;
use super::jwt_serialization::PresentationJwtClaims;
use super::JwtPresentationOptions;
use super::PresentationBuilder;
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct Presentation<CRED, T = Object> {
#[serde(rename = "@context")]
pub context: OneOrMany<Context>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<Url>,
#[serde(rename = "type")]
pub types: OneOrMany<String>,
#[serde(default = "Default::default", rename = "verifiableCredential")]
pub verifiable_credential: OneOrMany<CRED>,
pub holder: Url,
#[serde(default, rename = "refreshService", skip_serializing_if = "OneOrMany::is_empty")]
pub refresh_service: OneOrMany<RefreshService>,
#[serde(default, rename = "termsOfUse", skip_serializing_if = "OneOrMany::is_empty")]
pub terms_of_use: OneOrMany<Policy>,
#[serde(flatten)]
pub properties: T,
#[serde(skip_serializing_if = "Option::is_none")]
pub proof: Option<Proof>,
}
impl<CRED, T> Presentation<CRED, T> {
pub fn base_context() -> &'static Context {
Credential::<Object>::base_context()
}
pub const fn base_type() -> &'static str {
"VerifiablePresentation"
}
pub fn builder(holder: Url, properties: T) -> PresentationBuilder<CRED, T> {
PresentationBuilder::new(holder, properties)
}
pub fn from_builder(builder: PresentationBuilder<CRED, T>) -> Result<Self> {
let this: Self = Self {
context: builder.context.into(),
id: builder.id,
types: builder.types.into(),
verifiable_credential: builder.credentials.into(),
holder: builder.holder,
refresh_service: builder.refresh_service.into(),
terms_of_use: builder.terms_of_use.into(),
properties: builder.properties,
proof: None,
};
this.check_structure()?;
Ok(this)
}
pub fn check_structure(&self) -> Result<()> {
match self.context.get(0) {
Some(context) if context == Self::base_context() => {}
Some(_) | None => return Err(Error::MissingBaseContext),
}
if !self.types.iter().any(|type_| type_ == Self::base_type()) {
return Err(Error::MissingBaseType);
}
Ok(())
}
pub fn serialize_jwt(&self, options: &JwtPresentationOptions) -> Result<String>
where
T: ToOwned<Owned = T> + serde::Serialize + serde::de::DeserializeOwned,
CRED: ToOwned<Owned = CRED> + serde::Serialize + serde::de::DeserializeOwned + Clone,
{
let jwt_representation: PresentationJwtClaims<'_, CRED, T> = PresentationJwtClaims::new(self, options)?;
jwt_representation
.to_json()
.map_err(|err| Error::JwtClaimsSetSerializationError(err.into()))
}
pub fn set_proof(&mut self, proof: Option<Proof>) {
self.proof = proof;
}
}
impl<T> Display for Presentation<T>
where
T: Serialize,
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
self.fmt_json(f)
}
}