214 lines
4.4 KiB
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)
|
|
}
|