github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/fileevents/watcher/watcher.go (about)

     1  package watcher
     2  
     3  import (
     4  	"github.com/fsnotify/fsnotify"
     5  
     6  	"github.com/ActiveState/cli/internal/errs"
     7  	"github.com/ActiveState/cli/internal/fileutils"
     8  	"github.com/ActiveState/cli/internal/locale"
     9  	"github.com/ActiveState/cli/internal/logging"
    10  	"github.com/ActiveState/cli/internal/multilog"
    11  )
    12  
    13  type Closer func()
    14  
    15  type OnEvent func(filepath string, log logging.Logger) error
    16  
    17  func logInfo(msg string, args ...interface{}) {
    18  	logging.Info("File-Event: "+msg, args...)
    19  }
    20  
    21  func logError(msg string, args ...interface{}) {
    22  	multilog.Error("File-Event: "+msg, args...)
    23  }
    24  
    25  type Watcher struct {
    26  	fswatcher *fsnotify.Watcher
    27  	done      chan bool
    28  	onEvent   *OnEvent
    29  }
    30  
    31  func New() (*Watcher, error) {
    32  	w := &Watcher{}
    33  	var err error
    34  	w.fswatcher, err = fsnotify.NewWatcher()
    35  	if err != nil {
    36  		return nil, errs.Wrap(err, "Could not create filesystem watcher")
    37  	}
    38  
    39  	w.done = make(chan bool)
    40  	go func() {
    41  		for {
    42  			select {
    43  			case <-w.done:
    44  				return
    45  			case event, ok := <-w.fswatcher.Events:
    46  				if !ok || w.onEvent == nil {
    47  					continue
    48  				}
    49  
    50  				if event.Op&fsnotify.Write != fsnotify.Write {
    51  					logging.Debug(event.String() + ": Skip")
    52  					continue
    53  				}
    54  
    55  				logInfo(event.String())
    56  
    57  				if err := (*w.onEvent)(event.Name, logInfo); err != nil {
    58  					logError(errs.JoinMessage(err))
    59  					continue
    60  				}
    61  			case err, ok := <-w.fswatcher.Errors:
    62  				if !ok {
    63  					return
    64  				}
    65  				logError(err.Error())
    66  			}
    67  		}
    68  	}()
    69  
    70  	return w, nil
    71  }
    72  
    73  func (w *Watcher) Add(filepath string) error {
    74  	logInfo(locale.Tl("fileevent_watchig", "Watching {{.V0}}", filepath))
    75  	if !fileutils.TargetExists(filepath) {
    76  		return locale.NewInputError("err_fileevent_filenotexist", "Path does not exist: {{.V0}}.", filepath)
    77  	}
    78  	if err := w.fswatcher.Add(filepath); err != nil {
    79  		return locale.WrapExternalError(err, "err_fileevent_invalidpath", "Could not add filepath to filesystem watcher: {{.V0}}", filepath)
    80  	}
    81  	return nil
    82  }
    83  
    84  func (w *Watcher) OnEvent(cb OnEvent) error {
    85  	if w.onEvent != nil {
    86  		return errs.New("Already listening to events")
    87  	}
    88  	w.onEvent = &cb
    89  	return nil
    90  }
    91  
    92  func (w *Watcher) Close() {
    93  	logging.Debug("Closing watcher")
    94  	w.fswatcher.Close()
    95  	close(w.done)
    96  	logging.Debug("Watcher closed")
    97  }