Skip to content

Commit

Permalink
Package config: Implement the Port type.
Browse files Browse the repository at this point in the history
  • Loading branch information
octo committed May 19, 2020
1 parent f325540 commit c2b2da9
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
41 changes: 41 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package config

import (
"fmt"
"math"
"net"
"reflect"

"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -231,3 +233,42 @@ func storeStructConfigValues(cvs []Value, v reflect.Value) error {
type Unmarshaler interface {
UnmarshalConfig(Block) error
}

// Port represents a port number in the configuration. When a configuration is
// converted to Go types using Unmarshal, it implements special conversion
// rules:
// If the config option is a numeric value, it is ensured to be in the range
// [1–65535]. If the config option is a string, it is converted to a port
// number using "net".LookupPort (using "tcp" as network).
type Port int

// UnmarshalConfig converts b to a port number.
func (p *Port) UnmarshalConfig(b Block) error {
if len(b.Values) != 1 || len(b.Children) != 0 {
return fmt.Errorf("option %q has to be a single scalar value", b.Key)
}

v := b.Values[0]
if f, ok := v.Number(); ok {
if math.IsNaN(f) {
return fmt.Errorf("the value of the %q option (%v) is invalid", b.Key, f)
}
if f < 1 || f > math.MaxUint16 {
return fmt.Errorf("the value of the %q option (%v) is out of range", b.Key, f)
}
*p = Port(f)
return nil
}

if !v.IsString() {
return fmt.Errorf("the value of the %q option must be a number or a string", b.Key)
}

port, err := net.LookupPort("tcp", v.String())
if err != nil {
return fmt.Errorf("%s: %w", b.Key, err)
}

*p = Port(port)
return nil
}
121 changes: 121 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
"fmt"
"math"
"testing"

"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -356,6 +357,126 @@ func TestConfig_Unmarshal(t *testing.T) {
}{},
wantErr: true,
},
{
name: "port numeric success",
src: Block{
Key: "Plugin",
Values: []Value{StringValue("test")},
Children: []Block{
{
Key: "Port",
Values: []Value{Float64Value(80)},
},
},
},
dst: &struct {
Args string
Port Port
}{},
want: &struct {
Args string
Port Port
}{
Args: "test",
Port: Port(80),
},
},
{
name: "port out of range",
src: Block{
Key: "Plugin",
Values: []Value{StringValue("test")},
Children: []Block{
{
Key: "Port",
Values: []Value{Float64Value(1<<48)},
},
},
},
dst: &struct {
Args string
Port Port
}{},
wantErr: true,
},
{
name: "port not a number",
src: Block{
Key: "Plugin",
Values: []Value{StringValue("test")},
Children: []Block{
{
Key: "Port",
Values: []Value{Float64Value(math.NaN())},
},
},
},
dst: &struct {
Args string
Port Port
}{},
wantErr: true,
},
{
name: "port invalid type",
src: Block{
Key: "Plugin",
Values: []Value{StringValue("test")},
Children: []Block{
{
Key: "Port",
Values: []Value{BoolValue(true)},
},
},
},
dst: &struct {
Args string
Port Port
}{},
wantErr: true,
},
{
name: "port string success",
src: Block{
Key: "Plugin",
Values: []Value{StringValue("test")},
Children: []Block{
{
Key: "Port",
Values: []Value{StringValue("http")},
},
},
},
dst: &struct {
Args string
Port Port
}{},
want: &struct {
Args string
Port Port
}{
Args: "test",
Port: Port(80),
},
},
{
name: "port string failure",
src: Block{
Key: "Plugin",
Values: []Value{StringValue("test")},
Children: []Block{
{
Key: "Port",
Values: []Value{StringValue("--- invalid ---")},
},
},
},
dst: &struct {
Args string
Port Port
}{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit c2b2da9

Please sign in to comment.