...

Source file src/github.com/drone/envsubst/v2/template.go

Documentation: github.com/drone/envsubst/v2

     1  package envsubst
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"io/ioutil"
     7  
     8  	"github.com/drone/envsubst/v2/parse"
     9  )
    10  
    11  // state represents the state of template execution. It is not part of the
    12  // template so that multiple executions can run in parallel.
    13  type state struct {
    14  	template *Template
    15  	writer   io.Writer
    16  	node     parse.Node // current node
    17  
    18  	// maps variable names to values
    19  	mapper func(string) string
    20  }
    21  
    22  // Template is the representation of a parsed shell format string.
    23  type Template struct {
    24  	tree *parse.Tree
    25  }
    26  
    27  // Parse creates a new shell format template and parses the template
    28  // definition from string s.
    29  func Parse(s string) (t *Template, err error) {
    30  	t = new(Template)
    31  	t.tree, err = parse.Parse(s)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	return t, nil
    36  }
    37  
    38  // ParseFile creates a new shell format template and parses the template
    39  // definition from the named file.
    40  func ParseFile(path string) (*Template, error) {
    41  	b, err := ioutil.ReadFile(path)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	return Parse(string(b))
    46  }
    47  
    48  // Execute applies a parsed template to the specified data mapping.
    49  func (t *Template) Execute(mapping func(string) string) (str string, err error) {
    50  	b := new(bytes.Buffer)
    51  	s := new(state)
    52  	s.node = t.tree.Root
    53  	s.mapper = mapping
    54  	s.writer = b
    55  	err = t.eval(s)
    56  	if err != nil {
    57  		return
    58  	}
    59  	return b.String(), nil
    60  }
    61  
    62  func (t *Template) eval(s *state) (err error) {
    63  	switch node := s.node.(type) {
    64  	case *parse.TextNode:
    65  		err = t.evalText(s, node)
    66  	case *parse.FuncNode:
    67  		err = t.evalFunc(s, node)
    68  	case *parse.ListNode:
    69  		err = t.evalList(s, node)
    70  	}
    71  	return err
    72  }
    73  
    74  func (t *Template) evalText(s *state, node *parse.TextNode) error {
    75  	_, err := io.WriteString(s.writer, node.Value)
    76  	return err
    77  }
    78  
    79  func (t *Template) evalList(s *state, node *parse.ListNode) (err error) {
    80  	for _, n := range node.Nodes {
    81  		s.node = n
    82  		err = t.eval(s)
    83  		if err != nil {
    84  			return err
    85  		}
    86  	}
    87  	return nil
    88  }
    89  
    90  func (t *Template) evalFunc(s *state, node *parse.FuncNode) error {
    91  	var w = s.writer
    92  	var buf bytes.Buffer
    93  	var args []string
    94  	for _, n := range node.Args {
    95  		buf.Reset()
    96  		s.writer = &buf
    97  		s.node = n
    98  		err := t.eval(s)
    99  		if err != nil {
   100  			return err
   101  		}
   102  		args = append(args, buf.String())
   103  	}
   104  
   105  	// restore the origin writer
   106  	s.writer = w
   107  	s.node = node
   108  
   109  	v := s.mapper(node.Param)
   110  
   111  	fn := lookupFunc(node.Name, len(args))
   112  
   113  	_, err := io.WriteString(s.writer, fn(v, args...))
   114  	return err
   115  }
   116  
   117  // lookupFunc returns the parameters substitution function by name. If the
   118  // named function does not exists, a default function is returned.
   119  func lookupFunc(name string, args int) substituteFunc {
   120  	switch name {
   121  	case ",":
   122  		return toLowerFirst
   123  	case ",,":
   124  		return toLower
   125  	case "^":
   126  		return toUpperFirst
   127  	case "^^":
   128  		return toUpper
   129  	case "#":
   130  		if args == 0 {
   131  			return toLen
   132  		}
   133  		return trimShortestPrefix
   134  	case "##":
   135  		return trimLongestPrefix
   136  	case "%":
   137  		return trimShortestSuffix
   138  	case "%%":
   139  		return trimLongestSuffix
   140  	case ":":
   141  		return toSubstr
   142  	case "/#":
   143  		return replacePrefix
   144  	case "/%":
   145  		return replaceSuffix
   146  	case "/":
   147  		return replaceFirst
   148  	case "//":
   149  		return replaceAll
   150  	case "=", ":=", ":-":
   151  		return toDefault
   152  	case ":?", ":+", "-", "+":
   153  		return toDefault
   154  	default:
   155  		return toDefault
   156  	}
   157  }
   158  

View as plain text