use chrono::{DateTime, Utc};
use peanuts::{Element, FromElement, IntoElement, XML_NS};
pub const XMLNS: &str = "http://docs.oasis-open.org/ns/xri/xrd-1.0";
pub const SIGNATURE_XMLNS: &str = "http://www.w3.org/2000/09/xmldsig#";
#[derive(Debug, Clone)]
pub struct XRD {
pub id: Option<String>,
pub expires: Option<Expires>,
pub subject: Option<Subject>,
pub aliases: Vec<Alias>,
pub properties: Vec<Property>,
pub links: Vec<Link>,
pub signature: Vec<Signature>,
}
impl FromElement for XRD {
fn from_element(mut element: peanuts::Element) -> peanuts::DeserializeResult<Self> {
element.check_name("XRD")?;
element.check_namespace(XMLNS)?;
let id = element.attribute_opt_namespaced("id", XML_NS)?;
let expires = element.child_opt()?;
let subject = element.child_opt()?;
let aliases = element.children()?;
let properties = element.children()?;
let links = element.children()?;
let signature = element.children()?;
Ok(Self {
id,
expires,
subject,
aliases,
properties,
links,
signature,
})
}
}
impl IntoElement for XRD {
fn builder(&self) -> peanuts::ElementBuilder {
Element::builder("XRD", Some(XMLNS))
.push_attribute_opt_namespaced(XML_NS, "id", self.id.clone())
.push_child_opt(self.expires.clone())
.push_child_opt(self.subject.clone())
.push_children(self.aliases.clone())
.push_children(self.properties.clone())
.push_children(self.links.clone())
.push_children(self.signature.clone())
}
}
#[derive(Debug, Clone)]
pub struct Expires(pub DateTime<Utc>);
impl FromElement for Expires {
fn from_element(mut element: Element) -> peanuts::DeserializeResult<Self> {
element.check_name("Expires")?;
element.check_namespace(XMLNS)?;
Ok(Self(element.pop_value()?))
}
}
impl IntoElement for Expires {
fn builder(&self) -> peanuts::ElementBuilder {
Element::builder("Expires", Some(XMLNS))
.push_text(self.0.format("%C%y-%m-%dT%H:%M:%S%.3f%:z"))
}
}
// anyURI
#[derive(Debug, Clone)]
pub struct Subject(pub String);
impl FromElement for Subject {
fn from_element(mut element: Element) -> peanuts::DeserializeResult<Self> {
element.check_name("Subject")?;
element.check_namespace(XMLNS)?;
Ok(Self(element.pop_value()?))
}
}
impl IntoElement for Subject {
fn builder(&self) -> peanuts::ElementBuilder {
Element::builder("Subject", Some(XMLNS)).push_text(self.0.clone())
}
}
// anyURI
#[derive(Debug, Clone)]
pub struct Alias(pub String);
impl FromElement for Alias {
fn from_element(mut element: Element) -> peanuts::DeserializeResult<Self> {
element.check_name("Alias")?;
element.check_namespace(XMLNS)?;
Ok(Self(element.pop_value()?))
}
}
impl IntoElement for Alias {
fn builder(&self) -> peanuts::ElementBuilder {
Element::builder("Alias", Some(XMLNS)).push_text(self.0.clone())
}
}
#[derive(Debug, Clone)]
pub struct Property {
pub r#type: String,
pub property: Option<String>,
}
impl FromElement for Property {
fn from_element(mut element: Element) -> peanuts::DeserializeResult<Self> {
element.check_name("Property")?;
element.check_name(XMLNS)?;
let r#type = element.attribute("type")?;
let property = element.pop_value_opt()?;
Ok(Self { r#type, property })
}
}
impl IntoElement for Property {
fn builder(&self) -> peanuts::ElementBuilder {
Element::builder("Property", Some(XMLNS))
.push_attribute("type", self.r#type.clone())
.push_text_opt(self.property.clone())
}
}
#[derive(Debug, Clone)]
pub struct Link {
pub rel: Option<String>,
pub r#type: Option<String>,
pub href: Option<String>,
pub template: Option<String>,
pub titles: Vec<Title>,
pub properties: Vec<Property>,
}
impl FromElement for Link {
fn from_element(mut element: Element) -> peanuts::DeserializeResult<Self> {
element.check_name("Link")?;
element.check_namespace(XMLNS)?;
let rel = element.attribute_opt("rel")?;
let r#type = element.attribute_opt("type")?;
let href = element.attribute_opt("href")?;
let template = element.attribute_opt("template")?;
let titles = element.children()?;
let properties = element.children()?;
Ok(Self {
rel,
r#type,
href,
template,
titles,
properties,
})
}
}
impl IntoElement for Link {
fn builder(&self) -> peanuts::ElementBuilder {
Element::builder("Link", Some(XMLNS))
.push_attribute_opt("rel", self.rel.clone())
.push_attribute_opt("type", self.r#type.clone())
.push_attribute_opt("href", self.href.clone())
.push_attribute_opt("template", self.template.clone())
.push_children(self.titles.clone())
.push_children(self.properties.clone())
}
}
#[derive(Debug, Clone)]
pub struct Title {
pub lang: Option<String>,
pub title: String,
}
impl FromElement for Title {
fn from_element(mut element: Element) -> peanuts::DeserializeResult<Self> {
element.check_name("Title")?;
element.check_namespace(XMLNS)?;
let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
let title = element.pop_value_opt()?.unwrap_or_default();
Ok(Self { lang, title })
}
}
impl IntoElement for Title {
fn builder(&self) -> peanuts::ElementBuilder {
Element::builder("Title", Some(XMLNS))
.push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
.push_text(self.title.clone())
}
}
#[derive(Debug, Clone, Copy)]
pub struct Signature;
impl FromElement for Signature {
fn from_element(element: Element) -> peanuts::DeserializeResult<Self> {
element.check_name("Signature")?;
element.check_namespace(SIGNATURE_XMLNS)?;
Ok(Self)
}
}
impl IntoElement for Signature {
fn builder(&self) -> peanuts::ElementBuilder {
Element::builder("Signature", Some(SIGNATURE_XMLNS))
}
}