55mod structures;
66
77use crate :: structures:: { flatten_ranges, Part , RangeOutput } ;
8+ use combine:: parser:: token:: satisfy;
89use combine:: {
910 attempt, between, choice, eof,
1011 error:: { ParseError , StreamError } ,
6667 I : Stream < Token = char > ,
6768 I :: Error : ParseError < I :: Token , I :: Range , I :: Position > ,
6869{
69- many1 ( alpha_num ( ) . or ( dash ( ) ) . or ( token ( '.' ) ) )
70+ many1 ( alpha_num ( ) . or ( dash ( ) ) . or ( token ( '.' ) ) . or ( token ( ':' ) ) )
7071}
7172
7273fn digits < I > ( ) -> impl Parser < I , Output = String >
7778 many1 ( digit ( ) )
7879}
7980
81+ fn hex_digits < I > ( ) -> impl Parser < I , Output = String >
82+ where
83+ I : Stream < Token = char > ,
84+ I :: Error : ParseError < I :: Token , I :: Range , I :: Position > ,
85+ {
86+ many1 ( satisfy ( |c : char | c. is_ascii_hexdigit ( ) ) )
87+ }
88+
8089fn leading_zeros < I > ( ) -> impl Parser < I , Output = ( usize , u64 ) >
8190where
8291 I : Stream < Token = char > ,
@@ -95,6 +104,28 @@ where
95104 } )
96105}
97106
107+ fn leading_hex < I > ( ) -> impl Parser < I , Output = ( usize , u64 , bool ) >
108+ where
109+ I : Stream < Token = char > ,
110+ I :: Error : ParseError < I :: Token , I :: Range , I :: Position > ,
111+ {
112+ hex_digits ( ) . and_then ( |x| {
113+ let mut digits = x. chars ( ) . take_while ( |x| x == & '0' ) . count ( ) ;
114+
115+ if x. len ( ) == digits {
116+ digits -= 1 ;
117+ }
118+
119+ let has_alpha = x
120+ . chars ( )
121+ . any ( |c| c. is_ascii_hexdigit ( ) && !c. is_ascii_digit ( ) ) ;
122+
123+ u64:: from_str_radix ( & x, 16 )
124+ . map ( |num| ( digits, num, has_alpha) )
125+ . map_err ( StreamErrorFor :: < I > :: other)
126+ } )
127+ }
128+
98129fn range_digits < I > ( ) -> impl Parser < I , Output = RangeOutput >
99130where
100131 I : Stream < Token = char > ,
@@ -135,6 +166,49 @@ where
135166 } )
136167}
137168
169+ fn range_hex < I > ( ) -> impl Parser < I , Output = RangeOutput >
170+ where
171+ I : Stream < Token = char > ,
172+ I :: Error : ParseError < I :: Token , I :: Range , I :: Position > ,
173+ {
174+ attempt ( (
175+ leading_hex ( ) ,
176+ optional_spaces ( ) . with ( dash ( ) ) ,
177+ optional_spaces ( ) . with ( leading_hex ( ) ) ,
178+ ) )
179+ . and_then ( |( ( start_zeros, start, a1) , _, ( end_zeros, end, a2) ) | {
180+ if !( a1 || a2) {
181+ return Err ( StreamErrorFor :: < I > :: unexpected_static_message ( "not hex" ) ) ;
182+ }
183+ let mut xs = [ start, end] ;
184+ xs. sort_unstable ( ) ;
185+
186+ let same_prefix_len = start_zeros == end_zeros;
187+
188+ let ( range, start_zeros, end_zeros) = if start > end {
189+ (
190+ RangeOutput :: HexRangeReversed ( end_zeros, same_prefix_len, end, start) ,
191+ end_zeros,
192+ start_zeros,
193+ )
194+ } else {
195+ (
196+ RangeOutput :: HexRange ( start_zeros, same_prefix_len, start, end) ,
197+ start_zeros,
198+ end_zeros,
199+ )
200+ } ;
201+
202+ if end_zeros > start_zeros {
203+ Err ( StreamErrorFor :: < I > :: unexpected_static_message (
204+ "larger end padding" ,
205+ ) )
206+ } else {
207+ Ok ( range)
208+ }
209+ } )
210+ }
211+
138212fn disjoint_digits < I > ( ) -> impl Parser < I , Output = RangeOutput >
139213where
140214 I : Stream < Token = char > ,
@@ -157,6 +231,36 @@ where
157231 . map ( RangeOutput :: Disjoint )
158232}
159233
234+ fn disjoint_hex < I > ( ) -> impl Parser < I , Output = RangeOutput >
235+ where
236+ I : Stream < Token = char > ,
237+ I :: Error : ParseError < I :: Token , I :: Range , I :: Position > ,
238+ {
239+ let not_name = not_followed_by (
240+ optional_spaces ( )
241+ . with ( hex_digits ( ) )
242+ . skip ( optional_spaces ( ) )
243+ . skip ( dash ( ) )
244+ . map ( |_| "" ) ,
245+ ) ;
246+
247+ sep_by1 (
248+ optional_spaces ( )
249+ . with ( leading_hex ( ) )
250+ . skip ( optional_spaces ( ) ) ,
251+ attempt ( comma ( ) . skip ( not_name) ) ,
252+ )
253+ . and_then ( |xs : Vec < ( usize , u64 , bool ) > | {
254+ if xs. iter ( ) . any ( |( _, _, a) | * a) {
255+ Ok ( RangeOutput :: HexDisjoint (
256+ xs. into_iter ( ) . map ( |( z, n, _) | ( z, n) ) . collect ( ) ,
257+ ) )
258+ } else {
259+ Err ( StreamErrorFor :: < I > :: unexpected_static_message ( "not hex" ) )
260+ }
261+ } )
262+ }
263+
160264fn range < I > ( ) -> impl Parser < I , Output = Vec < RangeOutput > >
161265where
162266 I : Stream < Token = char > ,
@@ -165,7 +269,13 @@ where
165269 between (
166270 open_bracket ( ) ,
167271 close_bracket ( ) ,
168- sep_by1 ( range_digits ( ) . or ( disjoint_digits ( ) ) , comma ( ) ) ,
272+ sep_by1 (
273+ attempt ( range_hex ( ) )
274+ . or ( range_digits ( ) )
275+ . or ( attempt ( disjoint_hex ( ) ) )
276+ . or ( attempt ( disjoint_digits ( ) ) ) ,
277+ comma ( ) ,
278+ ) ,
169279 )
170280}
171281
@@ -492,4 +602,16 @@ mod tests {
492602 fn test_parse_osts ( ) {
493603 assert_debug_snapshot ! ( "Leading 0s" , parse( "OST01[00,01]" ) ) ;
494604 }
605+
606+ #[ test]
607+ fn test_parse_ip_addresses ( ) {
608+ assert_debug_snapshot ! ( "IPv4 single" , parse( "192.168.0.1" ) ) ;
609+ assert_debug_snapshot ! ( "IPv6 compressed" , parse( "2001:db8::1" ) ) ;
610+ assert_debug_snapshot ! (
611+ "IPv6 full" ,
612+ parse( "fe80:1234:5678:9abc:def0:1234:5678:9abc" )
613+ ) ;
614+ assert_debug_snapshot ! ( "Multiple IPv6 literals" , parse( "2001:db8::1, 2001:db8::2" ) ) ;
615+ assert_debug_snapshot ! ( "IPv6 expansion" , parse( "2001:db8::[0-f]" ) ) ;
616+ }
495617}
0 commit comments