diff options
-rw-r--r-- | Cargo.toml | 12 | ||||
-rw-r--r-- | src/call/account.rs | 31 | ||||
-rw-r--r-- | src/call/mod.rs | 7 | ||||
-rw-r--r-- | src/call/nameserver.rs | 133 | ||||
-rw-r--r-- | src/client.rs | 74 | ||||
-rw-r--r-- | src/common/mod.rs | 78 | ||||
-rw-r--r-- | src/common/nameserver.rs | 179 | ||||
-rw-r--r-- | src/error.rs | 27 | ||||
-rw-r--r-- | src/response/mod.rs | 11 | ||||
-rw-r--r-- | src/response/nameserver.rs | 96 |
10 files changed, 112 insertions, 536 deletions
@@ -6,15 +6,17 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -iso8601 = "0.4.0" +iso8601 = "0.3.0" url = "2.2.2" +serde = "1.0.147" +serde_derive = "1.0.147" [dependencies.reqwest] version = "0.11" default-features = false features = ["blocking", "cookies", "rustls-tls"] -[dependencies.xmlrpc] -version = "0.15.1" -default-features = false -features = ["http"] +[dependencies.serde_xmlrpc] +version = "0.1.1" +git = "https://github.com/HimbeerserverDE/serde_xml_rpc.git" +branch = "serializer" diff --git a/src/call/account.rs b/src/call/account.rs index ea6fa6f..73d6dab 100644 --- a/src/call/account.rs +++ b/src/call/account.rs @@ -1,27 +1,16 @@ -use super::Call; +use super::*; -use std::collections::BTreeMap; +use serde_derive::{Deserialize, Serialize}; // Contains login information. Used to create an API session. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub(crate) struct Login { pub(crate) user: String, pub(crate) pass: String, + #[serde(rename = "case-insensitive")] pub(crate) case_insensitive: bool, } -impl From<Login> for xmlrpc::Value { - fn from(login: Login) -> Self { - let mut map = BTreeMap::new(); - - map.insert("user".into(), login.user.into()); - map.insert("pass".into(), login.pass.into()); - map.insert("case-insensitive".into(), login.case_insensitive.into()); - - xmlrpc::Value::Struct(map) - } -} - impl Call for Login { fn method_name(&self) -> String { String::from("account.login") @@ -32,17 +21,13 @@ impl Call for Login { } } +impl Response<()> for Login {} + // Contains no information. This just signals to the server // that it should end the session. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub(crate) struct Logout; -impl From<Logout> for xmlrpc::Value { - fn from(_logout: Logout) -> Self { - xmlrpc::Value::Nil - } -} - impl Call for Logout { fn method_name(&self) -> String { String::from("account.logout") @@ -51,3 +36,5 @@ impl Call for Logout { vec![1500] } } + +impl Response<()> for Logout {} diff --git a/src/call/mod.rs b/src/call/mod.rs index f8fe0a2..517f459 100644 --- a/src/call/mod.rs +++ b/src/call/mod.rs @@ -1,8 +1,11 @@ -// A call to the API. -pub trait Call: Clone + std::fmt::Debug + Into<xmlrpc::Value> { +/// A call to the API. +pub trait Call: Clone + std::fmt::Debug + serde::Serialize { fn method_name(&self) -> String; fn expected(&self) -> Vec<i32>; } +/// This trait indicates the response data type to this a `Call`. +pub trait Response<T> {} + pub mod account; pub mod nameserver; diff --git a/src/call/nameserver.rs b/src/call/nameserver.rs index c490a31..3b59646 100644 --- a/src/call/nameserver.rs +++ b/src/call/nameserver.rs @@ -1,146 +1,55 @@ use super::Call; -use crate::common::nameserver::{RecordType, UrlRdrType}; -use std::collections::BTreeMap; +use serde_derive::{Deserialize, Serialize}; /// Optional search constraints to find nameserver records /// the account has access to. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct RecordInfo { + #[serde(rename = "domain")] pub domain_name: Option<String>, + #[serde(rename = "roId")] pub domain_id: Option<i32>, + #[serde(rename = "recordId")] pub record_id: Option<i32>, - pub record_type: Option<RecordType>, + #[serde(rename = "type")] + pub record_type: Option<String>, pub name: Option<String>, pub content: Option<String>, pub ttl: Option<i32>, + #[serde(rename = "prio")] pub priority: Option<i32>, } -impl From<RecordInfo> for xmlrpc::Value { - fn from(info: RecordInfo) -> Self { - let mut map = BTreeMap::new(); - - if let Some(domain_name) = info.domain_name { - map.insert("domain".into(), domain_name.into()); - } - - if let Some(domain_id) = info.domain_id { - map.insert("roId".into(), domain_id.into()); - } - - if let Some(record_id) = info.record_id { - map.insert("recordId".into(), record_id.into()); - } - - if let Some(record_type) = info.record_type { - map.insert("type".into(), record_type.into()); - } - - if let Some(content) = info.content { - map.insert("content".into(), content.into()); - } - - if let Some(ttl) = info.ttl { - map.insert("ttl".into(), ttl.into()); - } - - if let Some(priority) = info.priority { - map.insert("prio".into(), priority.into()); - } - - xmlrpc::Value::Struct(map) - } -} - -impl Call for RecordInfo { - fn method_name(&self) -> String { - String::from("nameserver.info") - } - - fn expected(&self) -> Vec<i32> { - vec![1000] - } -} - /// Update the records with the specified IDs. /// Any `None` variants will remain unchanged. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct RecordUpdate { + #[serde(rename = "id")] pub ids: Vec<i32>, pub name: Option<String>, - pub record_type: Option<RecordType>, + #[serde(rename = "type")] + pub record_type: Option<String>, pub content: Option<String>, pub ttl: Option<i32>, + #[serde(rename = "prio")] pub priority: Option<i32>, - pub url_rdr_type: Option<UrlRdrType>, + #[serde(rename = "urlRedirectType")] + pub url_rdr_type: Option<String>, + #[serde(rename = "urlRedirectTitle")] pub url_rdr_title: Option<String>, + #[serde(rename = "urlRedirectDescription")] pub url_rdr_desc: Option<String>, + #[serde(rename = "urlRedirectKeywords")] pub url_rdr_keywords: Option<String>, + #[serde(rename = "urlRedirectFavIcon")] pub url_rdr_favicon: Option<String>, + #[serde(rename = "urlAppend")] pub url_append: Option<bool>, + #[serde(rename = "testing")] pub testing_mode: bool, } -impl From<RecordUpdate> for xmlrpc::Value { - fn from(update: RecordUpdate) -> Self { - let mut map = BTreeMap::new(); - - map.insert( - "id".into(), - xmlrpc::Value::Array(update.ids.iter().map(|v| xmlrpc::Value::from(*v)).collect()), - ); - - if let Some(name) = update.name { - map.insert("name".into(), name.into()); - } - - if let Some(record_type) = update.record_type { - map.insert("type".into(), record_type.into()); - } - - if let Some(content) = update.content { - map.insert("content".into(), content.into()); - } - - if let Some(ttl) = update.ttl { - map.insert("ttl".into(), ttl.into()); - } - - if let Some(priority) = update.priority { - map.insert("prio".into(), priority.into()); - } - - if let Some(url_rdr_type) = update.url_rdr_type { - map.insert("urlRedirectType".into(), url_rdr_type.into()); - } - - if let Some(url_rdr_title) = update.url_rdr_title { - map.insert("urlRedirectTitle".into(), url_rdr_title.into()); - } - - if let Some(url_rdr_desc) = update.url_rdr_desc { - map.insert("urlRedirectDescription".into(), url_rdr_desc.into()); - } - - if let Some(url_rdr_keywords) = update.url_rdr_keywords { - map.insert("urlRedirectKeywords".into(), url_rdr_keywords.into()); - } - - if let Some(url_rdr_favicon) = update.url_rdr_favicon { - map.insert("urlRedirectFavIcon".into(), url_rdr_favicon.into()); - } - - if let Some(url_append) = update.url_append { - map.insert("urlAppend".into(), url_append.into()); - } - - map.insert("testing".into(), update.testing_mode.into()); - - xmlrpc::Value::Struct(map) - } -} - impl Call for RecordUpdate { fn method_name(&self) -> String { String::from("nameserver.updateRecord") diff --git a/src/client.rs b/src/client.rs index 8aeb099..add352d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,7 +1,6 @@ -use crate::{call, response}; +use crate::call::{self, Response}; use crate::{Error, Result}; -use std::collections::BTreeMap; use std::sync::Arc; use reqwest::{blocking, Url}; @@ -59,47 +58,42 @@ impl Client { /// Issues a `Call` and returns a `Response` /// if successful and if the status code /// matches one of the expected status codes. - pub fn call(&self, call: impl call::Call) -> Result<response::Response> { + pub fn call<T, U>(&self, call: T) -> Result<U> + where + T: call::Call + Response<U>, + U: serde::de::DeserializeOwned, + { let expected = call.expected(); + let xml = serde_xmlrpc::request_to_str(&call.method_name(), vec![call])?; - let transport = self.inner.http.post::<Url>(self.inner.endpoint.into()); - - let binding = call.method_name(); - let request = xmlrpc::Request::new(&binding).arg(call); - - let raw = request.call(transport)?; - match raw { - xmlrpc::Value::Struct(map) => { - let code = map - .get("code") - .ok_or_else(|| Error::Inexistent("code".into()))?; - - match code { - xmlrpc::Value::Int(code) => { - if expected.contains(code) { - let default = &xmlrpc::Value::Struct(BTreeMap::new()); - let data = map.get("resData").unwrap_or(default); - - match data { - xmlrpc::Value::Struct(response) => Ok(response::Response { - status: *code, - data: response.clone(), - }), - _ => Err(Error::Type( - "resData".into(), - "Struct".into(), - data.clone(), - )), - } - } else { - Err(Error::BadStatus(expected, *code)) - } - } - _ => Err(Error::Type("code".into(), "Int".into(), code.clone())), - } - } - _ => Err(Error::BadResponse(raw.clone())), + let raw_response = self.inner.http.post::<Url>(self.inner.endpoint.into()) + .body(xml) + .send()? + .text()?; + + let map = serde_xmlrpc::value_from_str(&raw_response)?; + + let resp = map + .as_struct() + .ok_or_else(|| Error::MalformedResponse(map.clone()))?; + + let code = resp + .get("code") + .ok_or_else(|| Error::MalformedResponse(map.clone()))? + .as_i32() + .ok_or_else(|| Error::MalformedResponse(map.clone()))?; + + if !expected.contains(&code) { + return Err(Error::BadStatus(expected, code)); } + + let data = resp + .get("resData") + .ok_or_else(|| Error::MalformedResponse(map.clone()))?; + + let res_data = serde_xmlrpc::value_to_string(data.clone())?; + + Ok(serde_xmlrpc::response_from_str(&res_data)?) } } diff --git a/src/common/mod.rs b/src/common/mod.rs index ef15d9c..aa5854f 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,79 +1 @@ -use crate::{Error, Result}; - -use std::collections::BTreeMap; - -use iso8601::DateTime; - -pub(crate) fn get_str(map: &BTreeMap<String, xmlrpc::Value>, key: String) -> Result<String> { - let value = map - .get(&key) - .ok_or_else(|| Error::Inexistent(key.clone()))? - .as_str() - .ok_or_else(|| Error::Type(key.clone(), "String".into(), map.get(&key).unwrap().clone()))?; - - Ok(value.to_owned()) -} - -pub(crate) fn get_i32(map: &BTreeMap<String, xmlrpc::Value>, key: String) -> Result<i32> { - let value = map - .get(&key) - .ok_or_else(|| Error::Inexistent(key.clone()))? - .as_i32() - .ok_or_else(|| Error::Type(key.clone(), "Int".into(), map.get(&key).unwrap().clone()))?; - - Ok(value) -} - -pub(crate) fn get_bool(map: &BTreeMap<String, xmlrpc::Value>, key: String) -> Result<bool> { - let value = map - .get(&key) - .ok_or_else(|| Error::Inexistent(key.clone()))? - .as_bool() - .ok_or_else(|| Error::Type(key.clone(), "Bool".into(), map.get(&key).unwrap().clone()))?; - - Ok(value) -} - -pub(crate) fn get_datetime(map: &BTreeMap<String, xmlrpc::Value>, key: String) -> Result<DateTime> { - let value = map - .get(&key) - .ok_or_else(|| Error::Inexistent(key.clone()))? - .as_datetime() - .ok_or_else(|| { - Error::Type( - key.clone(), - "DateTime".into(), - map.get(&key).unwrap().clone(), - ) - })?; - - Ok(value) -} - -pub(crate) fn get_array( - map: &BTreeMap<String, xmlrpc::Value>, - key: String, -) -> Result<Vec<xmlrpc::Value>> { - let value = map - .get(&key) - .ok_or_else(|| Error::Inexistent(key.clone()))? - .as_array() - .ok_or_else(|| Error::Type(key.clone(), "Array".into(), map.get(&key).unwrap().clone()))?; - - Ok(value.to_vec()) -} - -pub(crate) fn get_map( - map: &BTreeMap<String, xmlrpc::Value>, - key: String, -) -> Result<BTreeMap<String, xmlrpc::Value>> { - let value = map - .get(&key) - .ok_or_else(|| Error::Inexistent(key.clone()))? - .as_struct() - .ok_or_else(|| Error::Type(key.clone(), "Struct".into(), map.get(&key).unwrap().clone()))?; - - Ok(value.to_owned()) -} - pub mod nameserver; diff --git a/src/common/nameserver.rs b/src/common/nameserver.rs index 706ae73..3507a85 100644 --- a/src/common/nameserver.rs +++ b/src/common/nameserver.rs @@ -1,183 +1,8 @@ -use super::*; -use crate::{Error, Result}; - -use std::fmt; - -/// The DNS record type. -#[derive(Clone, Debug)] -pub enum RecordType { - A, - Aaaa, - Afsdb, - Alias, - Caa, - Cert, - Cname, - Hinfo, - Key, - Loc, - Mx, - NaPtr, - Ns, - OpenPgpKey, - Ptr, - Rp, - SmimeA, - Soa, - Srv, - Sshfp, - Tlsa, - Txt, - Uri, - Url, -} - -impl fmt::Display for RecordType { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::A => write!(fmt, "A"), - Self::Aaaa => write!(fmt, "AAAA"), - Self::Afsdb => write!(fmt, "AFSDB"), - Self::Alias => write!(fmt, "ALIAS"), - Self::Caa => write!(fmt, "CAA"), - Self::Cert => write!(fmt, "CERT"), - Self::Cname => write!(fmt, "CNAME"), - Self::Hinfo => write!(fmt, "HINFO"), - Self::Key => write!(fmt, "KEY"), - Self::Loc => write!(fmt, "LOC"), - Self::Mx => write!(fmt, "MX"), - Self::NaPtr => write!(fmt, "NAPTR"), - Self::Ns => write!(fmt, "NS"), - Self::OpenPgpKey => write!(fmt, "OPENPGPKEY"), - Self::Ptr => write!(fmt, "PTR"), - Self::Rp => write!(fmt, "RP"), - Self::SmimeA => write!(fmt, "SMIMEA"), - Self::Soa => write!(fmt, "SOA"), - Self::Srv => write!(fmt, "SRV"), - Self::Sshfp => write!(fmt, "SSHFP"), - Self::Tlsa => write!(fmt, "TLSA"), - Self::Txt => write!(fmt, "TXT"), - Self::Uri => write!(fmt, "URI"), - Self::Url => write!(fmt, "URL"), - } - } -} - -impl From<RecordType> for xmlrpc::Value { - fn from(rt: RecordType) -> Self { - xmlrpc::Value::String(rt.to_string()) - } -} - -impl TryFrom<String> for RecordType { - type Error = Error; - fn try_from(s: String) -> Result<Self> { - match s.as_str() { - "A" => Ok(Self::A), - "AAAA" => Ok(Self::Aaaa), - "AFSDB" => Ok(Self::Afsdb), - "ALIAS" => Ok(Self::Alias), - "CAA" => Ok(Self::Caa), - "CERT" => Ok(Self::Cert), - "CNAME" => Ok(Self::Cname), - "HINFO" => Ok(Self::Hinfo), - "KEY" => Ok(Self::Key), - "LOC" => Ok(Self::Loc), - "MX" => Ok(Self::Mx), - "NAPTR" => Ok(Self::NaPtr), - "NS" => Ok(Self::Ns), - "OPENPGPKEY" => Ok(Self::OpenPgpKey), - "PTR" => Ok(Self::Ptr), - "RP" => Ok(Self::Rp), - "SMIMEA" => Ok(Self::SmimeA), - "SOA" => Ok(Self::Soa), - "SRV" => Ok(Self::Srv), - "SSHFP" => Ok(Self::Sshfp), - "TLSA" => Ok(Self::Tlsa), - "TXT" => Ok(Self::Txt), - "URI" => Ok(Self::Uri), - "URL" => Ok(Self::Url), - _ => Err(Error::BadVariant("RecordType".into(), s)), - } - } -} - -/// The domain type. Can be master or slave. -#[derive(Clone, Debug)] -pub enum DomainType { - Master, - Slave, -} - -impl fmt::Display for DomainType { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(match self { - Self::Master => "MASTER", - Self::Slave => "SLAVE", - }) - } -} - -impl TryFrom<String> for DomainType { - type Error = Error; - fn try_from(s: String) -> Result<Self> { - match s.as_str() { - "MASTER" => Ok(Self::Master), - "SLAVE" => Ok(Self::Slave), - _ => Err(Error::BadVariant("DomainType".into(), s)), - } - } -} +use serde_derive::{Deserialize, Serialize}; /// Information on a slave nameserver. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct SlaveDns { pub hostname: String, pub address: String, } - -impl TryFrom<BTreeMap<String, xmlrpc::Value>> for SlaveDns { - type Error = Error; - fn try_from(map: BTreeMap<String, xmlrpc::Value>) -> Result<Self> { - Ok(Self { - hostname: get_str(&map, "name".into())?, - address: get_str(&map, "ip".into())?, - }) - } -} - -/// Type of URL redirect. Can be a HTTP 301, 302 or frame. -#[derive(Clone, Debug)] -pub enum UrlRdrType { - Permanent, - Temporary, - Frame, -} - -impl fmt::Display for UrlRdrType { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(match self { - Self::Permanent => "HEADER301", - Self::Temporary => "HEADER302", - Self::Frame => "FRAME", - }) - } -} - -impl TryFrom<String> for UrlRdrType { - type Error = Error; - fn try_from(s: String) -> Result<Self> { - match s.as_str() { - "HEADER301" => Ok(Self::Permanent), - "HEADER302" => Ok(Self::Temporary), - "FRAME" => Ok(Self::Frame), - _ => Err(Error::BadVariant("UrlRdrType".into(), s)), - } - } -} - -impl From<UrlRdrType> for xmlrpc::Value { - fn from(url_rdr_type: UrlRdrType) -> Self { - url_rdr_type.to_string().into() - } -} diff --git a/src/error.rs b/src/error.rs index c32dbf4..e5238c6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,12 +5,10 @@ use std::fmt; pub enum Error { ParseUrl(url::ParseError), Reqwest(reqwest::Error), - XmlRpc(xmlrpc::Error), + SerdeXmlRpc(serde_xmlrpc::Error), Inexistent(String), - Type(String, String, xmlrpc::Value), - BadResponse(xmlrpc::Value), + MalformedResponse(serde_xmlrpc::Value), BadStatus(Vec<i32>, i32), - BadVariant(String, String), } impl std::error::Error for Error {} @@ -19,24 +17,17 @@ impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::ParseUrl(e) => write!(fmt, "can't parse Url: {}", e), - Error::Reqwest(e) => write!(fmt, "reqwest eor: {}", e), - Error::XmlRpc(e) => write!(fmt, "xmlrpc eor: {}", e), + Error::Reqwest(e) => write!(fmt, "reqwest error: {}", e), + Error::SerdeXmlRpc(e) => write!(fmt, "serde_xmlrpc error: {}", e), Error::Inexistent(what) => { write!(fmt, "parameter {} does not exist", what) } - Error::Type(what, exp, got) => { - write!( - fmt, - "parameter {what} is of wrong type {got:?} (expected: {exp})" - ) + Error::MalformedResponse(resp) => { + write!(fmt, "malformed response: {:?}", resp) } - Error::BadResponse(resp) => write!(fmt, "bad response: {:?}", resp), Error::BadStatus(expected, got) => { write!(fmt, "bad status {} (expected: {:?}", got, expected) } - Error::BadVariant(enum_name, var) => { - write!(fmt, "{} is not a valid enum variant for {}", var, enum_name) - } } } } @@ -53,9 +44,9 @@ impl From<reqwest::Error> for Error { } } -impl From<xmlrpc::Error> for Error { - fn from(e: xmlrpc::Error) -> Self { - Self::XmlRpc(e) +impl From<serde_xmlrpc::Error> for Error { + fn from(e: serde_xmlrpc::Error) -> Self { + Self::SerdeXmlRpc(e) } } diff --git a/src/response/mod.rs b/src/response/mod.rs index 5aab5a5..aa5854f 100644 --- a/src/response/mod.rs +++ b/src/response/mod.rs @@ -1,12 +1 @@ -use std::collections::BTreeMap; - -/// A Response to an API call including status and data. -/// Data is guaranteed to be a `Struct`. -/// [`Struct`]: xmlrpc::Value::Struct -#[derive(Clone, Debug)] -pub struct Response { - pub status: i32, - pub(crate) data: BTreeMap<String, xmlrpc::Value>, -} - pub mod nameserver; diff --git a/src/response/nameserver.rs b/src/response/nameserver.rs index 6ac330b..76e7c85 100644 --- a/src/response/nameserver.rs +++ b/src/response/nameserver.rs @@ -1,97 +1,51 @@ -use super::Response; -use crate::common::nameserver::{DomainType, RecordType, SlaveDns, UrlRdrType}; -use crate::common::*; -use crate::{Error, Result}; +use crate::common::nameserver::SlaveDns; -use iso8601::DateTime; +use serde_derive::{Deserialize, Serialize}; + +type DateTime = String; /// A nameserver record. Contains DNS information as well as INWX metadata. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct Record { pub id: i32, pub name: String, - pub record_type: RecordType, + #[serde(rename = "type")] + pub record_type: String, pub content: String, pub ttl: i32, + #[serde(rename = "prio")] pub priority: i32, - pub url_rdr_type: Option<UrlRdrType>, + #[serde(rename = "urlRedirectType")] + pub url_rdr_type: Option<String>, + #[serde(rename = "urlRedirectTitle")] pub url_rdr_title: Option<String>, + #[serde(rename = "urlRedirectDescription")] pub url_rdr_desc: Option<String>, + #[serde(rename = "urlRedirectKeywords")] pub url_rdr_keywords: Option<String>, + #[serde(rename = "urlRedirectFavIcon")] pub url_rdr_favicon: Option<String>, + #[serde(rename = "urlAppend")] pub url_append: Option<bool>, } -impl TryFrom<xmlrpc::Value> for Record { - type Error = Error; - fn try_from(v: xmlrpc::Value) -> Result<Self> { - if let xmlrpc::Value::Struct(map) = v { - let record = Self { - id: get_i32(&map, "id".into())?, - name: get_str(&map, "name".into())?, - record_type: get_str(&map, "type".into())?.try_into()?, - content: get_str(&map, "content".into())?, - ttl: get_i32(&map, "ttl".into())?, - priority: get_i32(&map, "prio".into())?, - url_rdr_type: match get_str(&map, "urlRedirectType".into()).ok() { - Some(url_rdr_type) => url_rdr_type.try_into().ok(), - None => None, - }, - url_rdr_title: get_str(&map, "urlRedirectTitle".into()).ok(), - url_rdr_desc: get_str(&map, "urlRedirectDescription".into()).ok(), - url_rdr_keywords: get_str(&map, "urlRedirectKeywords".into()).ok(), - url_rdr_favicon: get_str(&map, "urlRedirectFavIcon".into()).ok(), - url_append: get_bool(&map, "urlAppend".into()).ok(), - }; - - Ok(record) - } else { - Err(Error::Type("record".into(), "Struct".into(), v)) - } - } -} - /// The records that match a search. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct RecordInfo { + #[serde(rename = "roId")] pub domain_id: Option<i32>, + #[serde(rename = "domain")] pub domain_name: Option<String>, - pub domain_type: Option<DomainType>, + #[serde(rename = "type")] + pub domain_type: Option<String>, + #[serde(rename = "masterIp")] pub master_address: Option<String>, + #[serde(rename = "lastZoneCheck")] pub last_zone_check: Option<DateTime>, + #[serde(rename = "slaveDns")] pub slave_dns: Option<SlaveDns>, + #[serde(rename = "SOAserial")] pub soa_serial: Option<String>, + #[serde(rename = "record")] pub records: Option<Vec<Record>>, } - -impl TryFrom<Response> for RecordInfo { - type Error = Error; - fn try_from(resp: Response) -> Result<Self> { - let info = Self { - domain_id: get_i32(&resp.data, "roId".into()).ok(), - domain_name: get_str(&resp.data, "domain".into()).ok(), - domain_type: match get_str(&resp.data, "type".into()).ok() { - Some(domain_type) => domain_type.try_into().ok(), - None => None, - }, - master_address: get_str(&resp.data, "masterIp".into()).ok(), - last_zone_check: { get_datetime(&resp.data, "lastZoneCheck".into()).ok() }, - slave_dns: match get_map(&resp.data, "slaveDns".into()).ok() { - Some(slave_dns) => slave_dns.try_into().ok(), - None => None, - }, - soa_serial: get_str(&resp.data, "SOAserial".into()).ok(), - records: match get_array(&resp.data, "record".into()).ok() { - Some(records) => Some( - records - .iter() - .map(|v| v.to_owned().try_into()) - .collect::<Result<Vec<Record>>>()?, - ), - None => None, - }, - }; - - Ok(info) - } -} |