// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. package gfsnotify import ( "errors" "fmt" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/container/glist" ) // Add monitors with callback function to the watcher. // The optional parameter specifies whether monitoring the recursively, which is true in default. func (w *Watcher) Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { return w.AddOnce("", path, callbackFunc, recursive...) } // AddOnce monitors with callback function only once using unique name to the watcher. // If AddOnce is called multiple times with the same parameter, is only added to monitor once. It returns error // if it's called twice with the same . // // The optional parameter specifies whether monitoring the recursively, which is true in default. func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { w.nameSet.AddIfNotExistFuncLock(name, func() string { // Firstly add the path to watcher. callback, err = w.addWithCallbackFunc(name, path, callbackFunc, recursive...) if err != nil { return "" } // If it's recursive adding, it then adds all sub-folders to the monitor. // NOTE: // 1. It only recursively adds **folders** to the monitor, NOT files, // because if the folders are monitored and their sub-files are also monitored. // 2. It bounds no callbacks to the folders, because it will search the callbacks // from its parent recursively if any event produced. if fileIsDir(path) && (len(recursive) == 0 || recursive[0]) { for _, subPath := range fileAllDirs(path) { if fileIsDir(subPath) { if err := w.watcher.Add(subPath); err != nil { intlog.Error(err) } } } } return name }) return } // addWithCallbackFunc adds the path to underlying monitor, creates and returns a callback object. func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { // Check and convert the given path to absolute path. if t := fileRealPath(path); t == "" { return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path)) } else { path = t } // Create callback object. callback = &Callback{ Id: callbackIdGenerator.Add(1), Func: callbackFunc, Path: path, name: name, recursive: true, } if len(recursive) > 0 { callback.recursive = recursive[0] } // Register the callback to watcher. w.callbacks.LockFunc(func(m map[string]interface{}) { list := (*glist.List)(nil) if v, ok := m[path]; !ok { list = glist.New(true) m[path] = list } else { list = v.(*glist.List) } callback.elem = list.PushBack(callback) }) // Add the path to underlying monitor. if err := w.watcher.Add(path); err != nil { intlog.Error(err) } // Add the callback to global callback map. callbackIdMap.Set(callback.Id, callback) //intlog.Print("addWithCallbackFunc", name, path, callback.recursive) return } // Close closes the watcher. func (w *Watcher) Close() { w.events.Close() if err := w.watcher.Close(); err != nil { intlog.Error(err) } close(w.closeChan) } // Remove removes monitor and all callbacks associated with the recursively. func (w *Watcher) Remove(path string) error { // Firstly remove the callbacks of the path. if r := w.callbacks.Remove(path); r != nil { list := r.(*glist.List) for { if r := list.PopFront(); r != nil { callbackIdMap.Remove(r.(*Callback).Id) } else { break } } } // Secondly remove monitor of all sub-files which have no callbacks. if subPaths, err := fileScanDir(path, "*", true); err == nil && len(subPaths) > 0 { for _, subPath := range subPaths { if w.checkPathCanBeRemoved(subPath) { if err := w.watcher.Remove(subPath); err != nil { intlog.Error(err) } } } } // Lastly remove the monitor of the path from underlying monitor. return w.watcher.Remove(path) } // checkPathCanBeRemoved checks whether the given path have no callbacks bound. func (w *Watcher) checkPathCanBeRemoved(path string) bool { // Firstly check the callbacks in the watcher directly. if v := w.callbacks.Get(path); v != nil { return false } // Secondly check its parent whether has callbacks. dirPath := fileDir(path) if v := w.callbacks.Get(dirPath); v != nil { for _, c := range v.(*glist.List).FrontAll() { if c.(*Callback).recursive { return false } } return false } // Recursively check its parent. parentDirPath := "" for { parentDirPath = fileDir(dirPath) if parentDirPath == dirPath { break } if v := w.callbacks.Get(parentDirPath); v != nil { for _, c := range v.(*glist.List).FrontAll() { if c.(*Callback).recursive { return false } } return false } dirPath = parentDirPath } return true } // RemoveCallback removes callback with given callback id from watcher. func (w *Watcher) RemoveCallback(callbackId int) { callback := (*Callback)(nil) if r := callbackIdMap.Get(callbackId); r != nil { callback = r.(*Callback) } if callback != nil { if r := w.callbacks.Get(callback.Path); r != nil { r.(*glist.List).Remove(callback.elem) } callbackIdMap.Remove(callbackId) if callback.name != "" { w.nameSet.Remove(callback.name) } } }