1use std::net::{IpAddr, Ipv4Addr};
2
3use winnow::{
4 ModalResult as WResult, Parser,
5 ascii::{Caseless, multispace0},
6 combinator::{fail, peek, repeat},
7 error::ContextError,
8 token::any,
9};
10
11use crate::symbol::ctx_desc;
12use crate::utils::peek_one;
13
14#[derive(PartialEq)]
15enum AddrKind {
16 Ipv4,
17 Ipv6,
18}
19
20fn head_ip<'a>(last: &mut Option<AddrKind>) -> impl Parser<&'a str, char, ContextError> + '_ {
21 move |input: &mut &'a str| {
22 let initial = (peek(any)).parse_next(input)?;
23 match initial {
24 '0'..='9' => any.parse_next(input),
25 'A'..='F' | 'a'..='f' => {
26 *last = Some(AddrKind::Ipv6);
27 any.parse_next(input)
28 }
29 '.' => {
30 if *last == Some(AddrKind::Ipv6) {
31 fail.parse_next(input)
32 } else {
33 *last = Some(AddrKind::Ipv4);
34 any.parse_next(input)
35 }
36 }
37 ':' => {
38 if *last == Some(AddrKind::Ipv4) {
39 fail.parse_next(input)
40 } else {
41 *last = Some(AddrKind::Ipv6);
42 any.parse_next(input)
43 }
44 }
45 _ => fail.parse_next(input),
46 }
47 }
48}
49
50pub fn ip_v4(input: &mut &str) -> WResult<IpAddr> {
51 let mut last_kind = None;
52 let ip_str = match repeat(1.., head_ip(&mut last_kind))
55 .fold(String::new, |mut acc, c| {
56 acc.push(c);
57 acc
58 })
59 .parse_next(input)
60 {
61 Ok(s) => s,
62 Err(_e) => return fail.context(ctx_desc("<ipv4>")).parse_next(input),
63 };
64 match ip_str.parse::<IpAddr>() {
65 Ok(ip) => Ok(ip),
66 Err(_e) => fail.context(ctx_desc("<ipv4>")).parse_next(input),
67 }
68}
69pub fn ip(input: &mut &str) -> WResult<IpAddr> {
70 multispace0.parse_next(input)?;
71
72 let str = peek_one.parse_next(input);
73 if let Ok(s) = str {
74 let addr = if s == "l" {
75 Caseless("localhost")
76 .map(|_| IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)))
77 .context(ctx_desc("<localhost>"))
78 .parse_next(input)?
79 } else {
80 ip_v4.context(ctx_desc("<ipv4>")).parse_next(input)?
81 };
82 Ok(addr)
83 } else {
84 fail.context(ctx_desc("ip error")).parse_next(input)
85 }
86}