grim/convey

closing merged branch
expand-list
2017-10-03, Gary Kramlich
345a52ef04c6
closing merged branch
/*
* Convey
* Copyright 2016-2017 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
}