Skip to content

Commit

Permalink
Merge pull request #837 from hairyhenderson/move-writer-tyes
Browse files Browse the repository at this point in the history
Move internal writers to an internal package
  • Loading branch information
Dave Henderson authored and GitHub committed May 9, 2020
2 parents 66f381d + ae434c5 commit d5dfc8b
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 136 deletions.
4 changes: 3 additions & 1 deletion gomplate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"github.com/hairyhenderson/gomplate/v3/conv"
"github.com/hairyhenderson/gomplate/v3/data"
"github.com/hairyhenderson/gomplate/v3/env"
"github.com/hairyhenderson/gomplate/v3/internal/writers"

"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -156,7 +158,7 @@ func TestCustomDelim(t *testing.T) {
func TestRunTemplates(t *testing.T) {
defer func() { Stdout = os.Stdout }()
buf := &bytes.Buffer{}
Stdout = &nopWCloser{buf}
Stdout = &writers.NopCloser{Writer: buf}
config := &Config{Input: "foo", OutputFiles: []string{"-"}}
err := RunTemplates(config)
assert.NoError(t, err)
Expand Down
88 changes: 88 additions & 0 deletions internal/writers/writers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package writers

import (
"bytes"
"errors"
"io"
)

type emptySkipper struct {
open func() (io.WriteCloser, error)

// internal
w io.WriteCloser
buf *bytes.Buffer
nw bool
}

// NewEmptySkipper creates an io.WriteCloser that will only start writing once a
// non-whitespace byte has been encountered. The wrapped io.WriteCloser must be
// provided by the `open` func.
func NewEmptySkipper(open func() (io.WriteCloser, error)) io.WriteCloser {
return &emptySkipper{
w: nil,
buf: &bytes.Buffer{},
nw: false,
open: open,
}
}

func (f *emptySkipper) Write(p []byte) (n int, err error) {
if !f.nw {
if allWhitespace(p) {
// buffer the whitespace
return f.buf.Write(p)
}

// first time around, so open the writer
f.nw = true
f.w, err = f.open()
if err != nil {
return 0, err
}
if f.w == nil {
return 0, errors.New("nil writer returned by open")
}
// empty the buffer into the wrapped writer
_, err = f.buf.WriteTo(f.w)
if err != nil {
return 0, err
}
}

return f.w.Write(p)
}

// Close - implements io.Closer
func (f *emptySkipper) Close() error {
if f.w != nil {
return f.w.Close()
}
return nil
}

func allWhitespace(p []byte) bool {
for _, b := range p {
if b == ' ' || b == '\t' || b == '\n' || b == '\r' || b == '\v' {
continue
}
return false
}
return true
}

// NopCloser returns a WriteCloser with a no-op Close method wrapping
// the provided io.Writer.
type NopCloser struct {
io.Writer
}

// Close - implements io.Closer
func (n *NopCloser) Close() error {
return nil
}

var (
_ io.WriteCloser = (*NopCloser)(nil)
_ io.WriteCloser = (*emptySkipper)(nil)
)
67 changes: 67 additions & 0 deletions internal/writers/writers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package writers

import (
"bytes"
"io"
"testing"

"github.com/stretchr/testify/assert"
)

func TestAllWhitespace(t *testing.T) {
testdata := []struct {
in []byte
expected bool
}{
{[]byte(" "), true},
{[]byte("foo"), false},
{[]byte(" \t\n\n\v\r\n"), true},
{[]byte(" foo "), false},
}

for _, d := range testdata {
assert.Equal(t, d.expected, allWhitespace(d.in))
}
}

func TestEmptySkipper(t *testing.T) {
testdata := []struct {
in []byte
empty bool
}{
{[]byte(" "), true},
{[]byte("foo"), false},
{[]byte(" \t\n\n\v\r\n"), true},
{[]byte(" foo "), false},
}

for _, d := range testdata {
w := &bufferCloser{&bytes.Buffer{}}
opened := false
f, ok := NewEmptySkipper(func() (io.WriteCloser, error) {
opened = true
return w, nil
}).(*emptySkipper)

assert.True(t, ok)
n, err := f.Write(d.in)
assert.NoError(t, err)
assert.Equal(t, len(d.in), n)
if d.empty {
assert.Nil(t, f.w)
assert.False(t, opened)
} else {
assert.NotNil(t, f.w)
assert.True(t, opened)
assert.EqualValues(t, d.in, w.Bytes())
}
}
}

type bufferCloser struct {
*bytes.Buffer
}

func (b *bufferCloser) Close() error {
return nil
}
81 changes: 3 additions & 78 deletions template.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package gomplate

import (
"bytes"
"fmt"
"io"
"io/ioutil"
Expand All @@ -10,10 +9,9 @@ import (
"text/template"

"github.com/hairyhenderson/gomplate/v3/internal/config"
"github.com/hairyhenderson/gomplate/v3/internal/writers"
"github.com/hairyhenderson/gomplate/v3/tmpl"

"github.com/pkg/errors"

"github.com/spf13/afero"
"github.com/zealic/xignore"
)
Expand Down Expand Up @@ -103,7 +101,7 @@ func gatherTemplates(cfg *config.Config, outFileNamer func(string) (string, erro

// --exec-pipe redirects standard out to the out pipe
if cfg.OutWriter != nil {
Stdout = &nopWCloser{cfg.OutWriter}
Stdout = &writers.NopCloser{Writer: cfg.OutWriter}
}

switch {
Expand Down Expand Up @@ -229,7 +227,7 @@ func fileToTemplates(inFile, outFile string, mode os.FileMode, modeOverride bool

func openOutFile(cfg *config.Config, filename string, mode os.FileMode, modeOverride bool) (out io.WriteCloser, err error) {
if cfg.SuppressEmpty {
out = newEmptySkipper(func() (io.WriteCloser, error) {
out = writers.NewEmptySkipper(func() (io.WriteCloser, error) {
if filename == "-" {
return Stdout, nil
}
Expand Down Expand Up @@ -275,76 +273,3 @@ func readInput(filename string) (string, error) {
}
return string(bytes), nil
}

// emptySkipper is a io.WriteCloser wrapper that will only start writing once a
// non-whitespace byte has been encountered. The writer must be provided by the
// `open` func
type emptySkipper struct {
open func() (io.WriteCloser, error)

// internal
w io.WriteCloser
buf *bytes.Buffer
nw bool
}

func newEmptySkipper(open func() (io.WriteCloser, error)) *emptySkipper {
return &emptySkipper{
w: nil,
buf: &bytes.Buffer{},
nw: false,
open: open,
}
}

func (f *emptySkipper) Write(p []byte) (n int, err error) {
if !f.nw {
if allWhitespace(p) {
// buffer the whitespace
return f.buf.Write(p)
}

// first time around, so open the writer
f.nw = true
f.w, err = f.open()
if err != nil {
return 0, err
}
if f.w == nil {
return 0, errors.New("nil writer returned by open")
}
// empty the buffer into the wrapped writer
_, err = f.buf.WriteTo(f.w)
if err != nil {
return 0, err
}
}

return f.w.Write(p)
}

func (f *emptySkipper) Close() error {
if f.w != nil {
return f.w.Close()
}
return nil
}

func allWhitespace(p []byte) bool {
for _, b := range p {
if b == ' ' || b == '\t' || b == '\n' || b == '\r' || b == '\v' {
continue
}
return false
}
return true
}

// like ioutil.NopCloser(), except for io.WriteClosers...
type nopWCloser struct {
io.Writer
}

func (n *nopWCloser) Close() error {
return nil
}
59 changes: 2 additions & 57 deletions template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"

"github.com/hairyhenderson/gomplate/v3/internal/config"
"github.com/hairyhenderson/gomplate/v3/internal/writers"
"github.com/spf13/afero"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -53,7 +54,7 @@ func TestOpenOutFile(t *testing.T) {
assert.Equal(t, os.FileMode(0644), i.Mode())

defer func() { Stdout = os.Stdout }()
Stdout = &nopWCloser{&bytes.Buffer{}}
Stdout = &writers.NopCloser{Writer: &bytes.Buffer{}}

f, err := openOutFile(cfg, "-", 0644, false)
assert.NoError(t, err)
Expand Down Expand Up @@ -247,59 +248,3 @@ func TestProcessTemplates(t *testing.T) {
fs.Remove("out")
}
}

func TestAllWhitespace(t *testing.T) {
testdata := []struct {
in []byte
expected bool
}{
{[]byte(" "), true},
{[]byte("foo"), false},
{[]byte(" \t\n\n\v\r\n"), true},
{[]byte(" foo "), false},
}

for _, d := range testdata {
assert.Equal(t, d.expected, allWhitespace(d.in))
}
}

func TestEmptySkipper(t *testing.T) {
testdata := []struct {
in []byte
empty bool
}{
{[]byte(" "), true},
{[]byte("foo"), false},
{[]byte(" \t\n\n\v\r\n"), true},
{[]byte(" foo "), false},
}

for _, d := range testdata {
w := &bufferCloser{&bytes.Buffer{}}
opened := false
f := newEmptySkipper(func() (io.WriteCloser, error) {
opened = true
return w, nil
})
n, err := f.Write(d.in)
assert.NoError(t, err)
assert.Equal(t, len(d.in), n)
if d.empty {
assert.Nil(t, f.w)
assert.False(t, opened)
} else {
assert.NotNil(t, f.w)
assert.True(t, opened)
assert.EqualValues(t, d.in, w.Bytes())
}
}
}

type bufferCloser struct {
*bytes.Buffer
}

func (b *bufferCloser) Close() error {
return nil
}

0 comments on commit d5dfc8b

Please sign in to comment.