grim/convey
Clone
Summary
Browse
Changes
Graph
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
}