vetrag/vendor/github.com/mfridman/interpolate/interpolate.go

214 lines
4.4 KiB
Go

package interpolate
import (
"bytes"
"fmt"
)
// Interpolate takes a set of environment and interpolates it into the provided string using shell
// script expansions
func Interpolate(env Env, str string) (string, error) {
if env == nil {
env = NewSliceEnv(nil)
}
expr, err := NewParser(str).Parse()
if err != nil {
return "", err
}
return expr.Expand(env)
}
// Indentifiers parses the identifiers from any expansions in the provided string
func Identifiers(str string) ([]string, error) {
expr, err := NewParser(str).Parse()
if err != nil {
return nil, err
}
return expr.Identifiers(), nil
}
// An expansion is something that takes in ENV and returns a string or an error
type Expansion interface {
Expand(env Env) (string, error)
Identifiers() []string
}
// VariableExpansion represents either $VAR or ${VAR}, our simplest expansion
type VariableExpansion struct {
Identifier string
}
func (e VariableExpansion) Identifiers() []string {
return []string{e.Identifier}
}
func (e VariableExpansion) Expand(env Env) (string, error) {
val, _ := env.Get(e.Identifier)
return val, nil
}
// EmptyValueExpansion returns either the value of an env, or a default value if it's unset or null
type EmptyValueExpansion struct {
Identifier string
Content Expression
}
func (e EmptyValueExpansion) Identifiers() []string {
return append([]string{e.Identifier}, e.Content.Identifiers()...)
}
func (e EmptyValueExpansion) Expand(env Env) (string, error) {
val, _ := env.Get(e.Identifier)
if val == "" {
return e.Content.Expand(env)
}
return val, nil
}
// UnsetValueExpansion returns either the value of an env, or a default value if it's unset
type UnsetValueExpansion struct {
Identifier string
Content Expression
}
func (e UnsetValueExpansion) Identifiers() []string {
return []string{e.Identifier}
}
func (e UnsetValueExpansion) Expand(env Env) (string, error) {
val, ok := env.Get(e.Identifier)
if !ok {
return e.Content.Expand(env)
}
return val, nil
}
// SubstringExpansion returns a substring (or slice) of the env
type SubstringExpansion struct {
Identifier string
Offset int
Length int
HasLength bool
}
func (e SubstringExpansion) Identifiers() []string {
return []string{e.Identifier}
}
func (e SubstringExpansion) Expand(env Env) (string, error) {
val, _ := env.Get(e.Identifier)
from := e.Offset
// Negative offsets = from end
if from < 0 {
from += len(val)
}
// Still negative = too far from end? Truncate to start.
if from < 0 {
from = 0
}
// Beyond end? Truncate to end.
if from > len(val) {
from = len(val)
}
if !e.HasLength {
return val[from:], nil
}
to := e.Length
if to >= 0 {
// Positive length = from offset
to += from
} else {
// Negative length = from end
to += len(val)
// Too far? Truncate to offset.
if to < from {
to = from
}
}
// Beyond end? Truncate to end.
if to > len(val) {
to = len(val)
}
return val[from:to], nil
}
// RequiredExpansion returns an env value, or an error if it is unset
type RequiredExpansion struct {
Identifier string
Message Expression
}
func (e RequiredExpansion) Identifiers() []string {
return []string{e.Identifier}
}
func (e RequiredExpansion) Expand(env Env) (string, error) {
val, ok := env.Get(e.Identifier)
if !ok {
msg, err := e.Message.Expand(env)
if err != nil {
return "", err
}
if msg == "" {
msg = "not set"
}
return "", fmt.Errorf("$%s: %s", e.Identifier, msg)
}
return val, nil
}
// Expression is a collection of either Text or Expansions
type Expression []ExpressionItem
func (e Expression) Identifiers() []string {
identifiers := []string{}
for _, item := range e {
if item.Expansion != nil {
identifiers = append(identifiers, item.Expansion.Identifiers()...)
}
}
return identifiers
}
func (e Expression) Expand(env Env) (string, error) {
buf := &bytes.Buffer{}
for _, item := range e {
if item.Expansion != nil {
result, err := item.Expansion.Expand(env)
if err != nil {
return "", err
}
_, _ = buf.WriteString(result)
} else {
_, _ = buf.WriteString(item.Text)
}
}
return buf.String(), nil
}
// ExpressionItem models either an Expansion or Text. Either/Or, never both.
type ExpressionItem struct {
Text string
// -- or --
Expansion Expansion
}
func (i ExpressionItem) String() string {
if i.Expansion != nil {
return fmt.Sprintf("%#v", i.Expansion)
}
return fmt.Sprintf("%q", i.Text)
}