Skip to content

Commit

Permalink
Merge pull request #4 from go-andiamo/additions
Browse files Browse the repository at this point in the history
Path vars
  • Loading branch information
marrow16 authored Nov 11, 2022
2 parents 8680e2b + 0477d98 commit 08fbf77
Show file tree
Hide file tree
Showing 14 changed files with 1,023 additions and 40 deletions.
81 changes: 81 additions & 0 deletions headers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package urit

import (
"errors"
)

type HeadersOption interface {
GetHeaders() (map[string]string, error)
}

type Headers interface {
HeadersOption
Set(key string, value interface{}) Headers
Get(key string) (interface{}, bool)
Has(key string) bool
Del(key string) Headers
Clone() Headers
}

func NewHeaders(namesAndValues ...interface{}) (Headers, error) {
if len(namesAndValues)%2 != 0 {
return nil, errors.New("must be a value for each name")
}
result := &headers{
entries: map[string]interface{}{},
}
for i := 0; i < len(namesAndValues)-1; i += 2 {
if k, ok := namesAndValues[i].(string); ok {
result.entries[k] = namesAndValues[i+1]
} else {
return nil, errors.New("name must be a string")
}
}
return result, nil
}

type headers struct {
entries map[string]interface{}
}

func (h *headers) GetHeaders() (map[string]string, error) {
result := map[string]string{}
for k, v := range h.entries {
if str, err := getValue(v); err == nil {
result[k] = str
} else {
return result, err
}
}
return result, nil
}

func (h *headers) Set(key string, value interface{}) Headers {
h.entries[key] = value
return h
}

func (h *headers) Get(key string) (interface{}, bool) {
v, ok := h.entries[key]
return v, ok
}

func (h *headers) Has(key string) bool {
_, ok := h.entries[key]
return ok
}

func (h *headers) Del(key string) Headers {
delete(h.entries, key)
return h
}

func (h *headers) Clone() Headers {
result := &headers{
entries: map[string]interface{}{},
}
for k, v := range h.entries {
result.entries[k] = v
}
return result
}
105 changes: 105 additions & 0 deletions headers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package urit

import (
"github.com/stretchr/testify/require"
"testing"
)

func TestNewHeaders(t *testing.T) {
h, err := NewHeaders()
require.NoError(t, err)
require.NotNil(t, h)
rh, ok := h.(*headers)
require.True(t, ok)
require.Equal(t, 0, len(rh.entries))

h, err = NewHeaders("foo", 1.23)
require.NoError(t, err)
require.NotNil(t, h)
rh, ok = h.(*headers)
require.True(t, ok)
require.Equal(t, 1, len(rh.entries))
require.Equal(t, 1.23, rh.entries["foo"])
}

func TestNewHeadersErrors(t *testing.T) {
_, err := NewHeaders("foo") // must be an even number!
require.Error(t, err)
require.Equal(t, `must be a value for each name`, err.Error())

_, err = NewHeaders(true, false) // first must be a string!
require.Error(t, err)
require.Equal(t, `name must be a string`, err.Error())
}

func TestHeaders_GetHeaders(t *testing.T) {
h, err := NewHeaders()
require.NoError(t, err)
hds, err := h.GetHeaders()
require.NoError(t, err)
require.Equal(t, 0, len(hds))

h, err = NewHeaders("foo", 1.23)
require.NoError(t, err)
hds, err = h.GetHeaders()
require.NoError(t, err)
require.Equal(t, 1, len(hds))
require.Equal(t, "1.23", hds["foo"])

h, err = NewHeaders("foo", nil)
require.NoError(t, err)
_, err = h.GetHeaders()
require.Error(t, err)
require.Equal(t, `unknown value type`, err.Error())

h, err = NewHeaders("foo", func() {
// this does not yield a string
})
require.NoError(t, err)
_, err = h.GetHeaders()
require.Error(t, err)
require.Equal(t, `unknown value type`, err.Error())
}

func TestHeaders_GetSet(t *testing.T) {
h, err := NewHeaders("foo", 1)
require.NoError(t, err)
v, ok := h.Get("foo")
require.True(t, ok)
require.Equal(t, 1, v)
h.Set("foo", 2)
v, ok = h.Get("foo")
require.True(t, ok)
require.Equal(t, 2, v)
_, ok = h.Get("bar")
require.False(t, ok)
}

func TestHeaders_HasDel(t *testing.T) {
h, err := NewHeaders("foo", 1)
require.NoError(t, err)
require.True(t, h.Has("foo"))
h.Del("foo")
require.False(t, h.Has("foo"))
require.False(t, h.Has("bar"))
}

func TestHeaders_Clone(t *testing.T) {
h1, err := NewHeaders("foo", 1)
require.NoError(t, err)
require.True(t, h1.Has("foo"))
require.False(t, h1.Has("bar"))
h2 := h1.Clone()
require.True(t, h2.Has("foo"))
require.False(t, h2.Has("bar"))
h2.Del("foo")
require.True(t, h1.Has("foo"))
require.False(t, h1.Has("bar"))
require.False(t, h2.Has("foo"))
require.False(t, h2.Has("bar"))
h2.Set("bar", 2)
require.True(t, h1.Has("foo"))
require.False(t, h1.Has("bar"))
require.False(t, h2.Has("foo"))
require.True(t, h2.Has("bar"))
}
23 changes: 23 additions & 0 deletions host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package urit

type HostOption interface {
GetAddress() string
}

type Host interface {
HostOption
}

func NewHost(address string) Host {
return &host{
address: address,
}
}

type host struct {
address string
}

func (h *host) GetAddress() string {
return h.address
}
15 changes: 15 additions & 0 deletions host_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package urit

import (
"github.com/stretchr/testify/require"
"testing"
)

func TestNewHost(t *testing.T) {
h := NewHost(`www.example.com`)
require.NotNil(t, h)
rh, ok := h.(*host)
require.True(t, ok)
require.Equal(t, `www.example.com`, rh.address)
require.Equal(t, `www.example.com`, h.GetAddress())
}
15 changes: 15 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ type VarMatchOption interface {

var (
_CaseInsensitiveFixed = &caseInsensitiveFixed{}
_PathRegexCheck = &pathRegexChecker{}
)
var (
CaseInsensitiveFixed = _CaseInsensitiveFixed // is a FixedMatchOption that can be used with templates to allow case-insensitive fixed path parts
PathRegexCheck = _PathRegexCheck // is a VarMatchOption that can be used with Template.PathFrom or Template.RequestFrom to check that vars passed in match regexes for the path part
)

type fixedMatchOptions []FixedMatchOption
Expand Down Expand Up @@ -64,3 +66,16 @@ type caseInsensitiveFixed struct{}
func (o *caseInsensitiveFixed) Match(value string, expected string, pathPos int, vars PathVars) bool {
return value == expected || strings.EqualFold(value, expected)
}

type pathRegexChecker struct{}

func (o *pathRegexChecker) Applicable(value string, position int, name string, rx *regexp.Regexp, rxs string, pathPos int, vars PathVars) bool {
return rx != nil
}

func (o *pathRegexChecker) Match(value string, position int, name string, rx *regexp.Regexp, rxs string, pathPos int, vars PathVars) (string, bool) {
if rx != nil && !rx.MatchString(value) {
return value, false
}
return value, true
}
41 changes: 34 additions & 7 deletions path_part.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package urit

import (
"errors"
"fmt"
"regexp"
"strconv"
Expand Down Expand Up @@ -150,27 +151,53 @@ func (pt *pathPart) pathFrom(tracker *positionsTracker) (string, error) {

type positionsTracker struct {
vars PathVars
position int
varPosition int
pathPosition int
namedPositions map[string]int
varMatches varMatchOptions
}

func (tr *positionsTracker) getVar(pt *pathPart) (string, error) {
if tr.vars.VarsType() == Positions {
if str, ok := tr.vars.GetPositional(tr.position); ok {
tr.position++
useVars := tr.vars
if useVars == nil {
useVars = Positional()
}
var err error
if useVars.VarsType() == Positions {
if str, ok := useVars.GetPositional(tr.varPosition); ok {
tr.varPosition++
return str, nil
}
return "", fmt.Errorf("no var for position %d", tr.position+1)
return "", fmt.Errorf("no var for varPosition %d", tr.varPosition+1)
} else {
np := tr.namedPositions[pt.name]
if str, ok := tr.vars.GetNamed(pt.name, np); ok {
if str, ok := useVars.GetNamed(pt.name, np); ok {
str, err = tr.checkVar(str, pt, tr.varPosition, tr.pathPosition)
if err != nil {
return "", err
}
tr.namedPositions[pt.name] = np + 1
tr.varPosition++
return str, nil
} else if np == 0 {
return "", fmt.Errorf("no var for '%s'", pt.name)
}
return "", fmt.Errorf("no var for '%s' (position %d)", pt.name, np+1)
return "", fmt.Errorf("no var for '%s' (varPosition %d)", pt.name, np+1)
}
}

func (tr *positionsTracker) checkVar(s string, pt *pathPart, pos int, pathPos int) (result string, err error) {
result = s
for _, ck := range tr.varMatches {
if ck.Applicable(result, pos, pt.name, pt.regexp, pt.orgRegexp, pathPos, tr.vars) {
if altS, ok := ck.Match(result, pos, pt.name, pt.regexp, pt.orgRegexp, pathPos, tr.vars); ok {
result = altS
} else {
err = errors.New("no match path var")
}
}
}
return
}

func addRegexHeadAndTail(rx string) string {
Expand Down
Loading

0 comments on commit 08fbf77

Please sign in to comment.