tmpl/registry/prompt.go

161 lines
3.1 KiB
Go

package registry
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"text/template"
"github.com/AlecAivazis/survey/v2"
"github.com/pelletier/go-toml"
)
type templatePrompt struct {
Key string `toml:"-"`
Value interface{} `toml:"-"`
Message string `toml:"prompt"`
Help string `toml:"help"`
Default interface{} `toml:"default"`
}
func prompt(dir string, defaults bool) (templatePrompts, error) {
templatePath := filepath.Join(dir, "template.toml")
if _, err := os.Lstat(templatePath); err != nil {
return nil, err
}
templateBytes, err := ioutil.ReadFile(templatePath)
if err != nil {
return nil, err
}
// Expand the template with environment variables
templateContents := os.ExpandEnv(string(templateBytes))
tree, err := toml.Load(templateContents)
if err != nil {
return nil, err
}
prompts := make(templatePrompts, len(tree.Keys()))
for idx, k := range tree.Keys() {
v := tree.Get(k)
obj, ok := v.(*toml.Tree)
if !ok {
prompts[idx] = templatePrompt{
Key: k,
Message: k,
Default: v,
}
continue
}
var p templatePrompt
if err := obj.Unmarshal(&p); err != nil {
return nil, err
}
p.Key = k
if p.Message == "" {
p.Message = p.Key
}
if p.Default == nil {
p.Default = ""
}
prompts[idx] = p
}
// Sort the prompts so they are consistent
sort.Sort(prompts)
for idx, prompt := range prompts {
// Check for env variable
if e, ok := os.LookupEnv(fmt.Sprintf("TMPL_VAR_%s", strings.ToUpper(prompt.Key))); ok {
prompts[idx].Value = e
continue
}
// Check if we are using defaults
if defaults {
prompts[idx].Value = prompt.Default
continue
}
var p survey.Prompt
switch t := prompt.Default.(type) {
case []string:
p = &survey.Select{
Message: prompt.Message,
Options: t,
Help: prompt.Help,
}
case bool:
p = &survey.Confirm{
Message: prompt.Message,
Default: t,
Help: prompt.Help,
}
case string:
p = &survey.Input{
Message: prompt.Message,
Default: t,
Help: prompt.Help,
}
default:
p = &survey.Input{
Message: prompt.Message,
Default: fmt.Sprintf("%v", t),
Help: prompt.Help,
}
}
var a string
if err := survey.AskOne(p, &a); err != nil {
return nil, err
}
prompts[idx].Value = a
}
return prompts, nil
}
type templatePrompts []templatePrompt
// ToMap converts a slice to templatePrompt into a suitable template context
func (t templatePrompts) ToMap() map[string]interface{} {
m := make(map[string]interface{})
for _, p := range t {
m[p.Key] = p.Value
}
return m
}
// ToFuncMap converts a slice of templatePrompt into a suitable template.FuncMap
func (t templatePrompts) ToFuncMap() template.FuncMap {
m := make(map[string]interface{})
for k, v := range t.ToMap() {
vv := v // Enclosure
m[k] = func() interface{} {
return vv
}
}
return m
}
// Len is for sort.Sort
func (t templatePrompts) Len() int {
return len(t)
}
// Less is for sort.Sort
func (t templatePrompts) Less(i, j int) bool {
return t[i].Key > t[j].Key
}
// Swap is for sort.Sort
func (t templatePrompts) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}