use {
crate::prelude::RdapResponse,
buildstructor::Builder,
serde::{Deserialize, Serialize},
std::{any::TypeId, fmt},
};
use crate::check::Checks;
#[derive(Builder, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct Name {
#[serde(rename = "description")]
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
pub type_field: Option<String>,
}
impl Name {
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
pub fn type_field(&self) -> Option<&str> {
self.type_field.as_deref()
}
}
#[derive(Builder, Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Default)]
pub struct Reason {
#[serde(rename = "description")]
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
pub type_field: Option<String>,
}
impl Reason {
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
pub fn type_field(&self) -> Option<&str> {
self.type_field.as_deref()
}
}
impl std::fmt::Display for Reason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let output = self.description.clone().unwrap_or_default();
write!(f, "{}", output)
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[derive(Default)]
pub enum Method {
#[default]
Removal,
EmptyValue,
PartialValue,
ReplacementValue,
}
#[derive(Builder, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct Redacted {
#[serde[rename = "name"]]
pub name: Name,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "reason")]
pub reason: Option<Reason>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "prePath")]
pub pre_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "postPath")]
pub post_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "pathLang")]
pub path_lang: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "replacementPath")]
pub replacement_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "method")]
pub method: Option<Method>,
}
impl Default for Name {
fn default() -> Self {
Self {
description: Some(String::default()),
type_field: None,
}
}
}
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Removal => write!(f, "Removal"),
Self::EmptyValue => write!(f, "EmptyValue"),
Self::PartialValue => write!(f, "PartialValue"),
Self::ReplacementValue => write!(f, "ReplacementValue"),
}
}
}
impl Redacted {
pub fn name(&self) -> &Name {
&self.name
}
pub fn reason(&self) -> Option<&Reason> {
self.reason.as_ref()
}
pub fn pre_path(&self) -> Option<&str> {
self.pre_path.as_deref()
}
pub fn post_path(&self) -> Option<&str> {
self.post_path.as_deref()
}
pub fn replacement_path(&self) -> Option<&str> {
self.replacement_path.as_deref()
}
pub fn path_lang(&self) -> Option<&str> {
self.path_lang.as_deref()
}
pub fn method(&self) -> Option<&Method> {
self.method.as_ref()
}
pub fn get_checks(&self, _check_params: crate::check::CheckParams<'_>) -> crate::check::Checks {
Checks {
rdap_struct: crate::check::RdapStructure::Redacted,
index: None,
items: vec![],
sub_checks: vec![],
}
}
pub fn get_type(&self) -> std::any::TypeId {
TypeId::of::<Self>()
}
}
static EMPTY_REDACTED_ARRAY: [Redacted; 0] = [];
pub fn redactions(rdap: &RdapResponse) -> &[Redacted] {
match rdap {
RdapResponse::Entity(entity) => {
entity.object_common.redacted.as_deref().unwrap_or_default()
}
RdapResponse::Domain(domain) => {
domain.object_common.redacted.as_deref().unwrap_or_default()
}
RdapResponse::Nameserver(nameserver) => nameserver
.object_common
.redacted
.as_deref()
.unwrap_or_default(),
RdapResponse::Autnum(autnum) => {
autnum.object_common.redacted.as_deref().unwrap_or_default()
}
RdapResponse::Network(network) => network
.object_common
.redacted
.as_deref()
.unwrap_or_default(),
_ => {
&EMPTY_REDACTED_ARRAY
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_redaction_builder() {
let name = Name {
description: Some("Registry Domain ID".to_string()),
type_field: None,
};
let redacted = Redacted::builder()
.name(name)
.reason(Reason::default())
.pre_path("$.handle".to_string())
.post_path("$.entities[?(@.roles[0]=='registrant'".to_string())
.path_lang("jsonpath".to_string())
.replacement_path(
"$.entities[?(@.roles[0]=='registrant')].vcardArray[1][?(@[0]=='contact-uri')]"
.to_string(),
)
.method(Method::Removal)
.build();
assert_eq!(
redacted.name.description,
Some("Registry Domain ID".to_string())
);
assert_eq!(redacted.pre_path, Some("$.handle".to_string()));
assert_eq!(
redacted.post_path,
Some("$.entities[?(@.roles[0]=='registrant'".to_string())
);
assert_eq!(redacted.path_lang, Some("jsonpath".to_string()));
assert_eq!(
redacted.replacement_path,
Some(
"$.entities[?(@.roles[0]=='registrant')].vcardArray[1][?(@[0]=='contact-uri')]"
.to_string()
)
);
assert_eq!(redacted.method, Some(Method::Removal));
}
#[test]
fn test_redaction_deserialization() {
let expected = r#"
{
"name": {
"type": "Registry Domain ID"
},
"prePath": "$.handle",
"pathLang": "jsonpath",
"postPath": "$.entities[?(@.roles[0]=='registrant'",
"replacementPath": "$.entities[?(@.roles[0]=='registrant')].vcardArray[1][?(@[0]=='contact-uri')]",
"method": "removal",
"reason": {
"description": "Server policy"
}
}
"#;
let name = Name {
type_field: Some("Registry Domain ID".to_string()),
description: None,
};
let reason: Reason = Reason {
description: Some("Server policy".to_string()),
type_field: None,
};
let mut sample_redact: Redacted = Redacted::builder()
.name(name)
.pre_path("$.handle".to_string())
.path_lang("jsonpath".to_string())
.post_path("$.entities[?(@.roles[0]=='registrant'".to_string())
.replacement_path(
"$.entities[?(@.roles[0]=='registrant')].vcardArray[1][?(@[0]=='contact-uri')]"
.to_string(),
)
.build();
sample_redact.method = Some(Method::Removal);
sample_redact.reason = Some(reason);
let actual: Result<Redacted, serde_json::Error> =
serde_json::from_str::<Redacted>(expected);
let actual: Redacted = actual.unwrap();
assert_eq!(actual, sample_redact); assert_eq!(
actual.name.type_field,
Some("Registry Domain ID".to_string())
);
assert_eq!(actual.pre_path, Some("$.handle".to_string()));
assert_eq!(
actual.post_path,
Some("$.entities[?(@.roles[0]=='registrant'".to_string())
);
assert_eq!(actual.path_lang, Some("jsonpath".to_string()));
assert_eq!(
actual.replacement_path,
Some(
"$.entities[?(@.roles[0]=='registrant')].vcardArray[1][?(@[0]=='contact-uri')]"
.to_string()
)
);
assert_eq!(actual.method, Some(Method::Removal));
}
}