diff --git a/src/extensions/rgp/mod.rs b/src/extensions/rgp/mod.rs index 5d0e529..679953c 100644 --- a/src/extensions/rgp/mod.rs +++ b/src/extensions/rgp/mod.rs @@ -2,8 +2,83 @@ //! //! As described in [RFC 3915](https://tools.ietf.org/html/rfc3915). +use instant_xml::FromXml; + pub mod poll; // Technically a separate extension (different namespace, RFC) pub mod report; pub mod request; +#[derive(Debug, PartialEq)] +pub enum RgpStatus { + AddPeriod, + AutoRenewPeriod, + RenewPeriod, + TransferPeriod, + RedemptionPeriod, + PendingRestore, + PendingDelete, +} + +impl<'xml> FromXml<'xml> for RgpStatus { + #[inline] + fn matches(id: ::instant_xml::Id<'_>, _: Option<::instant_xml::Id<'_>>) -> bool { + id == ::instant_xml::Id { + ns: XMLNS, + name: "rgpStatus", + } || id + == ::instant_xml::Id { + ns: poll::XMLNS, + name: "rgpStatus", + } + } + + fn deserialize<'cx>( + into: &mut Self::Accumulator, + field: &'static str, + deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, + ) -> Result<(), ::instant_xml::Error> { + use ::instant_xml::{Error, Id}; + use instant_xml::de::Node; + + 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 RgpStatus"))), + }; + + 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.as_ref() { + "addPeriod" => Self::AddPeriod, + "autoRenewPeriod" => Self::AutoRenewPeriod, + "renewPeriod" => Self::RenewPeriod, + "transferPeriod" => Self::TransferPeriod, + "redemptionPeriod" => Self::RedemptionPeriod, + "pendingRestore" => Self::PendingRestore, + "pendingDelete" => Self::PendingDelete, + val => { + return Err(Error::UnexpectedValue(format!( + "invalid RgpStatus '{val:?}'" + ))) + } + }); + + deserializer.ignore()?; + Ok(()) + } + + type Accumulator = Option; + + const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element; +} + pub const XMLNS: &str = "urn:ietf:params:xml:ns:rgp-1.0"; diff --git a/src/extensions/rgp/poll.rs b/src/extensions/rgp/poll.rs index 6b04911..f6d546b 100644 --- a/src/extensions/rgp/poll.rs +++ b/src/extensions/rgp/poll.rs @@ -3,6 +3,8 @@ use chrono::{DateTime, Utc}; use instant_xml::FromXml; +use super::RgpStatus; + /// RGP request status #[derive(Debug, FromXml)] #[xml(rename = "pollData", ns(XMLNS), rename_all = "camelCase")] @@ -13,19 +15,11 @@ pub struct RgpPollData { pub report_due_date: DateTime, } -/// Type that represents the `` tag for domain rgp restore request response -#[derive(Debug, FromXml)] -#[xml(rename = "rgpStatus", ns(XMLNS))] -pub struct RgpStatus { - /// The domain RGP status - #[xml(rename = "s", attribute)] - pub status: String, -} - -const XMLNS: &str = "http://www.verisign.com/epp/rgp-poll-1.0"; +pub(super) const XMLNS: &str = "http://www.verisign.com/epp/rgp-poll-1.0"; #[cfg(test)] mod tests { + use super::*; use crate::poll::{Poll, PollData}; use crate::tests::response_from_file; @@ -37,6 +31,6 @@ mod tests { }; assert_eq!(data.name, "EXAMPLE.COM"); - assert_eq!(data.rgp_status.status, "pendingRestore"); + assert_eq!(data.rgp_status, RgpStatus::PendingRestore); } } diff --git a/src/extensions/rgp/request.rs b/src/extensions/rgp/request.rs index f994c75..1323d07 100644 --- a/src/extensions/rgp/request.rs +++ b/src/extensions/rgp/request.rs @@ -7,7 +7,7 @@ use crate::{ request::{Extension, Transaction}, }; -use super::XMLNS; +use super::{RgpStatus, XMLNS}; impl<'a> Transaction>> for DomainUpdate<'a> {} @@ -42,15 +42,6 @@ impl Default for RgpRestoreRequest<'static> { // Response -/// Type that represents the `` tag for domain rgp restore request response -#[derive(Debug, FromXml)] -#[xml(rename = "rgpStatus", ns(XMLNS))] -pub struct RgpStatus { - /// The domain RGP status - #[xml(rename = "s", attribute)] - pub status: String, -} - #[derive(Debug, FromXml)] #[xml(rename = "upData", ns(XMLNS))] /// Type that represents the `` tag for domain transfer response @@ -81,6 +72,7 @@ mod tests { use crate::domain::info::DomainInfo; use crate::domain::update::{DomainChangeInfo, DomainUpdate}; use crate::extensions::rgp::request::RgpRequestResponse; + use crate::extensions::rgp::RgpStatus; use crate::response::ResultCode; use crate::tests::{assert_serialized, response_from_file_with_ext, SUCCESS_MSG, SVTRID}; @@ -120,7 +112,7 @@ mod tests { _ => panic!("Unexpected response type"), }; - assert_eq!(data.rgp_status[0].status, "pendingRestore".to_string()); + assert_eq!(data.rgp_status[0], RgpStatus::PendingRestore); assert_eq!(object.tr_ids.server_tr_id, SVTRID); } @@ -136,7 +128,7 @@ mod tests { _ => panic!("Unexpected response type"), }; - assert_eq!(data.rgp_status[0].status, "addPeriod"); - assert_eq!(data.rgp_status[1].status, "renewPeriod"); + assert_eq!(data.rgp_status[0], RgpStatus::AddPeriod); + assert_eq!(data.rgp_status[1], RgpStatus::RenewPeriod); } }