| package doublestar |
| |
| import "strings" |
| |
| // glob is an internal type to store options during globbing. |
| type glob struct { |
| failOnIOErrors bool |
| failOnPatternNotExist bool |
| filesOnly bool |
| noFollow bool |
| } |
| |
| // GlobOption represents a setting that can be passed to Glob, GlobWalk, and |
| // FilepathGlob. |
| type GlobOption func(*glob) |
| |
| // Construct a new glob object with the given options |
| func newGlob(opts ...GlobOption) *glob { |
| g := &glob{} |
| for _, opt := range opts { |
| opt(g) |
| } |
| return g |
| } |
| |
| // WithFailOnIOErrors is an option that can be passed to Glob, GlobWalk, or |
| // FilepathGlob. If passed, doublestar will abort and return IO errors when |
| // encountered. Note that if the glob pattern references a path that does not |
| // exist (such as `nonexistent/path/*`), this is _not_ considered an IO error: |
| // it is considered a pattern with no matches. |
| // |
| func WithFailOnIOErrors() GlobOption { |
| return func(g *glob) { |
| g.failOnIOErrors = true |
| } |
| } |
| |
| // WithFailOnPatternNotExist is an option that can be passed to Glob, GlobWalk, |
| // or FilepathGlob. If passed, doublestar will abort and return |
| // ErrPatternNotExist if the pattern references a path that does not exist |
| // before any meta charcters such as `nonexistent/path/*`. Note that alts (ie, |
| // `{...}`) are expanded before this check. In other words, a pattern such as |
| // `{a,b}/*` may fail if either `a` or `b` do not exist but `*/{a,b}` will |
| // never fail because the star may match nothing. |
| // |
| func WithFailOnPatternNotExist() GlobOption { |
| return func(g *glob) { |
| g.failOnPatternNotExist = true |
| } |
| } |
| |
| // WithFilesOnly is an option that can be passed to Glob, GlobWalk, or |
| // FilepathGlob. If passed, doublestar will only return files that match the |
| // pattern, not directories. |
| // |
| // Note: if combined with the WithNoFollow option, symlinks to directories |
| // _will_ be included in the result since no attempt is made to follow the |
| // symlink. |
| // |
| func WithFilesOnly() GlobOption { |
| return func(g *glob) { |
| g.filesOnly = true |
| } |
| } |
| |
| // WithNoFollow is an option that can be passed to Glob, GlobWalk, or |
| // FilepathGlob. If passed, doublestar will not follow symlinks while |
| // traversing the filesystem. However, due to io/fs's _very_ poor support for |
| // querying the filesystem about symlinks, there's a caveat here: if part of |
| // the pattern before any meta characters contains a reference to a symlink, it |
| // will be followed. For example, a pattern such as `path/to/symlink/*` will be |
| // followed assuming it is a valid symlink to a directory. However, from this |
| // same example, a pattern such as `path/to/**` will not traverse the |
| // `symlink`, nor would `path/*/symlink/*` |
| // |
| // Note: if combined with the WithFilesOnly option, symlinks to directories |
| // _will_ be included in the result since no attempt is made to follow the |
| // symlink. |
| // |
| func WithNoFollow() GlobOption { |
| return func(g *glob) { |
| g.noFollow = true |
| } |
| } |
| |
| // forwardErrIfFailOnIOErrors is used to wrap the return values of I/O |
| // functions. When failOnIOErrors is enabled, it will return err; otherwise, it |
| // always returns nil. |
| // |
| func (g *glob) forwardErrIfFailOnIOErrors(err error) error { |
| if g.failOnIOErrors { |
| return err |
| } |
| return nil |
| } |
| |
| // handleErrNotExist handles fs.ErrNotExist errors. If |
| // WithFailOnPatternNotExist has been enabled and canFail is true, this will |
| // return ErrPatternNotExist. Otherwise, it will return nil. |
| // |
| func (g *glob) handlePatternNotExist(canFail bool) error { |
| if canFail && g.failOnPatternNotExist { |
| return ErrPatternNotExist |
| } |
| return nil |
| } |
| |
| // Format options for debugging/testing purposes |
| func (g *glob) GoString() string { |
| var b strings.Builder |
| b.WriteString("opts: ") |
| |
| hasOpts := false |
| if g.failOnIOErrors { |
| b.WriteString("WithFailOnIOErrors") |
| hasOpts = true |
| } |
| if g.failOnPatternNotExist { |
| if hasOpts { |
| b.WriteString(", ") |
| } |
| b.WriteString("WithFailOnPatternNotExist") |
| hasOpts = true |
| } |
| if g.filesOnly { |
| if hasOpts { |
| b.WriteString(", ") |
| } |
| b.WriteString("WithFilesOnly") |
| hasOpts = true |
| } |
| if g.noFollow { |
| if hasOpts { |
| b.WriteString(", ") |
| } |
| b.WriteString("WithNoFollow") |
| hasOpts = true |
| } |
| |
| if !hasOpts { |
| b.WriteString("nil") |
| } |
| return b.String() |
| } |