rpsl/parser/api.rs
1use winnow::{
2 ascii::multispace0,
3 combinator::{delimited, repeat},
4 error::ContextError,
5 Parser,
6};
7
8use super::core::{object_block, object_block_padded};
9use crate::{Object, ParseError};
10
11/// Parse RPSL into an [`Object`], borrowing from the source.
12///
13/// ```text
14/// role: ACME Company
15/// address: Packet Street 6
16/// address: 128 Series of Tubes
17/// address: Internet
18/// email: rpsl-rs@github.com
19/// nic-hdl: RPSL1-RIPE
20/// source: RIPE
21/// ↓
22/// role: ACME Company ◀─────────────── &"role": &"ACME Company"
23/// address: Packet Street 6 ◀──────────── &"address": &"Packet Street 6"
24/// address: 128 Series of Tubes ◀──────── &"address": &"128 Series of Tubes"
25/// address: Internet ◀─────────────────── &"address": &"Internet"
26/// email: rpsl-rs@github.com ◀───────── &"email": &"rpsl-rs@github.com"
27/// nic-hdl: RPSL1-RIPE ◀───────────────── &"nic-hdl": &"RPSL1-RIPE"
28/// source: RIPE ◀─────────────────────── &"source": &"RIPE"
29/// ```
30///
31/// # Errors
32/// Returns a [`ParseError`] if the input is not valid RPSL.
33///
34/// # Examples
35/// ```
36/// # use rpsl::{parse_object, object};
37/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
38/// let role_acme = "
39/// role: ACME Company
40/// address: Packet Street 6
41/// address: 128 Series of Tubes
42/// address: Internet
43/// email: rpsl-rs@github.com
44/// nic-hdl: RPSL1-RIPE
45/// source: RIPE
46///
47/// ";
48/// let parsed = parse_object(role_acme)?;
49/// assert_eq!(
50/// parsed,
51/// object! {
52/// "role": "ACME Company";
53/// "address": "Packet Street 6";
54/// "address": "128 Series of Tubes";
55/// "address": "Internet";
56/// "email": "rpsl-rs@github.com";
57/// "nic-hdl": "RPSL1-RIPE";
58/// "source": "RIPE";
59/// }
60/// );
61/// # Ok(())
62/// # }
63/// ```
64///
65/// Values spread over multiple lines can be parsed too.
66/// ```
67/// # use rpsl::{parse_object, object};
68/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
69/// let multiline_remark = "
70/// remarks: Value 1
71/// Value 2
72///
73/// ";
74/// assert_eq!(
75/// parse_object(multiline_remark)?,
76/// object! {
77/// "remarks": "Value 1", "Value 2";
78/// }
79/// );
80/// # Ok(())
81/// # }
82/// ```
83///
84/// An attribute that does not have a value is valid.
85/// ```
86/// # use rpsl::{parse_object, object};
87/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
88/// let without_value = "
89/// as-name: REMARKABLE
90/// remarks:
91/// remarks: ^^^^^^^^^^ nothing here
92///
93/// ";
94/// assert_eq!(
95/// parse_object(without_value)?,
96/// object! {
97/// "as-name": "REMARKABLE";
98/// "remarks": "";
99/// "remarks": "^^^^^^^^^^ nothing here";
100/// }
101/// );
102/// # Ok(())
103/// # }
104/// ```
105///
106/// The same goes for values containing only whitespace.
107/// Since whitespace to the left of a value is trimmed, they are equivalent to no value.
108///
109/// ```
110/// # use rpsl::{parse_object, object};
111/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
112/// let whitespace_value = "
113/// as-name: REMARKABLE
114/// remarks:
115/// remarks: ^^^^^^^^^^ nothing but hot air (whitespace)
116///
117/// ";
118/// assert_eq!(
119/// parse_object(whitespace_value)?,
120/// object! {
121/// "as-name": "REMARKABLE";
122/// "remarks": "";
123/// "remarks": "^^^^^^^^^^ nothing but hot air (whitespace)";
124/// }
125/// );
126/// # Ok(())
127/// # }
128/// ```
129pub fn parse_object(rpsl: &str) -> Result<Object, ParseError> {
130 let block_parser = object_block::<ContextError>();
131 let object = delimited(multispace0, block_parser, multispace0).parse(rpsl)?;
132 Ok(object)
133}
134
135/// Parse a WHOIS server response into [`Object`]s contained within.
136///
137/// # Errors
138/// Returns a [`ParseError`] error if the input is not valid RPSL.
139///
140/// # Examples
141/// ```
142/// # use rpsl::{parse_whois_response, object};
143/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
144/// let whois_response = "
145/// ASNumber: 32934
146/// ASName: FACEBOOK
147/// ASHandle: AS32934
148/// RegDate: 2004-08-24
149/// Updated: 2012-02-24
150/// Comment: Please send abuse reports to abuse@facebook.com
151/// Ref: https://rdap.arin.net/registry/autnum/32934
152///
153///
154/// OrgName: Facebook, Inc.
155/// OrgId: THEFA-3
156/// Address: 1601 Willow Rd.
157/// City: Menlo Park
158/// StateProv: CA
159/// PostalCode: 94025
160/// Country: US
161/// RegDate: 2004-08-11
162/// Updated: 2012-04-17
163/// Ref: https://rdap.arin.net/registry/entity/THEFA-3
164///
165/// ";
166/// let objects = parse_whois_response(whois_response)?;
167/// assert_eq!(
168/// objects,
169/// vec![
170/// object! {
171/// "ASNumber": "32934";
172/// "ASName": "FACEBOOK";
173/// "ASHandle": "AS32934";
174/// "RegDate": "2004-08-24";
175/// "Updated": "2012-02-24";
176/// "Comment": "Please send abuse reports to abuse@facebook.com";
177/// "Ref": "https://rdap.arin.net/registry/autnum/32934";
178/// },
179/// object! {
180/// "OrgName": "Facebook, Inc.";
181/// "OrgId": "THEFA-3";
182/// "Address": "1601 Willow Rd.";
183/// "City": "Menlo Park";
184/// "StateProv": "CA";
185/// "PostalCode": "94025";
186/// "Country": "US";
187/// "RegDate": "2004-08-11";
188/// "Updated": "2012-04-17";
189/// "Ref": "https://rdap.arin.net/registry/entity/THEFA-3";
190/// }
191/// ]
192/// );
193/// # Ok(())
194/// # }
195pub fn parse_whois_response(response: &str) -> Result<Vec<Object>, ParseError> {
196 let block_parser = object_block_padded(object_block::<ContextError>());
197 let objects = repeat(1.., block_parser).parse(response)?;
198 Ok(objects)
199}