diff --git a/interpol.go b/interpol.go index 1b56975..5e04a48 100644 --- a/interpol.go +++ b/interpol.go @@ -35,24 +35,37 @@ func New(opts ...Option) *Interpolator { // NewWithOptions creates a new interpolator with the given options. func NewWithOptions(opts *Options) *Interpolator { + startDelimiter := '{' + endDelimiter := '}' + var empty rune + if opts.StartDelimiter != empty { + startDelimiter = opts.StartDelimiter + } + if opts.EndDelimiter != empty { + endDelimiter = opts.EndDelimiter + } return &Interpolator{ - template: templateReader(opts), - output: outputWriter(opts), - format: opts.Format, - rb: make([]rune, 0, 64), - start: -1, - closing: false, + startDelimiter: startDelimiter, + endDelimiter: endDelimiter, + template: templateReader(opts), + output: outputWriter(opts), + format: opts.Format, + rb: make([]rune, 0, 64), + start: -1, + closing: false, } } // Interpolator interpolates Template to Output, according to Format. type Interpolator struct { - template io.RuneReader - output runeWriter - format Func - rb []rune - start int - closing bool + template io.RuneReader + output runeWriter + format Func + rb []rune + start int + closing bool + startDelimiter rune + endDelimiter rune } // Interpolate reads runes from Template and writes them to Output, with the @@ -75,9 +88,9 @@ func (i *Interpolator) Interpolate() error { func (i *Interpolator) parse(r rune, pos int) error { switch r { - case '{': + case i.startDelimiter: return i.open(pos) - case '}': + case i.endDelimiter: return i.close() default: return i.append(r) diff --git a/interpol_test.go b/interpol_test.go index 3a25ffb..ee00c4d 100644 --- a/interpol_test.go +++ b/interpol_test.go @@ -228,6 +228,38 @@ func TestWithMapKeyNotFound(t *testing.T) { } } +func TestWithMapKeyExistsDifferentDelimiters(t *testing.T) { + m := map[string]string{ + "test": "World", + "data": "!!!", + } + buffer := bytes.NewBuffer(nil) + format := func(key string, w io.Writer) error { + value, ok := m[key] + if !ok { + return ErrKeyNotFound + } + _, err := w.Write([]byte(value)) + return err + } + opts := &Options{ + Template: strings.NewReader(`{"hello": ""}`), + Output: buffer, + Format: format, + StartDelimiter: '<', + EndDelimiter: '>', + } + i := NewWithOptions(opts) + err := i.Interpolate() + if err != nil { + t.Fatal(err) + } + got := buffer.String() + if got != `{"hello": "World!!!"}` { + t.Errorf("Invalid string: %q", got) + } +} + func TestWithMapNil(t *testing.T) { str, err := WithMap("hello {test}!!!", nil) if len(str) != 0 || err != ErrKeyNotFound { diff --git a/options.go b/options.go index 032863e..87536d6 100644 --- a/options.go +++ b/options.go @@ -4,9 +4,11 @@ import "io" // Options contains all options supported by an Interpolator. type Options struct { - Template io.Reader - Format Func - Output io.Writer + Template io.Reader + Format Func + Output io.Writer + StartDelimiter rune + EndDelimiter rune } // Option is an option that can be applied to an Interpolator.