epp_client/
common.rs

1//! Common data types included in EPP Requests and Responses
2
3use std::ops::Deref;
4use std::{borrow::Cow, fmt::Display, net::IpAddr};
5
6use serde::ser::SerializeSeq;
7use serde::{Deserialize, Serialize};
8
9use crate::request::Extension;
10
11pub(crate) const EPP_XMLNS: &str = "urn:ietf:params:xml:ns:epp-1.0";
12
13/// Wraps String for easier serialization to and from values that are inner text
14/// for tags rather than attributes
15#[derive(Default, Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
16pub struct StringValue<'a>(Cow<'a, str>);
17
18impl Deref for StringValue<'_> {
19    type Target = str;
20
21    fn deref(&self) -> &Self::Target {
22        self.0.as_ref()
23    }
24}
25
26impl<'a> AsRef<str> for StringValue<'a> {
27    fn as_ref(&self) -> &str {
28        self.0.as_ref()
29    }
30}
31
32impl Display for StringValue<'_> {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "{}", self.0)
35    }
36}
37
38impl<'a> From<&'a str> for StringValue<'a> {
39    fn from(s: &'a str) -> Self {
40        Self(s.into())
41    }
42}
43
44impl From<String> for StringValue<'static> {
45    fn from(s: String) -> Self {
46        Self(s.into())
47    }
48}
49
50#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
51/// An empty placeholder tag. To be refactored to something more compliant later.
52pub struct NoExtension;
53
54impl Extension for NoExtension {
55    type Response = NoExtension;
56}
57
58/// Type that represents the &lt;name&gt; tag for host check response
59#[derive(Deserialize, Debug)]
60struct Available {
61    /// The resource name
62    #[serde(rename = "$value")]
63    pub id: StringValue<'static>,
64    /// The resource (un)availability
65    #[serde(rename = "avail")]
66    pub available: bool,
67}
68
69/// Type that represents the &lt;cd&gt; tag for domain check response
70#[derive(Deserialize, Debug)]
71struct CheckResponseDataItem {
72    /// Data under the &lt;name&gt; tag
73    #[serde(rename = "name", alias = "id")]
74    pub resource: Available,
75    /// The reason for (un)availability
76    pub reason: Option<StringValue<'static>>,
77}
78
79/// Type that represents the &lt;chkData&gt; tag for host check response
80#[derive(Deserialize, Debug)]
81struct CheckData {
82    /// Data under the &lt;cd&gt; tag
83    #[serde(rename = "cd")]
84    pub list: Vec<CheckResponseDataItem>,
85}
86
87/// Type that represents the &lt;resData&gt; tag for host check response
88#[derive(Deserialize, Debug)]
89struct DeserializedCheckResponse {
90    /// Data under the &lt;chkData&gt; tag
91    #[serde(rename = "chkData")]
92    pub check_data: CheckData,
93}
94
95#[derive(Debug)]
96pub struct Checked {
97    pub id: String,
98    pub available: bool,
99    pub reason: Option<String>,
100}
101
102#[derive(Deserialize, Debug)]
103#[serde(from = "DeserializedCheckResponse")]
104pub struct CheckResponse {
105    pub list: Vec<Checked>,
106}
107
108impl From<DeserializedCheckResponse> for CheckResponse {
109    fn from(rsp: DeserializedCheckResponse) -> Self {
110        Self {
111            list: rsp
112                .check_data
113                .list
114                .into_iter()
115                .map(|item| Checked {
116                    id: item.resource.id.0.into_owned(),
117                    available: item.resource.available,
118                    reason: item.reason.map(|r| r.0.into_owned()),
119                })
120                .collect(),
121        }
122    }
123}
124
125/// The <option> type in EPP XML login requests
126#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
127#[serde(rename = "options")]
128pub struct Options<'a> {
129    /// The EPP version being used
130    pub version: StringValue<'a>,
131    /// The language that will be used during EPP transactions
132    pub lang: StringValue<'a>,
133}
134
135impl<'a> Options<'a> {
136    /// Creates an Options object with version and lang data
137    pub fn build(version: &'a str, lang: &'a str) -> Self {
138        Self {
139            version: version.into(),
140            lang: lang.into(),
141        }
142    }
143}
144
145/// The <svcExtension> type in EPP XML
146#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
147#[serde(rename = "svcExtension")]
148pub struct ServiceExtension<'a> {
149    /// The service extension URIs being represented by <extURI> in EPP XML
150    #[serde(rename = "extURI")]
151    pub ext_uris: Option<Vec<StringValue<'a>>>,
152}
153
154/// The <svcs> type in EPP XML
155#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
156pub struct Services<'a> {
157    /// The service URIs being used by this EPP session represented by <objURI> in EPP XML
158    #[serde(rename = "objURI")]
159    pub obj_uris: Vec<StringValue<'a>>,
160    /// The <svcExtention> being used in this EPP session
161    #[serde(rename = "svcExtension")]
162    pub svc_ext: Option<ServiceExtension<'a>>,
163}
164
165/// The &lt;hostAddr&gt; types domain or host transactions
166#[derive(Serialize, Deserialize, Debug)]
167pub(crate) struct HostAddr<'a> {
168    #[serde(rename = "ip")]
169    pub ip_version: Option<Cow<'a, str>>,
170    #[serde(rename = "$value")]
171    pub address: Cow<'a, str>,
172}
173
174impl From<&IpAddr> for HostAddr<'static> {
175    fn from(addr: &IpAddr) -> Self {
176        Self {
177            ip_version: Some(match addr {
178                IpAddr::V4(_) => "v4".into(),
179                IpAddr::V6(_) => "v6".into(),
180            }),
181            address: addr.to_string().into(),
182        }
183    }
184}
185
186pub(crate) fn serialize_host_addrs_option<T: AsRef<[IpAddr]>, S>(
187    addrs: &Option<T>,
188    ser: S,
189) -> Result<S::Ok, S::Error>
190where
191    S: serde::ser::Serializer,
192{
193    let addrs = match addrs {
194        Some(addrs) => addrs.as_ref(),
195        None => return ser.serialize_none(),
196    };
197
198    let mut seq = ser.serialize_seq(Some(addrs.len()))?;
199    for addr in addrs {
200        seq.serialize_element(&HostAddr::from(addr))?;
201    }
202    seq.end()
203}
204
205/// The &lt;status&gt; type on contact transactions
206#[derive(Serialize, Deserialize, Debug)]
207pub struct ObjectStatus<'a> {
208    /// The status name, represented by the 's' attr on &lt;status&gt; tags
209    #[serde(rename = "s")]
210    pub status: Cow<'a, str>,
211}
212
213/// This type contains a single DER-encoded X.509 certificate.
214///
215/// The rustls-pemfile crate can be used to parse a PEM file.
216pub struct Certificate(pub Vec<u8>);
217
218/// This type contains a DER-encoded ASN.1 private key in PKCS#8 or PKCS#1 format.
219///
220/// The rustls-pemfile crate can be used to parse a PEM file in these formats.
221pub struct PrivateKey(pub Vec<u8>);