ingress-nginx-helm/vendor/github.com/imkira/go-interpol/interpol.go
Manuel Alejandro de Brito Fontes 307bf76454 Update go dependencies
2020-02-19 19:42:50 -03:00

171 lines
3.9 KiB
Go

// Package interpol provides utility functions for doing format-string like
// string interpolation using named parameters.
// Currently, a template only accepts variable placeholders delimited by brace
// characters (eg. "Hello {foo} {bar}").
package interpol
import (
"bytes"
"errors"
"io"
"strings"
)
// Errors returned when formatting templates.
var (
ErrUnexpectedClose = errors.New("interpol: unexpected close in template")
ErrExpectingClose = errors.New("interpol: expecting close in template")
ErrKeyNotFound = errors.New("interpol: key not found")
ErrReadByteFailed = errors.New("interpol: read byte failed")
)
// Func receives the placeholder key and writes to the io.Writer. If an error
// happens, the function can return an error, in which case the interpolation
// will be aborted.
type Func func(key string, w io.Writer) error
// New creates a new interpolator with the given list of options.
// You can use options such as the ones returned by WithTemplate, WithFormat
// and WithOutput.
func New(opts ...Option) *Interpolator {
opts2 := &Options{}
setOptions(opts, newOptionSetter(opts2))
return NewWithOptions(opts2)
}
// NewWithOptions creates a new interpolator with the given options.
func NewWithOptions(opts *Options) *Interpolator {
return &Interpolator{
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
}
// Interpolate reads runes from Template and writes them to Output, with the
// exception of placeholders which are passed to Format.
func (i *Interpolator) Interpolate() error {
for pos := 0; ; pos++ {
r, _, err := i.template.ReadRune()
if err != nil {
if err == io.EOF {
break
}
return err
}
if err := i.parse(r, pos); err != nil {
return err
}
}
return i.finish()
}
func (i *Interpolator) parse(r rune, pos int) error {
switch r {
case '{':
return i.open(pos)
case '}':
return i.close()
default:
return i.append(r)
}
}
func (i *Interpolator) open(pos int) error {
if i.closing {
return ErrUnexpectedClose
}
if i.start >= 0 {
if _, err := i.output.WriteRune('{'); err != nil {
return err
}
i.start = -1
} else {
i.start = pos + 1
}
return nil
}
func (i *Interpolator) close() error {
if i.start >= 0 {
if err := i.format(string(i.rb), i.output); err != nil {
return err
}
i.rb = i.rb[:0]
i.start = -1
} else if i.closing {
i.closing = false
if _, err := i.output.WriteRune('}'); err != nil {
return err
}
} else {
i.closing = true
}
return nil
}
func (i *Interpolator) append(r rune) error {
if i.closing {
return ErrUnexpectedClose
}
if i.start < 0 {
_, err := i.output.WriteRune(r)
return err
}
i.rb = append(i.rb, r)
return nil
}
func (i *Interpolator) finish() error {
if i.start >= 0 {
return ErrExpectingClose
}
if i.closing {
return ErrUnexpectedClose
}
return nil
}
// WithFunc interpolates the specified template with replacements using the
// given function.
func WithFunc(template string, format Func) (string, error) {
buffer := bytes.NewBuffer(make([]byte, 0, len(template)))
opts := &Options{
Template: strings.NewReader(template),
Output: buffer,
Format: format,
}
i := NewWithOptions(opts)
if err := i.Interpolate(); err != nil {
return "", err
}
return buffer.String(), nil
}
// WithMap interpolates the specified template with replacements using the
// given map. If a placeholder is used for which a value is not found, an error
// is returned.
func WithMap(template string, m map[string]string) (string, error) {
format := func(key string, w io.Writer) error {
value, ok := m[key]
if !ok {
return ErrKeyNotFound
}
_, err := w.Write([]byte(value))
return err
}
return WithFunc(template, format)
}