glob-parent 0.1.1

Extract the non-magic parent path from a glob: foo/bar/*.js -> foo/bar. A faithful port of the glob-parent npm package. Zero dependencies, no_std.
Documentation
//! Behavioral spec for `glob-parent`, cross-checked against the npm package.

use glob_parent::{glob_parent, is_extglob, is_glob};

#[test]
fn basic_globs() {
    assert_eq!(glob_parent("path/to/*.js"), "path/to");
    assert_eq!(glob_parent("path/*/to/file.js"), "path");
    assert_eq!(glob_parent("path/**/*.js"), "path");
    assert_eq!(glob_parent("/path/to/*"), "/path/to");
    assert_eq!(glob_parent("path/to/*"), "path/to");
    assert_eq!(glob_parent("foo/*/bar/*/baz"), "foo");
    assert_eq!(glob_parent("**/*.js"), ".");
    assert_eq!(glob_parent("some/**/needle.txt"), "some");
}

#[test]
fn no_glob() {
    assert_eq!(glob_parent("path/to/file.js"), "path/to");
    assert_eq!(glob_parent("a/b/c/d"), "a/b/c");
    assert_eq!(glob_parent("foo/bar.js"), "foo");
    assert_eq!(glob_parent("foo"), ".");
    assert_eq!(glob_parent(""), ".");
    assert_eq!(glob_parent("."), ".");
    assert_eq!(glob_parent("/"), "/");
    assert_eq!(glob_parent("/root"), "/");
}

#[test]
fn trailing_slash_and_relative() {
    assert_eq!(glob_parent("path/to/"), "path/to");
    assert_eq!(glob_parent("c/"), "c");
    assert_eq!(glob_parent("a/b/"), "a/b");
    assert_eq!(glob_parent("./foo/*.js"), "./foo");
    assert_eq!(glob_parent("../foo/*"), "../foo");
}

#[test]
fn braces_brackets_parens() {
    assert_eq!(glob_parent("path/{foo,bar}/*.js"), "path");
    assert_eq!(glob_parent("path/[a-z]/x"), "path");
    assert_eq!(glob_parent("path/to/{a,b}"), "path/to");
    assert_eq!(glob_parent("foo/{a,b}"), "foo");
    assert_eq!(glob_parent("foo/[ab]"), "foo");
    assert_eq!(glob_parent("path/(a|b)/c"), "path");
    // a closed, non-extglob paren group is not magic
    assert_eq!(glob_parent("path/with/(parens)/x"), "path/with/(parens)");
    // an enclosure spanning a separator collapses to "."
    assert_eq!(glob_parent("{foo,bar/baz}"), ".");
    assert_eq!(glob_parent("[a/b]"), ".");
}

#[test]
fn extglobs_and_wildcards() {
    assert_eq!(glob_parent("path/!(foo)/x"), "path");
    assert_eq!(glob_parent("path/@(a|b)/y"), "path");
    assert_eq!(glob_parent("foo/+(x)"), "foo");
    assert_eq!(glob_parent("seg/x?y/z"), "seg/x?y");
    assert_eq!(glob_parent("seg/x*y"), "seg");
}

#[test]
fn escapes() {
    // an escaped wildcard is literal, so the parent is its directory (escape removed)
    assert_eq!(glob_parent("path/to/\\*.js"), "path/to");
}

#[test]
fn is_glob_helper() {
    assert!(is_glob("a/*.js"));
    assert!(is_glob("a/{b,c}"));
    assert!(is_glob("a/[bc]"));
    assert!(is_glob("!(a|b)"));
    assert!(!is_glob("a/b.js"));
    assert!(!is_glob(""));
    assert!(!is_glob("a/(b)"));
}

// Regression: JS regex `.` never matches a line terminator, so an extglob whose
// parens span one is not an extglob.
#[test]
fn extglob_across_line_terminator() {
    assert!(!is_extglob("@(a\nb)"));
    assert!(!is_glob("@(a\nb)"));
    assert_eq!(glob_parent("src/dir/!(x\ny)/z"), "src/dir/!(x\ny)");
    // tab/space/vtab/ff inside an extglob are fine (JS `.` matches them)
    assert!(is_extglob("@(a\tb)"));
}

// Regression: indexing follows UTF-16 code units, like the JS reference.
#[test]
fn non_bmp_escape() {
    assert!(!is_glob("\\😀!"));
    assert_eq!(glob_parent("a/\\😀!/b"), "a/\\😀!");
}

#[test]
fn is_extglob_helper() {
    assert!(is_extglob("@(a|b)"));
    assert!(is_extglob("foo/!(bar)"));
    assert!(is_extglob("+(x)"));
    assert!(!is_extglob("(a|b)"));
    assert!(!is_extglob("\\@(a)")); // escaped
    assert!(!is_extglob(""));
}