1use std::cmp::Ordering;
2
3use itertools::{EitherOrBoth, Itertools};
4use serde::{Deserialize, Serialize};
5
6use crate::ast::*;
7
8mod parser;
9
10pub use Pattern as NamePattern;
11
12#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct Pattern {
14 elems: Vec<PatElem>,
15}
16
17#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
18enum PatElem {
19 Ident {
21 name: String,
22 generics: Vec<PatTy>,
23 is_trait: bool,
25 },
26 Impl(Box<Pattern>),
29 Glob,
31}
32
33#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
34enum PatTy {
35 Pat(Pattern),
37 Ref(RefKind, Box<Self>),
39}
40
41impl Pattern {
42 pub fn parse(i: &str) -> Result<Self, nom_supreme::error::ErrorTree<String>> {
43 use std::str::FromStr;
44 Self::from_str(i)
45 }
46
47 fn len(&self) -> usize {
48 self.elems.len()
49 }
50
51 pub fn matches(&self, ctx: &TranslatedCrate, name: &Name) -> bool {
52 self.matches_with_generics(ctx, name, None)
53 }
54
55 pub fn matches_item(&self, ctx: &TranslatedCrate, item: AnyTransItem<'_>) -> bool {
56 let generics = item.identity_args();
57 let name = &item.item_meta().name;
58 self.matches_with_generics(ctx, name, Some(&generics))
59 }
60
61 pub fn matches_with_generics(
62 &self,
63 ctx: &TranslatedCrate,
64 name: &Name,
65 args: Option<&GenericArgs>,
66 ) -> bool {
67 let zipped = self.elems.iter().zip_longest(&name.name).collect_vec();
68 let zipped_len = zipped.len();
69 for (i, x) in zipped.into_iter().enumerate() {
70 let is_last = i + 1 == zipped_len;
71 match x {
72 EitherOrBoth::Both(pat, elem) => {
73 let args = if is_last { args } else { None };
74 if !pat.matches_with_generics(ctx, elem, args) {
75 return false;
76 }
77 }
78 EitherOrBoth::Right(_) => return true,
81 EitherOrBoth::Left(_) => return false,
83 }
84 }
85 true
87 }
88
89 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
90 if let [PatElem::Glob] = self.elems.as_slice() {
91 return true;
92 }
93 match ty.kind() {
94 TyKind::Adt(TypeId::Adt(type_id), args) => {
95 let Some(type_name) = ctx.item_name(*type_id) else {
96 return false;
97 };
98 self.matches_with_generics(ctx, type_name, Some(args))
99 }
100 TyKind::Adt(TypeId::Builtin(builtin_ty), args) => {
101 let name = builtin_ty.get_name();
102 self.matches_with_generics(ctx, &name, Some(args))
103 }
104 TyKind::Adt(TypeId::Tuple, _)
105 | TyKind::TypeVar(..)
106 | TyKind::Literal(..)
107 | TyKind::Never
108 | TyKind::Ref(..)
109 | TyKind::RawPtr(..)
110 | TyKind::TraitType(..)
111 | TyKind::DynTrait(..)
112 | TyKind::Arrow(..)
113 | TyKind::Closure { .. }
114 | TyKind::Error(..) => false,
115 }
116 }
117
118 pub fn matches_const(&self, _ctx: &TranslatedCrate, _c: &ConstGeneric) -> bool {
119 if let [PatElem::Glob] = self.elems.as_slice() {
120 return true;
121 }
122 todo!("non-trivial const generics patterns aren't implemented")
123 }
124
125 pub fn compare(&self, other: &Self) -> Ordering {
129 use Ordering::*;
130 use PatElem::*;
131 match self.len().cmp(&other.len()) {
132 o @ (Less | Greater) => return o,
133 _ if self.len() == 0 => return Equal,
134 Equal => {}
135 }
136 match (self.elems.last().unwrap(), other.elems.last().unwrap()) {
137 (Glob, Glob) => Equal,
138 (Glob, _) => Less,
139 (_, Glob) => Greater,
140 _ => Equal,
142 }
143 }
144}
145
146impl Ord for Pattern {
149 fn cmp(&self, other: &Self) -> Ordering {
150 self.compare(other)
151 }
152}
153impl PartialOrd for Pattern {
154 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
155 Some(self.compare(other))
156 }
157}
158
159impl PatElem {
160 fn matches_with_generics(
161 &self,
162 ctx: &TranslatedCrate,
163 elem: &PathElem,
164 args: Option<&GenericArgs>,
165 ) -> bool {
166 match (self, elem) {
167 (PatElem::Glob, _) => true,
168 (
169 PatElem::Ident {
170 name: pat_ident,
171 generics,
172 ..
173 },
174 PathElem::Ident(ident, _),
175 ) => {
176 let same_ident =
178 pat_ident == ident || (pat_ident == "crate" && ident == &ctx.crate_name);
179 same_ident && PatTy::matches_generics(ctx, generics, args)
180 }
181 (PatElem::Impl(_pat), PathElem::Impl(ImplElem::Ty(..), _)) => {
182 false
184 }
185 (PatElem::Impl(pat), PathElem::Impl(ImplElem::Trait(impl_id), _)) => {
186 let Some(timpl) = ctx.trait_impls.get(*impl_id) else {
187 return false;
188 };
189 let Some(trait_name) = ctx.item_name(timpl.impl_trait.trait_id) else {
190 return false;
191 };
192 pat.matches_with_generics(ctx, trait_name, Some(&timpl.impl_trait.generics))
193 }
194 _ => false,
195 }
196 }
197}
198
199impl PatTy {
200 pub fn matches_generics(
201 ctx: &TranslatedCrate,
202 pats: &[Self],
203 generics: Option<&GenericArgs>,
204 ) -> bool {
205 let Some(generics) = generics else {
206 return true;
208 };
209 if pats.is_empty() {
210 return true;
212 }
213 if pats.len() != generics.types.elem_count() + generics.const_generics.elem_count() {
215 return false;
216 }
217 let (type_pats, const_pats) = pats.split_at(generics.types.elem_count());
218 let types_match = generics
219 .types
220 .iter()
221 .zip(type_pats)
222 .all(|(ty, pat)| pat.matches_ty(ctx, ty));
223 let consts_match = generics
224 .const_generics
225 .iter()
226 .zip(const_pats)
227 .all(|(c, pat)| pat.matches_const(ctx, c));
228 types_match && consts_match
229 }
230
231 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
232 match (self, ty.kind()) {
233 (PatTy::Pat(p), _) => p.matches_ty(ctx, ty),
234 (PatTy::Ref(pat_mtbl, p_ty), TyKind::Ref(_, ty, ty_mtbl)) => {
235 pat_mtbl == ty_mtbl && p_ty.matches_ty(ctx, ty)
236 }
237 _ => false,
238 }
239 }
240
241 pub fn matches_const(&self, ctx: &TranslatedCrate, c: &ConstGeneric) -> bool {
242 match self {
243 PatTy::Pat(p) => p.matches_const(ctx, c),
244 PatTy::Ref(..) => false,
245 }
246 }
247}
248
249#[test]
250fn test_compare() {
251 use Ordering::*;
252 let tests = [
253 ("_", Less, "crate"),
254 ("crate::_", Less, "crate::foo"),
255 ("crate::foo", Less, "crate::foo::_"),
256 ];
257 for (x, o, y) in tests {
258 let x = Pattern::parse(x).unwrap();
259 let y = Pattern::parse(y).unwrap();
260 assert_eq!(x.compare(&y), o);
261 }
262}