diff options
| author | Shulhan <ms@kilabit.info> | 2022-04-06 00:08:17 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2022-04-06 00:12:44 +0700 |
| commit | 523abd0c1f44340ca5bf8cf2da5f35fba5ed1b77 (patch) | |
| tree | b18b5bfa8e2a7c60d4cf76e2df9a367bc271b264 | |
| parent | f4ab4581eb2e7811db9e416d0a8d6d16c4339dee (diff) | |
| download | pakakeh.go-523abd0c1f44340ca5bf8cf2da5f35fba5ed1b77.tar.xz | |
lib/memfs: change the Watch method to accept struct
Previously, we assume that the list of files being Watch-ed is equal
to list of files being in Includes.
This may not be correct.
For example, we may want to watch "*.ts" files only but did not want
it to be included during GoEmbed.
This changes introduce list of pattern for files to be watched in
the WatchOptions.Watches field.
If this field is empty, only files pass the Includes filter will be
watched.
| -rw-r--r-- | lib/memfs/embed.go | 25 | ||||
| -rw-r--r-- | lib/memfs/memfs.go | 179 | ||||
| -rw-r--r-- | lib/memfs/memfs_example_test.go | 6 | ||||
| -rw-r--r-- | lib/memfs/memfs_test.go | 27 | ||||
| -rw-r--r-- | lib/memfs/template.go | 4 | ||||
| -rw-r--r-- | lib/memfs/watch_options.go | 22 |
6 files changed, 196 insertions, 67 deletions
diff --git a/lib/memfs/embed.go b/lib/memfs/embed.go index 550a7470..cbe82969 100644 --- a/lib/memfs/embed.go +++ b/lib/memfs/embed.go @@ -29,7 +29,13 @@ type generateData struct { // If you are not sure, call Remount. // func (mfs *MemFS) GoEmbed() (err error) { - logp := "GoEmbed" + var ( + logp = "GoEmbed" + + node *Node + genData *generateData + name string + ) if len(mfs.Opts.Embed.PackageName) == 0 { mfs.Opts.Embed.PackageName = DefaultEmbedPackageName @@ -41,7 +47,7 @@ func (mfs *MemFS) GoEmbed() (err error) { mfs.Opts.Embed.GoFileName = DefaultEmbedGoFileName } - genData := &generateData{ + genData = &generateData{ Opts: mfs.Opts, PathNode: mfs.PathNodes, } @@ -63,15 +69,22 @@ func (mfs *MemFS) GoEmbed() (err error) { goto fail } - for x := 0; x < len(names); x++ { + for _, name = range names { + node = mfs.PathNodes.Get(name) + // Ignore and delete the file from map if its the output // itself. - if strings.HasSuffix(names[x], mfs.Opts.Embed.GoFileName) { - mfs.PathNodes.Delete(names[x]) + if strings.HasSuffix(name, mfs.Opts.Embed.GoFileName) { + mfs.PathNodes.Delete(name) + continue + } + + if len(node.GenFuncName) == 0 { + // Node is watched only, not included. continue } - genData.Node = mfs.PathNodes.Get(names[x]) + genData.Node = node err = tmpl.ExecuteTemplate(f, templateNameGenerateNode, genData) if err != nil { diff --git a/lib/memfs/memfs.go b/lib/memfs/memfs.go index 461faa4d..78e19d87 100644 --- a/lib/memfs/memfs.go +++ b/lib/memfs/memfs.go @@ -37,8 +37,9 @@ type MemFS struct { Opts *Options dw *DirWatcher - incRE []*regexp.Regexp - excRE []*regexp.Regexp + watchRE []*regexp.Regexp + incRE []*regexp.Regexp + excRE []*regexp.Regexp } // @@ -102,19 +103,42 @@ func New(opts *Options) (mfs *MemFS, err error) { } // -// AddChild add new child to parent node. +// AddChild add FileInfo fi as new child of parent node. +// +// It will return nil without an error if the system path of parent+fi.Name() +// is excluded by one of Options.Excludes pattern. // func (mfs *MemFS) AddChild(parent *Node, fi os.FileInfo) (child *Node, err error) { - sysPath := filepath.Join(parent.SysPath, fi.Name()) + var ( + logp = "AddChild" + sysPath = filepath.Join(parent.SysPath, fi.Name()) + fiMode = fi.Mode() + ) - if !mfs.isIncluded(sysPath, fi.Mode()) { + if mfs.isExcluded(sysPath, fiMode) { return nil, nil } + if mfs.isWatched(sysPath, fiMode) { + child, err = parent.addChild(sysPath, fi, mfs.Opts.MaxFileSize) + if err != nil { + return nil, fmt.Errorf("%s %s: %w", logp, sysPath, err) + } + + mfs.PathNodes.Set(child.Path, child) + } + if !mfs.isIncluded(sysPath, fiMode) { + if child != nil { + // The path being watched, but not included. + // Set the generate function name to empty, to prevent + // GoEmbed embed the content of this node. + child.GenFuncName = "" + } + return child, nil + } child, err = parent.addChild(sysPath, fi, mfs.Opts.MaxFileSize) if err != nil { - log.Printf("AddChild %s: %s", fi.Name(), err.Error()) - return nil, nil + return nil, fmt.Errorf("%s %s: %w", logp, sysPath, err) } mfs.PathNodes.Set(child.Path, child) @@ -426,39 +450,6 @@ func (mfs *MemFS) Update(node *Node, newInfo os.FileInfo) { } } -// -// Watch create and start the DirWatcher that monitor the memfs Root -// directory. -// The MemFS will update the tree and node content automatically if the file -// get deleted or updated. -// The returned DirWatcher is ready to use. -// To stop watching for update call the StopWatch. -// -func (mfs *MemFS) Watch(d time.Duration) (dw *DirWatcher, err error) { - var ( - logp = "Watch" - ) - - if mfs.dw != nil { - return mfs.dw, nil - } - - mfs.dw = &DirWatcher{ - fs: mfs, - Delay: d, - Options: *mfs.Opts, - } - - err = mfs.dw.Start() - if err != nil { - // There should be no error here, since we already check and - // filled the required fields for DirWatcher. - return nil, fmt.Errorf("%s: %w", logp, err) - } - - return mfs.dw, nil -} - func (mfs *MemFS) createRoot() error { logp := "createRoot" @@ -486,41 +477,55 @@ func (mfs *MemFS) createRoot() error { } // -// isIncluded will return true if the child node pass the included filter or -// excluded filter; otherwise it will return false. +// isExcluded will return true if the system path is excluded from being +// watched or included. // -func (mfs *MemFS) isIncluded(sysPath string, mode os.FileMode) bool { - if len(mfs.incRE) == 0 && len(mfs.excRE) == 0 { - return true - } - for _, re := range mfs.excRE { +func (mfs *MemFS) isExcluded(sysPath string, mode os.FileMode) bool { + var ( + re *regexp.Regexp + ) + for _, re = range mfs.excRE { if re.MatchString(sysPath) { - return false + return true } } + return false +} + +// +// isIncluded will return true if the system path is filtered to be included, +// pass the list of Includes regexp or no filter defined. +// +func (mfs *MemFS) isIncluded(sysPath string, mode os.FileMode) bool { + var ( + re *regexp.Regexp + fi os.FileInfo + absPath string + err error + ) + if len(mfs.incRE) == 0 { // No filter defined, default to always included. return true } - - for _, re := range mfs.incRE { + for _, re = range mfs.incRE { if re.MatchString(sysPath) { return true } } if mode&os.ModeSymlink == 0 { - // If file is NOT a symlink andits a directory, include it. + // If file is NOT a symlink and its a directory, include it. return mode.IsDir() } // File is symlink, get the real FileInfo to check if its // directory or not. - absPath, err := filepath.EvalSymlinks(sysPath) + absPath, err = filepath.EvalSymlinks(sysPath) if err != nil { return false } - fi, err := os.Lstat(absPath) + fi, err = os.Lstat(absPath) if err != nil { return false } @@ -531,6 +536,21 @@ func (mfs *MemFS) isIncluded(sysPath string, mode os.FileMode) bool { } // +// isWatched will return true if the system path is filtered to be watched. +// +func (mfs *MemFS) isWatched(sysPath string, mode os.FileMode) bool { + var ( + re *regexp.Regexp + ) + for _, re = range mfs.watchRE { + if re.MatchString(sysPath) { + return true + } + } + return false +} + +// // mount the directory recursively into the memory as root directory. // For example, if we mount directory "/tmp" and "/tmp" contains file "a", to // access file "a" we call Get("/a"), not Get("/tmp/a"). @@ -671,3 +691,56 @@ func (mfs *MemFS) refresh(url string) (node *Node, err error) { func (mfs *MemFS) resetAllModTime(t time.Time) { mfs.Root.resetAllModTime(t) } + +// +// Watch create and start the DirWatcher that monitor the memfs Root +// directory based on the list of pattern on WatchOptions.Watches and +// Options.Includes. +// +// The MemFS will remove or update the tree and node content automatically if +// the file being watched get deleted or updated. +// +// The returned DirWatcher is ready to use. +// To stop watching for update call the StopWatch. +// +func (mfs *MemFS) Watch(opts WatchOptions) (dw *DirWatcher, err error) { + var ( + logp = "Watch" + + re *regexp.Regexp + v string + ) + + if mfs.dw != nil { + return mfs.dw, nil + } + + mfs.watchRE = nil + for _, v = range opts.Watches { + re, err = regexp.Compile(v) + if err != nil { + return nil, fmt.Errorf("%s: %w", logp, err) + } + mfs.watchRE = append(mfs.watchRE, re) + } + + mfs.dw = &DirWatcher{ + fs: mfs, + Delay: opts.Delay, + Options: *mfs.Opts, + } + + _, err = mfs.scanDir(mfs.Root) + if err != nil { + return nil, fmt.Errorf("%s: %w", logp, err) + } + + err = mfs.dw.Start() + if err != nil { + // There should be no error here, since we already check and + // filled the required fields for DirWatcher. + return nil, fmt.Errorf("%s: %w", logp, err) + } + + return mfs.dw, nil +} diff --git a/lib/memfs/memfs_example_test.go b/lib/memfs/memfs_example_test.go index 25f257e2..d6fef23e 100644 --- a/lib/memfs/memfs_example_test.go +++ b/lib/memfs/memfs_example_test.go @@ -94,6 +94,10 @@ func ExampleMemFS_Search() { func ExampleMemFS_Watch() { var ( + watchOpts = WatchOptions{ + Delay: 200 * time.Millisecond, + } + mfs *MemFS dw *DirWatcher node *Node @@ -116,7 +120,7 @@ func ExampleMemFS_Watch() { log.Fatal(err) } - dw, err = mfs.Watch(200 * time.Millisecond) + dw, err = mfs.Watch(watchOpts) if err != nil { log.Fatal(err) } diff --git a/lib/memfs/memfs_test.go b/lib/memfs/memfs_test.go index cdfd44a7..22c71bf5 100644 --- a/lib/memfs/memfs_test.go +++ b/lib/memfs/memfs_test.go @@ -510,27 +510,40 @@ func TestMemFS_isIncluded(t *testing.T) { }, }} + var ( + opts *Options + mfs *MemFS + fi os.FileInfo + sysPath string + err error + x int + got bool + ) for _, c := range cases { t.Log(c.desc) - opts := &Options{ + opts = &Options{ Includes: c.inc, Excludes: c.exc, } - mfs, err := New(opts) + mfs, err = New(opts) if err != nil { t.Fatal(err) } - for x, sysPath := range c.sysPath { - fi, err := os.Stat(sysPath) + for x, sysPath = range c.sysPath { + fi, err = os.Stat(sysPath) if err != nil { t.Fatal(err) } - got := mfs.isIncluded(sysPath, fi.Mode()) - - test.Assert(t, sysPath, c.exp[x], got) + got = mfs.isExcluded(sysPath, fi.Mode()) + if got { + test.Assert(t, sysPath, !c.exp[x], got) + } else { + got = mfs.isIncluded(sysPath, fi.Mode()) + test.Assert(t, sysPath, c.exp[x], got) + } } } } diff --git a/lib/memfs/template.go b/lib/memfs/template.go index 3ed45ac2..286f3cd9 100644 --- a/lib/memfs/template.go +++ b/lib/memfs/template.go @@ -68,7 +68,9 @@ func {{ .Node.GenFuncName}}() *memfs.Node { node.SetName("{{.Node.Name}}") node.SetSize({{.Node.Size}}) {{- range $x, $child := .Node.Childs}} + {{- if $child.GenFuncName}} node.AddChild(_{{$varname}}_getNode({{$varname}}, "{{.Path}}", {{$child.GenFuncName}})) + {{- end}} {{- end}} return node } @@ -115,8 +117,10 @@ func init() { {{- range $x, $path := .PathNode.Paths }} {{- $node := $.PathNode.Get $path }} + {{- if $node.GenFuncName}} {{$varname}}.PathNodes.Set("{{$path}}", _{{$varname}}_getNode({{$varname}}, "{{$path}}", {{ $node.GenFuncName }})) + {{- end}} {{- end}} {{$varname}}.Root = {{$varname}}.PathNodes.Get("/") diff --git a/lib/memfs/watch_options.go b/lib/memfs/watch_options.go new file mode 100644 index 00000000..119ea145 --- /dev/null +++ b/lib/memfs/watch_options.go @@ -0,0 +1,22 @@ +// Copyright 2022, Shulhan <ms@kilabit.info>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package memfs + +import "time" + +// WatchOptions define an options for the MemFS Watch method. +type WatchOptions struct { + // Watches contain list of regular expressions for files to be watched + // inside the Root, as addition to Includes pattern. + // If this field is empty, only files pass the Includes filter will be + // watched. + Watches []string + + // Delay define the duration when the new changes will be checked from + // system. + // This field set the DirWatcher.Delay returned from Watch(). + // This field is optional, default is 5 seconds. + Delay time.Duration +} |
