use std::collections::BTreeMap;
use chrono::{DateTime, FixedOffset};
use iref::{Iri, IriBuf, UriBuf};
use nonempty_collections::NEVec;
use serde::{Deserialize, Serialize};
use super::serialization::{not_null, one_or_many};
pub const VERIFIABLE_CREDENTIAL_CONTEXT: &Iri =
static_iref::iri!("https://www.w3.org/ns/credentials/v2");
pub const VERIFIABLE_CREDENTIAL_TYPE: &str = "VerifiableCredential";
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))]
pub struct CredentialV2<T>
where
T: VerifiableCredentialSubtype,
{
#[serde(rename = "@context")]
pub contexts: NEVec<IriBuf>,
#[serde(
default,
deserialize_with = "not_null",
skip_serializing_if = "Option::is_none"
)]
pub id: Option<UriBuf>,
#[serde(rename = "type")]
pub types: NEVec<String>,
#[serde(rename = "credentialSubject")]
#[serde(with = "one_or_many")]
pub credential_subjects: NEVec<T>,
pub issuer: UriBuf,
#[serde(rename = "validFrom")]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub valid_from: Option<DateTime<FixedOffset>>,
#[serde(rename = "validUntil")]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub valid_until: Option<DateTime<FixedOffset>>,
#[serde(flatten)]
pub extra_properties: BTreeMap<String, serde_json::Value>,
}
impl<T> CredentialV2<T>
where
T: VerifiableCredentialSubtype,
{
pub fn new(id: Option<UriBuf>, issuer: UriBuf, credential_subjects: NEVec<T>) -> Self {
let mut extra_contexts: Vec<IriBuf> = credential_subjects
.first()
.required_contexts()
.iter()
.map(|context_| context_.to_owned().to_owned())
.collect();
let mut contexts: NEVec<IriBuf> = NEVec::new(VERIFIABLE_CREDENTIAL_CONTEXT.to_owned());
contexts.append(&mut extra_contexts);
let mut extra_types = credential_subjects
.first()
.required_types()
.iter()
.map(|type_| type_.to_string())
.collect();
let mut types: NEVec<String> = NEVec::new(VERIFIABLE_CREDENTIAL_TYPE.to_owned());
types.append(&mut extra_types);
Self {
contexts,
id,
types,
issuer,
credential_subjects,
valid_from: None,
valid_until: None,
extra_properties: BTreeMap::<String, serde_json::Value>::default(),
}
}
}
pub trait VerifiableCredentialSubtype {
fn required_contexts(&self) -> &[&'static Iri];
fn required_types(&self) -> &[&'static str];
}