Skip to main content

icann_rdap_client/md/
mod.rs

1//! Converts RDAP to Markdown.
2
3use {
4    crate::rdap::rr::RequestData,
5    buildstructor::Builder,
6    icann_rdap_common::{httpdata::HttpData, response::RdapResponse},
7    std::char,
8};
9
10pub mod autnum;
11pub mod domain;
12pub mod entity;
13pub mod error;
14pub mod help;
15pub mod nameserver;
16pub mod network;
17pub mod redacted;
18pub mod search;
19pub mod string;
20pub mod table;
21pub mod ttl;
22pub mod types;
23
24pub(crate) const _CODE_INDENT: &str = "    ";
25
26pub(crate) const HR: &str = "----------------------------------------\n";
27
28/// Specifies options for generating markdown.
29pub struct MdOptions {
30    /// If true, do not use Unicode characters.
31    pub no_unicode_chars: bool,
32
33    /// The character used for text styling of bold and italics.
34    pub text_style_char: char,
35
36    /// If true, headers use the hash marks or under lines.
37    pub hash_headers: bool,
38
39    /// If true, the text_style_char will appear in a justified text.
40    pub style_in_justify: bool,
41}
42
43impl Default for MdOptions {
44    fn default() -> Self {
45        Self {
46            no_unicode_chars: false,
47            text_style_char: '*',
48            hash_headers: true,
49            style_in_justify: false,
50        }
51    }
52}
53
54impl MdOptions {
55    /// Defaults for markdown that looks more like plain text.
56    pub fn plain_text() -> Self {
57        Self {
58            no_unicode_chars: true,
59            text_style_char: '_',
60            hash_headers: false,
61            style_in_justify: true,
62        }
63    }
64}
65
66#[derive(Clone, Copy)]
67pub struct MdParams<'a> {
68    pub heading_level: usize,
69    pub root: &'a RdapResponse,
70    pub http_data: &'a HttpData,
71    pub options: &'a MdOptions,
72    pub req_data: &'a RequestData,
73    pub show_rfc9537_redactions: bool,
74    pub highlight_simple_redactions: bool,
75}
76
77impl MdParams<'_> {
78    pub fn from_parent(&self) -> Self {
79        Self {
80            heading_level: self.heading_level,
81            root: self.root,
82            http_data: self.http_data,
83            options: self.options,
84            req_data: self.req_data,
85            show_rfc9537_redactions: self.show_rfc9537_redactions,
86            highlight_simple_redactions: self.highlight_simple_redactions,
87        }
88    }
89
90    pub fn next_level(&self) -> Self {
91        Self {
92            heading_level: self.heading_level + 1,
93            ..*self
94        }
95    }
96}
97
98pub trait ToMd {
99    fn to_md(&self, params: MdParams) -> String;
100}
101
102impl ToMd for RdapResponse {
103    fn to_md(&self, params: MdParams) -> String {
104        let mut md = String::new();
105        md.push_str(&params.http_data.to_md(params));
106        let variant_md = match &self {
107            Self::Entity(entity) => entity.to_md(params),
108            Self::Domain(domain) => domain.to_md(params),
109            Self::Nameserver(nameserver) => nameserver.to_md(params),
110            Self::Autnum(autnum) => autnum.to_md(params),
111            Self::Network(network) => network.to_md(params),
112            Self::DomainSearchResults(results) => results.to_md(params),
113            Self::EntitySearchResults(results) => results.to_md(params),
114            Self::NameserverSearchResults(results) => results.to_md(params),
115            Self::ErrorResponse(error) => error.to_md(params),
116            Self::Help(help) => help.to_md(params),
117        };
118        md.push_str(&variant_md);
119        md
120    }
121}
122
123pub trait MdUtil {
124    fn get_header_text(&self) -> MdHeaderText;
125}
126
127#[derive(Builder)]
128pub struct MdHeaderText {
129    header_text: String,
130    children: Vec<MdHeaderText>,
131}
132
133#[allow(clippy::to_string_trait_impl)]
134impl ToString for MdHeaderText {
135    fn to_string(&self) -> String {
136        self.header_text.clone()
137    }
138}
139
140impl MdUtil for RdapResponse {
141    fn get_header_text(&self) -> MdHeaderText {
142        match &self {
143            Self::Entity(entity) => entity.get_header_text(),
144            Self::Domain(domain) => domain.get_header_text(),
145            Self::Nameserver(nameserver) => nameserver.get_header_text(),
146            Self::Autnum(autnum) => autnum.get_header_text(),
147            Self::Network(network) => network.get_header_text(),
148            Self::DomainSearchResults(results) => results.get_header_text(),
149            Self::EntitySearchResults(results) => results.get_header_text(),
150            Self::NameserverSearchResults(results) => results.get_header_text(),
151            Self::ErrorResponse(error) => error.get_header_text(),
152            Self::Help(help) => help.get_header_text(),
153        }
154    }
155}