GitRoot
craft your forge, build your project, grow your community freely
1// SPDX-FileCopyrightText: 2025 Romain Maneschi <romain@gitroot.dev>
2//
3// SPDX-License-Identifier: MIT
4
5package glob
6
7import (
8 "regexp"
9 "strings"
10)
11
12// inspired by https://github.com/golang/go/issues/11862#issuecomment-1517792571
13
14var replaces = regexp.MustCompile(`(\.)|(\*\*\/)|(\*)|([^\/\*]+)|(\/)`)
15
16func toRegexp(pattern string) string {
17 pat := replaces.ReplaceAllStringFunc(pattern, func(s string) string {
18 switch s {
19 case "/":
20 return "\\/"
21 case ".":
22 return "\\."
23 case "**/":
24 return ".*"
25 case "*":
26 return "[^/]*"
27 default:
28 return s
29 }
30 })
31 return "^" + pat + "$"
32}
33
34type Glob struct {
35 reg *regexp.Regexp
36 regNeg *regexp.Regexp
37}
38
39func NewGlob(path string) (*Glob, error) {
40 parts := strings.Split(path, "|")
41 norm := []string{}
42 neg := []string{}
43 for _, part := range parts {
44 if strings.HasPrefix(part, "!") {
45 neg = append(neg, strings.TrimPrefix(part, "!"))
46 } else {
47 norm = append(norm, part)
48 }
49 }
50 reg, err := regexp.Compile(toRegexp(strings.Join(norm, "|")))
51 if err != nil {
52 return nil, err
53 }
54 var regNeg *regexp.Regexp
55 if len(neg) > 0 {
56 regNeg, err = regexp.Compile(toRegexp(strings.Join(neg, "|")))
57 if err != nil {
58 return nil, err
59 }
60 }
61 return &Glob{
62 reg: reg,
63 regNeg: regNeg,
64 }, nil
65}
66
67func (g *Glob) Match(path string) bool {
68 if g.regNeg == nil {
69 return g.reg.MatchString(path)
70 } else {
71 return g.reg.MatchString(path) && !g.regNeg.MatchString(path)
72 }
73}