use std::str::FromStr; use peanuts::{ element::{FromElement, IntoElement}, DeserializeError, Element, }; pub const XMLNS: &str = "jabber:x:data"; #[derive(Debug, Clone)] pub struct X { pub r#type: XType, pub instructions: Vec, pub title: Option, pub fields: Vec<Field>, pub reported: Option<Reported>, pub items: Vec<Item>, } impl FromElement for X { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { element.check_name("x")?; element.check_namespace(XMLNS)?; let r#type = element.attribute("type")?; let instructions = element.pop_children()?; let title = element.pop_child_opt()?; let fields = element.pop_children()?; let reported = element.pop_child_opt()?; let items = element.pop_children()?; Ok(Self { r#type, instructions, title, fields, reported, items, }) } } impl IntoElement for X { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("x", Some(XMLNS)) .push_attribute("type", self.r#type.clone()) .push_children(self.instructions.clone()) .push_child_opt(self.title.clone()) .push_children(self.fields.clone()) .push_child_opt(self.reported.clone()) .push_children(self.items.clone()) } } #[derive(Debug, Clone)] pub enum XType { Cancel, Form, Result, Submit, } impl FromStr for XType { type Err = DeserializeError; fn from_str(s: &str) -> Result<Self, Self::Err> { match s { "cancel" => Ok(Self::Cancel), "form" => Ok(Self::Form), "result" => Ok(Self::Result), "submit" => Ok(Self::Submit), s => Err(DeserializeError::FromStr(s.to_string())), } } } impl ToString for XType { fn to_string(&self) -> String { match self { XType::Cancel => "cancel", XType::Form => "form", XType::Result => "result", XType::Submit => "submit", } .to_owned() } } #[derive(Debug, Clone)] pub struct Instructions(pub String); impl FromElement for Instructions { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { element.check_name("instructions")?; element.check_namespace(XMLNS)?; Ok(Self(element.pop_value_opt()?.unwrap_or_default())) } } impl IntoElement for Instructions { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("instructions", Some(XMLNS)).push_text(self.0.clone()) } } #[derive(Debug, Clone)] pub struct Title(pub String); impl FromElement for Title { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { element.check_name("instructions")?; element.check_namespace(XMLNS)?; Ok(Self(element.pop_value_opt()?.unwrap_or_default())) } } impl IntoElement for Title { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("title", Some(XMLNS)).push_text(self.0.clone()) } } #[derive(Debug, Clone)] pub struct Field { pub label: Option<String>, pub r#type: Option<FieldType>, pub var: Option<String>, pub desc: Option<Desc>, pub required: bool, pub values: Vec<Value>, pub options: Vec<XOption>, } impl FromElement for Field { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { element.check_name("field")?; element.check_namespace(XMLNS)?; let label = element.attribute_opt("label")?; let r#type = element.attribute_opt("type")?; let var = element.attribute_opt("var")?; let desc = element.pop_child_opt()?; let required; if let Some(_) = element.pop_child_opt::<Required>()? { required = true; } else { required = false; } let values = element.pop_children()?; let options = element.pop_children()?; Ok(Self { label, r#type, var, desc, required, values, options, }) } } impl IntoElement for Field { fn builder(&self) -> peanuts::element::ElementBuilder { let mut builder = Element::builder("field", Some(XMLNS)) .push_attribute_opt("label", self.label.clone()) .push_attribute_opt("type", self.r#type.clone()) .push_attribute_opt("var", self.var.clone()) .push_child_opt(self.desc.clone()); if self.required { builder = builder.push_child(Required) } builder .push_children(self.values.clone()) .push_children(self.options.clone()) } } #[derive(Debug, Clone)] pub enum FieldType { Boolean, Fixed, Hidden, JIDMulti, JIDSingle, ListMulti, ListSingle, TextMulti, TextPrivate, TextSingle, } impl FromStr for FieldType { type Err = DeserializeError; fn from_str(s: &str) -> Result<Self, Self::Err> { match s { "boolean" => Ok(Self::Boolean), "fixed" => Ok(Self::Fixed), "hidden" => Ok(Self::Hidden), "jid-multi" => Ok(Self::JIDMulti), "jid-single" => Ok(Self::JIDSingle), "list-multi" => Ok(Self::ListMulti), "list-single" => Ok(Self::ListSingle), "text-multi" => Ok(Self::TextMulti), "text-private" => Ok(Self::TextPrivate), "text-single" => Ok(Self::TextSingle), s => Err(DeserializeError::FromStr(s.to_string())), } } } impl ToString for FieldType { fn to_string(&self) -> String { match self { FieldType::Boolean => "boolean", FieldType::Fixed => "fixed", FieldType::Hidden => "hidden", FieldType::JIDMulti => "jid-multi", FieldType::JIDSingle => "jid-single", FieldType::ListMulti => "list-multi", FieldType::ListSingle => "list-single", FieldType::TextMulti => "text-multi", FieldType::TextPrivate => "text-private", FieldType::TextSingle => "text-single", } .to_owned() } } #[derive(Debug, Clone)] pub struct Desc(pub String); impl FromElement for Desc { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { element.check_name("desc")?; element.check_namespace(XMLNS)?; Ok(Self(element.pop_value_opt()?.unwrap_or_default())) } } impl IntoElement for Desc { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("desc", Some(XMLNS)).push_text(self.0.clone()) } } #[derive(Debug, Clone)] pub struct Required; impl FromElement for Required { fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> { element.check_name("required")?; element.check_namespace(XMLNS)?; Ok(Self) } } impl IntoElement for Required { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("required", Some(XMLNS)) } } #[derive(Debug, Clone)] pub struct XOption { pub label: Option<String>, pub value: Value, } impl FromElement for XOption { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { element.check_name("option")?; element.check_namespace(XMLNS)?; let label = element.attribute_opt("label")?; let value = element.pop_child_one()?; Ok(Self { label, value }) } } impl IntoElement for XOption { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("option", Some(XMLNS)) .push_attribute_opt("label", self.label.clone()) .push_child(self.value.clone()) } } #[derive(Debug, Clone)] pub struct Value(pub String); impl FromElement for Value { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { element.check_name("value")?; element.check_namespace(XMLNS)?; Ok(Self(element.pop_value_opt()?.unwrap_or_default())) } } impl IntoElement for Value { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("value", Some(XMLNS)).push_text(self.0.clone()) } } #[derive(Debug, Clone)] pub struct Reported { /// fields SHOULD NOT contain value children fields: Vec<Field>, } impl FromElement for Reported { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { element.check_name("reported")?; element.check_namespace(XMLNS)?; let fields = element.pop_children()?; Ok(Self { fields }) } } impl IntoElement for Reported { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("reported", Some(XMLNS)).push_children(self.fields.clone()) } } #[derive(Debug, Clone)] pub struct Item { fields: Vec<Field>, } impl FromElement for Item { fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { element.check_name("item")?; element.check_namespace(XMLNS)?; let fields = element.pop_children()?; Ok(Self { fields }) } } impl IntoElement for Item { fn builder(&self) -> peanuts::element::ElementBuilder { Element::builder("item", Some(XMLNS)).push_children(self.fields.clone()) } }