1use std::{
2 collections::BTreeMap,
3 fmt::{Debug, Formatter},
4 future::Future,
5};
6
7use patricia_tree::PatriciaMap;
8use serde::{
9 Deserialize, Deserializer, Serialize, Serializer,
10 de::{MapAccess, Visitor},
11 ser::SerializeMap,
12};
13use serde_bytes::{ByteBuf, Bytes};
14use turbo_rcstr::RcStr;
15use turbo_tasks::{
16 NonLocalValue,
17 debug::{ValueDebugFormat, ValueDebugFormatString, internal::PassthroughDebug},
18 trace::{TraceRawVcs, TraceRawVcsContext},
19};
20
21use super::pattern::Pattern;
22
23#[derive(Clone)]
32pub struct AliasMap<T> {
33 map: PatriciaMap<BTreeMap<AliasKey, T>>,
34}
35
36impl<T> Default for AliasMap<T> {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl<T> PartialEq for AliasMap<T>
43where
44 T: PartialEq,
45{
46 fn eq(&self, other: &Self) -> bool {
47 if self.map.len() != other.map.len() {
48 return false;
49 }
50
51 self.map.iter().zip(other.map.iter()).all(|(a, b)| a == b)
52 }
53}
54
55impl<T> Eq for AliasMap<T> where T: Eq {}
56
57impl<T> Serialize for AliasMap<T>
58where
59 T: Serialize,
60{
61 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
62 where
63 S: Serializer,
64 {
65 let mut map = serializer.serialize_map(Some(self.map.len()))?;
66 for (prefix, value) in self.map.iter() {
67 let key = ByteBuf::from(prefix);
68 map.serialize_entry(&key, value)?;
69 }
70 map.end()
71 }
72}
73
74struct AliasMapVisitor<T> {
75 marker: std::marker::PhantomData<T>,
76}
77
78impl<'de, T> Visitor<'de> for AliasMapVisitor<T>
79where
80 T: Deserialize<'de>,
81{
82 type Value = AliasMap<T>;
83
84 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
85 formatter.write_str("a map of alias patterns to templates")
86 }
87
88 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
89 where
90 M: MapAccess<'de>,
91 {
92 let mut map = AliasMap::new();
93 while let Some((key, value)) = access.next_entry::<&Bytes, _>()? {
94 map.map.insert(key, value);
95 }
96 Ok(map)
97 }
98}
99
100impl<'a, T> Deserialize<'a> for AliasMap<T>
101where
102 T: Deserialize<'a>,
103{
104 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
105 where
106 D: Deserializer<'a>,
107 {
108 deserializer.deserialize_map(AliasMapVisitor {
109 marker: std::marker::PhantomData,
110 })
111 }
112}
113
114impl<T> TraceRawVcs for AliasMap<T>
115where
116 T: TraceRawVcs,
117{
118 fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
119 for (_, map) in self.map.iter() {
120 for value in map.values() {
121 value.trace_raw_vcs(trace_context);
122 }
123 }
124 }
125}
126
127unsafe impl<T: NonLocalValue> NonLocalValue for AliasMap<T> {}
128
129impl<T> ValueDebugFormat for AliasMap<T>
130where
131 T: ValueDebugFormat,
132{
133 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
134 if depth == 0 {
135 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
136 }
137
138 let values = self
139 .map
140 .iter()
141 .flat_map(|(key, map)| {
142 let key = String::from_utf8(key).expect("invalid UTF-8 key in AliasMap");
143 map.iter().map(move |(alias_key, value)| match alias_key {
144 AliasKey::Exact => (
145 key.clone(),
146 value.value_debug_format(depth.saturating_sub(1)),
147 ),
148 AliasKey::Wildcard { suffix } => (
149 format!("{key}*{suffix}"),
150 value.value_debug_format(depth.saturating_sub(1)),
151 ),
152 })
153 })
154 .collect::<Vec<_>>();
155
156 ValueDebugFormatString::Async(Box::pin(async move {
157 let mut values_string = std::collections::HashMap::new();
158 for (key, value) in values {
159 match value {
160 ValueDebugFormatString::Sync(string) => {
161 values_string.insert(key, PassthroughDebug::new_string(string));
162 }
163 ValueDebugFormatString::Async(future) => {
164 values_string.insert(key, PassthroughDebug::new_string(future.await?));
165 }
166 }
167 }
168 Ok(format!("{values_string:#?}"))
169 }))
170 }
171}
172
173impl<T> Debug for AliasMap<T>
174where
175 T: Debug,
176{
177 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
178 f.debug_map()
179 .entries(self.map.iter().flat_map(|(key, map)| {
180 let key = String::from_utf8(key).expect("invalid UTF-8 key in AliasMap");
181 map.iter().map(move |(alias_key, value)| match alias_key {
182 AliasKey::Exact => (key.clone(), value),
183 AliasKey::Wildcard { suffix } => (format!("{key}*{suffix}"), value),
184 })
185 }))
186 .finish()
187 }
188}
189
190impl<T> AliasMap<T> {
191 pub fn new() -> Self {
193 AliasMap {
194 map: PatriciaMap::new(),
195 }
196 }
197
198 pub fn lookup<'a>(&'a self, request: &'a Pattern) -> AliasMapLookupIterator<'a, T>
202 where
203 T: Debug,
204 {
205 if matches!(request, Pattern::Alternatives(_)) {
206 panic!("AliasMap::lookup must not be called on alternatives, received {request:?}");
207 }
208
209 let common_prefixes = self
213 .map
214 .common_prefixes(request.constant_prefix().as_bytes());
215 let mut prefixes_stack = common_prefixes
216 .map(|(p, tree)| {
217 let s = match std::str::from_utf8(p) {
218 Ok(s) => s,
219 Err(e) => std::str::from_utf8(&p[..e.valid_up_to()]).unwrap(),
220 };
221 (s, tree)
222 })
223 .collect::<Vec<_>>();
224 AliasMapLookupIterator {
225 request,
226 current_prefix_iterator: prefixes_stack
227 .pop()
228 .map(|(prefix, map)| (prefix, map.iter())),
229 prefixes_stack,
230 }
231 }
232
233 pub fn lookup_with_prefix_predicate<'a>(
238 &'a self,
239 request: &'a Pattern,
240 mut prefix_predicate: impl FnMut(&str) -> bool,
241 ) -> AliasMapLookupIterator<'a, T>
242 where
243 T: Debug,
244 {
245 let common_prefixes = self
249 .map
250 .common_prefixes(request.constant_prefix().as_bytes());
251 let mut prefixes_stack = common_prefixes
252 .filter_map(|(p, tree)| {
253 let s = match std::str::from_utf8(p) {
254 Ok(s) => s,
255 Err(e) => std::str::from_utf8(&p[..e.valid_up_to()]).unwrap(),
256 };
257 if prefix_predicate(s) {
258 Some((s, tree))
259 } else {
260 None
261 }
262 })
263 .collect::<Vec<_>>();
264 AliasMapLookupIterator {
265 request,
266 current_prefix_iterator: prefixes_stack
267 .pop()
268 .map(|(prefix, map)| (prefix, map.iter())),
269 prefixes_stack,
270 }
271 }
272
273 pub fn insert(&mut self, pattern: AliasPattern, template: T) -> Option<T> {
280 let (prefix_key, alias_key, value) = match pattern {
281 AliasPattern::Exact(exact) => (exact, AliasKey::Exact, template),
282 AliasPattern::Wildcard { prefix, suffix } => {
283 (prefix, AliasKey::Wildcard { suffix }, template)
284 }
285 };
286 if let Some(map) = self.map.get_mut(&prefix_key) {
293 map.insert(alias_key, value)
294 } else {
295 let mut map = BTreeMap::new();
296 map.insert(alias_key, value);
297 self.map.insert(prefix_key, map);
298 None
299 }
300 }
301}
302
303impl<T> IntoIterator for AliasMap<T> {
304 type Item = (AliasPattern, T);
305
306 type IntoIter = AliasMapIntoIter<T>;
307
308 fn into_iter(self) -> Self::IntoIter {
309 AliasMapIntoIter {
310 iter: self.map.into_iter(),
311 current_prefix_iterator: None,
312 }
313 }
314}
315
316impl<'a, T> IntoIterator for &'a AliasMap<T> {
317 type Item = (AliasPattern, &'a T);
318
319 type IntoIter = AliasMapIter<'a, T>;
320
321 fn into_iter(self) -> Self::IntoIter {
322 AliasMapIter {
323 iter: self.map.iter(),
324 current_prefix_iterator: None,
325 }
326 }
327}
328
329pub struct AliasMapIntoIter<T> {
336 iter: patricia_tree::map::IntoIter<BTreeMap<AliasKey, T>>,
337 current_prefix_iterator: Option<AliasMapIntoIterItem<T>>,
338}
339
340struct AliasMapIntoIterItem<T> {
341 prefix: RcStr,
342 iterator: std::collections::btree_map::IntoIter<AliasKey, T>,
343}
344
345impl<T> AliasMapIntoIter<T> {
346 fn advance_iter(&mut self) -> Option<&mut AliasMapIntoIterItem<T>> {
347 let (prefix, map) = self.iter.next()?;
348 let prefix = String::from_utf8(prefix)
349 .expect("invalid UTF-8 key in AliasMap")
350 .into();
351 self.current_prefix_iterator = Some(AliasMapIntoIterItem {
352 prefix,
353 iterator: map.into_iter(),
354 });
355 self.current_prefix_iterator.as_mut()
356 }
357}
358
359impl<T> Iterator for AliasMapIntoIter<T> {
360 type Item = (AliasPattern, T);
361
362 fn next(&mut self) -> Option<Self::Item> {
363 let mut current_prefix_iterator = match self.current_prefix_iterator {
364 None => self.advance_iter()?,
365 Some(ref mut current_prefix_iterator) => current_prefix_iterator,
366 };
367 let mut current_value = current_prefix_iterator.iterator.next();
368 loop {
369 match current_value {
370 None => {
371 current_prefix_iterator = self.advance_iter()?;
372 current_value = current_prefix_iterator.iterator.next();
373 }
374 Some(current_value) => {
375 return Some(match current_value {
376 (AliasKey::Exact, value) => (
377 AliasPattern::Exact(current_prefix_iterator.prefix.clone()),
378 value,
379 ),
380 (AliasKey::Wildcard { suffix }, value) => (
381 AliasPattern::Wildcard {
382 prefix: current_prefix_iterator.prefix.clone(),
383 suffix,
384 },
385 value,
386 ),
387 });
388 }
389 }
390 }
391 }
392}
393
394pub struct AliasMapIter<'a, T> {
401 iter: patricia_tree::map::Iter<'a, BTreeMap<AliasKey, T>>,
402 current_prefix_iterator: Option<AliasMapIterItem<'a, T>>,
403}
404
405struct AliasMapIterItem<'a, T> {
406 prefix: RcStr,
407 iterator: std::collections::btree_map::Iter<'a, AliasKey, T>,
408}
409
410impl<T> AliasMapIter<'_, T> {
411 fn advance_iter(&mut self) -> bool {
412 let Some((prefix, map)) = self.iter.next() else {
413 return false;
414 };
415 let prefix = String::from_utf8(prefix)
416 .expect("invalid UTF-8 key in AliasMap")
417 .into();
418 self.current_prefix_iterator = Some(AliasMapIterItem {
419 prefix,
420 iterator: map.iter(),
421 });
422 true
423 }
424}
425
426impl<'a, T> Iterator for AliasMapIter<'a, T> {
427 type Item = (AliasPattern, &'a T);
428
429 fn next(&mut self) -> Option<Self::Item> {
430 let (current_prefix_iterator, current_value) = loop {
431 let Some(current_prefix_iterator) = &mut self.current_prefix_iterator else {
432 if !self.advance_iter() {
433 return None;
434 }
435 continue;
436 };
437 if let Some(current_value) = current_prefix_iterator.iterator.next() {
438 break (&*current_prefix_iterator, current_value);
439 }
440 self.current_prefix_iterator = None;
441 continue;
442 };
443 Some(match current_value {
444 (AliasKey::Exact, value) => (
445 AliasPattern::Exact(current_prefix_iterator.prefix.clone()),
446 value,
447 ),
448 (AliasKey::Wildcard { suffix }, value) => (
449 AliasPattern::Wildcard {
450 prefix: current_prefix_iterator.prefix.clone(),
451 suffix: suffix.clone(),
452 },
453 value,
454 ),
455 })
456 }
457}
458
459impl<T> Extend<(AliasPattern, T)> for AliasMap<T> {
460 fn extend<It>(&mut self, iter: It)
461 where
462 It: IntoIterator<Item = (AliasPattern, T)>,
463 {
464 for (pattern, value) in iter {
465 self.insert(pattern, value);
466 }
467 }
468}
469
470pub struct AliasMapLookupIterator<'a, T> {
476 request: &'a Pattern,
477 prefixes_stack: Vec<(&'a str, &'a BTreeMap<AliasKey, T>)>,
478 current_prefix_iterator: Option<(&'a str, std::collections::btree_map::Iter<'a, AliasKey, T>)>,
479}
480
481impl<'a, T> Iterator for AliasMapLookupIterator<'a, T>
482where
483 T: AliasTemplate,
484{
485 type Item = AliasMatch<'a, T>;
486
487 fn next(&mut self) -> Option<Self::Item> {
488 let (prefix, current_prefix_iterator) = self.current_prefix_iterator.as_mut()?;
489
490 loop {
491 for (key, template) in &mut *current_prefix_iterator {
492 match key {
493 AliasKey::Exact => {
494 if self.request.is_match(prefix) {
495 return Some(AliasMatch::Exact(template.convert()));
496 }
497 }
498 AliasKey::Wildcard { suffix } => {
499 let mut remaining = self.request.clone();
500 remaining.strip_prefix(prefix.len());
501 let remaining_suffix = remaining.constant_suffix();
502 if !remaining_suffix.ends_with(&**suffix) {
503 continue;
504 }
505 remaining.strip_suffix(suffix.len());
506
507 let output = template.replace(&remaining);
508 return Some(AliasMatch::Replaced(output));
509 }
510 }
511 }
512
513 let (new_prefix, new_current_prefix_iterator) = self.prefixes_stack.pop()?;
514 *prefix = new_prefix;
515 *current_prefix_iterator = new_current_prefix_iterator.iter();
516 }
517 }
518}
519
520#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
522pub enum AliasPattern {
523 Exact(RcStr),
525 Wildcard { prefix: RcStr, suffix: RcStr },
527}
528
529impl AliasPattern {
530 pub fn parse<'a, T>(pattern: T) -> Self
535 where
536 T: Into<RcStr> + 'a,
537 {
538 let pattern = pattern.into();
539 if let Some(wildcard_index) = pattern.find('*') {
540 let mut pattern = pattern.into_owned();
541
542 let suffix = pattern[wildcard_index + 1..].into();
543 pattern.truncate(wildcard_index);
544 AliasPattern::Wildcard {
545 prefix: pattern.into(),
546 suffix,
547 }
548 } else {
549 AliasPattern::Exact(pattern)
550 }
551 }
552
553 pub fn exact<'a, T>(pattern: T) -> Self
555 where
556 T: Into<RcStr> + 'a,
557 {
558 AliasPattern::Exact(pattern.into())
559 }
560
561 pub fn wildcard<'p, 's, P, S>(prefix: P, suffix: S) -> Self
566 where
567 P: Into<RcStr> + 'p,
568 S: Into<RcStr> + 's,
569 {
570 AliasPattern::Wildcard {
571 prefix: prefix.into(),
572 suffix: suffix.into(),
573 }
574 }
575}
576
577#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
578enum AliasKey {
579 Exact,
580 Wildcard { suffix: RcStr },
581}
582
583#[derive(Debug, PartialEq)]
585pub enum AliasMatch<'a, T>
586where
587 T: AliasTemplate + 'a,
588{
589 Exact(T::Output<'a>),
591 Replaced(T::Output<'a>),
593}
594
595impl<'a, T> AliasMatch<'a, T>
596where
597 T: AliasTemplate,
598{
599 pub fn as_exact(&self) -> Option<&T::Output<'a>> {
601 if let Self::Exact(v) = self {
602 Some(v)
603 } else {
604 None
605 }
606 }
607
608 pub fn as_replaced(&self) -> Option<&T::Output<'a>> {
610 if let Self::Replaced(v) = self {
611 Some(v)
612 } else {
613 None
614 }
615 }
616
617 pub fn as_self(&self) -> &T::Output<'a> {
619 match self {
620 Self::Exact(v) => v,
621 Self::Replaced(v) => v,
622 }
623 }
624}
625
626impl<'a, T, R, E> AliasMatch<'a, T>
627where
628 T: AliasTemplate<Output<'a> = Result<R, E>> + Clone,
629{
630 pub fn try_into_self(self) -> Result<R, E> {
636 Ok(match self {
637 Self::Exact(v) => v?,
638 Self::Replaced(v) => v?,
639 })
640 }
641}
642
643impl<'a, T, R, E, F> AliasMatch<'a, T>
644where
645 F: Future<Output = Result<R, E>>,
646 T: AliasTemplate<Output<'a> = F> + Clone,
647{
648 pub async fn try_join_into_self(self) -> Result<R, E> {
654 Ok(match self {
655 Self::Exact(v) => v.await?,
656 Self::Replaced(v) => v.await?,
657 })
658 }
659}
660
661impl PartialOrd for AliasKey {
662 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
663 Some(self.cmp(other))
664 }
665}
666
667impl Ord for AliasKey {
668 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
672 match (self, other) {
673 (AliasKey::Wildcard { suffix: l_suffix }, AliasKey::Wildcard { suffix: r_suffix }) => {
674 l_suffix
675 .len()
676 .cmp(&r_suffix.len())
677 .reverse()
678 .then_with(|| l_suffix.cmp(r_suffix))
679 }
680 (AliasKey::Wildcard { .. }, _) => std::cmp::Ordering::Less,
681 (_, AliasKey::Wildcard { .. }) => std::cmp::Ordering::Greater,
682 _ => std::cmp::Ordering::Equal,
683 }
684 }
685}
686
687pub trait AliasTemplate {
689 type Output<'a>
691 where
692 Self: 'a;
693
694 fn convert(&self) -> Self::Output<'_>;
696
697 fn replace<'a>(&'a self, capture: &Pattern) -> Self::Output<'a>;
699}
700
701#[cfg(test)]
702mod test {
703 use std::assert_matches::assert_matches;
704
705 use super::{AliasMap, AliasPattern, AliasTemplate};
706 use crate::resolve::pattern::Pattern;
707
708 macro_rules! assert_alias_matches {
713 ($map:expr, $request:expr$(, $($tail:tt)*)?) => {
714 let request = Pattern::Constant($request.into());
715 let mut lookup = $map.lookup(&request);
716
717 $(assert_alias_matches!(@next lookup, $($tail)*);)?
718 assert_matches!(lookup.next(), None);
719 };
720
721 (@next $lookup:ident, exact($pattern:expr)$(, $($tail:tt)*)?) => {
722 match $lookup.next().unwrap() {
723 super::AliasMatch::Exact(Pattern::Constant(c)) if c == $pattern => {}
724 m => panic!("unexpected match {:?}", m),
725 }
726 $(assert_alias_matches!(@next $lookup, $($tail)*);)?
727 };
728
729 (@next $lookup:ident, replaced($pattern:expr)$(, $($tail:tt)*)?) => {
730 match $lookup.next().unwrap() {
731 super::AliasMatch::Replaced(Pattern::Constant(c)) if c == $pattern => {}
732 m => panic!("unexpected match {:?}", m),
733 }
734 $(assert_alias_matches!(@next $lookup, $($tail)*);)?
735 };
736
737 (@next $lookup:ident, replaced_owned($value:expr)$(, $($tail:tt)*)?) => {
738 match $lookup.next().unwrap() {
739 super::AliasMatch::Replaced(Pattern::Constant(c)) if c == $value => {}
740 m => panic!("unexpected match {:?}", m),
741 }
742 $(assert_alias_matches!(@next $lookup, $($tail)*);)?
743 };
744
745 (@next $lookup:ident,) => {};
747 }
748
749 impl<'a> AliasTemplate for &'a str {
750 type Output<'b>
751 = Pattern
752 where
753 Self: 'b;
754
755 fn replace(&self, capture: &Pattern) -> Self::Output<'a> {
756 capture.spread_into_star(self)
757 }
758
759 fn convert(&self) -> Self::Output<'a> {
760 Pattern::Constant(self.to_string().into())
761 }
762 }
763
764 #[test]
765 fn test_one_exact() {
766 let mut map = AliasMap::new();
767 map.insert(AliasPattern::parse("foo"), "bar");
768
769 assert_alias_matches!(map, "");
770 assert_alias_matches!(map, "foo", exact("bar"));
771 assert_alias_matches!(map, "foobar");
772 }
773
774 #[test]
775 fn test_many_exact() {
776 let mut map = AliasMap::new();
777 map.insert(AliasPattern::parse("foo"), "bar");
778 map.insert(AliasPattern::parse("bar"), "foo");
779 map.insert(AliasPattern::parse("foobar"), "barfoo");
780
781 assert_alias_matches!(map, "");
782 assert_alias_matches!(map, "foo", exact("bar"));
783 assert_alias_matches!(map, "bar", exact("foo"));
784 assert_alias_matches!(map, "foobar", exact("barfoo"));
785 }
786
787 #[test]
788 fn test_empty() {
789 let mut map = AliasMap::new();
790 map.insert(AliasPattern::parse(""), "empty");
791 map.insert(AliasPattern::parse("foo"), "bar");
792
793 assert_alias_matches!(map, "", exact("empty"));
794 assert_alias_matches!(map, "foo", exact("bar"));
795 }
796
797 #[test]
798 fn test_left_wildcard() {
799 let mut map = AliasMap::new();
800 map.insert(AliasPattern::parse("foo*"), "bar");
801
802 assert_alias_matches!(map, "");
803 assert_alias_matches!(map, "foo", replaced("bar"));
804 assert_alias_matches!(map, "foobar", replaced("bar"));
805 }
806
807 #[test]
808 fn test_wildcard_replace_suffix() {
809 let mut map = AliasMap::new();
810 map.insert(AliasPattern::parse("foo*"), "bar*");
811 map.insert(AliasPattern::parse("foofoo*"), "barbar*");
812
813 assert_alias_matches!(map, "");
814 assert_alias_matches!(map, "foo", replaced_owned("bar"));
815 assert_alias_matches!(map, "foobar", replaced_owned("barbar"));
816 assert_alias_matches!(
817 map,
818 "foofoobar",
819 replaced_owned("barbarbar"),
821 replaced_owned("barfoobar"),
822 );
823 }
824
825 #[test]
826 fn test_wildcard_replace_prefix() {
827 let mut map = AliasMap::new();
828 map.insert(AliasPattern::parse("*foo"), "*bar");
829 map.insert(AliasPattern::parse("*foofoo"), "*barbar");
830
831 assert_alias_matches!(map, "");
832 assert_alias_matches!(map, "foo", replaced_owned("bar"));
833 assert_alias_matches!(map, "barfoo", replaced_owned("barbar"));
834 assert_alias_matches!(
835 map,
836 "barfoofoo",
837 replaced_owned("barbarbar"),
839 replaced_owned("barfoobar"),
840 );
841 }
842
843 #[test]
844 fn test_wildcard_replace_infix() {
845 let mut map = AliasMap::new();
846 map.insert(AliasPattern::parse("foo*foo"), "bar*bar");
847 map.insert(AliasPattern::parse("foo*foofoo"), "bar*barbar");
848 map.insert(AliasPattern::parse("foofoo*foo"), "bazbaz*baz");
849
850 assert_alias_matches!(map, "");
851 assert_alias_matches!(map, "foo");
852 assert_alias_matches!(map, "foofoo", replaced_owned("barbar"));
853 assert_alias_matches!(map, "foobazfoo", replaced_owned("barbazbar"));
854 assert_alias_matches!(
855 map,
856 "foofoofoo",
857 replaced_owned("bazbazbaz"),
859 replaced_owned("barbarbar"),
861 replaced_owned("barfoobar"),
862 );
863 assert_alias_matches!(
864 map,
865 "foobazfoofoo",
866 replaced_owned("barbazbarbar"),
868 replaced_owned("barbazfoobar"),
869 );
870 assert_alias_matches!(
871 map,
872 "foofoobarfoo",
873 replaced_owned("bazbazbarbaz"),
875 replaced_owned("barfoobarbar"),
876 );
877 assert_alias_matches!(
878 map,
879 "foofoofoofoofoo",
880 replaced_owned("bazbazfoofoobaz"),
882 replaced_owned("barfoofoobarbar"),
884 replaced_owned("barfoofoofoobar"),
885 );
886 }
887
888 #[test]
889 fn test_wildcard_replace_only() {
890 let mut map = AliasMap::new();
891 map.insert(AliasPattern::parse("*"), "foo*foo");
892 map.insert(AliasPattern::parse("**"), "bar*foo");
893
894 assert_alias_matches!(map, "", replaced_owned("foofoo"));
895 assert_alias_matches!(map, "bar", replaced_owned("foobarfoo"));
896 assert_alias_matches!(
897 map,
898 "*",
899 replaced_owned("barfoo"),
900 replaced_owned("foo*foo"),
901 );
902 assert_alias_matches!(
903 map,
904 "**",
905 replaced_owned("bar*foo"),
906 replaced_owned("foo**foo")
907 );
908 }
909
910 #[test]
911 fn test_pattern() {
912 let mut map = AliasMap::new();
913 map.insert(AliasPattern::parse("card/*"), "src/cards/*");
914 map.insert(AliasPattern::parse("comp/*/x"), "src/comps/*/x");
915 map.insert(AliasPattern::parse("head/*/x"), "src/heads/*");
916
917 assert_eq!(
918 map.lookup(&Pattern::Concatenation(vec![
919 Pattern::Constant("card/".into()),
920 Pattern::Dynamic
921 ]))
922 .collect::<Vec<_>>(),
923 vec![super::AliasMatch::Replaced(Pattern::Concatenation(vec![
924 Pattern::Constant("src/cards/".into()),
925 Pattern::Dynamic
926 ]))]
927 );
928 assert_eq!(
929 map.lookup(&Pattern::Concatenation(vec![
930 Pattern::Constant("comp/".into()),
931 Pattern::Dynamic,
932 Pattern::Constant("/x".into()),
933 ]))
934 .collect::<Vec<_>>(),
935 vec![super::AliasMatch::Replaced(Pattern::Concatenation(vec![
936 Pattern::Constant("src/comps/".into()),
937 Pattern::Dynamic,
938 Pattern::Constant("/x".into()),
939 ]))]
940 );
941 assert_eq!(
942 map.lookup(&Pattern::Concatenation(vec![
943 Pattern::Constant("head/".into()),
944 Pattern::Dynamic,
945 Pattern::Constant("/x".into()),
946 ]))
947 .collect::<Vec<_>>(),
948 vec![super::AliasMatch::Replaced(Pattern::Concatenation(vec![
949 Pattern::Constant("src/heads/".into()),
950 Pattern::Dynamic,
951 ]))]
952 );
953 }
954}