Add parseNumberMenu()

This function is designed to replace the existing number menu
in upcoming commits.
This commit is contained in:
morganamilo 2018-03-09 02:03:04 +00:00
parent f0f20269a0
commit 6988537552
No known key found for this signature in database
GPG Key ID: 6FE9E7996B0B082E
4 changed files with 212 additions and 1 deletions

105
parser.go
View File

@ -4,6 +4,7 @@ import (
"fmt"
"io"
"os"
"strconv"
"strings"
)
@ -38,6 +39,20 @@ func (set stringSet) toSlice() []string {
return slice
}
func SliceToStringSet(in []string) stringSet {
set := make(stringSet)
for _, v := range in {
set.set(v)
}
return set
}
func makeStringSet(in ...string) stringSet {
return SliceToStringSet(in)
}
// Parses command line arguments in a way we can interact with programmatically but
// also in a way that can easily be passed to pacman later on.
type arguments struct {
@ -554,3 +569,93 @@ func (parser *arguments) parseCommandLine() (err error) {
return
}
type intRange struct {
min int
max int
}
func makeIntRange(min, max int) intRange {
return intRange{
min,
max,
}
}
func (r intRange) get(n int) bool {
return n >= r.min && n <= r.max
}
type intRanges []intRange
func (rs intRanges) get(n int) bool {
for _, r := range rs {
if r.get(n) {
return true
}
}
return false
}
//parses input for number menus
//supports individual selection: 1 2 3 4
//supports range selections: 1-4 10-20
//supports negation: ^1 ^1-4
//
//include and excule holds numbers that should be added and should not be added
//respectively. other holds anythign that can't be parsed as an int. This is
//intended to allow words inside of number menus. e.g. 'all' 'none' 'abort'
//of course the implementation is up to the caller, this function mearley parses
//the input and organizes it
func parseNumberMenu(input string) (intRanges, intRanges, stringSet, stringSet) {
include := make(intRanges, 0, 0)
exclude := make(intRanges, 0, 0)
otherInclude := make(stringSet)
otherExclude := make(stringSet)
words := strings.Fields(input)
for _, word := range words {
var num1 int
var num2 int
var err error
invert := false
other := otherInclude
if word[0] == '^' {
invert = true
other = otherExclude
word = word[1:]
}
ranges := strings.SplitN(word, "-", 2)
num1, err = strconv.Atoi(ranges[0])
if err != nil {
other.set(strings.ToLower(word))
continue
}
if len(ranges) == 2 {
num2, err = strconv.Atoi(ranges[1])
if err != nil {
other.set(strings.ToLower(word))
continue
}
} else {
num2 = num1
}
mi := min(num1, num2)
ma := max(num1, num2)
if !invert {
include = append(include, makeIntRange(mi, ma))
} else {
exclude = append(exclude, makeIntRange(mi, ma))
}
}
return include, exclude, otherInclude, otherExclude
}

99
parser_test.go Normal file
View File

@ -0,0 +1,99 @@
package main
import "testing"
func intRangesEqual(a, b intRanges) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
if len(a) != len(b) {
return false
}
for n := range a {
r1 := a[n]
r2 := b[n]
if r1.min != r1.min || r1.max != r2.max {
return false
}
}
return true
}
func stringSetEqual(a, b stringSet) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
if len(a) != len(b) {
return false
}
for n := range a {
if !b.get(n) {
return false
}
}
return true
}
func TestParseNumberMenu(t *testing.T) {
type result struct {
Include intRanges
Exclude intRanges
OtherInclude stringSet
OtherExclude stringSet
}
inputs := []string{
"1 2 3 4 5",
"1-10 5-15",
"10-5 90-85",
"1 ^2 ^10-5 99 ^40-38 ^123 60-62",
"abort all none",
"a-b ^a-b ^abort",
"1\t2 3 4\t\t \t 5",
"",
" \t ",
"A B C D E",
}
expected := []result{
{intRanges{makeIntRange(1, 1), makeIntRange(2, 2), makeIntRange(3, 3), makeIntRange(4, 4), makeIntRange(5, 5)}, intRanges{}, make(stringSet), make(stringSet)},
{intRanges{makeIntRange(1, 10), makeIntRange(5, 15)}, intRanges{}, make(stringSet), make(stringSet)},
{intRanges{makeIntRange(5, 10), makeIntRange(85, 90)}, intRanges{}, make(stringSet), make(stringSet)},
{intRanges{makeIntRange(1, 1), makeIntRange(99, 99), makeIntRange(60, 62)}, intRanges{makeIntRange(2, 2), makeIntRange(5, 10), makeIntRange(38, 40), makeIntRange(123, 123)}, make(stringSet), make(stringSet)},
{intRanges{}, intRanges{}, makeStringSet("abort", "all", "none"), make(stringSet)},
{intRanges{}, intRanges{}, makeStringSet("a-b"), makeStringSet("abort", "a-b")},
{intRanges{makeIntRange(1, 1), makeIntRange(2, 2), makeIntRange(3, 3), makeIntRange(4, 4), makeIntRange(5, 5)}, intRanges{}, make(stringSet), make(stringSet)},
{intRanges{}, intRanges{}, make(stringSet), make(stringSet)},
{intRanges{}, intRanges{}, make(stringSet), make(stringSet)},
{intRanges{}, intRanges{}, makeStringSet("a", "b", "c", "d", "e"), make(stringSet)},
}
for n, in := range inputs {
res := expected[n]
include, exclude, otherInclude, otherExclude := parseNumberMenu(in)
if !intRangesEqual(include, res.Include) ||
!intRangesEqual(exclude, res.Exclude) ||
!stringSetEqual(otherInclude, res.OtherInclude) ||
!stringSetEqual(otherExclude, res.OtherExclude) {
t.Fatalf("Test %d Failed: Expected: include=%+v exclude=%+v otherInclude=%+v otherExclude=%+v got include=%+v excluive=%+v otherInclude=%+v otherExclude=%+v",
n+1, res.Include, res.Exclude, res.OtherInclude, res.OtherExclude, include, exclude, otherInclude, otherExclude)
}
}
}

View File

@ -317,6 +317,13 @@ func min(a, b int) int {
return b
}
func max(a, b int) int {
if a < b {
return b
}
return a
}
// Queries the aur for information about specified packages.
// All packages should be queried in a single rpc request except when the number
// of packages exceeds the number set in config.RequestSplitN.

View File

@ -60,7 +60,7 @@ func TestParsing(t *testing.T) {
branch != compare.Branch ||
!isEqual(protocols, compare.Protocols) {
t.Fatalf("Expected url=%+v branch=%+v protocols=%+v\ngot url=%+v branch=%+v protocols=%+v", url, branch, protocols, compare.URL, compare.Branch, compare.Protocols)
t.Fatalf("Test %d failed: Expected: url=%+v branch=%+v protocols=%+v\ngot url=%+v branch=%+v protocols=%+v", n+1, url, branch, protocols, compare.URL, compare.Branch, compare.Protocols)
}
}