1use std::{
2 net::IpAddr,
3 path::PathBuf,
4 time::{Duration, SystemTime},
5};
6
7use {
8 buildstructor::Builder,
9 icann_rdap_common::{
10 prelude::Numberish,
11 response::{
12 Autnum, Cidr0Cidr, Domain, Entity, GetSelfLink, Nameserver, Network, RdapResponse,
13 SelfLink,
14 },
15 },
16 ipnet::{IpNet, Ipv4Subnets, Ipv6Subnets},
17 serde::{Deserialize, Serialize},
18 serde_json::Value,
19 tokio::time::sleep,
20 tracing::{debug, info, warn},
21};
22
23use strum::Display as EnumDisplay;
24
25use crate::{
26 config::ServiceConfig,
27 error::RdapServerError,
28 storage::{StoreOps, TxHandle},
29};
30
31pub const UPDATE: &str = "update";
32pub const RELOAD: &str = "reload";
33
34#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, EnumDisplay)]
35#[serde(untagged)]
36pub enum Template {
37 Domain {
38 domain: DomainOrError,
39 ids: Vec<DomainId>,
40 },
41 Entity {
42 entity: EntityOrError,
43 ids: Vec<EntityId>,
44 },
45 Nameserver {
46 nameserver: NameserverOrError,
47 ids: Vec<NameserverId>,
48 },
49 Autnum {
50 autnum: AutnumOrError,
51 ids: Vec<AutnumId>,
52 },
53 Network {
54 network: NetworkOrError,
55 ids: Vec<NetworkId>,
56 },
57}
58
59#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
60pub enum DomainOrError {
61 #[serde(rename = "object")]
62 DomainObject(Box<Domain>),
63 #[serde(rename = "error")]
64 ErrorResponse(icann_rdap_common::response::Rfc9083Error),
65}
66
67#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
68pub enum EntityOrError {
69 #[serde(rename = "object")]
70 EntityObject(Box<Entity>),
71 #[serde(rename = "error")]
72 ErrorResponse(icann_rdap_common::response::Rfc9083Error),
73}
74
75#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
76pub enum NameserverOrError {
77 #[serde(rename = "object")]
78 NameserverObject(Box<Nameserver>),
79 #[serde(rename = "error")]
80 ErrorResponse(icann_rdap_common::response::Rfc9083Error),
81}
82
83#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
84pub enum AutnumOrError {
85 #[serde(rename = "object")]
86 AutnumObject(Box<Autnum>),
87 #[serde(rename = "error")]
88 ErrorResponse(icann_rdap_common::response::Rfc9083Error),
89}
90
91#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
92pub enum NetworkOrError {
93 #[serde(rename = "object")]
94 NetworkObject(Box<Network>),
95 #[serde(rename = "error")]
96 ErrorResponse(icann_rdap_common::response::Rfc9083Error),
97}
98
99#[derive(Clone, Serialize, Deserialize, Builder, Debug, PartialEq, Eq)]
100pub struct DomainId {
101 #[serde(rename = "ldhName")]
102 pub ldh_name: String,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 #[serde(rename = "unicodeName")]
105 pub unicode_name: Option<String>,
106}
107
108#[derive(Clone, Serialize, Deserialize, Builder, Debug, PartialEq, Eq)]
109pub struct EntityId {
110 pub handle: String,
111}
112
113#[derive(Clone, Serialize, Deserialize, Builder, Debug, PartialEq, Eq)]
114pub struct NameserverId {
115 #[serde(rename = "ldhName")]
116 pub ldh_name: String,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 #[serde(rename = "unicodeName")]
119 pub unicode_name: Option<String>,
120}
121
122#[derive(Clone, Serialize, Deserialize, Builder, Debug, PartialEq, Eq)]
123pub struct AutnumId {
124 #[serde(rename = "startAutnum")]
125 pub start_autnum: u32,
126 #[serde(rename = "endAutnum")]
127 pub end_autnum: u32,
128}
129
130#[derive(Clone, Serialize, Deserialize, Builder, Debug, PartialEq, Eq)]
131pub struct NetworkId {
132 #[serde(rename = "networkId")]
133 pub network_id: NetworkIdType,
134}
135
136#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
137#[serde(untagged)]
138pub enum NetworkIdType {
139 Cidr(IpNet),
140 Range {
141 #[serde(rename = "startAddress")]
142 start_address: String,
143 #[serde(rename = "endAddress")]
144 end_address: String,
145 },
146}
147
148pub async fn load_data(
176 config: &ServiceConfig,
177 store: &dyn StoreOps,
178 truncate: bool,
179) -> Result<(), RdapServerError> {
180 let mut json_count: usize = 0;
181 let mut template_count: usize = 0;
182 let mut srvhelp_count: usize = 0;
183 let mut tx = if truncate {
184 store.new_truncate_tx().await?
185 } else {
186 store.new_tx().await?
187 };
188 let path = PathBuf::from(&config.data_dir);
189 if !path.exists() || !path.is_dir() {
190 warn!(
191 "Directory {} does not exist or is not a directory. Server has no content to serve.",
192 path.to_string_lossy()
193 );
194 return Ok(());
195 }
196
197 let mut entries = tokio::fs::read_dir(path).await?;
198 while let Some(entry) = entries.next_entry().await? {
199 let entry_path = entry.path();
200 let contents = tokio::fs::read_to_string(&entry_path).await?;
201 if entry_path.extension().is_some_and(|ext| ext == "template") {
202 load_rdap_template(&contents, &entry_path.to_string_lossy(), &mut tx).await?;
203 template_count += 1;
204 } else if entry_path.extension().is_some_and(|ext| ext == "json") {
205 load_rdap(&contents, &entry_path.to_string_lossy(), &mut tx).await?;
206 json_count += 1;
207 } else if entry_path.extension().is_some_and(|ext| ext == "help") {
208 load_srvhelp(
209 &contents,
210 &entry_path.to_string_lossy(),
211 &entry.file_name().to_string_lossy(),
212 &mut tx,
213 )
214 .await?;
215 srvhelp_count += 1;
216 }
217 }
218
219 info!("{json_count} RDAP JSON files loaded.");
220 info!("{template_count} RDAP template files loaded.");
221 info!("{srvhelp_count} RDAP server help files loaded.");
222 if json_count == 0 && template_count == 0 && srvhelp_count == 0 {
223 warn!("No data loaded. Server has no content to serve.");
224 }
225 tx.commit().await?;
226 Ok(())
227}
228
229async fn load_rdap(
231 contents: &str,
232 path_name: &str,
233 tx: &mut Box<dyn TxHandle>,
234) -> Result<(), RdapServerError> {
235 debug!("loading {path_name} into storage");
236 let json = serde_json::from_str::<Value>(contents);
237 let Ok(value) = json else {
238 return Err(RdapServerError::NonJsonFile(path_name.to_owned()));
239 };
240 let rdap = RdapResponse::try_from(value);
241 let Ok(rdap) = rdap else {
242 return Err(RdapServerError::NonRdapJsonFile(path_name.to_owned()));
243 };
244 match rdap {
245 RdapResponse::Entity(entity) => tx.add_entity(&entity).await,
246 RdapResponse::Domain(domain) => tx.add_domain(&domain).await,
247 RdapResponse::Nameserver(nameserver) => tx.add_nameserver(&nameserver).await,
248 RdapResponse::Autnum(autnum) => tx.add_autnum(&autnum).await,
249 RdapResponse::Network(network) => tx.add_network(&network).await,
250 _ => return Err(RdapServerError::NonRdapJsonFile(path_name.to_owned())),
251 }?;
252 Ok(())
253}
254
255async fn load_srvhelp(
257 contents: &str,
258 path_name: &str,
259 file_name: &str,
260 tx: &mut Box<dyn TxHandle>,
261) -> Result<(), RdapServerError> {
262 debug!("loading {path_name} into storage");
263 let Some(host) = file_name.strip_suffix(".help") else {
264 return Err(RdapServerError::NonRdapJsonFile(path_name.to_string()));
265 };
266 let host = host.replace('_', ".");
267 let json = serde_json::from_str::<Value>(contents);
268 let Ok(value) = json else {
269 return Err(RdapServerError::NonJsonFile(path_name.to_owned()));
270 };
271 let rdap = RdapResponse::try_from(value);
272 let Ok(rdap) = rdap else {
273 return Err(RdapServerError::NonRdapJsonFile(path_name.to_owned()));
274 };
275 match rdap {
276 RdapResponse::Help(srvhelp) => tx.add_srv_help(&srvhelp, Some(&host)).await,
277 _ => return Err(RdapServerError::NonRdapJsonFile(path_name.to_owned())),
278 }?;
279 Ok(())
280}
281
282async fn load_rdap_template(
285 contents: &str,
286 path_name: &str,
287 tx: &mut Box<dyn TxHandle>,
288) -> Result<(), RdapServerError> {
289 debug!("processing {path_name} template");
290 let json = serde_json::from_str::<Template>(contents);
291 if let Ok(value) = json {
292 match value {
293 Template::Domain { domain, ids } => {
294 for id in ids {
295 debug!("adding domain from template for {id:?}");
296 match &domain {
297 DomainOrError::DomainObject(domain) => {
298 let domain = make_domain_from_template(domain, id);
299 tx.add_domain(&domain).await?;
300 }
301 DomainOrError::ErrorResponse(error) => {
302 tx.add_domain_err(&id, error).await?;
303 }
304 };
305 }
306 }
307 Template::Entity { entity, ids } => {
308 for id in ids {
309 debug!("adding entity from template for {id:?}");
310 match &entity {
311 EntityOrError::EntityObject(entity) => {
312 let entity = make_entity_from_template(entity, id);
313 tx.add_entity(&entity).await?;
314 }
315 EntityOrError::ErrorResponse(error) => {
316 tx.add_entity_err(&id, error).await?;
317 }
318 };
319 }
320 }
321 Template::Nameserver { nameserver, ids } => {
322 for id in ids {
323 debug!("adding nameserver from template for {id:?}");
324 match &nameserver {
325 NameserverOrError::NameserverObject(nameserver) => {
326 let nameserver = make_nameserver_from_template(nameserver, id);
327 tx.add_nameserver(&nameserver).await?;
328 }
329 NameserverOrError::ErrorResponse(error) => {
330 tx.add_nameserver_err(&id, error).await?;
331 }
332 };
333 }
334 }
335 Template::Autnum { autnum, ids } => {
336 for id in ids {
337 debug!("adding autnum from template for {id:?}");
338 match &autnum {
339 AutnumOrError::AutnumObject(autnum) => {
340 let autnum = make_autnum_from_template(autnum, id);
341 tx.add_autnum(&autnum).await?;
342 }
343 AutnumOrError::ErrorResponse(error) => {
344 tx.add_autnum_err(&id, error).await?;
345 }
346 };
347 }
348 }
349 Template::Network { network, ids } => {
350 for id in ids {
351 debug!("adding network from template for {id:?}");
352 match &network {
353 NetworkOrError::NetworkObject(network) => {
354 let network = make_network_from_template(network, id)?;
355 tx.add_network(&network).await?;
356 }
357 NetworkOrError::ErrorResponse(error) => {
358 tx.add_network_err(&id, error).await?;
359 }
360 };
361 }
362 }
363 };
364 } else {
365 return Err(RdapServerError::NonJsonFile(path_name.to_owned()));
366 }
367 Ok(())
368}
369
370pub(crate) async fn reload_data(
371 store: Box<dyn StoreOps>,
372 config: ServiceConfig,
373) -> Result<(), RdapServerError> {
374 let update_path = PathBuf::from(&config.data_dir);
375 let update_path = update_path.join(UPDATE);
376 let reload_path = PathBuf::from(&config.data_dir);
377 let reload_path = reload_path.join(RELOAD);
378 let mut last_time = SystemTime::now();
379 loop {
380 sleep(Duration::from_millis(1000)).await;
381 let update_meta = tokio::fs::metadata(&update_path).await;
382 if let Ok(update_meta) = update_meta {
383 let modified = update_meta.modified()?;
384 if modified > last_time {
385 last_time = modified;
386 info!("Data being updated.");
387 load_data(&config, &*store, false).await?;
388 }
389 };
390 let reload_meta = tokio::fs::metadata(&reload_path).await;
391 if let Ok(reload_meta) = reload_meta {
392 let modified = reload_meta.modified()?;
393 if modified > last_time {
394 last_time = modified;
395 info!("Data being reloaded.");
396 load_data(&config, &*store, true).await?;
397 }
398 };
399 }
400}
401
402pub async fn trigger_reload(data_dir: &str) -> Result<(), RdapServerError> {
403 let reload_path = PathBuf::from(&data_dir);
404 let reload_path = reload_path.join(RELOAD);
405 tokio::fs::File::create(reload_path).await?;
406 Ok(())
407}
408
409pub async fn trigger_update(data_dir: &str) -> Result<(), RdapServerError> {
410 let update_path = PathBuf::from(&data_dir);
411 let update_path = update_path.join(UPDATE);
412 tokio::fs::File::create(update_path).await?;
413 Ok(())
414}
415
416fn change_self_link<T: GetSelfLink + SelfLink>(mut object: T, segment: &str, id: &str) -> T {
417 if let Some(self_link) = object.self_link() {
418 if let Some(self_href) = &self_link.href {
419 if let Some(self_href_split) = self_href.rsplit_once(segment) {
420 let mut new_self_link = self_link.clone();
421 new_self_link.href = Some(format!("{}{segment}/{}", self_href_split.0, id));
422 object = object.with_self_link(new_self_link);
423 } else {
424 warn!("Unable to rewrite self link for {segment} {}", id);
425 }
426 } else {
427 warn!("Unable to use self link because it has not href")
428 }
429 } else {
430 warn!("No self link for {segment} {}", id);
431 }
432 object
433}
434
435fn make_domain_from_template(domain: &Domain, id: DomainId) -> Domain {
436 let mut domain = domain.clone();
437 domain = change_self_link(domain, "domain", &id.ldh_name);
438 domain.ldh_name = Some(id.ldh_name);
439 if let Some(unicode_name) = id.unicode_name {
440 domain.unicode_name = Some(unicode_name);
441 };
442 domain
443}
444
445fn make_entity_from_template(entity: &Entity, id: EntityId) -> Entity {
446 let mut entity = entity.clone();
447 entity = change_self_link(entity, "entity", &id.handle);
448 entity.object_common.handle = Some(id.handle.into());
449 entity
450}
451
452fn make_nameserver_from_template(nameserver: &Nameserver, id: NameserverId) -> Nameserver {
453 let mut nameserver = nameserver.clone();
454 nameserver = change_self_link(nameserver, "nameserver", &id.ldh_name);
455 nameserver.ldh_name = Some(id.ldh_name);
456 if let Some(unicode_name) = id.unicode_name {
457 nameserver.unicode_name = Some(unicode_name);
458 };
459 nameserver
460}
461
462fn make_autnum_from_template(autnum: &Autnum, id: AutnumId) -> Autnum {
463 let mut autnum = autnum.clone();
464 autnum = change_self_link(autnum, "autnum", &id.start_autnum.to_string());
465 autnum.start_autnum = Some(Numberish::<u32>::from(id.start_autnum));
466 autnum.end_autnum = Some(Numberish::<u32>::from(id.end_autnum));
467 autnum
468}
469
470fn make_network_from_template(
471 network: &Network,
472 id: NetworkId,
473) -> Result<Network, RdapServerError> {
474 let mut network = network.clone();
475 match id.network_id {
476 NetworkIdType::Cidr(cidr) => match cidr {
477 IpNet::V4(v4) => {
478 network.start_address = Some(v4.network().to_string());
479 network.end_address = Some(v4.broadcast().to_string());
480 network.ip_version = Some("v4".to_string().into());
481 network.cidr0_cidrs = Some(vec![
482 Cidr0Cidr::v4()
483 .prefix(v4.network().to_string())
484 .length(v4.prefix_len())
485 .build(),
486 ])
487 }
488 IpNet::V6(v6) => {
489 network.start_address = Some(v6.network().to_string());
490 network.end_address = Some(v6.broadcast().to_string());
491 network.ip_version = Some("v6".to_string().into());
492 network.cidr0_cidrs = Some(vec![
493 Cidr0Cidr::v6()
494 .prefix(v6.network().to_string())
495 .length(v6.prefix_len())
496 .build(),
497 ]);
498 }
499 },
500 NetworkIdType::Range {
501 start_address,
502 end_address,
503 } => {
504 let addr: IpAddr = start_address.parse()?;
505 if addr.is_ipv4() {
506 network.ip_version = Some("v4".to_string().into());
507 network.cidr0_cidrs = Some(
508 Ipv4Subnets::new(start_address.parse()?, end_address.parse()?, 0)
509 .map(|net| {
510 Cidr0Cidr::v4()
511 .prefix(net.network().to_string())
512 .length(net.prefix_len())
513 .build()
514 })
515 .collect::<Vec<Cidr0Cidr>>(),
516 );
517 } else {
518 network.ip_version = Some("v6".to_string().into());
519 network.cidr0_cidrs = Some(
520 Ipv6Subnets::new(start_address.parse()?, end_address.parse()?, 0)
521 .map(|net| {
522 Cidr0Cidr::v6()
523 .prefix(net.network().to_string())
524 .length(net.prefix_len())
525 .build()
526 })
527 .collect::<Vec<Cidr0Cidr>>(),
528 );
529 }
530 network.start_address = Some(start_address);
531 network.end_address = Some(end_address);
532 }
533 }
534 let first_cidr = network
535 .cidr0_cidrs
536 .as_ref()
537 .expect("cidrs should be on network")
538 .first()
539 .map(|cidr| cidr.to_string())
540 .expect("cidrs on network are empty");
541 network = change_self_link(network, "ip", &first_cidr);
542 Ok(network)
543}
544
545#[cfg(test)]
546mod tests {
547
548 use icann_rdap_common::{
549 prelude::Stringish,
550 response::{Domain, Link},
551 };
552
553 use super::*;
554
555 #[test]
556 fn test_template_domain_serialize() {
557 let template = Template::Domain {
559 domain: DomainOrError::DomainObject(Box::new(
560 Domain::response_obj().ldh_name("foo.example").build(),
561 )),
562 ids: vec![DomainId::builder().ldh_name("bar.example").build()],
563 };
564
565 let actual = serde_json::to_string(&template).expect("serializing template");
567
568 assert_eq!(
570 actual,
571 r#"{"domain":{"object":{"rdapConformance":["rdap_level_0"],"objectClassName":"domain","ldhName":"foo.example"}},"ids":[{"ldhName":"bar.example"}]}"#
572 );
573 }
574
575 #[test]
576 fn test_template_domain_text_deserialize() {
577 let json_text = r#"{"domain":{"object":{"rdapConformance":["rdap_level_0"],"objectClassName":"domain","ldhName":"foo.example"}},"ids":[{"ldhName":"bar.example"}]}"#;
579
580 let actual: Template = serde_json::from_str(json_text).expect("deserializing template");
582
583 let expected = Template::Domain {
585 domain: DomainOrError::DomainObject(Box::new(
586 Domain::response_obj().ldh_name("foo.example").build(),
587 )),
588 ids: vec![DomainId::builder().ldh_name("bar.example").build()],
589 };
590 assert_eq!(actual, expected);
591 }
592
593 #[test]
594 fn test_template_network_with_cidr_serialize() {
595 let template = Template::Network {
597 network: NetworkOrError::NetworkObject(Box::new(
598 Network::response_obj()
599 .cidr("10.0.0.0/24")
600 .build()
601 .expect("cidr parsing"),
602 )),
603 ids: vec![
604 NetworkId::builder()
605 .network_id(NetworkIdType::Cidr(
606 "10.0.0.0/24".parse().expect("ipnet parsing"),
607 ))
608 .build(),
609 ],
610 };
611
612 let actual = serde_json::to_string(&template).expect("serializing template");
614 let actual: Template = serde_json::from_str(&actual).expect("deserialize network template");
615
616 let expected = r#"
618 {
619 "network":{
620 "object":{
621 "rdapConformance":["cidr0","rdap_level_0"],
622 "objectClassName":"ip network",
623 "startAddress":"10.0.0.0",
624 "endAddress":"10.0.0.255",
625 "ipVersion":"v4",
626 "cidr0_cidrs":[{"v4prefix":"10.0.0.0","length":24}]
627 }
628 },
629 "ids":[
630 {"networkId":"10.0.0.0/24"}
631 ]
632 }"#;
633 let expected: Template = serde_json::from_str(expected).expect("deserialize expected");
634 assert_eq!(actual, expected);
635 }
636
637 #[test]
638 fn test_template_network_with_start_and_end_serialize() {
639 let template = Template::Network {
641 network: NetworkOrError::NetworkObject(Box::new(
642 Network::response_obj()
643 .cidr("10.0.0.0/24")
644 .build()
645 .expect("cidr parsing"),
646 )),
647 ids: vec![
648 NetworkId::builder()
649 .network_id(NetworkIdType::Range {
650 start_address: "10.0.0.0".to_string(),
651 end_address: "10.0.0.255".to_string(),
652 })
653 .build(),
654 ],
655 };
656
657 let actual = serde_json::to_string(&template).expect("serializing template");
659 let actual: Template = serde_json::from_str(&actual).expect("deserialize network template");
660
661 let expected = r#"
663 {
664 "network":{
665 "object":{
666 "rdapConformance":["cidr0","rdap_level_0"],
667 "objectClassName":"ip network",
668 "startAddress":"10.0.0.0",
669 "endAddress":"10.0.0.255",
670 "ipVersion":"v4",
671 "cidr0_cidrs":[
672 {"v4prefix":"10.0.0.0","length":24}
673 ]
674 }
675 },
676 "ids":[
677 {"networkId":
678 {"startAddress":"10.0.0.0","endAddress":"10.0.0.255"}
679 }
680 ]
681 }"#;
682 let expected: Template = serde_json::from_str(expected).expect("deserialize expected");
683 assert_eq!(actual, expected);
684 }
685
686 #[test]
687 fn test_template_network_with_cidr_text_deserialize() {
688 let text = r#"
690 {
691 "network":{
692 "object":{
693 "rdapConformance":["cidr0","rdap_level_0"],
694 "objectClassName":"ip network",
695 "startAddress":"10.0.0.0",
696 "endAddress":"10.0.0.255",
697 "ipVersion":"v4",
698 "cidr0_cidrs":[{"v4prefix":"10.0.0.0","length":24}]
699 }
700 },
701 "ids":[
702 {"networkId":"10.0.0.0/24"}
703 ]
704 }"#;
705
706 let actual: Template = serde_json::from_str(text).expect("deserialize network template");
708
709 let expected = Template::Network {
711 network: NetworkOrError::NetworkObject(Box::new(
712 Network::response_obj()
713 .cidr("10.0.0.0/24")
714 .build()
715 .expect("cidr parsing"),
716 )),
717 ids: vec![
718 NetworkId::builder()
719 .network_id(NetworkIdType::Cidr(
720 "10.0.0.0/24".parse().expect("ipnet parsing"),
721 ))
722 .build(),
723 ],
724 };
725 assert_eq!(actual, expected);
726 }
727
728 #[test]
729 fn test_template_network_with_range_text_deserialize() {
730 let text = r#"
732 {
733 "network":{
734 "object":{
735 "rdapConformance":["cidr0","rdap_level_0"],
736 "objectClassName":"ip network",
737 "startAddress":"10.0.0.0",
738 "endAddress":"10.0.0.255",
739 "ipVersion":"v4",
740 "cidr0_cidrs":[
741 {"v4prefix":"10.0.0.0","length":24}
742 ]
743 }
744 },
745 "ids":[
746 {"networkId":
747 {"startAddress":"10.0.0.0","endAddress":"10.0.0.255"}
748 }
749 ]
750 }"#;
751
752 let actual: Template = serde_json::from_str(text).expect("deserialize network template");
754
755 let expected = Template::Network {
757 network: NetworkOrError::NetworkObject(Box::new(
758 Network::response_obj()
759 .cidr("10.0.0.0/24")
760 .build()
761 .expect("cidr parsing"),
762 )),
763 ids: vec![
764 NetworkId::builder()
765 .network_id(NetworkIdType::Range {
766 start_address: "10.0.0.0".to_string(),
767 end_address: "10.0.0.255".to_string(),
768 })
769 .build(),
770 ],
771 };
772 assert_eq!(actual, expected);
773 }
774
775 #[test]
776 fn test_make_domain_with_domain_and_id() {
777 let domain = Domain::builder()
779 .ldh_name("foo.example")
780 .link(
781 Link::builder()
782 .rel("self")
783 .href("http://reg.example/domain/foo.example")
784 .value("http://reg.example/domain/foo.example")
785 .build(),
786 )
787 .build();
788 let id = DomainId::builder().ldh_name("bar.example").build();
789
790 let actual = make_domain_from_template(&domain, id);
792
793 assert_eq!(
795 actual.ldh_name.as_ref().expect("no ldhname on domain"),
796 "bar.example"
797 );
798 let self_link = actual.self_link().expect("self link messing");
799 assert_eq!(
800 self_link.href.as_ref().expect("link has no href"),
801 "http://reg.example/domain/bar.example"
802 );
803 }
804
805 #[test]
806 fn test_make_entity_with_entity_and_id() {
807 let entity = Entity::builder()
809 .handle("foo")
810 .link(
811 Link::builder()
812 .rel("self")
813 .href("http://reg.example/entity/foo")
814 .value("http://reg.example/entity/foo")
815 .build(),
816 )
817 .build();
818 let id = EntityId::builder().handle("bar").build();
819
820 let actual = make_entity_from_template(&entity, id);
822
823 assert_eq!(
825 actual
826 .object_common
827 .handle
828 .as_ref()
829 .expect("no handle on entity"),
830 &Stringish::from("bar")
831 );
832 let self_link = actual.self_link().expect("self link messing");
833 assert_eq!(
834 self_link.href.as_ref().expect("link has no href"),
835 "http://reg.example/entity/bar"
836 );
837 }
838
839 #[test]
840 fn test_make_ns_with_nameserver_and_id() {
841 let nameserver = Nameserver::builder()
843 .ldh_name("ns.foo.example")
844 .link(
845 Link::builder()
846 .rel("self")
847 .href("http://reg.example/nameserver/ns.foo.example")
848 .value("http://reg.example/nameserver/ns.foo.example")
849 .build(),
850 )
851 .build()
852 .expect("creation of nameserver");
853 let id = NameserverId::builder().ldh_name("ns.bar.example").build();
854
855 let actual = make_nameserver_from_template(&nameserver, id);
857
858 assert_eq!(
860 actual.ldh_name.as_ref().expect("no ldhname on nameserver"),
861 "ns.bar.example"
862 );
863 let self_link = actual.self_link().expect("self link messing");
864 assert_eq!(
865 self_link.href.as_ref().expect("link has no href"),
866 "http://reg.example/nameserver/ns.bar.example"
867 );
868 }
869
870 #[test]
871 fn test_make_autnum_with_autnum_and_id() {
872 let autnum = Autnum::builder()
874 .autnum_range(700..710)
875 .link(
876 Link::builder()
877 .rel("self")
878 .href("http://reg.example/autnum/700")
879 .value("http://reg.example/autnum/700")
880 .build(),
881 )
882 .build();
883 let id = AutnumId::builder()
884 .start_autnum(900)
885 .end_autnum(999)
886 .build();
887
888 let actual = make_autnum_from_template(&autnum, id);
890
891 assert_eq!(
893 *actual.start_autnum.as_ref().expect("no startnum on autnum"),
894 Numberish::<u32>::from(900)
895 );
896 assert_eq!(
897 *actual.end_autnum.as_ref().expect("no end on autnum"),
898 Numberish::<u32>::from(999)
899 );
900 let self_link = actual.self_link().expect("self link messing");
901 assert_eq!(
902 self_link.href.as_ref().expect("link has href"),
903 "http://reg.example/autnum/900"
904 );
905 }
906
907 #[test]
908 fn test_make_network_with_id_and_range() {
909 let network = Network::builder()
911 .cidr("10.0.0.0/24")
912 .link(
913 Link::builder()
914 .rel("self")
915 .href("http://reg.example/ip/10.0.0.0/24")
916 .value("http://reg.example/ip/10.0.0.0/24")
917 .build(),
918 )
919 .build()
920 .expect("creating test network");
921 let id = NetworkId::builder()
922 .network_id(NetworkIdType::Range {
923 start_address: "11.0.0.0".to_string(),
924 end_address: "11.0.0.255".to_string(),
925 })
926 .build();
927
928 let actual = make_network_from_template(&network, id).expect("unable to make network");
930
931 assert_eq!(
933 actual
934 .start_address
935 .as_ref()
936 .expect("no start address on network"),
937 "11.0.0.0"
938 );
939 assert_eq!(
940 actual
941 .end_address
942 .as_ref()
943 .expect("no end address on network"),
944 "11.0.0.255"
945 );
946 let cidr0 = actual.cidr0_cidrs.as_ref().expect("no cidr0");
947 let v4cidr = cidr0.first().expect("cidr0 is empty");
948 assert_eq!(v4cidr.prefix(), Some("11.0.0.0".to_string()));
949 assert_eq!(v4cidr.length(), Some(24));
950 let self_link = actual.self_link().expect("self link messing");
951 assert_eq!(
952 self_link.href.as_ref().expect("link has no href"),
953 "http://reg.example/ip/11.0.0.0/24"
954 );
955 }
956
957 #[test]
958 fn test_make_network_with_cidr_and_id() {
959 let network = Network::builder()
961 .cidr("10.0.0.0/24")
962 .link(
963 Link::builder()
964 .rel("self")
965 .href("http://reg.example/ip/10.0.0.0/24")
966 .value("http://reg.example/ip/10.0.0.0/24")
967 .build(),
968 )
969 .build()
970 .expect("creating test network");
971 let id = NetworkId::builder()
972 .network_id(NetworkIdType::Cidr(IpNet::V4(
973 "11.0.0.0/24".parse().expect("bad cidr"),
974 )))
975 .build();
976
977 let actual = make_network_from_template(&network, id).expect("unable to make network");
979
980 assert_eq!(
982 actual
983 .start_address
984 .as_ref()
985 .expect("no start address on network"),
986 "11.0.0.0"
987 );
988 assert_eq!(
989 actual
990 .end_address
991 .as_ref()
992 .expect("no end address on network"),
993 "11.0.0.255"
994 );
995 let cidr0 = actual.cidr0_cidrs.as_ref().expect("no cidr0");
996 let v4cidr = cidr0.first().expect("cidr0 is empty");
997 assert_eq!(v4cidr.prefix(), Some("11.0.0.0".to_string()));
998 assert_eq!(v4cidr.length(), Some(24));
999 let self_link = actual.self_link().expect("self link messing");
1000 assert_eq!(
1001 self_link.href.as_ref().expect("link has no href"),
1002 "http://reg.example/ip/11.0.0.0/24"
1003 );
1004 }
1005}