cmd: add IntRange parser util

This commit is contained in:
Richard Patel 2022-09-10 14:25:23 +02:00
parent 46f6563f6d
commit be154eae0f
2 changed files with 162 additions and 0 deletions

View File

@ -0,0 +1,97 @@
package util
import "strconv"
type Ints []IntRange
func (i Ints) Iter(fn func(uint64) bool) bool {
for _, r := range i {
if !r.Iter(fn) {
return false
}
}
return true
}
type IntRange struct {
Start, Stop uint64
}
func (r IntRange) Iter(fn func(uint64) bool) bool {
for i := r.Start; i < r.Stop; i++ {
if !fn(i) {
return false
}
}
return true
}
// ParseInts parses a string indicating ranges of integers.
//
// e.g. `"0:7,234,1000:2333"`
func ParseInts(s string) (Ints, bool) {
if s == "" {
return nil, true
}
var p intsParser
ok := p.parse(s)
return p.res, ok
}
type intsParser struct {
res []IntRange
}
func (n *intsParser) parse(s string) bool {
var ok bool
for {
s, ok = n.parseRange(s)
if !ok {
return false
}
if len(s) == 0 {
return true
}
if s[0] != ',' {
return false
}
s = s[1:]
}
}
func (n *intsParser) parseRange(s string) (string, bool) {
// Parse start integer
i := 0
for i < len(s) && s[i] >= '0' && s[i] <= '9' {
i++
}
if i == 0 {
return s, false
}
start, err := strconv.ParseUint(s[:i], 0, 64)
if err != nil {
return s, false
}
s = s[i:]
r := IntRange{Start: start, Stop: start + 1}
// Parse optional :stop part
if len(s) > 0 && s[0] == ':' {
i = 1
for i < len(s) && s[i] >= '0' && s[i] <= '9' {
i++
}
if i > 1 {
r.Stop, err = strconv.ParseUint(s[1:i], 0, 64)
if err != nil {
return s, false
}
s = s[i:]
// if range is invalid or empty, skip it
if r.Stop <= start {
return s, true
}
}
}
n.res = append(n.res, r)
return s, true
}

View File

@ -0,0 +1,65 @@
package util
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseInts(t *testing.T) {
cases := []struct {
name string
input string
out Ints
fail bool
}{
{
name: "Empty",
input: "",
out: nil,
},
{
name: "Invalid",
input: "abc",
fail: true,
},
{
name: "Single",
input: "12",
out: Ints{
{Start: 12, Stop: 13},
},
},
{
name: "SingleRange",
input: "12:23",
out: Ints{
{Start: 12, Stop: 23},
},
},
{
name: "EmptyRange",
input: "1:3,12:12",
out: Ints{
{Start: 1, Stop: 3},
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
out, ok := ParseInts(tc.input)
assert.Equal(t, !tc.fail, ok)
assert.Equal(t, tc.out, out)
})
}
}
func FuzzParseInts(f *testing.F) {
f.Add("12")
f.Add("56:23")
f.Add("23,95:30")
f.Add("1,2,3")
f.Fuzz(func(t *testing.T, s string) {
_, _ = ParseInts(s)
})
}