globoptions.go

  1package doublestar
  2
  3import "strings"
  4
  5// glob is an internal type to store options during globbing.
  6type glob struct {
  7	failOnIOErrors        bool
  8	failOnPatternNotExist bool
  9	filesOnly             bool
 10	noFollow              bool
 11}
 12
 13// GlobOption represents a setting that can be passed to Glob, GlobWalk, and
 14// FilepathGlob.
 15type GlobOption func(*glob)
 16
 17// Construct a new glob object with the given options
 18func newGlob(opts ...GlobOption) *glob {
 19	g := &glob{}
 20	for _, opt := range opts {
 21		opt(g)
 22	}
 23	return g
 24}
 25
 26// WithFailOnIOErrors is an option that can be passed to Glob, GlobWalk, or
 27// FilepathGlob. If passed, doublestar will abort and return IO errors when
 28// encountered. Note that if the glob pattern references a path that does not
 29// exist (such as `nonexistent/path/*`), this is _not_ considered an IO error:
 30// it is considered a pattern with no matches.
 31//
 32func WithFailOnIOErrors() GlobOption {
 33	return func(g *glob) {
 34		g.failOnIOErrors = true
 35	}
 36}
 37
 38// WithFailOnPatternNotExist is an option that can be passed to Glob, GlobWalk,
 39// or FilepathGlob. If passed, doublestar will abort and return
 40// ErrPatternNotExist if the pattern references a path that does not exist
 41// before any meta charcters such as `nonexistent/path/*`. Note that alts (ie,
 42// `{...}`) are expanded before this check. In other words, a pattern such as
 43// `{a,b}/*` may fail if either `a` or `b` do not exist but `*/{a,b}` will
 44// never fail because the star may match nothing.
 45//
 46func WithFailOnPatternNotExist() GlobOption {
 47	return func(g *glob) {
 48		g.failOnPatternNotExist = true
 49	}
 50}
 51
 52// WithFilesOnly is an option that can be passed to Glob, GlobWalk, or
 53// FilepathGlob. If passed, doublestar will only return files that match the
 54// pattern, not directories.
 55//
 56// Note: if combined with the WithNoFollow option, symlinks to directories
 57// _will_ be included in the result since no attempt is made to follow the
 58// symlink.
 59//
 60func WithFilesOnly() GlobOption {
 61	return func(g *glob) {
 62		g.filesOnly = true
 63	}
 64}
 65
 66// WithNoFollow is an option that can be passed to Glob, GlobWalk, or
 67// FilepathGlob. If passed, doublestar will not follow symlinks while
 68// traversing the filesystem. However, due to io/fs's _very_ poor support for
 69// querying the filesystem about symlinks, there's a caveat here: if part of
 70// the pattern before any meta characters contains a reference to a symlink, it
 71// will be followed. For example, a pattern such as `path/to/symlink/*` will be
 72// followed assuming it is a valid symlink to a directory. However, from this
 73// same example, a pattern such as `path/to/**` will not traverse the
 74// `symlink`, nor would `path/*/symlink/*`
 75//
 76// Note: if combined with the WithFilesOnly option, symlinks to directories
 77// _will_ be included in the result since no attempt is made to follow the
 78// symlink.
 79//
 80func WithNoFollow() GlobOption {
 81	return func(g *glob) {
 82		g.noFollow = true
 83	}
 84}
 85
 86// forwardErrIfFailOnIOErrors is used to wrap the return values of I/O
 87// functions. When failOnIOErrors is enabled, it will return err; otherwise, it
 88// always returns nil.
 89//
 90func (g *glob) forwardErrIfFailOnIOErrors(err error) error {
 91	if g.failOnIOErrors {
 92		return err
 93	}
 94	return nil
 95}
 96
 97// handleErrNotExist handles fs.ErrNotExist errors. If
 98// WithFailOnPatternNotExist has been enabled and canFail is true, this will
 99// return ErrPatternNotExist. Otherwise, it will return nil.
100//
101func (g *glob) handlePatternNotExist(canFail bool) error {
102	if canFail && g.failOnPatternNotExist {
103		return ErrPatternNotExist
104	}
105	return nil
106}
107
108// Format options for debugging/testing purposes
109func (g *glob) GoString() string {
110	var b strings.Builder
111	b.WriteString("opts: ")
112
113	hasOpts := false
114	if g.failOnIOErrors {
115		b.WriteString("WithFailOnIOErrors")
116		hasOpts = true
117	}
118	if g.failOnPatternNotExist {
119		if hasOpts {
120			b.WriteString(", ")
121		}
122		b.WriteString("WithFailOnPatternNotExist")
123		hasOpts = true
124	}
125	if g.filesOnly {
126		if hasOpts {
127			b.WriteString(", ")
128		}
129		b.WriteString("WithFilesOnly")
130		hasOpts = true
131	}
132	if g.noFollow {
133		if hasOpts {
134			b.WriteString(", ")
135		}
136		b.WriteString("WithNoFollow")
137		hasOpts = true
138	}
139
140	if !hasOpts {
141		b.WriteString("nil")
142	}
143	return b.String()
144}