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}