summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/content/functions/file.md116
-rw-r--r--file/file.go45
-rw-r--r--file/file_test.go43
-rw-r--r--funcs.go1
-rw-r--r--funcs/file.go57
-rw-r--r--funcs/file_test.go32
-rw-r--r--test/integration/file_test.go30
7 files changed, 324 insertions, 0 deletions
diff --git a/docs/content/functions/file.md b/docs/content/functions/file.md
new file mode 100644
index 00000000..cff6af2c
--- /dev/null
+++ b/docs/content/functions/file.md
@@ -0,0 +1,116 @@
+---
+title: file functions
+menu:
+ main:
+ parent: functions
+---
+
+## `file.Exists`
+
+Reports whether a file or directory exists at the given path.
+
+### Usage
+```go
+file.Exists path
+```
+
+### Example
+
+_`input.tmpl`:_
+```
+{{ if (file.Exists "/tmp/foo") }}yes{{else}}no{{end}}
+```
+
+```console
+$ gomplate -f input.tmpl
+no
+$ touch /tmp/foo
+$ gomplate -f input.tmpl
+yes
+```
+
+## `file.IsDir`
+
+Reports whether a given path is a directory.
+
+### Usage
+```go
+file.IsDir path
+```
+
+### Example
+
+_`input.tmpl`:_
+```
+{{ if (file.IsDir "/tmp/foo") }}yes{{else}}no{{end}}
+```
+
+```console
+$ gomplate -f input.tmpl
+no
+$ touch /tmp/foo
+$ gomplate -f input.tmpl
+no
+$ rm /tmp/foo && mkdir /tmp/foo
+$ gomplate -f input.tmpl
+yes
+```
+
+## `file.Read`
+
+Reads a given file _as text_. Note that this will succeed if the given file
+is binary, but
+
+### Usage
+```go
+file.Read path
+```
+
+### Examples
+
+```console
+$ echo "hello world" > /tmp/hi
+$ gomplate -i '{{file.Read "/tmp/hi"}}'
+hello world
+```
+
+## `file.ReadDir`
+
+Reads a directory and lists the files and directories contained within.
+
+### Usage
+```go
+file.ReadDir path
+```
+
+### Examples
+
+```console
+$ mkdir /tmp/foo
+$ touch /tmp/foo/a; touch /tmp/foo/b; touch /tmp/foo/c
+$ mkdir /tmp/foo/d
+$ gomplate -i '{{ range (file.ReadDir "/tmp/foo") }}{{.}}{{"\n"}}{{end}}'
+a
+b
+c
+d
+```
+
+## `file.Stat`
+
+Returns a [`os.FileInfo`](https://golang.org/pkg/os/#FileInfo) describing
+the named path.
+Essentially a wrapper for Go's [`os.Stat`](https://golang.org/pkg/os/#Stat) function.
+
+### Usage
+```go
+file.Stat path
+```
+
+### Examples
+
+```console
+$ echo "hello world" > /tmp/foo
+$ gomplate -i '{{ $s := file.Stat "/tmp/foo" }}{{ $s.Mode }} {{ $s.Size }} {{ $s.Name }}'
+-rw-r--r-- 12 foo
+```
diff --git a/file/file.go b/file/file.go
new file mode 100644
index 00000000..10f58a41
--- /dev/null
+++ b/file/file.go
@@ -0,0 +1,45 @@
+package file
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ "github.com/spf13/afero"
+)
+
+// for overriding in tests
+var fs = afero.NewOsFs()
+
+// Read -
+func Read(filename string) (string, error) {
+ inFile, err := fs.OpenFile(filename, os.O_RDONLY, 0)
+ if err != nil {
+ return "", fmt.Errorf("failed to open %s\n%v", filename, err)
+ }
+ // nolint: errcheck
+ defer inFile.Close()
+ bytes, err := ioutil.ReadAll(inFile)
+ if err != nil {
+ err = fmt.Errorf("read failed for %s\n%v", filename, err)
+ return "", err
+ }
+ return string(bytes), nil
+}
+
+// ReadDir -
+func ReadDir(path string) ([]string, error) {
+ f, err := fs.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ i, err := f.Stat()
+ if err != nil {
+ return nil, err
+ }
+ if i.IsDir() {
+ return f.Readdirnames(0)
+ }
+ return nil, errors.New("file is not a directory")
+}
diff --git a/file/file_test.go b/file/file_test.go
new file mode 100644
index 00000000..97cfa0fb
--- /dev/null
+++ b/file/file_test.go
@@ -0,0 +1,43 @@
+package file
+
+import (
+ "testing"
+
+ "github.com/spf13/afero"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRead(t *testing.T) {
+ origfs := fs
+ defer func() { fs = origfs }()
+ fs = afero.NewMemMapFs()
+ _ = fs.Mkdir("/tmp", 0777)
+ f, _ := fs.Create("/tmp/foo")
+ _, _ = f.Write([]byte("foo"))
+
+ actual, err := Read("/tmp/foo")
+ assert.NoError(t, err)
+ assert.Equal(t, "foo", actual)
+
+ actual, err = Read("/tmp/bar")
+ assert.Error(t, err)
+}
+
+func TestReadDir(t *testing.T) {
+ origfs := fs
+ defer func() { fs = origfs }()
+ fs = afero.NewMemMapFs()
+ fs.Mkdir("/tmp", 0777)
+ fs.Create("/tmp/foo")
+ fs.Create("/tmp/bar")
+ fs.Create("/tmp/baz")
+ fs.Mkdir("/tmp/qux", 0777)
+ fs.Create("/tmp/qux/quux")
+
+ actual, err := ReadDir("/tmp")
+ assert.NoError(t, err)
+ assert.Equal(t, []string{"bar", "baz", "foo", "qux"}, actual)
+
+ _, err = ReadDir("/tmp/foo")
+ assert.Error(t, err)
+}
diff --git a/funcs.go b/funcs.go
index 9e20ee8c..99445bc3 100644
--- a/funcs.go
+++ b/funcs.go
@@ -21,5 +21,6 @@ func initFuncs(d *data.Data) template.FuncMap {
funcs.AddTimeFuncs(f)
funcs.AddMathFuncs(f)
funcs.AddCryptoFuncs(f)
+ funcs.AddFileFuncs(f)
return f
}
diff --git a/funcs/file.go b/funcs/file.go
new file mode 100644
index 00000000..1f1a30bd
--- /dev/null
+++ b/funcs/file.go
@@ -0,0 +1,57 @@
+package funcs
+
+import (
+ "os"
+ "sync"
+
+ "github.com/hairyhenderson/gomplate/file"
+ "github.com/spf13/afero"
+)
+
+var (
+ ff *FileFuncs
+ ffInit sync.Once
+)
+
+// FileNS - the File namespace
+func FileNS() *FileFuncs {
+ ffInit.Do(func() { ff = &FileFuncs{afero.NewOsFs()} })
+ return ff
+}
+
+// AddFileFuncs -
+func AddFileFuncs(f map[string]interface{}) {
+ f["file"] = FileNS
+}
+
+// FileFuncs -
+type FileFuncs struct {
+ fs afero.Fs
+}
+
+// Read -
+func (f *FileFuncs) Read(path string) (string, error) {
+ return file.Read(path)
+}
+
+// Stat -
+func (f *FileFuncs) Stat(path string) (os.FileInfo, error) {
+ return f.fs.Stat(path)
+}
+
+// Exists -
+func (f *FileFuncs) Exists(path string) bool {
+ _, err := f.Stat(path)
+ return err == nil
+}
+
+// IsDir -
+func (f *FileFuncs) IsDir(path string) bool {
+ i, err := f.Stat(path)
+ return err == nil && i.IsDir()
+}
+
+// ReadDir -
+func (f *FileFuncs) ReadDir(path string) ([]string, error) {
+ return file.ReadDir(path)
+}
diff --git a/funcs/file_test.go b/funcs/file_test.go
new file mode 100644
index 00000000..2a3ce9a9
--- /dev/null
+++ b/funcs/file_test.go
@@ -0,0 +1,32 @@
+package funcs
+
+import (
+ "testing"
+
+ "github.com/spf13/afero"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFileExists(t *testing.T) {
+ fs := afero.NewMemMapFs()
+ ff := &FileFuncs{fs}
+
+ _ = fs.Mkdir("/tmp", 0777)
+ f, _ := fs.Create("/tmp/foo")
+ _, _ = f.Write([]byte("foo"))
+
+ assert.True(t, ff.Exists("/tmp/foo"))
+ assert.False(t, ff.Exists("/tmp/bar"))
+}
+
+func TestFileIsDir(t *testing.T) {
+ fs := afero.NewMemMapFs()
+ ff := &FileFuncs{fs}
+
+ _ = fs.Mkdir("/tmp", 0777)
+ f, _ := fs.Create("/tmp/foo")
+ _, _ = f.Write([]byte("foo"))
+
+ assert.True(t, ff.IsDir("/tmp"))
+ assert.False(t, ff.IsDir("/tmp/foo"))
+}
diff --git a/test/integration/file_test.go b/test/integration/file_test.go
new file mode 100644
index 00000000..4d3fed22
--- /dev/null
+++ b/test/integration/file_test.go
@@ -0,0 +1,30 @@
+//+build !xxintegration
+//+build !windows
+
+package integration
+
+import (
+ . "gopkg.in/check.v1"
+
+ "github.com/gotestyourself/gotestyourself/fs"
+)
+
+type FileSuite struct {
+ tmpDir *fs.Dir
+}
+
+var _ = Suite(&FileSuite{})
+
+func (s *FileSuite) SetUpSuite(c *C) {
+ s.tmpDir = fs.NewDir(c, "gomplate-inttests",
+ fs.WithFile("one", "hi\n"),
+ fs.WithFile("two", "hello\n"))
+}
+
+func (s *FileSuite) TearDownSuite(c *C) {
+ s.tmpDir.Remove()
+}
+
+func (s *FileSuite) TestReadsFile(c *C) {
+ inOutTest(c, `{{ file.Read "`+s.tmpDir.Join("one")+`"}}`, "hi")
+}