grim/convey

Bump the version for release
v0.14.0-alpha3
2018-02-20, Gary Kramlich
166a6d1979fa
Bump the version for release
// Convey
// Copyright 2016-2018 Gary Kramlich <grim@reaperworld.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package state
import (
"regexp"
)
var re1 = regexp.MustCompile("\\$[A-Za-z_][A-Za-z0-9_]*")
var re2 = regexp.MustCompile("\\${[A-Za-z_][A-Za-z0-9_]*}")
// getNames extracts all variables with the format $var or ${var} from
// the given string.
func getNames(env string) []string {
names := []string{}
for _, match := range re1.FindAllString(env, -1) {
// Remove leading $
names = append(names, match[1:])
}
for _, match := range re2.FindAllString(env, -1) {
// Remove leading ${ and trailing }
names = append(names, match[2:len(match)-1])
}
return names
}
// product will return the cartesian product of the input map. For
// example, if the input map is A => 1, 2, 3; B => 4, 5, 6, then
// the list of outputs will contain the following:
// - A => 1; B => 4
// - A => 1; B => 5
// - A => 1; B => 6
// - A => 2; B => 4
// - A => 2; B => 5
// - A => 2; B => 6
// - A => 3; B => 4
// - A => 3; B => 5
// - A => 3; B => 6
func product(expansions map[string][]string) []map[string]string {
var (
order = []string{}
indices = make([]int, len(expansions))
maps = []map[string]string{}
)
if len(expansions) == 0 {
return nil
}
// Create a stable order of keys - this will determine our
// iteration order (in the above [A B] is the list as the
// index for B will increase more frequently than A).
for key := range expansions {
order = append(order, key)
}
// The index map starts out as [0 0 ... 0] and will increase
// in the following order:
// - [0 0 ... 0 1]
// - [0 0 ... 0 2]
// - [0 0 ... 0 (n-1)]
// - ...
// - [0 0 ... 1 0]
//
// Once the first index (which increases the most slowly)
// exceeds the length of that slice, we've seen everything.
for indices[0] < len(expansions[order[0]]) {
// Create a single element from the current state
// of the index map.
snapshot := map[string]string{}
for i, j := range indices {
snapshot[order[i]] = expansions[order[i]][j]
}
maps = append(maps, snapshot)
// Advance the index map. Start from the end of the
// index map and increment by one. If we end up rolling
// over from n - 1 to 0 (where n is the length of the
// associated slice), then we also flip the index
// immediately to the left.
for j := len(indices) - 1; j >= 0; j-- {
indices[j]++
if j == 0 || indices[j] < len(expansions[order[j]]) {
break
}
indices[j] = 0
}
}
return maps
}
// removeDupliates returns a list without duplicate elements.
func removeDuplicates(env []string) []string {
set := map[string]struct{}{}
for _, v := range env {
set[v] = struct{}{}
}
pruned := []string{}
for k := range set {
pruned = append(pruned, k)
}
return pruned
}
// Determine if two slices have the same elements.
func isSame(a, b []string) bool {
if len(a) != len(b) {
return false
}
for _, val := range a {
found := false
for _, match := range b {
if val == match {
found = true
break
}
}
if !found {
return false
}
}
return true
}