Skip to content
Permalink
Browse files

Preserving key order

  • Loading branch information...
Depado committed Oct 24, 2018
1 parent bdbd501 commit 3cc013cbf71899866a8cbe3a990d2aa5eb2a6261
Showing with 85 additions and 57 deletions.
  1. +5 −5 conf/conf.go
  2. +4 −2 conf/file.go
  3. +2 −4 conf/root.go
  4. +74 −46 conf/variables.go
@@ -11,11 +11,11 @@ import (
// Config is a configuration that can be applied to a single file (inline conf)
// or to an entire directory
type Config struct {
Delimiters []string `yaml:"delimiters"`
Copy *bool `yaml:"copy"`
Ignore *bool `yaml:"ignore"`
Variables Variables `yaml:"variables"`
If string `yaml:"if"`
Delimiters []string `yaml:"delimiters"`
Copy *bool `yaml:"copy"`
Ignore *bool `yaml:"ignore"`
Variables *Variables `yaml:"variables"`
If string `yaml:"if"`
}

// ConfigFile is the combination of File and Config
@@ -81,7 +81,7 @@ func (f *File) ParseFrontMatter() error {
return err
}
f.Metadata = &r
if f.Metadata.Variables != nil && len(f.Metadata.Variables) > 0 {
if f.Metadata.Variables != nil && len(f.Metadata.Variables.s) > 0 {
utils.OkPrintln("Variables for single file", color.YellowString(f.Path))
f.Metadata.Variables.Prompt()
}
@@ -190,7 +190,9 @@ func (f *File) Render() error {
}
delims = r.Delimiters
}
r.Variables.AddToCtx("", ctx)
if r.Variables != nil {
r.Variables.AddToCtx("", ctx)
}
}
if f.Metadata != nil {
if f.Metadata.If != "" {
@@ -43,10 +43,8 @@ func (r Root) ExecuteCommands(dir string) {
for _, cmd := range r.After {
if cmd.Cmd != "" {
if cmd.If != "" && r.Variables != nil {
if v, ok := r.Variables[cmd.If]; ok {
if v.Confirm != nil && *v.Confirm || v.Result != "" {
cmd.Run()
}
if v, ok := r.Variables.m[cmd.If]; ok && v.True() {
cmd.Run()
}
} else {
cmd.Run()
@@ -2,71 +2,69 @@ package conf

import (
"fmt"
"sort"

"github.com/Depado/projectmpl/utils"
"github.com/fatih/color"
survey "gopkg.in/AlecAivazis/survey.v1"
yaml "gopkg.in/yaml.v2"
)

// Variables represents a map of variable
type Variables map[string]*Variable

// func (e *Variables) UnmarshalYAML(unmarshal func(interface{}) error) error {
// n := yaml.MapSlice{}
// err := unmarshal(&n)
// if err != nil {
// return err
// }
// for _, v := range n {
// var inv &Variable
// fmt.Println("============")
// fmt.Println(v.Key)
// for _, vv := range v.Value.(yaml.MapSlice) {
// switch vv.Key {
// case "default":

// }
// fmt.Println("\t", vv.Key, vv.Value)
// }
// }
// return nil
// }

// Prompt will prompt the variables
func (vv Variables) Prompt() {
// Order the variables alphabetically to keep the same order
var ordered []*Variable
for k, v := range vv {
if v == nil { // Unconfigured values do have a key but no value
v = &Variable{Name: k}
} else {
v.Name = k
}
ordered = append(ordered, v)
type Variables struct {
m map[string]*Variable
s []*Variable
}

// FromMapSlice fills in the Variables struct with the data stored in a
// yaml.MapSlice. Used to recursively parse nested variables.
func (vv *Variables) FromMapSlice(in yaml.MapSlice) {
for _, i := range in {
inv := &Variable{}
inv.FromMapItem(i)

k := i.Key.(string)
inv.Name = k
vv.m[k] = inv
vv.s = append(vv.s, inv)
}
sort.Slice(ordered, func(i, j int) bool {
return ordered[i].Name < ordered[j].Name
})
}

for _, variable := range ordered {
variable.Prompt()
// UnmarshalYAML defines a custom way to unmarshal to the Variables type.
// Specifically this allows to conserve the key order
func (vv *Variables) UnmarshalYAML(unmarshal func(interface{}) error) error {
variables := Variables{
m: make(map[string]*Variable),
}
n := yaml.MapSlice{}
err := unmarshal(&n)
if err != nil {
return err
}
variables.FromMapSlice(n)
*vv = variables
return nil
}

// Prompt will prompt
func (vv Variables) Prompt() {
for _, v := range vv.s {
v.Prompt()
}
}

// Ctx generates the context from the variables
func (vv Variables) Ctx() map[string]interface{} {
ctx := make(map[string]interface{})
for k, v := range vv {
for _, v := range vv.s {
if v != nil {
if v.Confirm != nil {
ctx[k] = *v.Confirm
ctx[v.Name] = *v.Confirm
} else {
ctx[k] = v.Result
ctx[v.Name] = v.Result
}
}
if v.Variables != nil {
v.Variables.AddToCtx(k, ctx)
v.Variables.AddToCtx(v.Name, ctx)
}
}
return ctx
@@ -103,13 +101,43 @@ type Variable struct {

// Confirm is used both for default variable and to store the result.
// If this field isn't nil, then a confirmation survey is used.
Confirm *bool `yaml:"confirm,omitempty"`
Variables Variables `yaml:"variables,omitempty"`
Confirm *bool `yaml:"confirm,omitempty"`
Variables *Variables `yaml:"variables,omitempty"`

Result string
Name string
}

// FromMapItem will fill the variable with the data stored in the input
// yaml.MapItem. Used to recursively parse nested variables.
func (v *Variable) FromMapItem(i yaml.MapItem) {
for _, data := range i.Value.(yaml.MapSlice) {
switch data.Key.(string) {
case "default":
v.Default = data.Value.(string)
case "prompt":
v.CustomPrompt = data.Value.(string)
case "values":
for _, p := range data.Value.([]interface{}) {
v.Values = append(v.Values, p.(string))
}
case "help":
v.Help = data.Value.(string)
case "required":
v.Required = data.Value.(bool)
case "confirm":
b := data.Value.(bool)
v.Confirm = &b
case "variables":
vv := &Variables{
m: make(map[string]*Variable),
}
vv.FromMapSlice(data.Value.(yaml.MapSlice))
v.Variables = vv
}
}
}

// True returns if the variable has been filled
func (v *Variable) True() bool {
return v.Result != "" || v.Confirm != nil && *v.Confirm

0 comments on commit 3cc013c

Please sign in to comment.
You can’t perform that action at this time.