value.go

  1// Copyright (C) 2016 Kohei YOSHIDA. All rights reserved.
  2//
  3// This program is free software; you can redistribute it and/or
  4// modify it under the terms of The BSD 3-Clause License
  5// that can be found in the LICENSE file.
  6
  7package uritemplate
  8
  9import "strings"
 10
 11// A varname containing pct-encoded characters is not the same variable as
 12// a varname with those same characters decoded.
 13//
 14// -- https://tools.ietf.org/html/rfc6570#section-2.3
 15type Values map[string]Value
 16
 17func (v Values) Set(name string, value Value) {
 18	v[name] = value
 19}
 20
 21func (v Values) Get(name string) Value {
 22	if v == nil {
 23		return Value{}
 24	}
 25	return v[name]
 26}
 27
 28type ValueType uint8
 29
 30const (
 31	ValueTypeString = iota
 32	ValueTypeList
 33	ValueTypeKV
 34	valueTypeLast
 35)
 36
 37var valueTypeNames = []string{
 38	"String",
 39	"List",
 40	"KV",
 41}
 42
 43func (vt ValueType) String() string {
 44	if vt < valueTypeLast {
 45		return valueTypeNames[vt]
 46	}
 47	return ""
 48}
 49
 50type Value struct {
 51	T ValueType
 52	V []string
 53}
 54
 55func (v Value) String() string {
 56	if v.Valid() && v.T == ValueTypeString {
 57		return v.V[0]
 58	}
 59	return ""
 60}
 61
 62func (v Value) List() []string {
 63	if v.Valid() && v.T == ValueTypeList {
 64		return v.V
 65	}
 66	return nil
 67}
 68
 69func (v Value) KV() []string {
 70	if v.Valid() && v.T == ValueTypeKV {
 71		return v.V
 72	}
 73	return nil
 74}
 75
 76func (v Value) Valid() bool {
 77	switch v.T {
 78	default:
 79		return false
 80	case ValueTypeString:
 81		return len(v.V) > 0
 82	case ValueTypeList:
 83		return len(v.V) > 0
 84	case ValueTypeKV:
 85		return len(v.V) > 0 && len(v.V)%2 == 0
 86	}
 87}
 88
 89func (v Value) expand(w *strings.Builder, spec varspec, exp *expression) error {
 90	switch v.T {
 91	case ValueTypeString:
 92		val := v.V[0]
 93		var maxlen int
 94		if max := len(val); spec.maxlen < 1 || spec.maxlen > max {
 95			maxlen = max
 96		} else {
 97			maxlen = spec.maxlen
 98		}
 99
100		if exp.named {
101			w.WriteString(spec.name)
102			if val == "" {
103				w.WriteString(exp.ifemp)
104				return nil
105			}
106			w.WriteByte('=')
107		}
108		return exp.escape(w, val[:maxlen])
109	case ValueTypeList:
110		var sep string
111		if spec.explode {
112			sep = exp.sep
113		} else {
114			sep = ","
115		}
116
117		var pre string
118		var preifemp string
119		if spec.explode && exp.named {
120			pre = spec.name + "="
121			preifemp = spec.name + exp.ifemp
122		}
123
124		if !spec.explode && exp.named {
125			w.WriteString(spec.name)
126			w.WriteByte('=')
127		}
128		for i := range v.V {
129			val := v.V[i]
130			if i > 0 {
131				w.WriteString(sep)
132			}
133			if val == "" {
134				w.WriteString(preifemp)
135				continue
136			}
137			w.WriteString(pre)
138
139			if err := exp.escape(w, val); err != nil {
140				return err
141			}
142		}
143	case ValueTypeKV:
144		var sep string
145		var kvsep string
146		if spec.explode {
147			sep = exp.sep
148			kvsep = "="
149		} else {
150			sep = ","
151			kvsep = ","
152		}
153
154		var ifemp string
155		var kescape escapeFunc
156		if spec.explode && exp.named {
157			ifemp = exp.ifemp
158			kescape = escapeLiteral
159		} else {
160			ifemp = ","
161			kescape = exp.escape
162		}
163
164		if !spec.explode && exp.named {
165			w.WriteString(spec.name)
166			w.WriteByte('=')
167		}
168
169		for i := 0; i < len(v.V); i += 2 {
170			if i > 0 {
171				w.WriteString(sep)
172			}
173			if err := kescape(w, v.V[i]); err != nil {
174				return err
175			}
176			if v.V[i+1] == "" {
177				w.WriteString(ifemp)
178				continue
179			}
180			w.WriteString(kvsep)
181
182			if err := exp.escape(w, v.V[i+1]); err != nil {
183				return err
184			}
185		}
186	}
187	return nil
188}
189
190// String returns Value that represents string.
191func String(v string) Value {
192	return Value{
193		T: ValueTypeString,
194		V: []string{v},
195	}
196}
197
198// List returns Value that represents list.
199func List(v ...string) Value {
200	return Value{
201		T: ValueTypeList,
202		V: v,
203	}
204}
205
206// KV returns Value that represents associative list.
207// KV panics if len(kv) is not even.
208func KV(kv ...string) Value {
209	if len(kv)%2 != 0 {
210		panic("uritemplate.go: count of the kv must be even number")
211	}
212	return Value{
213		T: ValueTypeKV,
214		V: kv,
215	}
216}