diff --git a/parsers/src/data_forms_validate.rs b/parsers/src/data_forms_validate.rs index 2fc18ce9..8f50fbb0 100644 --- a/parsers/src/data_forms_validate.rs +++ b/parsers/src/data_forms_validate.rs @@ -4,19 +4,18 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::borrow::Cow; use std::fmt::{Display, Formatter}; use std::str::FromStr; -use minidom::{Element, IntoAttributeValue}; -use xso::{ - error::{Error, FromElementError}, - AsXml, FromXml, -}; +use minidom::IntoAttributeValue; +use xso::{error::Error, AsXml, AsXmlText, FromXml, FromXmlText}; -use crate::ns::{self, XDATA_VALIDATE}; +use crate::ns; /// Validation Method -#[derive(Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] +#[xml(namespace = ns::XDATA_VALIDATE)] pub enum Method { /// … to indicate that the value(s) should simply match the field type and datatype constraints, /// the `` element shall contain a `` child element. Using `` validation, @@ -24,6 +23,7 @@ pub enum Method { /// the field type. /// /// + #[xml(name = "basic")] Basic, /// For "list-single" or "list-multi", to indicate that the user may enter a custom value @@ -34,6 +34,7 @@ pub enum Method { /// "list-multi", with no options and all values automatically selected. /// /// + #[xml(name = "open")] Open, /// To indicate that the value should fall within a certain range, the `` element shall @@ -51,10 +52,14 @@ pub enum Method { /// constraints. /// /// + #[xml(name = "range")] Range { /// The 'min' attribute specifies the minimum allowable value. + #[xml(attribute(default))] min: Option, + /// The 'max' attribute specifies the maximum allowable value. + #[xml(attribute(default))] max: Option, }, @@ -65,7 +70,8 @@ pub enum Method { /// character data only. /// /// - Regex(String), + #[xml(name = "regex")] + Regex(#[xml(text)] String), } /// Selection Ranges in "list-multi" @@ -75,6 +81,7 @@ pub struct ListRange { /// The 'min' attribute specifies the minimum allowable number of selected/entered values. #[xml(attribute(default))] pub min: Option, + /// The 'max' attribute specifies the maximum allowable number of selected/entered values. #[xml(attribute(default))] pub max: Option, @@ -181,7 +188,8 @@ pub enum Datatype { } /// Validation rules for a DataForms Field. -#[derive(Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] +#[xml(namespace = ns::XDATA_VALIDATE, name = "validate")] pub struct Validate { /// The 'datatype' attribute specifies the datatype. This attribute is OPTIONAL, and defaults /// to "xs:string". It MUST meet one of the following conditions: @@ -191,6 +199,7 @@ pub struct Validate { /// - Start with "x:", and specify a user-defined datatype. /// /// Note that while "x:" allows for ad-hoc definitions, its use is NOT RECOMMENDED. + #[xml(attribute(default))] pub datatype: Option, /// The validation method. If no validation method is specified, form processors MUST @@ -202,6 +211,7 @@ pub struct Validate { /// defined by that method. /// /// + #[xml(child(default))] pub method: Option, /// For "list-multi", validation can indicate (via the `` element) that a minimum @@ -215,108 +225,10 @@ pub struct Validate { /// selection constraints. /// /// + #[xml(child(default))] pub list_range: Option, } -impl TryFrom for Validate { - type Error = FromElementError; - - fn try_from(elem: Element) -> Result { - check_self!(elem, "validate", XDATA_VALIDATE); - check_no_unknown_attributes!(elem, "item", ["datatype"]); - - let mut validate = Validate { - datatype: elem - .attr("datatype") - .map(Datatype::from_str) - .transpose() - .map_err(|err| FromElementError::Invalid(Error::TextParseError(err.into())))?, - method: None, - list_range: None, - }; - - for child in elem.children() { - match child { - _ if child.is("list-range", XDATA_VALIDATE) => { - let list_range = ListRange::try_from(child.clone())?; - if validate.list_range.is_some() { - return Err(Error::Other( - "Encountered unsupported number (n > 1) of list-range in validate element.", - ).into()); - } - validate.list_range = Some(list_range); - } - _ => { - let method = Method::try_from(child.clone())?; - if validate.method.is_some() { - return Err(Error::Other( - "Encountered unsupported number (n > 1) of validation methods in validate element.", - ).into()); - } - validate.method = Some(method); - } - } - } - - Ok(validate) - } -} - -impl From for Element { - fn from(value: Validate) -> Self { - Element::builder("validate", XDATA_VALIDATE) - .attr("datatype", value.datatype) - .append_all(value.method) - .append_all(value.list_range) - .build() - } -} - -impl TryFrom for Method { - type Error = Error; - - fn try_from(elem: Element) -> Result { - let method = match elem { - _ if elem.is("basic", XDATA_VALIDATE) => { - check_no_attributes!(elem, "basic"); - Method::Basic - } - _ if elem.is("open", XDATA_VALIDATE) => { - check_no_attributes!(elem, "open"); - Method::Open - } - _ if elem.is("range", XDATA_VALIDATE) => { - check_no_unknown_attributes!(elem, "range", ["min", "max"]); - Method::Range { - min: elem.attr("min").map(ToString::to_string), - max: elem.attr("max").map(ToString::to_string), - } - } - _ if elem.is("regex", XDATA_VALIDATE) => { - check_no_attributes!(elem, "regex"); - check_no_children!(elem, "regex"); - Method::Regex(elem.text()) - } - _ => return Err(Error::Other("Encountered invalid validation method.")), - }; - Ok(method) - } -} - -impl From for Element { - fn from(value: Method) -> Self { - match value { - Method::Basic => Element::builder("basic", XDATA_VALIDATE), - Method::Open => Element::builder("open", XDATA_VALIDATE), - Method::Range { min, max } => Element::builder("range", XDATA_VALIDATE) - .attr("min", min) - .attr("max", max), - Method::Regex(regex) => Element::builder("regex", XDATA_VALIDATE).append(regex), - } - .build() - } -} - impl FromStr for Datatype { type Err = DatatypeError; @@ -404,9 +316,22 @@ impl IntoAttributeValue for Datatype { } } +impl FromXmlText for Datatype { + fn from_xml_text(s: String) -> Result { + s.parse().map_err(Error::text_parse_error) + } +} + +impl AsXmlText for Datatype { + fn as_xml_text(&self) -> Result, Error> { + Ok(Cow::Owned(self.to_string())) + } +} + #[cfg(test)] mod tests { use super::*; + use minidom::Element; #[test] fn test_parse_datatype() -> Result<(), DatatypeError> {