Use enum type to represent contact::Status

This commit is contained in:
Dirkjan Ochtman 2023-03-02 14:01:49 +01:00
parent cd92b45156
commit 96268d9528
4 changed files with 114 additions and 22 deletions

View File

@ -56,7 +56,7 @@ pub struct InfoData {
/// The contact ROID /// The contact ROID
pub roid: String, pub roid: String,
/// The list of contact statuses /// The list of contact statuses
pub statuses: Vec<Status<'static>>, pub statuses: Vec<Status>,
/// The postal info for the contact /// The postal info for the contact
pub postal_info: PostalInfo<'static>, pub postal_info: PostalInfo<'static>,
/// The voice data for the contact /// The voice data for the contact
@ -93,6 +93,7 @@ mod tests {
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
use super::ContactInfo; use super::ContactInfo;
use crate::contact::Status;
use crate::response::ResultCode; use crate::response::ResultCode;
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID}; use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
@ -116,7 +117,7 @@ mod tests {
assert_eq!(object.result.message, SUCCESS_MSG); assert_eq!(object.result.message, SUCCESS_MSG);
assert_eq!(result.id, "eppdev-contact-3"); assert_eq!(result.id, "eppdev-contact-3");
assert_eq!(result.roid, "UNDEF-ROID"); assert_eq!(result.roid, "UNDEF-ROID");
assert_eq!(result.statuses[0].status, "ok"); assert_eq!(result.statuses[0], Status::Ok);
assert_eq!(result.postal_info.info_type, "loc"); assert_eq!(result.postal_info.info_type, "loc");
assert_eq!(result.postal_info.name, "John Doe"); assert_eq!(result.postal_info.name, "John Doe");
assert_eq!(result.postal_info.organization, "Acme Widgets"); assert_eq!(result.postal_info.organization, "Acme Widgets");

View File

@ -6,7 +6,7 @@ use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use instant_xml::{display_to_xml, from_xml_str, FromXml, ToXml}; use instant_xml::{display_to_xml, from_xml_str, Deserializer, FromXml, Serializer, ToXml};
pub mod check; pub mod check;
pub use check::ContactCheck; pub use check::ContactCheck;
@ -220,10 +220,107 @@ impl<'a> PostalInfo<'a> {
} }
/// The `<status>` type on contact transactions /// The `<status>` type on contact transactions
#[derive(Debug, FromXml, ToXml)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[xml(rename = "status", ns(XMLNS))] pub enum Status {
pub struct Status<'a> { ClientDeleteProhibited,
/// The status name, represented by the 's' attr on `<status>` tags ServerDeleteProhibited,
#[xml(attribute, rename = "s")] ClientTransferProhibited,
pub status: Cow<'a, str>, ServerTransferProhibited,
ClientUpdateProhibited,
ServerUpdateProhibited,
Linked,
Ok,
PendingCreate,
PendingDelete,
PendingTransfer,
PendingUpdate,
}
impl Status {
pub fn as_str(&self) -> &'static str {
use Status::*;
match self {
ClientDeleteProhibited => "clientDeleteProhibited",
ServerDeleteProhibited => "serverDeleteProhibited",
ClientTransferProhibited => "clientTransferProhibited",
ServerTransferProhibited => "serverTransferProhibited",
ClientUpdateProhibited => "clientUpdateProhibited",
ServerUpdateProhibited => "serverUpdateProhibited",
Linked => "linked",
Ok => "ok",
PendingCreate => "pendingCreate",
PendingDelete => "pendingDelete",
PendingTransfer => "pendingTransfer",
PendingUpdate => "pendingUpdate",
}
}
}
impl ToXml for Status {
fn serialize<W: fmt::Write + ?Sized>(
&self,
_: Option<instant_xml::Id<'_>>,
serializer: &mut Serializer<W>,
) -> Result<(), instant_xml::Error> {
serializer.write_start("status", XMLNS)?;
serializer.write_attr("s", XMLNS, &self.as_str())?;
serializer.end_empty()
}
}
impl<'xml> FromXml<'xml> for Status {
fn matches(id: instant_xml::Id<'_>, _: Option<instant_xml::Id<'_>>) -> bool {
id == instant_xml::Id {
ns: XMLNS,
name: "status",
}
}
fn deserialize<'cx>(
into: &mut Self::Accumulator,
field: &'static str,
deserializer: &mut Deserializer<'cx, 'xml>,
) -> Result<(), instant_xml::Error> {
use instant_xml::de::Node;
use instant_xml::{Error, Id};
let node = match deserializer.next() {
Some(result) => result?,
None => return Err(Error::MissingValue(field)),
};
let attr = match node {
Node::Attribute(attr) => attr,
Node::Open(_) | Node::Text(_) => return Err(Error::MissingValue(field)),
node => return Err(Error::UnexpectedNode(format!("{node:?} in Status"))),
};
let id = deserializer.attribute_id(&attr)?;
let expected = Id { ns: "", name: "s" };
if id != expected {
return Err(Error::MissingValue(field));
}
*into = Some(match attr.value {
"clientDeleteProhibited" => Status::ClientDeleteProhibited,
"serverDeleteProhibited" => Status::ServerDeleteProhibited,
"clientTransferProhibited" => Status::ClientTransferProhibited,
"serverTransferProhibited" => Status::ServerTransferProhibited,
"clientUpdateProhibited" => Status::ClientUpdateProhibited,
"serverUpdateProhibited" => Status::ServerUpdateProhibited,
"linked" => Status::Linked,
"ok" => Status::Ok,
"pendingCreate" => Status::PendingCreate,
"pendingDelete" => Status::PendingDelete,
"pendingTransfer" => Status::PendingTransfer,
"pendingUpdate" => Status::PendingUpdate,
val => return Err(Error::UnexpectedValue(format!("invalid status {val:?}"))),
});
deserializer.ignore()?;
Ok(())
}
type Accumulator = Option<Status>;
const KIND: instant_xml::Kind = instant_xml::Kind::Element;
} }

View File

@ -74,19 +74,19 @@ pub struct ContactChangeInfo<'a> {
/// Type for list of elements of the `<status>` tag for contact update request /// Type for list of elements of the `<status>` tag for contact update request
#[derive(Debug, ToXml)] #[derive(Debug, ToXml)]
pub struct StatusList<'a> { pub struct StatusList<'a> {
status: &'a [Status<'a>], status: &'a [Status],
} }
#[derive(Debug, ToXml)] #[derive(Debug, ToXml)]
#[xml(rename = "add", ns(XMLNS))] #[xml(rename = "add", ns(XMLNS))]
struct AddStatuses<'a> { struct AddStatuses<'a> {
statuses: &'a [Status<'a>], statuses: &'a [Status],
} }
#[derive(Debug, ToXml)] #[derive(Debug, ToXml)]
#[xml(rename = "rem", ns(XMLNS))] #[xml(rename = "rem", ns(XMLNS))]
struct RemoveStatuses<'a> { struct RemoveStatuses<'a> {
statuses: &'a [Status<'a>], statuses: &'a [Status],
} }
/// Type for elements under the contact `<update>` tag /// Type for elements under the contact `<update>` tag
@ -125,14 +125,8 @@ mod tests {
let voice = Voice::new("+33.47237942"); let voice = Voice::new("+33.47237942");
object.set_info("newemail@eppdev.net", postal_info, voice, "eppdev-387323"); object.set_info("newemail@eppdev.net", postal_info, voice, "eppdev-387323");
let add_statuses = &[Status { object.add(&[Status::ClientTransferProhibited]);
status: "clientTransferProhibited".into(), object.remove(&[Status::ClientDeleteProhibited]);
}];
object.add(add_statuses);
let remove_statuses = &[Status {
status: "clientDeleteProhibited".into(),
}];
object.remove(remove_statuses);
assert_serialized("request/contact/update.xml", &object); assert_serialized("request/contact/update.xml", &object);
} }

View File

@ -5,10 +5,10 @@
<update xmlns="urn:ietf:params:xml:ns:contact-1.0"> <update xmlns="urn:ietf:params:xml:ns:contact-1.0">
<id>eppdev-contact-3</id> <id>eppdev-contact-3</id>
<add> <add>
<status s="clientTransferProhibited"></status> <status s="clientTransferProhibited" />
</add> </add>
<rem> <rem>
<status s="clientDeleteProhibited"></status> <status s="clientDeleteProhibited" />
</rem> </rem>
<chg> <chg>
<postalInfo type="loc"> <postalInfo type="loc">