Skip to content

Commit

Permalink
add testcases and fix expanding bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
yosida95 committed Dec 5, 2016
1 parent d5129b3 commit 7e48507
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 59 deletions.
5 changes: 5 additions & 0 deletions escape.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ func pctEncode(w *bytes.Buffer, r rune) {

type escapeFunc func(*bytes.Buffer, string) error

func escapeLiteral(w *bytes.Buffer, v string) error {
w.WriteString(v)
return nil
}

func escapeExceptU(w *bytes.Buffer, v string) error {
for i := 0; i < len(v); {
r, size := utf8.DecodeRuneInString(v[i:])
Expand Down
39 changes: 5 additions & 34 deletions expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,52 +38,23 @@ type expression struct {

func (e *expression) expand(w *bytes.Buffer, values Values) error {
first := true
for i := 0; i < len(e.vars); i++ {
varspec := e.vars[i]
for _, varspec := range e.vars {
value := values.Get(varspec.name)
if value == nil || !value.defined() {
continue
}

if first {
w.WriteString(e.first)
first = false
} else {
w.WriteString(e.sep)
}

value.expand(varspec.maxlen, func(k string, v string, first bool) error {
if !first {
if varspec.explode {
w.WriteString(e.sep)
} else {
w.Write([]byte{','})
}
}
if k == "" && e.named && (first || varspec.explode) {
w.WriteString(varspec.name)
if value.empty() {
w.WriteString(e.ifemp)
return nil
}
w.Write([]byte{'='})
}
if k != "" {
if e.named && varspec.explode {
w.WriteString(k)
} else {
if err := e.escape(w, k); err != nil {
return err
}
}
if e.named || varspec.explode {
w.Write([]byte{'='})
} else {
w.Write([]byte{','})
}
}
if err := value.expand(w, varspec, e); err != nil {
return err
}

return e.escape(w, v)
})
}
return nil
}
51 changes: 51 additions & 0 deletions expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,57 @@ var (
{"X{.keys*}", "X.semi=%3B.dot=..comma=%2C"},
{"X{.empty_keys}", "X"},
{"X{.empty_keys*}", "X"},
// § 3.2.6
{"{/who}", "/fred"},
{"{/who,who}", "/fred/fred"},
{"{/half,who}", "/50%25/fred"},
{"{/who,dub}", "/fred/me%2Ftoo"},
{"{/var}", "/value"},
{"{/var,empty}", "/value/"},
{"{/var,undef}", "/value"},
{"{/var,x}/here", "/value/1024/here"},
{"{/var:1,var}", "/v/value"},
{"{/list}", "/red,green,blue"},
{"{/list*}", "/red/green/blue"},
{"{/list*,path:4}", "/red/green/blue/%2Ffoo"},
{"{/keys}", "/semi,%3B,dot,.,comma,%2C"},
{"{/keys*}", "/semi=%3B/dot=./comma=%2C"},
// § 3.2.7
{"{;who}", ";who=fred"},
{"{;half}", ";half=50%25"},
{"{;empty}", ";empty"},
{"{;v,empty,who}", ";v=6;empty;who=fred"},
{"{;v,bar,who}", ";v=6;who=fred"},
{"{;x,y}", ";x=1024;y=768"},
{"{;x,y,empty}", ";x=1024;y=768;empty"},
{"{;x,y,undef}", ";x=1024;y=768"},
{"{;hello:5}", ";hello=Hello"},
{"{;list}", ";list=red,green,blue"},
{"{;list*}", ";list=red;list=green;list=blue"},
{"{;keys}", ";keys=semi,%3B,dot,.,comma,%2C"},
{"{;keys*}", ";semi=%3B;dot=.;comma=%2C"},
// § 3.2.8
{"{?who}", "?who=fred"},
{"{?half}", "?half=50%25"},
{"{?x,y}", "?x=1024&y=768"},
{"{?x,y,empty}", "?x=1024&y=768&empty="},
{"{?x,y,undef}", "?x=1024&y=768"},
{"{?var:3}", "?var=val"},
{"{?list}", "?list=red,green,blue"},
{"{?list*}", "?list=red&list=green&list=blue"},
{"{?keys}", "?keys=semi,%3B,dot,.,comma,%2C"},
{"{?keys*}", "?semi=%3B&dot=.&comma=%2C"},
// § 3.2.9
{"{&who}", "&who=fred"},
{"{&half}", "&half=50%25"},
{"?fixed=yes{&x}", "?fixed=yes&x=1024"},
{"{&x,y,empty}", "&x=1024&y=768&empty="},
{"{&x,y,undef}", "&x=1024&y=768"},
{"{&var:3}", "&var=val"},
{"{&list}", "&list=red,green,blue"},
{"{&list*}", "&list=red&list=green&list=blue"},
{"{&keys}", "&keys=semi,%3B,dot,.,comma,%2C"},
{"{&keys*}", "&semi=%3B&dot=.&comma=%2C"},
}
testExpressionExpandVarMap = Values{
"count": List("one", "two", "three"),
Expand Down
115 changes: 90 additions & 25 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

package uritemplate

import (
"bytes"
)

type Values map[string]Value

func (v Values) Set(name string, value Value) {
Expand All @@ -21,8 +25,7 @@ func (v Values) Get(name string) Value {

type Value interface {
defined() bool
empty() bool
expand(int, func(k string, v string, first bool) error) error
expand(w *bytes.Buffer, spec varspec, e *expression) error
}

// String returns Value that represents string.
Expand All @@ -36,18 +39,23 @@ func (v valueString) defined() bool {
return true
}

func (v valueString) empty() bool {
return len(v) == 0
}

func (v valueString) expand(maxlen int, found func(string, string, bool) error) error {
if maxlen < 1 {
maxlen = len(v)
}
if max := len(v); maxlen > max {
func (v valueString) expand(w *bytes.Buffer, spec varspec, exp *expression) error {
var maxlen int
if max := len(v); spec.maxlen < 1 || spec.maxlen > max {
maxlen = max
} else {
maxlen = spec.maxlen
}

if exp.named {
w.WriteString(spec.name)
if v == "" {
w.WriteString(exp.ifemp)
return nil
}
w.WriteByte('=')
}
return found("", string(v)[:maxlen], true)
return exp.escape(w, string(v[:maxlen]))
}

// List returns Value that represents list.
Expand All @@ -58,16 +66,40 @@ func List(v ...string) Value {
type valueList []string

func (v valueList) defined() bool {
return len(v) > 0
return v != nil && len(v) > 0
}

func (v valueList) empty() bool {
return !v.defined()
}
func (v valueList) expand(w *bytes.Buffer, spec varspec, exp *expression) error {
var sep string
if spec.explode {
sep = exp.sep
} else {
sep = ","
}

var pre string
var preifemp string
if spec.explode && exp.named {
pre = spec.name + "="
preifemp = spec.name + exp.ifemp
}

func (v valueList) expand(_ int, found func(string, string, bool) error) error {
for i := 0; i < len(v); i++ {
if err := found("", v[i], i == 0); err != nil {
if !spec.explode && exp.named {
w.WriteString(spec.name)
w.WriteByte('=')
}

for i := range v {
if i > 0 {
w.WriteString(sep)
}
if v[i] == "" {
w.WriteString(preifemp)
continue
}
w.WriteString(pre)

if err := exp.escape(w, v[i]); err != nil {
return err
}
}
Expand All @@ -86,16 +118,49 @@ func KV(kv ...string) Value {
type valueKV []string

func (v valueKV) defined() bool {
return len(v) > 0
return v != nil && len(v) > 0
}

func (v valueKV) empty() bool {
return !v.defined()
}
func (v valueKV) expand(w *bytes.Buffer, spec varspec, exp *expression) error {
var sep string
var kvsep string
if spec.explode {
sep = exp.sep
kvsep = "="
} else {
sep = ","
kvsep = ","
}

var ifemp string
var kescape escapeFunc
if spec.explode && exp.named {
ifemp = exp.ifemp
kescape = escapeLiteral
} else {
ifemp = ","
kescape = exp.escape
}

if !spec.explode && exp.named {
w.WriteString(spec.name)
w.WriteByte('=')
}

func (v valueKV) expand(_ int, found func(string, string, bool) error) error {
for i := 0; i < len(v); i += 2 {
if err := found(v[i], v[i+1], i == 0); err != nil {
if i > 0 {
w.WriteString(sep)
}
if err := kescape(w, v[i]); err != nil {
return err
}
if v[i+1] == "" {
w.WriteString(ifemp)
continue
}
w.WriteString(kvsep)

if err := exp.escape(w, v[i+1]); err != nil {
return err
}
}
Expand Down

0 comments on commit 7e48507

Please sign in to comment.