diff options
author | 2025-04-30 14:41:40 +0100 | |
---|---|---|
committer | 2025-04-30 16:15:15 +0100 | |
commit | 74e5ab6702bbbe292fcb38ccb97036ce5b376956 (patch) | |
tree | 54416ab9cb9794a46f42a56a38e0a05abf354ae4 | |
parent | 1f1f267df88638229f09b875e72fd118c08a7ae7 (diff) | |
download | luz-74e5ab6702bbbe292fcb38ccb97036ce5b376956.tar.gz luz-74e5ab6702bbbe292fcb38ccb97036ce5b376956.tar.bz2 luz-74e5ab6702bbbe292fcb38ccb97036ce5b376956.zip |
feat(stanza): XRD
-rw-r--r-- | stanza/Cargo.toml | 1 | ||||
-rw-r--r-- | stanza/src/lib.rs | 2 | ||||
-rw-r--r-- | stanza/src/xep_0156.rs | 233 |
3 files changed, 236 insertions, 0 deletions
diff --git a/stanza/Cargo.toml b/stanza/Cargo.toml index 2832ed6..54577ff 100644 --- a/stanza/Cargo.toml +++ b/stanza/Cargo.toml @@ -20,6 +20,7 @@ xep_0084 = [] xep_0115 = [] xep_0128 = ["xep_0004"] xep_0131 = [] +xep_0156 = ["dep:chrono"] xep_0172 = [] xep_0199 = [] xep_0203 = ["dep:chrono"] diff --git a/stanza/src/lib.rs b/stanza/src/lib.rs index 5e30cd0..8f8d430 100644 --- a/stanza/src/lib.rs +++ b/stanza/src/lib.rs @@ -25,6 +25,8 @@ pub mod xep_0084; pub mod xep_0115; #[cfg(feature = "xep_0131")] pub mod xep_0131; +#[cfg(feature = "xep_0156")] +pub mod xep_0156; #[cfg(feature = "xep_0172")] pub mod xep_0172; #[cfg(feature = "xep_0199")] diff --git a/stanza/src/xep_0156.rs b/stanza/src/xep_0156.rs new file mode 100644 index 0000000..ad22f7e --- /dev/null +++ b/stanza/src/xep_0156.rs @@ -0,0 +1,233 @@ +use chrono::{DateTime, Utc}; +use peanuts::{ + element::{FromElement, IntoElement}, + Element, 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::element::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::element::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::element::DeserializeResult<Self> { + element.check_name("Expires")?; + element.check_namespace(XMLNS)?; + + Ok(Self(element.pop_value()?)) + } +} + +impl IntoElement for Expires { + fn builder(&self) -> peanuts::element::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::element::DeserializeResult<Self> { + element.check_name("Subject")?; + element.check_namespace(XMLNS)?; + + Ok(Self(element.pop_value()?)) + } +} + +impl IntoElement for Subject { + fn builder(&self) -> peanuts::element::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::element::DeserializeResult<Self> { + element.check_name("Alias")?; + element.check_namespace(XMLNS)?; + + Ok(Self(element.pop_value()?)) + } +} + +impl IntoElement for Alias { + fn builder(&self) -> peanuts::element::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::element::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::element::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::element::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::element::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::element::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::element::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::element::DeserializeResult<Self> { + element.check_name("Signature")?; + element.check_namespace(SIGNATURE_XMLNS)?; + + Ok(Self) + } +} + +impl IntoElement for Signature { + fn builder(&self) -> peanuts::element::ElementBuilder { + Element::builder("Signature", Some(SIGNATURE_XMLNS)) + } +} |