aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2019-04-17 01:31:23 +0700
committerShulhan <ms@kilabit.info>2019-04-17 01:31:54 +0700
commit5ba996b3e0e52d06c09d79d2f10526f4e6277028 (patch)
tree8896ce5460e04304a43da819ba67fabb6bbd50b2
parentda60d648bb8789d63ad1707599aab0986562e8ca (diff)
downloadpakakeh.go-5ba996b3e0e52d06c09d79d2f10526f4e6277028.tar.xz
memfs: refactoring go generate file to use type from memfs
Previous Go generated file use their own Node and provide single function Get() to get the node based on path. This method does not work when using it directly with memfs. For example, on server that contains instance of memfs and want to use the output from GoGenerate, it would need to add their own wrapper for Get() and memfs.Get(). It should be more simple than that. This change add global variable GeneratedPathNode to memfs package, that will be set by file from go generate. If the GeneratedPathNode is not nil, memfs will use it as internal mapping of path and node.
-rw-r--r--.gitignore2
-rw-r--r--lib/memfs/doc.go10
-rw-r--r--lib/memfs/generate.go4
-rw-r--r--lib/memfs/generate_test.go7
-rw-r--r--lib/memfs/generate_test/memfs_generate_test.go72
-rw-r--r--lib/memfs/memfs.go53
-rw-r--r--lib/memfs/memfs_test.go2
-rw-r--r--lib/memfs/pathnode.go61
-rw-r--r--lib/memfs/template.go62
-rw-r--r--lib/memfs/testdata/memfs_generate_test.go52
10 files changed, 189 insertions, 136 deletions
diff --git a/.gitignore b/.gitignore
index 3fc769c4..4d045bfb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,7 @@
/lib/dns/testdata/hosts.block.out
/lib/email/maildir/testdata/
/lib/git/testdata/beku_test
-/lib/memfs/testdata/memfs_generate.go
+/lib/memfs/generate_test/memfs_generate.go
/lib/websocket/testdata/bin
/lib/websocket/testdata/client/client
/lib/websocket/testdata/client/reports
diff --git a/lib/memfs/doc.go b/lib/memfs/doc.go
index 8bea80b5..4cb34fa0 100644
--- a/lib/memfs/doc.go
+++ b/lib/memfs/doc.go
@@ -3,7 +3,8 @@
// license that can be found in the LICENSE file.
//
-// Package memfs provide a library for mapping file system into memory.
+// Package memfs provide a library for mapping file system into memory and to
+// generate a go file.
//
// Usage
//
@@ -24,8 +25,7 @@
// }
// mfs, err := memfs.New(incs, excs)
//
-// and then we mount the system directory that we want into memory using
-// "Mount()",
+// and then we mount the directory that we want into memory using "Mount()",
//
// err := mfs.Mount("./testdata")
//
@@ -55,7 +55,7 @@
//
// mfs.GoGenerate("mypackage", "output/path/file.go")
//
-// The Go generate file will be defined with package named "mypackage" in file
-// "output/path/file.go".
+// The Go generated file will be defined with package named "mypackage" in
+// file "output/path/file.go".
//
package memfs
diff --git a/lib/memfs/generate.go b/lib/memfs/generate.go
index e55a48cd..ccfe2f0d 100644
--- a/lib/memfs/generate.go
+++ b/lib/memfs/generate.go
@@ -38,14 +38,14 @@ func (mfs *MemFS) GoGenerate(pkgName, out string) (err error) {
goto fail
}
- for _, node := range mfs.mapPathNode {
+ for _, node := range mfs.pn.v {
err = tmpl.ExecuteTemplate(f, "GENERATE_NODE", node)
if err != nil {
goto fail
}
}
- err = tmpl.ExecuteTemplate(f, "PATHFUNCS", mfs.mapPathNode)
+ err = tmpl.ExecuteTemplate(f, "PATHFUNCS", mfs.pn.v)
if err != nil {
goto fail
}
diff --git a/lib/memfs/generate_test.go b/lib/memfs/generate_test.go
index 9e411eb5..798f90fd 100644
--- a/lib/memfs/generate_test.go
+++ b/lib/memfs/generate_test.go
@@ -10,10 +10,7 @@ import (
)
func TestGoGenerate(t *testing.T) {
- excs := []string{
- "memfs_generate.go",
- }
- mfs, err := New(nil, excs)
+ mfs, err := New(nil, nil)
if err != nil {
t.Fatal(err)
}
@@ -23,7 +20,7 @@ func TestGoGenerate(t *testing.T) {
t.Fatal(err)
}
- err = mfs.GoGenerate("testdata", "testdata/memfs_generate.go")
+ err = mfs.GoGenerate("test", "generate_test/memfs_generate.go")
if err != nil {
t.Fatal(err)
}
diff --git a/lib/memfs/generate_test/memfs_generate_test.go b/lib/memfs/generate_test/memfs_generate_test.go
new file mode 100644
index 00000000..c7dd86b0
--- /dev/null
+++ b/lib/memfs/generate_test/memfs_generate_test.go
@@ -0,0 +1,72 @@
+// Copyright 2019, 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 test
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/shuLhan/share/lib/memfs"
+ "github.com/shuLhan/share/lib/test"
+)
+
+func TestGeneratePathNode(t *testing.T) {
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ wd = strings.TrimSuffix(wd, "generate_test")
+
+ cases := []struct {
+ path string
+ exp *memfs.Node
+ expError string
+ }{{
+ path: "/memfs_generate.go",
+ expError: "file does not exist",
+ }, {
+ path: "/",
+ exp: &memfs.Node{
+ SysPath: filepath.Join(wd, "testdata"),
+ Path: "/",
+ Name: "/",
+ ContentType: "",
+ Mode: 2147484141,
+ Size: 4096,
+ V: []byte{},
+ },
+ }, {
+ path: "/exclude/index.html",
+ exp: &memfs.Node{
+ SysPath: filepath.Join(wd, "testdata", "exclude", "index.html"),
+ Path: "/exclude/index.html",
+ Name: "index.html",
+ ContentType: "text/html; charset=utf-8",
+ Mode: 420,
+ Size: 14,
+ V: []byte("<html></html>\n"),
+ },
+ }}
+
+ mfs, err := memfs.New(nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, c := range cases {
+ t.Log(c.path)
+
+ got, err := mfs.Get(c.path)
+ if err != nil {
+ test.Assert(t, "error", c.expError, err.Error(), true)
+ continue
+ }
+
+ test.Assert(t, "Node", c.exp, got, true)
+ }
+}
diff --git a/lib/memfs/memfs.go b/lib/memfs/memfs.go
index bf146659..8030c180 100644
--- a/lib/memfs/memfs.go
+++ b/lib/memfs/memfs.go
@@ -21,27 +21,46 @@ var (
// Development define a flag to bypass file in memory. If its
// true, any call to Get will result in direct read to file system.
Development bool //nolint: gochecknoglobals
+
+ // GeneratedPathNode contains the mapping of path and node. Its will
+ // be used and initialized by ".go" file generated from GoGenerate().
+ GeneratedPathNode *PathNode //nolint: gochecknoglobals
)
//
// MemFS contains the configuration and content of memory file system.
//
type MemFS struct {
- incRE []*regexp.Regexp
- excRE []*regexp.Regexp
- root *Node
- mapPathNode map[string]*Node
+ incRE []*regexp.Regexp
+ excRE []*regexp.Regexp
+ root *Node
+ pn *PathNode
}
//
// New create and initialize new memory file system using list of regular
// expresssion for including or excluding files.
+//
// The includes and excludes pattern applied to path of file in file system,
// not to the path in memory.
//
+// On directory that contains output from GoGenerate(), the includes and
+// excludes does not have any effect, since the content of path and nodes will
+// be overwritten by GeneratedPathNode.
+//
func New(includes, excludes []string) (*MemFS, error) {
+ if !Development && GeneratedPathNode != nil {
+ mfs := &MemFS{
+ pn: GeneratedPathNode,
+ }
+ return mfs, nil
+ }
+
mfs := &MemFS{
- mapPathNode: make(map[string]*Node),
+ pn: &PathNode{
+ v: make(map[string]*Node),
+ f: nil,
+ },
}
for _, inc := range includes {
re, err := regexp.Compile(inc)
@@ -66,8 +85,8 @@ func New(includes, excludes []string) (*MemFS, error) {
// will return os.ErrNotExist.
//
func (mfs *MemFS) Get(path string) (*Node, error) {
- node, ok := mfs.mapPathNode[path]
- if !ok {
+ node := mfs.pn.Get(path)
+ if node == nil {
return nil, os.ErrNotExist
}
@@ -84,11 +103,11 @@ func (mfs *MemFS) Get(path string) (*Node, error) {
// ListNames list all files in memory sorted by name.
//
func (mfs *MemFS) ListNames() (paths []string) {
- if len(mfs.mapPathNode) > 0 {
- paths = make([]string, 0, len(mfs.mapPathNode))
+ if len(mfs.pn.v) > 0 {
+ paths = make([]string, 0, len(mfs.pn.v))
}
- for k := range mfs.mapPathNode {
+ for k := range mfs.pn.v {
if len(paths) == 0 {
paths = append(paths, k)
continue
@@ -117,10 +136,16 @@ func (mfs *MemFS) ListNames() (paths []string) {
// For example, if we mount directory "/tmp" and "/tmp" contains file "a", to
// access file "a" we call Get("/a"), not Get("/tmp/a").
//
+// Mount does not have any effect if current directory contains ".go"
+// generated file from GoGenerate().
+//
func (mfs *MemFS) Mount(dir string) error {
if len(dir) == 0 {
return nil
}
+ if !Development && GeneratedPathNode != nil {
+ return nil
+ }
f, err := os.Open(dir)
if err != nil {
@@ -163,7 +188,7 @@ func (mfs *MemFS) createRoot(dir string, f *os.File) error {
Parent: nil,
}
- mfs.mapPathNode[mfs.root.Path] = mfs.root
+ mfs.pn.v[mfs.root.Path] = mfs.root
return nil
}
@@ -232,7 +257,7 @@ func (mfs *MemFS) addChild(parent *Node, fi os.FileInfo) (*Node, error) {
parent.Childs = append(parent.Childs, child)
- mfs.mapPathNode[child.Path] = child
+ mfs.pn.v[child.Path] = child
if child.Mode.IsDir() {
return child, nil
@@ -281,7 +306,7 @@ func (mfs *MemFS) isIncluded(child *Node) bool {
// pruneEmptyDirs remove node that is directory and does not have childs.
//
func (mfs *MemFS) pruneEmptyDirs() {
- for k, node := range mfs.mapPathNode {
+ for k, node := range mfs.pn.v {
if !node.Mode.IsDir() {
continue
}
@@ -293,6 +318,6 @@ func (mfs *MemFS) pruneEmptyDirs() {
}
node.Parent.removeChild(node)
- delete(mfs.mapPathNode, k)
+ delete(mfs.pn.v, k)
}
}
diff --git a/lib/memfs/memfs_test.go b/lib/memfs/memfs_test.go
index 4b20787f..b68b0820 100644
--- a/lib/memfs/memfs_test.go
+++ b/lib/memfs/memfs_test.go
@@ -134,7 +134,6 @@ func TestMount(t *testing.T) {
"/index.css",
"/index.html",
"/index.js",
- "/memfs_generate_test.go",
"/plain",
},
}, {
@@ -154,7 +153,6 @@ func TestMount(t *testing.T) {
"/include/index.html",
"/index.css",
"/index.html",
- "/memfs_generate_test.go",
"/plain",
},
}, {
diff --git a/lib/memfs/pathnode.go b/lib/memfs/pathnode.go
new file mode 100644
index 00000000..800d84d4
--- /dev/null
+++ b/lib/memfs/pathnode.go
@@ -0,0 +1,61 @@
+// Copyright 2019, 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
+
+//
+// PathNode contains a mapping between path and Node.
+//
+type PathNode struct {
+ v map[string]*Node
+ f map[string]func() *Node
+}
+
+//
+// NewPathNode create and initialize new PathNode.
+//
+func NewPathNode() *PathNode {
+ return &PathNode{
+ v: make(map[string]*Node),
+ f: make(map[string]func() *Node),
+ }
+}
+
+//
+// Get the node by path, or nil if path is not exist.
+//
+func (pn *PathNode) Get(path string) *Node {
+ node, ok := pn.v[path]
+ if ok {
+ return node
+ }
+ if pn.f != nil {
+ f, ok := pn.f[path]
+ if ok {
+ return f()
+ }
+ }
+ return nil
+}
+
+//
+// Set mapping of path to Node.
+//
+func (pn *PathNode) Set(path string, node *Node) {
+ if len(path) == 0 || node == nil {
+ return
+ }
+ pn.v[path] = node
+}
+
+//
+// SetFunc set mapping of path as function that return a Node.
+// Both path and function parameters should have values.
+//
+func (pn *PathNode) SetFunc(path string, fn func() *Node) {
+ if len(path) == 0 || fn == nil {
+ return
+ }
+ pn.f[path] = fn
+}
diff --git a/lib/memfs/template.go b/lib/memfs/template.go
index b1259c2c..c7019cc5 100644
--- a/lib/memfs/template.go
+++ b/lib/memfs/template.go
@@ -19,44 +19,12 @@ func generateTemplate() (tmpl *template.Template, err error) {
package {{.}}
import (
- "os"
+ "github.com/shuLhan/share/lib/memfs"
)
-
-var pathNode = map[string]*Node{}
-
-//
-// Node represent a single file.
-//
-type Node struct {
- SysPath string // The original file path in system.
- Path string // Absolute file path in memory.
- Name string // File name.
- ContentType string // File type per MIME, e.g. "application/json".
- Mode os.FileMode // File mode.
- Size int64 // Size of file.
- V []byte // Content of file.
- Parent *Node // Pointer to parent directory.
- Childs []*Node // List of files in directory.
-}
-
-//
-// Get node at path, or nil if path is not exist.
-//
-func Get(path string) *Node {
- node, ok := pathNode[path]
- if ok {
- return node
- }
- f, ok := pathFuncs[path]
- if ok {
- return f()
- }
- return nil
-}
{{end}}
{{define "GENERATE_NODE"}}
-func generate{{ funcname .Path | printf "%s"}}() *Node {
- node := &Node{
+func generate{{ funcname .Path | printf "%s"}}() *memfs.Node {
+ node := &memfs.Node{
SysPath: "{{.SysPath}}",
Path: "{{.Path}}",
Name: "{{.Name}}",
@@ -72,20 +40,19 @@ func generate{{ funcname .Path | printf "%s"}}() *Node {
},
}
{{ end }}
- pathNode["{{.Path}}"] = node
+ memfs.GeneratedPathNode.Set("{{.Path}}", node)
return node
}
{{end}}
{{define "PATHFUNCS"}}
-{{- $maxlen := pathlen . }}
-var pathFuncs = map[string]func() *Node{
+func init() {
+ memfs.GeneratedPathNode = memfs.NewPathNode()
{{- range $path, $node := .}}
- "{{$path}}":{{pad $maxlen $path | printf "%s"}}generate{{funcname $node.Path | printf "%s"}},
+ memfs.GeneratedPathNode.SetFunc("{{$path}}", generate{{funcname $node.Path | printf "%s" }})
{{- end}}
}
{{end}}
`
-
tmplFuncs := template.FuncMap{
"funcname": func(path string) []byte {
return libbytes.InReplace([]byte(path), []byte(libbytes.ASCIILettersNumber), '_')
@@ -99,21 +66,6 @@ var pathFuncs = map[string]func() *Node{
"isdir": func(fm os.FileMode) bool {
return fm.IsDir()
},
- "pathlen": func(paths map[string]*Node) (l int) {
- for k := range paths {
- if l < len(k) {
- l = len(k)
- }
- }
- return
- },
- "pad": func(maxlen int, path string) (spaces []byte) {
- spaces = make([]byte, 0, maxlen-len(path))
- for x := 0; x <= maxlen-len(path); x++ {
- spaces = append(spaces, ' ')
- }
- return
- },
}
tmpl, err = template.New("memfs").Funcs(tmplFuncs).Parse(textTemplate)
diff --git a/lib/memfs/testdata/memfs_generate_test.go b/lib/memfs/testdata/memfs_generate_test.go
deleted file mode 100644
index e92ae612..00000000
--- a/lib/memfs/testdata/memfs_generate_test.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2019, 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 testdata
-
-import (
- "testing"
-
- "github.com/shuLhan/share/lib/test"
-)
-
-func TestGet(t *testing.T) {
- cases := []struct {
- path string
- exp *Node
- }{{
- path: "/memfs_generate.go",
- }, {
- path: "/",
- exp: &Node{
- SysPath: "/home/ms/src/github.com/shuLhan/share/lib/memfs/testdata",
- Path: "/",
- Name: "/",
- ContentType: "",
- Mode: 2147484141,
- Size: 4096,
- V: []byte{},
- },
- }, {
- path: "/exclude/index.html",
- exp: &Node{
- SysPath: "/home/ms/src/github.com/shuLhan/share/lib/memfs/testdata/exclude/index.html",
- Path: "/exclude/index.html",
- Name: "index.html",
- ContentType: "text/html; charset=utf-8",
- Mode: 420,
- Size: 14,
- V: []byte{
- 60, 104, 116, 109, 108, 62, 60, 47, 104, 116, 109, 108, 62, 10,
- },
- },
- }}
-
- for _, c := range cases {
- t.Log(c.path)
-
- got := Get(c.path)
-
- test.Assert(t, "Node", c.exp, got, true)
- }
-}