diff --git a/lang/core/os/readfile_func.go b/lang/core/os/readfile_func.go index 1ddbd6a2..5a9d9555 100644 --- a/lang/core/os/readfile_func.go +++ b/lang/core/os/readfile_func.go @@ -151,9 +151,9 @@ func (obj *ReadFileFunc) Stream(ctx context.Context) error { obj.recWatcher = &recwatch.RecWatcher{ Path: *obj.filename, Recurse: false, - Flags: recwatch.Flags{ - // TODO: add Logf - Debug: obj.init.Debug, + Opts: []recwatch.Option{ + recwatch.Logf(obj.init.Logf), + recwatch.Debug(obj.init.Debug), }, } if err := obj.recWatcher.Init(); err != nil { diff --git a/util/recwatch/flags.go b/util/recwatch/flags.go deleted file mode 100644 index 26e22c96..00000000 --- a/util/recwatch/flags.go +++ /dev/null @@ -1,35 +0,0 @@ -// Mgmt -// Copyright (C) 2013-2024+ James Shubin and the project contributors -// Written by James Shubin and the project contributors -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Additional permission under GNU GPL version 3 section 7 -// -// If you modify this program, or any covered work, by linking or combining it -// with embedded mcl code and modules (and that the embedded mcl code and -// modules which link with this program, contain a copy of their source code in -// the authoritative form) containing parts covered by the terms of any other -// license, the licensors of this program grant you additional permission to -// convey the resulting work. Furthermore, the licensors of this program grant -// the original author, James Shubin, additional permission to update this -// additional permission if he deems it necessary to achieve the goals of this -// additional permission. - -package recwatch - -// Flags contains all the constant flags that recwatch needs. -type Flags struct { - Debug bool -} diff --git a/util/recwatch/recwatch.go b/util/recwatch/recwatch.go index b9dc5fec..d16f483b 100644 --- a/util/recwatch/recwatch.go +++ b/util/recwatch/recwatch.go @@ -32,7 +32,6 @@ package recwatch import ( "fmt" - "log" "math" "os" "path" @@ -54,11 +53,13 @@ type Event struct { // RecWatcher is the struct for the recursive watcher. Run Init() on it. type RecWatcher struct { - Path string // computed path - Recurse bool // should we watch recursively? - Flags Flags - isDir bool // computed isDir - safename string // safe path + Path string // computed path + Recurse bool // should we watch recursively? + Opts []Option // list of options we use + + options *recwatchOptions // computed options + isDir bool // computed isDir + safename string // safe path watcher *fsnotify.Watcher watches map[string]struct{} events chan Event // one channel for events and err... @@ -69,10 +70,11 @@ type RecWatcher struct { } // NewRecWatcher creates an initializes a new recursive watcher. -func NewRecWatcher(path string, recurse bool) (*RecWatcher, error) { +func NewRecWatcher(path string, recurse bool, opts ...Option) (*RecWatcher, error) { obj := &RecWatcher{ Path: path, Recurse: recurse, + Opts: opts, } return obj, obj.Init() } @@ -85,6 +87,19 @@ func (obj *RecWatcher) Init() error { obj.exit = make(chan struct{}) obj.isDir = strings.HasSuffix(obj.Path, "/") // dirs have trailing slashes obj.safename = path.Clean(obj.Path) // no trailing slash + obj.options = &recwatchOptions{ // default recwatch options + debug: false, + logf: func(format string, v ...interface{}) { + // noop + }, + } + for _, optionFunc := range obj.Opts { // apply the recwatch options + optionFunc(obj.options) + } + + if obj.options.logf == nil { + return fmt.Errorf("recwatch: logf must not be nil") + } var err error obj.watcher, err = fsnotify.NewWatcher() @@ -163,13 +178,13 @@ func (obj *RecWatcher) Watch() error { if current == "" { // the empty string top is the root dir ("/") current = "/" } - if obj.Flags.Debug { - log.Printf("watching: %s", current) // attempting to watch... + if obj.options.debug { + obj.options.logf("watching: %s", current) // attempting to watch... } // initialize in the loop so that we can reset on rm-ed handles if err := obj.watcher.Add(current); err != nil { - if obj.Flags.Debug { - log.Printf("watcher.Add(%s): Error: %v", current, err) + if obj.options.debug { + obj.options.logf("watcher.Add(%s): Error: %v", current, err) } // ENOENT for linux, etc and IsNotExist for macOS if err == syscall.ENOENT || os.IsNotExist(err) { @@ -191,8 +206,8 @@ func (obj *RecWatcher) Watch() error { select { case event := <-obj.watcher.Events: - if obj.Flags.Debug { - log.Printf("watch(%s), event(%s): %v", current, event.Name, event.Op) + if obj.options.debug { + obj.options.logf("watch(%s), event(%s): %v", current, event.Name, event.Op) } // the deeper you go, the bigger the deltaDepth is... // this is the difference between what we're watching, @@ -228,11 +243,11 @@ func (obj *RecWatcher) Watch() error { // event.Name: /tmp/mgmt/f3 and current: /tmp/mgmt/f2 continue } - //log.Printf("the delta depth is: %v", deltaDepth) + //obj.options.logf("the delta depth is: %v", deltaDepth) // if we have what we wanted, awesome, send an event... if event.Name == obj.safename { - //log.Printf("event!") + //obj.options.logf("event!") // FIXME: should all these below cases trigger? send = true @@ -244,7 +259,7 @@ func (obj *RecWatcher) Watch() error { // file removed, move the watch upwards if deltaDepth >= 0 && (event.Op&fsnotify.Remove == fsnotify.Remove) { - //log.Printf("removal!") + //obj.options.logf("removal!") obj.watcher.Remove(current) index-- } @@ -265,16 +280,16 @@ func (obj *RecWatcher) Watch() error { // if safename starts with event.Name, we're above, and no event should be sent } else if util.HasPathPrefix(obj.safename, event.Name) { - //log.Printf("above!") + //obj.options.logf("above!") if deltaDepth >= 0 && (event.Op&fsnotify.Remove == fsnotify.Remove) { - log.Printf("removal!") + //obj.options.logf("removal!") obj.watcher.Remove(current) index-- } if deltaDepth < 0 { - log.Printf("parent!") + //obj.options.logf("parent!") if util.PathPrefixDelta(obj.safename, event.Name) == 1 { // we're the parent dir send = true } @@ -284,7 +299,7 @@ func (obj *RecWatcher) Watch() error { // if event.Name startswith safename, send event, we're already deeper } else if util.HasPathPrefix(event.Name, obj.safename) { - //log.Printf("event2!") + //obj.options.logf("event2!") send = true } @@ -317,8 +332,8 @@ func (obj *RecWatcher) addSubFolders(p string) error { } // look at all subfolders... walkFn := func(path string, info os.FileInfo, err error) error { - if obj.Flags.Debug { - log.Printf("walk: %s (%v): %v", path, info, err) + if obj.options.debug { + obj.options.logf("walk: %s (%v): %v", path, info, err) } if err != nil { return nil @@ -336,6 +351,29 @@ func (obj *RecWatcher) addSubFolders(p string) error { return err } +// Option is a type that can be used to configure the recwatcher. +type Option func(*recwatchOptions) + +type recwatchOptions struct { + debug bool + logf func(format string, v ...interface{}) + // TODO: add more options +} + +// Debug specifies whether we should run in debug mode or not. +func Debug(debug bool) Option { + return func(rwo *recwatchOptions) { + rwo.debug = debug + } +} + +// Logf passes a logger function that we can use if so desired. +func Logf(logf func(format string, v ...interface{})) Option { + return func(rwo *recwatchOptions) { + rwo.logf = logf + } +} + func isDir(path string) bool { finfo, err := os.Stat(path) if err != nil {