Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added mapper for regex #427

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net/url"
"os"
"reflect"
"regexp"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -285,6 +286,7 @@ func (r *Registry) RegisterDefaults() *Registry {
RegisterType(reflect.TypeOf(time.Duration(0)), durationDecoder()).
RegisterType(reflect.TypeOf(&url.URL{}), urlMapper()).
RegisterType(reflect.TypeOf(&os.File{}), fileMapper(r)).
RegisterType(reflect.TypeOf(&regexp.Regexp{}), regexMapper()).
RegisterName("path", pathMapper(r)).
RegisterName("existingfile", existingFileMapper(r)).
RegisterName("existingdir", existingDirMapper(r)).
Expand Down Expand Up @@ -733,6 +735,30 @@ func fileContentMapper(r *Registry) MapperFunc {
}
}

func regexMapper() MapperFunc {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regexp.Regexp actually implements encoding.Text(Un)marshaler, so I don't think this is necessary either after all...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh boy, you're right! 🤦
totally overlooked that too 🙈

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for consuming you time 🤦

i removed all mappers. we can close those this pr.

I added some regex tests in the last commit, if you have some time, maybe you can explain to me why the TestRegex,TestRegexSlice,TestRegexPointer are working but the TestRegexPointerSlice is not?

  • somehow i used to always use *regex.Regexp instead of regex.Regexp but the pointer version does not with the slice

return func(ctx *DecodeContext, target reflect.Value) error {
t, err := ctx.Scan.PopValue("regex")
if err != nil {
return err
}

var f *regexp.Regexp
switch v := t.Value.(type) {
case string:
f, err = regexp.Compile(v)
if err != nil {
return fmt.Errorf("expected regular expression but got %q: %w", v, err)
}
default:
return fmt.Errorf("expected string but got %q", v)
}

target.Set(reflect.ValueOf(f))

return nil
}
}

type ptrMapper struct {
r *Registry
}
Expand Down
154 changes: 154 additions & 0 deletions mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import (
"encoding/json"
"fmt"
"math"
"net"
"net/netip"
"net/url"
"os"
"path/filepath"
"reflect"
"regexp"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -138,6 +141,157 @@ func TestDurationMapperJSONResolver(t *testing.T) {
assert.Equal(t, time.Second*5, cli.Flag)
}

func TestIPAddress(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"127.0.0.1", "127.0.0.1"},
{"2001:db8:abcd:0012::1", "2001:db8:abcd:12::1"},
}

t.Run("TestNetPackage", func(t *testing.T) {
var cli1 struct {
Flag net.IP
}
k1 := mustNew(t, &cli1)

for _, tt := range tests {
_, err := k1.Parse([]string{"--flag", tt.input})
assert.NoError(t, err)
assert.Equal(t, tt.expected, cli1.Flag.String())
}
})

t.Run("TestNetipPackage", func(t *testing.T) {
var cli1 struct {
Flag netip.Addr
}
k1 := mustNew(t, &cli1)

for _, tt := range tests {
_, err := k1.Parse([]string{"--flag", tt.input})
assert.NoError(t, err)
assert.Equal(t, tt.expected, cli1.Flag.String())
}
})
}

func TestIPAddressSlice(t *testing.T) {
tests := []struct {
input []string
expected []string
}{
{input: []string{"127.0.0.1", "192.168.0.1"}, expected: []string{"127.0.0.1", "192.168.0.1"}},
{input: []string{"2001:db8:abcd:0012::1", "::1"}, expected: []string{"2001:db8:abcd:12::1", "::1"}},
}

t.Run("TestNetPackage", func(t *testing.T) {
var cli struct {
Flag []net.IP
}
k := mustNew(t, &cli)

for _, tt := range tests {
_, err := k.Parse([]string{"--flag", strings.Join(tt.input, ",")})
assert.NoError(t, err)
assert.Equal(t, len(tt.input), len(cli.Flag))
}
})

t.Run("TestNetipPackage", func(t *testing.T) {
var cli struct {
Flag []netip.Addr
}
k := mustNew(t, &cli)

for _, tt := range tests {
_, err := k.Parse([]string{"--flag", strings.Join(tt.input, ",")})
assert.NoError(t, err)
assert.Equal(t, len(tt.input), len(cli.Flag))
}
})
}

func TestCIDR(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"127.0.0.1/16", "127.0.0.1/16"},
{"2001:db8:abcd:0012::0/64", "2001:db8:abcd:12::/64"},
}

var cli1 struct {
Flag netip.Prefix
}
k1 := mustNew(t, &cli1)

for _, tt := range tests {
_, err := k1.Parse([]string{"--flag", tt.input})
assert.NoError(t, err)
assert.Equal(t, tt.expected, cli1.Flag.String())
}
}

func TestCIDRSlice(t *testing.T) {
tests := []struct {
input []string
expected []string
}{
{
[]string{"127.0.0.1/16", "192.168.1.0/20"},
[]string{"127.0.0.1/16", "192.168.1.0/20"},
},
{
[]string{"2001:db8:abcd:0012::0/64", "2001:db8:abcd:0012::0/128"},
[]string{"2001:db8:abcd:12::/64", "2001:db8:abcd:12::/128"},
},
}

var cli struct {
Flag []netip.Prefix
}
k := mustNew(t, &cli)

for _, tt := range tests {
_, err := k.Parse([]string{"--flag", strings.Join(tt.input, ",")})
assert.NoError(t, err)
assert.Equal(t, len(tt.input), len(cli.Flag))
for i := 0; i < len(cli.Flag); i++ {
assert.Equal(t, tt.expected[i], cli.Flag[i].String())
}
}
}

func TestRegex(t *testing.T) {
var cli struct {
Test *regexp.Regexp
}

k := mustNew(t, &cli)
_, err := k.Parse([]string{"--test", "a.+[a-b]{2,4}"})
assert.NoError(t, err)
assert.Equal(t, "a.+[a-b]{2,4}", cli.Test.String())
}

func TestRegexSlice(t *testing.T) {
var cli struct {
Test []*regexp.Regexp `kong:"sep='none'"`
}

k := mustNew(t, &cli)
_, err := k.Parse([]string{
"--test", "foo.+[b-r]{2,4}",
"--test", "foo=bar",
})
assert.NoError(t, err)

assert.Equal(t, 2, len(cli.Test))
assert.Equal(t, "foo.+[b-r]{2,4}", cli.Test[0].String())
assert.Equal(t, "foo=bar", cli.Test[1].String())
}

func TestSplitEscaped(t *testing.T) {
assert.Equal(t, []string{"a", "b"}, kong.SplitEscaped("a,b", ','))
assert.Equal(t, []string{"a,b", "c"}, kong.SplitEscaped(`a\,b,c`, ','))
Expand Down