1use crate::Resolver;
2use daachorse::{CharwiseDoubleArrayAhoCorasick, CharwiseDoubleArrayAhoCorasickBuilder, MatchKind};
3use once_cell::sync::Lazy;
4
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum PathKind {
7 Relative,
8 AbsoluteWin,
9 AbsolutePosix,
10 Internal,
11 Normal,
12}
13
14static ABSOLUTE_WIN_PATTERN_LENGTH_TWO: [&str; 52] = [
15 "a:", "b:", "c:", "d:", "e:", "f:", "g:", "h:", "i:", "j:", "k:", "l:", "m:", "n:", "o:", "p:",
16 "q:", "r:", "s:", "t:", "u:", "v:", "w:", "x:", "y:", "z:", "A:", "B:", "C:", "D:", "E:", "F:",
17 "G:", "H:", "I:", "J:", "K:", "L:", "M:", "N:", "O:", "P:", "Q:", "R:", "S:", "T:", "U:", "V:",
18 "W:", "X:", "Y:", "Z:",
19];
20
21static ABSOLUTE_WIN_PATTERN_REST: [&str; 104] = [
22 "a:\\", "b:\\", "c:\\", "d:\\", "e:\\", "f:\\", "g:\\", "h:\\", "i:\\", "j:\\", "k:\\", "l:\\",
23 "m:\\", "n:\\", "o:\\", "p:\\", "q:\\", "r:\\", "s:\\", "t:\\", "u:\\", "v:\\", "w:\\", "x:\\",
24 "y:\\", "z:\\", "A:\\", "B:\\", "C:\\", "D:\\", "E:\\", "F:\\", "G:\\", "H:\\", "I:\\", "J:\\",
25 "K:\\", "L:\\", "M:\\", "N:\\", "O:\\", "P:\\", "Q:\\", "R:\\", "S:\\", "T:\\", "U:\\", "V:\\",
26 "W:\\", "X:\\", "Y:\\", "Z:\\", "a:/", "b:/", "c:/", "d:/", "e:/", "f:/", "g:/", "h:/", "i:/",
27 "j:/", "k:/", "l:/", "m:/", "n:/", "o:/", "p:/", "q:/", "r:/", "s:/", "t:/", "u:/", "v:/",
28 "w:/", "x:/", "y:/", "z:/", "A:/", "B:/", "C:/", "D:/", "E:/", "F:/", "G:/", "H:/", "I:/",
29 "J:/", "K:/", "L:/", "M:/", "N:/", "O:/", "P:/", "Q:/", "R:/", "S:/", "T:/", "U:/", "V:/",
30 "W:/", "X:/", "Y:/", "Z:/",
31];
32
33static PMA: Lazy<CharwiseDoubleArrayAhoCorasick<usize>> = Lazy::new(|| {
34 CharwiseDoubleArrayAhoCorasickBuilder::new()
35 .match_kind(MatchKind::LeftmostLongest)
36 .build(ABSOLUTE_WIN_PATTERN_REST)
37 .unwrap()
38});
39
40impl Resolver {
41 pub(crate) fn get_target_kind(target: &str) -> PathKind {
42 if target.is_empty() {
43 return PathKind::Relative;
44 }
45
46 let path_kind = if target.starts_with('#') {
47 PathKind::Internal
48 } else if target.starts_with('/') {
49 PathKind::AbsolutePosix
50 } else if target == "."
51 || target.starts_with("./")
52 || target.starts_with("../")
53 || target == ".."
54 {
55 PathKind::Relative
56 } else {
57 if target.len() == 2 && ABSOLUTE_WIN_PATTERN_LENGTH_TWO.contains(&target) {
58 return PathKind::AbsoluteWin;
59 }
60 let mut iter = PMA.leftmost_find_iter(target);
61 if let Some(mat) = iter.next() {
62 let match_pattern_len = ABSOLUTE_WIN_PATTERN_REST[mat.value()].len();
63 if mat.start() == 0 && mat.end() - mat.start() == match_pattern_len {
64 return PathKind::AbsoluteWin;
65 }
66 }
67 PathKind::Normal
68 };
69 path_kind
70 }
71}
72
73#[test]
74fn test_resolver() {
75 assert!(matches!(Resolver::get_target_kind(""), PathKind::Relative));
76 assert!(matches!(Resolver::get_target_kind("."), PathKind::Relative));
77 assert!(matches!(
78 Resolver::get_target_kind(".."),
79 PathKind::Relative
80 ));
81 assert!(matches!(
82 Resolver::get_target_kind("../a.js"),
83 PathKind::Relative
84 ));
85 assert!(matches!(
86 Resolver::get_target_kind("./a.js"),
87 PathKind::Relative
88 ));
89 assert!(matches!(
90 Resolver::get_target_kind("D:"),
91 PathKind::AbsoluteWin
92 ));
93 assert!(matches!(
94 Resolver::get_target_kind("C:path"),
95 PathKind::Normal
96 ));
97 assert!(matches!(
98 Resolver::get_target_kind("C:\\a"),
99 PathKind::AbsoluteWin
100 ));
101 assert!(matches!(
102 Resolver::get_target_kind("c:/a"),
103 PathKind::AbsoluteWin
104 ));
105 assert!(matches!(
106 Resolver::get_target_kind("cc:/a"),
107 PathKind::Normal
108 ));
109 assert!(matches!(Resolver::get_target_kind("fs"), PathKind::Normal));
110}