1use std::borrow::Cow;
6use std::fmt;
7use std::str::FromStr;
8
9use instant_xml::{display_to_xml, from_xml_str, Deserializer, FromXml, Serializer, ToXml};
10
11pub mod check;
12pub use check::ContactCheck;
13
14pub mod create;
15pub use create::ContactCreate;
16
17pub mod delete;
18pub use delete::ContactDelete;
19
20pub mod info;
21pub use info::ContactInfo;
22
23pub mod update;
24pub use update::ContactUpdate;
25
26pub const XMLNS: &str = "urn:ietf:params:xml:ns:contact-1.0";
27
28#[derive(Clone, Debug)]
29pub struct Country(celes::Country);
30
31impl<'xml> FromXml<'xml> for Country {
32 fn matches(id: instant_xml::Id<'_>, _: Option<instant_xml::Id<'_>>) -> bool {
33 id == instant_xml::Id {
34 ns: XMLNS,
35 name: "cc",
36 }
37 }
38
39 fn deserialize<'cx>(
40 into: &mut Self::Accumulator,
41 field: &'static str,
42 deserializer: &mut instant_xml::Deserializer<'cx, 'xml>,
43 ) -> Result<(), instant_xml::Error> {
44 from_xml_str(into, field, deserializer)
45 }
46
47 type Accumulator = Option<Self>;
48 const KIND: instant_xml::Kind = instant_xml::Kind::Scalar;
49}
50
51impl ToXml for Country {
52 fn serialize<W: fmt::Write + ?Sized>(
53 &self,
54 field: Option<instant_xml::Id<'_>>,
55 serializer: &mut instant_xml::Serializer<W>,
56 ) -> Result<(), instant_xml::Error> {
57 display_to_xml(&self.0.alpha2, field, serializer)
58 }
59}
60
61impl FromStr for Country {
62 type Err = <celes::Country as FromStr>::Err;
63
64 fn from_str(s: &str) -> Result<Self, Self::Err> {
65 Ok(Self(celes::Country::from_str(s)?))
66 }
67}
68
69impl std::ops::Deref for Country {
70 type Target = celes::Country;
71
72 fn deref(&self) -> &Self::Target {
73 &self.0
74 }
75}
76
77#[derive(Clone, Debug, FromXml, PartialEq, ToXml)]
79#[xml(rename = "authInfo", ns(XMLNS))]
80pub struct ContactAuthInfo<'a> {
81 #[xml(rename = "pw")]
83 pub password: Cow<'a, str>,
84}
85
86impl<'a> ContactAuthInfo<'a> {
87 pub fn new(password: &'a str) -> Self {
89 Self {
90 password: password.into(),
91 }
92 }
93}
94
95#[derive(Clone, Debug, FromXml, PartialEq, ToXml)]
97#[xml(rename = "voice", ns(XMLNS))]
98pub struct Voice<'a> {
99 #[xml(rename = "x", attribute)]
101 pub extension: Option<Cow<'a, str>>,
102 #[xml(direct)]
104 pub number: Cow<'a, str>,
105}
106
107impl<'a> Voice<'a> {
108 pub fn new(number: &'a str) -> Self {
110 Self {
111 extension: None,
112 number: number.into(),
113 }
114 }
115
116 pub fn set_extension(&mut self, ext: &'a str) {
118 self.extension = Some(ext.into());
119 }
120}
121
122#[derive(Clone, Debug, FromXml, PartialEq, ToXml)]
124#[xml(rename = "fax", ns(XMLNS))]
125pub struct Fax<'a> {
126 #[xml(rename = "x", attribute)]
128 pub extension: Option<Cow<'a, str>>,
129 #[xml(direct)]
131 pub number: Cow<'a, str>,
132}
133
134impl<'a> Fax<'a> {
135 pub fn new(number: &'a str) -> Self {
137 Self {
138 extension: None,
139 number: number.into(),
140 }
141 }
142
143 pub fn set_extension(&mut self, ext: &'a str) {
145 self.extension = Some(ext.into());
146 }
147}
148
149#[derive(Clone, Debug, FromXml, ToXml)]
151#[xml(rename = "addr", ns(XMLNS))]
152pub struct Address<'a> {
153 pub street: Vec<Cow<'a, str>>,
155 pub city: Cow<'a, str>,
157 #[xml(rename = "sp")]
159 pub province: Option<Cow<'a, str>>,
160 #[xml(rename = "pc")]
162 pub postal_code: Option<Cow<'a, str>>,
163 #[xml(rename = "cc")]
165 pub country: Country,
166}
167
168impl<'a> Address<'a> {
169 pub fn new(
171 street: &[&'a str],
172 city: &'a str,
173 province: Option<&'a str>,
174 postal_code: Option<&'a str>,
175 country: Country,
176 ) -> Self {
177 let street = street.iter().map(|&s| s.into()).collect();
178
179 Self {
180 street,
181 city: city.into(),
182 province: province.map(|sp| sp.into()),
183 postal_code: postal_code.map(|pc| pc.into()),
184 country,
185 }
186 }
187}
188
189#[derive(Clone, Debug, FromXml, ToXml)]
191#[xml(rename = "postalInfo", ns(XMLNS))]
192pub struct PostalInfo<'a> {
193 #[xml(rename = "type", attribute)]
195 pub info_type: Cow<'a, str>,
196 pub name: Cow<'a, str>,
198 #[xml(rename = "org")]
200 pub organization: Option<Cow<'a, str>>,
201 pub address: Address<'a>,
203}
204
205impl<'a> PostalInfo<'a> {
206 pub fn new(
208 info_type: &'a str,
209 name: &'a str,
210 organization: Option<&'a str>,
211 address: Address<'a>,
212 ) -> Self {
213 Self {
214 info_type: info_type.into(),
215 name: name.into(),
216 organization: organization.map(|org| org.into()),
217 address,
218 }
219 }
220}
221
222#[derive(Clone, Copy, Debug, Eq, PartialEq)]
224pub enum Status {
225 ClientDeleteProhibited,
226 ServerDeleteProhibited,
227 ClientTransferProhibited,
228 ServerTransferProhibited,
229 ClientUpdateProhibited,
230 ServerUpdateProhibited,
231 Linked,
232 Ok,
233 PendingCreate,
234 PendingDelete,
235 PendingTransfer,
236 PendingUpdate,
237}
238
239impl Status {
240 pub fn as_str(&self) -> &'static str {
241 use Status::*;
242 match self {
243 ClientDeleteProhibited => "clientDeleteProhibited",
244 ServerDeleteProhibited => "serverDeleteProhibited",
245 ClientTransferProhibited => "clientTransferProhibited",
246 ServerTransferProhibited => "serverTransferProhibited",
247 ClientUpdateProhibited => "clientUpdateProhibited",
248 ServerUpdateProhibited => "serverUpdateProhibited",
249 Linked => "linked",
250 Ok => "ok",
251 PendingCreate => "pendingCreate",
252 PendingDelete => "pendingDelete",
253 PendingTransfer => "pendingTransfer",
254 PendingUpdate => "pendingUpdate",
255 }
256 }
257}
258
259impl ToXml for Status {
260 fn serialize<W: fmt::Write + ?Sized>(
261 &self,
262 _: Option<instant_xml::Id<'_>>,
263 serializer: &mut Serializer<W>,
264 ) -> Result<(), instant_xml::Error> {
265 serializer.write_start("status", XMLNS)?;
266 serializer.write_attr("s", XMLNS, &self.as_str())?;
267 serializer.end_empty()
268 }
269}
270
271impl<'xml> FromXml<'xml> for Status {
272 fn matches(id: instant_xml::Id<'_>, _: Option<instant_xml::Id<'_>>) -> bool {
273 id == instant_xml::Id {
274 ns: XMLNS,
275 name: "status",
276 }
277 }
278
279 fn deserialize<'cx>(
280 into: &mut Self::Accumulator,
281 field: &'static str,
282 deserializer: &mut Deserializer<'cx, 'xml>,
283 ) -> Result<(), instant_xml::Error> {
284 use instant_xml::de::Node;
285 use instant_xml::{Error, Id};
286
287 let node = match deserializer.next() {
288 Some(result) => result?,
289 None => return Err(Error::MissingValue(field)),
290 };
291
292 let attr = match node {
293 Node::Attribute(attr) => attr,
294 Node::Open(_) | Node::Text(_) => return Err(Error::MissingValue(field)),
295 node => return Err(Error::UnexpectedNode(format!("{node:?} in Status"))),
296 };
297
298 let id = deserializer.attribute_id(&attr)?;
299 let expected = Id { ns: "", name: "s" };
300 if id != expected {
301 return Err(Error::MissingValue(field));
302 }
303
304 *into = Some(match attr.value.as_ref() {
305 "clientDeleteProhibited" => Self::ClientDeleteProhibited,
306 "serverDeleteProhibited" => Self::ServerDeleteProhibited,
307 "clientTransferProhibited" => Self::ClientTransferProhibited,
308 "serverTransferProhibited" => Self::ServerTransferProhibited,
309 "clientUpdateProhibited" => Self::ClientUpdateProhibited,
310 "serverUpdateProhibited" => Self::ServerUpdateProhibited,
311 "linked" => Self::Linked,
312 "ok" => Self::Ok,
313 "pendingCreate" => Self::PendingCreate,
314 "pendingDelete" => Self::PendingDelete,
315 "pendingTransfer" => Self::PendingTransfer,
316 "pendingUpdate" => Self::PendingUpdate,
317 val => return Err(Error::UnexpectedValue(format!("invalid status {val:?}"))),
318 });
319
320 deserializer.ignore()?;
321 Ok(())
322 }
323
324 type Accumulator = Option<Self>;
325 const KIND: instant_xml::Kind = instant_xml::Kind::Element;
326}