1use std::{fmt, str::FromStr};
2
3use itertools::Itertools;
4use nom::{
5 Finish, Parser,
6 bytes::complete::{tag, take_while},
7 character::complete::{multispace0, multispace1},
8 combinator::{all_consuming, cut, map_res, opt, success},
9 error::{Error, ParseError},
10 multi::separated_list0,
11 sequence::{delimited, preceded, terminated},
12};
13
14use super::{PatElem, PatTy, Pattern};
15use crate::ast::RefKind;
16
17type ParseResult<'a, T> = nom::IResult<&'a str, T, Error<&'a str>>;
18
19trait ParserExt<I, O, E>: Parser<I, O, E> + Sized
21where
22 I: Clone,
23 E: ParseError<I>,
24{
25 fn followed_by<F, O2>(self, suffix: F) -> impl Parser<I, O, E>
26 where
27 F: Parser<I, O2, E>,
28 {
29 terminated(self, suffix)
30 }
31
32 fn precedes<F, O2>(self, next: F) -> impl Parser<I, O2, E>
33 where
34 F: Parser<I, O2, E>,
35 {
36 preceded(self, next)
37 }
38
39 fn opt(self) -> impl Parser<I, Option<O>, E> {
40 opt(self)
41 }
42
43 fn cut(self) -> impl Parser<I, O, E> {
44 cut(self)
45 }
46}
47impl<I, O, E, P> ParserExt<I, O, E> for P
48where
49 I: Clone,
50 E: ParseError<I>,
51 P: Parser<I, O, E>,
52{
53}
54
55impl FromStr for Pattern {
57 type Err = Error<String>;
58 fn from_str(s: &str) -> Result<Self, Self::Err> {
59 parse_pattern_complete(s)
60 }
61}
62
63fn parse_pattern_complete(i: &str) -> Result<Pattern, Error<String>> {
64 all_consuming(parse_pattern)
65 .parse(i)
66 .finish()
67 .map(|(_, pattern)| pattern)
68 .map_err(|e| Error::new(e.input.to_string(), e.code))
69}
70
71fn parse_pattern(i: &str) -> ParseResult<'_, Pattern> {
72 separated_list0(tag("::").followed_by(multispace0), parse_pat_elem)
73 .map(|elems| Pattern { elems })
74 .parse(i)
75}
76
77impl fmt::Display for Pattern {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 self.elems.iter().format("::").fmt(f)
80 }
81}
82
83impl fmt::Debug for Pattern {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 write!(f, "{self}")
86 }
87}
88
89fn parse_pat_elem(i: &str) -> ParseResult<'_, PatElem> {
90 let parse_glob = tag("*").map(|_| PatElem::Glob);
91 parse_glob
92 .or(parse_impl_elem)
93 .or(parse_simple_elem)
94 .parse(i)
95}
96
97fn parse_simple_elem(i: &str) -> ParseResult<'_, PatElem> {
98 let ident = take_while(|c: char| c.is_alphanumeric() || c == '_');
99 let (i, ident) = ident.followed_by(multispace0).parse(i)?;
100 if ident == "_" {
101 success(PatElem::Glob).parse(i)
102 } else {
103 let args = delimited(
104 tag("<").followed_by(multispace0),
105 separated_list0(
106 tag(",").followed_by(multispace0),
107 parse_pat_ty.followed_by(multispace0),
108 ),
109 tag(">"),
110 );
111 args.opt()
112 .map(|args| PatElem::Ident {
113 name: ident.to_string(),
114 generics: args.unwrap_or_default(),
115 is_trait: false,
116 })
117 .parse(i)
118 }
119}
120
121fn parse_impl_elem(i: &str) -> ParseResult<'_, PatElem> {
122 let for_ty = preceded(
123 tag("for").followed_by(multispace1),
124 parse_pat_ty.followed_by(multispace0),
125 );
126 let impl_contents = parse_pattern.followed_by(multispace0).and(for_ty.opt());
127 let impl_pattern = tag("{").followed_by(multispace0).precedes(
128 delimited(
129 tag("impl").followed_by(multispace1.cut()).opt(),
130 impl_contents,
131 tag("}"),
132 )
133 .cut(),
134 );
135 map_res(impl_pattern, |(mut pat, for_ty)| {
136 if let Some(for_ty) = for_ty {
137 let last_elem = pat
138 .elems
139 .last_mut()
140 .ok_or_else(|| anyhow::anyhow!("trait path must be nonempty"))?;
141 let PatElem::Ident {
142 generics, is_trait, ..
143 } = last_elem
144 else {
145 return Err(anyhow::anyhow!("trait path must end in an ident"));
146 };
147 generics.insert(0, for_ty);
149 *is_trait = true;
150 }
151 Ok(PatElem::Impl(pat.into()))
152 })
153 .parse(i)
154}
155
156impl fmt::Display for PatElem {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 PatElem::Ident {
160 name,
161 generics,
162 is_trait,
163 } => {
164 write!(f, "{name}")?;
165 let generics = generics.as_slice();
166 let (ty, generics) = if let [ty, generics @ ..] = generics
167 && *is_trait
168 {
169 (Some(ty), generics)
170 } else {
171 (None, generics)
172 };
173 if !generics.is_empty() {
174 write!(f, "<{}>", generics.iter().format(", "))?;
175 }
176 if let Some(ty) = ty {
177 write!(f, " for {ty}")?;
178 }
179 Ok(())
180 }
181 PatElem::Impl(pat) => write!(f, "{{impl {pat}}}"),
182 PatElem::Glob => write!(f, "_"),
183 }
184 }
185}
186
187fn parse_pat_ty(i: &str) -> ParseResult<'_, PatTy> {
188 let mutability = tag("mut").followed_by(multispace0).opt().map(|mtbl| {
189 if mtbl.is_some() {
190 RefKind::Mut
191 } else {
192 RefKind::Shared
193 }
194 });
195 tag("&")
196 .followed_by(multispace0)
197 .precedes(mutability.and(parse_pat_ty))
198 .map(|(mtbl, ty)| PatTy::Ref(mtbl, ty.into()))
199 .or(parse_pattern.map(PatTy::Pat))
200 .parse(i)
201}
202
203impl fmt::Display for PatTy {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 match self {
206 PatTy::Pat(p) => write!(f, "{p}"),
207 PatTy::Ref(RefKind::Shared, ty) => write!(f, "&{ty}"),
208 PatTy::Ref(RefKind::Mut, ty) => write!(f, "&mut {ty}"),
209 }
210 }
211}
212
213#[test]
214fn test_roundtrip() {
215 let idempotent_test_strings = [
216 "crate::foo::bar",
217 "blah::_",
218 "blah::_foo",
219 "a::b::Type",
220 "a::b::Type<_, _>",
221 "Clone",
222 "usize",
223 "foo::{impl Clone for usize}::clone",
224 "foo::{impl Clone for &&usize}",
225 "foo::{impl PartialEq<_> for Type<_, _>}",
226 "foo::{impl PartialEq<usize> for Box<u8>}",
227 "foo::{impl foo::Trait<core::option::Option<_>> for alloc::boxed::Box<_>}::method",
228 ];
229 let other_test_strings = [
230 ("blah::*", "blah::_"),
231 ("crate :: foo ::bar ", "crate::foo::bar"),
232 ("a::b::Type < _ , _ >", "a::b::Type<_, _>"),
233 ("{ impl Clone for usize }", "{impl Clone for usize}"),
234 ("{Clone for usize}", "{impl Clone for usize}"),
235 ];
236 let failures = [
237 "{implClone for usize}",
238 "{impl Clone forusize}",
239 "foo::{impl for alloc::boxed::Box<_>}::method",
240 "foo::{impl foo::_ for alloc::boxed::Box<_>}::method",
241 "foo::{impl &Clone for usize}",
242 ];
243
244 let test_strings = idempotent_test_strings
245 .into_iter()
246 .map(|s| (s, s))
247 .chain(other_test_strings);
248 for (input, expected) in test_strings {
249 let pat = Pattern::parse(input).map_err(|e| e.to_string()).unwrap();
250 assert_eq!(pat.to_string(), expected);
251 }
252
253 for input in failures {
254 assert!(
255 Pattern::parse(input).is_err(),
256 "Pattern parsed correctly but shouldn't: `{input}`"
257 );
258 }
259}