Skip to content

Commit 3004c1b

Browse files
committed
Initial commit
1 parent 00f3a73 commit 3004c1b

11 files changed

+382
-1
lines changed

.editorconfig

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# EditorConfig: https://EditorConfig.org
2+
3+
root = true
4+
5+
[*]
6+
end_of_line = lf
7+
insert_final_newline = true
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
indent_size = 2
11+
indent_style = space
12+
13+
[*.go]
14+
indent_style = tab
15+
indent_size = 8
16+
17+
[Makefile]
18+
indent_style = tab
19+
20+
[*.{json, yml, yaml}]
21+
indent_style = space
22+
indent_size = 2
23+

.gitignore

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Created by .ignore support plugin (hsz.mobi)
2+
### JetBrains template
3+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
4+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
5+
6+
# User-specific stuff
7+
.idea/**/workspace.xml
8+
.idea/**/tasks.xml
9+
.idea/**/usage.statistics.xml
10+
.idea/**/dictionaries
11+
.idea/**/shelf
12+
13+
# Generated files
14+
.idea/**/contentModel.xml
15+
16+
# Sensitive or high-churn files
17+
.idea/**/dataSources/
18+
.idea/**/dataSources.ids
19+
.idea/**/dataSources.local.xml
20+
.idea/**/sqlDataSources.xml
21+
.idea/**/dynamic.xml
22+
.idea/**/uiDesigner.xml
23+
.idea/**/dbnavigator.xml
24+
25+
# Gradle
26+
.idea/**/gradle.xml
27+
.idea/**/libraries
28+
29+
# Gradle and Maven with auto-import
30+
# When using Gradle or Maven with auto-import, you should exclude module files,
31+
# since they will be recreated, and may cause churn. Uncomment if using
32+
# auto-import.
33+
# .idea/modules.xml
34+
# .idea/*.iml
35+
# .idea/modules
36+
# *.iml
37+
# *.ipr
38+
39+
# CMake
40+
cmake-build-*/
41+
42+
# Mongo Explorer plugin
43+
.idea/**/mongoSettings.xml
44+
45+
# File-based project format
46+
*.iws
47+
48+
# IntelliJ
49+
out/
50+
51+
# mpeltonen/sbt-idea plugin
52+
.idea_modules/
53+
54+
# JIRA plugin
55+
atlassian-ide-plugin.xml
56+
57+
# Cursive Clojure plugin
58+
.idea/replstate.xml
59+
60+
# Crashlytics plugin (for Android Studio and IntelliJ)
61+
com_crashlytics_export_strings.xml
62+
crashlytics.properties
63+
crashlytics-build.properties
64+
fabric.properties
65+
66+
# Editor-based Rest Client
67+
.idea/httpRequests
68+
69+
# Android studio 3.1+ serialized cache file
70+
.idea/caches/build_file_checksums.ser
71+
72+
### Go template
73+
# Binaries for programs and plugins
74+
*.exe
75+
*.exe~
76+
*.dll
77+
*.so
78+
*.dylib
79+
80+
# Test binary, built with `go test -c`
81+
*.test
82+
83+
# Output of the go coverage tool, specifically when used with LiteIDE
84+
*.out
85+
86+
# Dependency directories (remove the comment below to include it)
87+
# vendor/
88+

.idea/golang-vfstemplate.iml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+23-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,23 @@
1-
# golang-httpfs-template
1+
golang-vfstemplate
2+
=======================
3+
4+
## Installation
5+
6+
```
7+
go get github.com/shakahl/golang-vfstemplate
8+
```
9+
10+
## Usage
11+
12+
Parsing templates by glob pattern:
13+
14+
```
15+
template.Must(vfstemplate.ParseGlob(myhttpfs, nil, "/views/*.html")
16+
```
17+
18+
Parsing templates by specifying a list of file paths:
19+
20+
```
21+
vfstemplate.ParseFiles(myhttpfs, nil, "/views/first.html", "/views/second.html")
22+
```
23+

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/shakahl/golang-vfstemplate
2+
3+
go 1.12

match.go

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package vfstemplate
2+
3+
import (
4+
"net/http"
5+
"os"
6+
"path"
7+
"sort"
8+
"strings"
9+
)
10+
11+
const separator = "/"
12+
13+
// Glob returns the names of all files matching pattern or nil
14+
// if there is no matching file. The syntax of patterns is the same
15+
// as in path.Match. The pattern may describe hierarchical names such as
16+
// /usr/*/bin/ed.
17+
//
18+
// Glob ignores file system errors such as I/O errors reading directories.
19+
// The only possible returned error is ErrBadPattern, when pattern
20+
// is malformed.
21+
func Glob(fs http.FileSystem, pattern string) (matches []string, err error) {
22+
if !hasMeta(pattern) {
23+
if _, err = Stat(fs, pattern); err != nil {
24+
return nil, nil
25+
}
26+
return []string{pattern}, nil
27+
}
28+
29+
dir, file := path.Split(pattern)
30+
switch dir {
31+
case "":
32+
dir = "."
33+
case string(separator):
34+
// nothing
35+
default:
36+
dir = dir[0 : len(dir)-1] // chop off trailing separator
37+
}
38+
39+
if !hasMeta(dir) {
40+
return glob(fs, dir, file, nil)
41+
}
42+
43+
var m []string
44+
m, err = Glob(fs, dir)
45+
if err != nil {
46+
return
47+
}
48+
for _, d := range m {
49+
matches, err = glob(fs, d, file, matches)
50+
if err != nil {
51+
return
52+
}
53+
}
54+
return
55+
}
56+
57+
// glob searches for files matching pattern in the directory dir
58+
// and appends them to matches. If the directory cannot be
59+
// opened, it returns the existing matches. New matches are
60+
// added in lexicographical order.
61+
func glob(fs http.FileSystem, dir, pattern string, matches []string) (m []string, e error) {
62+
m = matches
63+
fi, err := Stat(fs, dir)
64+
if err != nil {
65+
return
66+
}
67+
if !fi.IsDir() {
68+
return
69+
}
70+
fis, err := ReadDir(fs, dir)
71+
if err != nil {
72+
return
73+
}
74+
75+
sort.Sort(byName(fis))
76+
77+
for _, fi := range fis {
78+
n := fi.Name()
79+
matched, err := path.Match(path.Clean(pattern), n)
80+
if err != nil {
81+
return m, err
82+
}
83+
if matched {
84+
m = append(m, path.Join(dir, n))
85+
}
86+
}
87+
return
88+
}
89+
90+
// hasMeta reports whether path contains any of the magic characters
91+
// recognized by Match.
92+
func hasMeta(path string) bool {
93+
return strings.ContainsAny(path, "*?[")
94+
}
95+
96+
// byName implements sort.Interface.
97+
type byName []os.FileInfo
98+
99+
func (f byName) Len() int { return len(f) }
100+
func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
101+
func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }

template.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package vfstemplate
2+
3+
import (
4+
"fmt"
5+
"html/template"
6+
"net/http"
7+
"path"
8+
)
9+
10+
// ParseFiles creates a new Template if t is nil and parses the template definitions from
11+
// the named files. The returned template's name will have the (base) name and
12+
// (parsed) contents of the first file. There must be at least one file.
13+
// If an error occurs, parsing stops and the returned *Template is nil.
14+
func ParseFiles(fs http.FileSystem, t *template.Template, filenames ...string) (*template.Template, error) {
15+
return parseFiles(fs, t, filenames...)
16+
}
17+
18+
// ParseGlob parses the template definitions in the files identified by the
19+
// pattern and associates the resulting templates with t. The pattern is
20+
// processed by vfspath.Glob and must match at least one file. ParseGlob is
21+
// equivalent to calling t.ParseFiles with the list of files matched by the
22+
// pattern.
23+
func ParseGlob(fs http.FileSystem, t *template.Template, pattern string) (*template.Template, error) {
24+
filenames, err := Glob(fs, pattern)
25+
if err != nil {
26+
return nil, err
27+
}
28+
if len(filenames) == 0 {
29+
return nil, fmt.Errorf("vfstemplate: pattern matches no files: %#q", pattern)
30+
}
31+
return parseFiles(fs, t, filenames...)
32+
}
33+
34+
// parseFiles is the helper for the method and function. If the argument
35+
// template is nil, it is created from the first file.
36+
func parseFiles(fs http.FileSystem, t *template.Template, filenames ...string) (*template.Template, error) {
37+
if len(filenames) == 0 {
38+
// Not really a problem, but be consistent.
39+
return nil, fmt.Errorf("vfstemplate: no files named in call to ParseFiles")
40+
}
41+
for _, filename := range filenames {
42+
b, err := ReadFile(fs, filename)
43+
if err != nil {
44+
return nil, err
45+
}
46+
s := string(b)
47+
name := path.Base(filename)
48+
// First template becomes return value if not already defined,
49+
// and we use that one for subsequent New calls to associate
50+
// all the templates together. Also, if this file has the same name
51+
// as t, this file becomes the contents of t, so
52+
// t, err := New(name).Funcs(xxx).ParseFiles(name)
53+
// works. Otherwise we create a new template associated with t.
54+
var tmpl *template.Template
55+
if t == nil {
56+
t = template.New(name)
57+
}
58+
if name == t.Name() {
59+
tmpl = t
60+
} else {
61+
tmpl = t.New(name)
62+
}
63+
_, err = tmpl.Parse(s)
64+
if err != nil {
65+
return nil, err
66+
}
67+
}
68+
return t, nil
69+
}

util.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package vfstemplate
2+
3+
import (
4+
"io/ioutil"
5+
"net/http"
6+
"os"
7+
)
8+
9+
// ReadDir reads the contents of the directory associated with file and
10+
// returns a slice of FileInfo values in directory order.
11+
func ReadDir(fs http.FileSystem, name string) ([]os.FileInfo, error) {
12+
f, err := fs.Open(name)
13+
if err != nil {
14+
return nil, err
15+
}
16+
defer f.Close()
17+
return f.Readdir(-1)
18+
}
19+
20+
// Stat returns the FileInfo structure describing file.
21+
func Stat(fs http.FileSystem, name string) (os.FileInfo, error) {
22+
f, err := fs.Open(name)
23+
if err != nil {
24+
return nil, err
25+
}
26+
defer f.Close()
27+
return f.Stat()
28+
}
29+
30+
// ReadFile reads the file named by path from fs and returns the contents.
31+
func ReadFile(fs http.FileSystem, path string) ([]byte, error) {
32+
rc, err := fs.Open(path)
33+
if err != nil {
34+
return nil, err
35+
}
36+
defer rc.Close()
37+
return ioutil.ReadAll(rc)
38+
}
39+
40+
// ReadFileString reads the file named by path from fs and returns the contents.
41+
func ReadFileString(fs http.FileSystem, path string) (string, error) {
42+
buf, err := ReadFile(fs, path)
43+
if err != nil {
44+
return "", err
45+
}
46+
return string(buf), nil
47+
}

0 commit comments

Comments
 (0)