nodejs-resolver 0.1.1

nodejs resolve
Documentation
use crate::Resolver;
use daachorse::{CharwiseDoubleArrayAhoCorasick, CharwiseDoubleArrayAhoCorasickBuilder, MatchKind};
use once_cell::sync::Lazy;

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PathKind {
    Relative,
    AbsoluteWin,
    AbsolutePosix,
    Internal,
    Normal,
}

static ABSOLUTE_WIN_PATTERN_LENGTH_TWO: [&str; 52] = [
    "a:", "b:", "c:", "d:", "e:", "f:", "g:", "h:", "i:", "j:", "k:", "l:", "m:", "n:", "o:", "p:",
    "q:", "r:", "s:", "t:", "u:", "v:", "w:", "x:", "y:", "z:", "A:", "B:", "C:", "D:", "E:", "F:",
    "G:", "H:", "I:", "J:", "K:", "L:", "M:", "N:", "O:", "P:", "Q:", "R:", "S:", "T:", "U:", "V:",
    "W:", "X:", "Y:", "Z:",
];

static ABSOLUTE_WIN_PATTERN_REST: [&str; 104] = [
    "a:\\", "b:\\", "c:\\", "d:\\", "e:\\", "f:\\", "g:\\", "h:\\", "i:\\", "j:\\", "k:\\", "l:\\",
    "m:\\", "n:\\", "o:\\", "p:\\", "q:\\", "r:\\", "s:\\", "t:\\", "u:\\", "v:\\", "w:\\", "x:\\",
    "y:\\", "z:\\", "A:\\", "B:\\", "C:\\", "D:\\", "E:\\", "F:\\", "G:\\", "H:\\", "I:\\", "J:\\",
    "K:\\", "L:\\", "M:\\", "N:\\", "O:\\", "P:\\", "Q:\\", "R:\\", "S:\\", "T:\\", "U:\\", "V:\\",
    "W:\\", "X:\\", "Y:\\", "Z:\\", "a:/", "b:/", "c:/", "d:/", "e:/", "f:/", "g:/", "h:/", "i:/",
    "j:/", "k:/", "l:/", "m:/", "n:/", "o:/", "p:/", "q:/", "r:/", "s:/", "t:/", "u:/", "v:/",
    "w:/", "x:/", "y:/", "z:/", "A:/", "B:/", "C:/", "D:/", "E:/", "F:/", "G:/", "H:/", "I:/",
    "J:/", "K:/", "L:/", "M:/", "N:/", "O:/", "P:/", "Q:/", "R:/", "S:/", "T:/", "U:/", "V:/",
    "W:/", "X:/", "Y:/", "Z:/",
];

static PMA: Lazy<CharwiseDoubleArrayAhoCorasick<usize>> = Lazy::new(|| {
    CharwiseDoubleArrayAhoCorasickBuilder::new()
        .match_kind(MatchKind::LeftmostLongest)
        .build(ABSOLUTE_WIN_PATTERN_REST)
        .unwrap()
});

impl Resolver {
    pub(crate) fn get_target_kind(target: &str) -> PathKind {
        if target.is_empty() {
            return PathKind::Relative;
        }

        let path_kind = if target.starts_with('#') {
            PathKind::Internal
        } else if target.starts_with('/') {
            PathKind::AbsolutePosix
        } else if target == "."
            || target.starts_with("./")
            || target.starts_with("../")
            || target == ".."
        {
            PathKind::Relative
        } else {
            if target.len() == 2 && ABSOLUTE_WIN_PATTERN_LENGTH_TWO.contains(&target) {
                return PathKind::AbsoluteWin;
            }
            let mut iter = PMA.leftmost_find_iter(target);
            if let Some(mat) = iter.next() {
                let match_pattern_len = ABSOLUTE_WIN_PATTERN_REST[mat.value()].len();
                if mat.start() == 0 && mat.end() - mat.start() == match_pattern_len {
                    return PathKind::AbsoluteWin;
                }
            }
            PathKind::Normal
        };
        path_kind
    }
}

#[test]
fn test_resolver() {
    assert!(matches!(Resolver::get_target_kind(""), PathKind::Relative));
    assert!(matches!(Resolver::get_target_kind("."), PathKind::Relative));
    assert!(matches!(
        Resolver::get_target_kind(".."),
        PathKind::Relative
    ));
    assert!(matches!(
        Resolver::get_target_kind("../a.js"),
        PathKind::Relative
    ));
    assert!(matches!(
        Resolver::get_target_kind("./a.js"),
        PathKind::Relative
    ));
    assert!(matches!(
        Resolver::get_target_kind("D:"),
        PathKind::AbsoluteWin
    ));
    assert!(matches!(
        Resolver::get_target_kind("C:path"),
        PathKind::Normal
    ));
    assert!(matches!(
        Resolver::get_target_kind("C:\\a"),
        PathKind::AbsoluteWin
    ));
    assert!(matches!(
        Resolver::get_target_kind("c:/a"),
        PathKind::AbsoluteWin
    ));
    assert!(matches!(
        Resolver::get_target_kind("cc:/a"),
        PathKind::Normal
    ));
    assert!(matches!(Resolver::get_target_kind("fs"), PathKind::Normal));
}