Add filesystem glob support
This commit is contained in:
parent
4630bc96b4
commit
13808e841e
94
glob.go
94
glob.go
@ -1,6 +1,10 @@
|
|||||||
package pcre
|
package pcre
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"go.arsenm.dev/pcre/lib"
|
"go.arsenm.dev/pcre/lib"
|
||||||
@ -66,3 +70,93 @@ func CompileGlob(glob string) (*Regexp, error) {
|
|||||||
// Compile converted glob and return results
|
// Compile converted glob and return results
|
||||||
return Compile(pattern)
|
return Compile(pattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Glob returns a list of matches for the given glob pattern.
|
||||||
|
// It returns nil if there was no match. If the glob contains
|
||||||
|
// "**", it will recurse through the directory, which may be
|
||||||
|
// extremely slow depending on which directory is being searched.
|
||||||
|
func Glob(glob string) ([]string, error) {
|
||||||
|
// If glob is empty, return nil
|
||||||
|
if glob == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the glob is a file path, return the file
|
||||||
|
_, err := os.Lstat(glob)
|
||||||
|
if err == nil {
|
||||||
|
return []string{glob}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the glob has no glob characters, return nil
|
||||||
|
if !hasGlobChars(glob) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split glob by filepath separator
|
||||||
|
paths := strings.Split(glob, string(filepath.Separator))
|
||||||
|
|
||||||
|
var splitDir []string
|
||||||
|
// For every path in split list
|
||||||
|
for _, path := range paths {
|
||||||
|
// If glob characters forund, stop
|
||||||
|
if hasGlobChars(path) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Add path to splitDir
|
||||||
|
splitDir = append(splitDir, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join splitDir and add filepath separator. This is the directory that will be searched.
|
||||||
|
dir := filepath.Join(splitDir...)
|
||||||
|
|
||||||
|
if filepath.IsAbs(glob) {
|
||||||
|
dir = string(filepath.Separator) + dir
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the directory is not accessible, return error
|
||||||
|
_, err = os.Lstat(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile glob pattern
|
||||||
|
r, err := CompileGlob(glob)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
var matches []string
|
||||||
|
// If glob contains "**" (starstar), walk recursively. Otherwise, only search dir.
|
||||||
|
if strings.Contains(glob, "**") {
|
||||||
|
err = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if r.MatchString(path) {
|
||||||
|
matches = append(matches, path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
files, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
// Get full path of file
|
||||||
|
path := filepath.Join(dir, file.Name())
|
||||||
|
if r.MatchString(path) {
|
||||||
|
matches = append(matches, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasGlobChars checks if the string has any
|
||||||
|
// characters that are part of a glob.
|
||||||
|
func hasGlobChars(s string) bool {
|
||||||
|
return strings.ContainsAny(s, "*[]?")
|
||||||
|
}
|
||||||
|
83
glob_test.go
83
glob_test.go
@ -1,6 +1,7 @@
|
|||||||
package pcre_test
|
package pcre_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go.arsenm.dev/pcre"
|
"go.arsenm.dev/pcre"
|
||||||
@ -36,3 +37,85 @@ func TestCompileGlob(t *testing.T) {
|
|||||||
t.Error("expected /home not to match")
|
t.Error("expected /home not to match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGlob(t *testing.T) {
|
||||||
|
err := os.MkdirAll("pcretest/dir1", 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.MkdirAll("pcretest/dir2", 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.MkdirAll("pcretest/test1/dir4", 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = touch("pcretest/file1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = touch("pcretest/file2")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = touch("pcretest/test1/dir4/text.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, err := pcre.Glob("pcretest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(matches) != 1 || matches[0] != "pcretest" {
|
||||||
|
t.Errorf("expected [pcretest], got %v", matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, err = pcre.Glob("pcretest/dir*")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(matches) != 2 ||
|
||||||
|
matches[0] != "pcretest/dir1" ||
|
||||||
|
matches[1] != "pcretest/dir2" {
|
||||||
|
t.Errorf("expected [pcretest/dir1 pcretest/dir2], got %v", matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, err = pcre.Glob("pcretest/file*")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(matches) != 2 ||
|
||||||
|
matches[0] != "pcretest/file1" ||
|
||||||
|
matches[1] != "pcretest/file2" {
|
||||||
|
t.Errorf("expected [pcretest/file1 pcretest/file2], got %v", matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, err = pcre.Glob("pcretest/**/*.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(matches) != 1 ||
|
||||||
|
matches[0] != "pcretest/test1/dir4/text.txt" {
|
||||||
|
t.Errorf("expected [pcretest/test1/dir4/text.txt], got %v", matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.RemoveAll("pcretest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func touch(path string) error {
|
||||||
|
fl, err := os.OpenFile(path, os.O_CREATE, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fl.Close()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user