Skip to content

Commit dd654bc

Browse files
authored
Coerce booleans and integers to other types
Since UCI doesn't really have types anywhere, we have to be very careful about how we parse things. If an option has a value of `"1"`, this could be a boolean, integer, or string; depending on the config it's a part of. Frankly, this is an incredibly bad representation. And it feels like the sort of thing where a language is not designed, but extended over years without taking a step back and removing these awkward cases. We add support for coercing the different types that we can at least identify as possibly being something else. We almost surely are missing some cases here. But, we'll deal with that when they arise. For now, we at least can coerce some booleans and integers to other types. Hopefully, this catches most of the cases. Branch: joneshf/coerce-booleans-and-integers-to-other-types Pull-Request: #116
1 parent f671e60 commit dd654bc

File tree

2 files changed

+106
-4
lines changed

2 files changed

+106
-4
lines changed

lucirpc/options.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,23 +194,42 @@ func (os *Options) UnmarshalJSON(raw []byte) error {
194194
}
195195

196196
type optionBoolean struct {
197-
value bool
197+
original string
198+
value bool
198199
}
199200

200201
func (o *optionBoolean) AsBoolean() (bool, error) {
201202
return o.value, nil
202203
}
203204

204205
func (o *optionBoolean) AsInteger() (int, error) {
205-
return 0, NewOptionTypeMismatchError("an integer", "a boolean")
206+
switch o.original {
207+
case "0":
208+
return 0, nil
209+
210+
case "1":
211+
return 1, nil
212+
213+
default:
214+
return 0, NewOptionTypeMismatchError("an integer", "a boolean")
215+
}
206216
}
207217

208218
func (o *optionBoolean) AsListString() ([]string, error) {
209219
return nil, NewOptionTypeMismatchError("a list of strings", "a boolean")
210220
}
211221

212222
func (o *optionBoolean) AsString() (string, error) {
213-
return "", NewOptionTypeMismatchError("a string", "a boolean")
223+
switch o.original {
224+
case "0", "no", "off", "false", "disabled":
225+
return o.original, nil
226+
227+
case "1", "yes", "on", "true", "enabled":
228+
return o.original, nil
229+
230+
default:
231+
return "", NewOptionTypeMismatchError("a string", "a boolean")
232+
}
214233
}
215234

216235
func (o *optionBoolean) Equal(other *optionBoolean) bool {
@@ -239,6 +258,7 @@ func (o *optionBoolean) UnmarshalJSON(raw []byte) error {
239258
var value bool
240259
err := json.Unmarshal(raw, &value)
241260
if err == nil {
261+
o.original = string(raw)
242262
o.value = value
243263
return nil
244264
}
@@ -253,10 +273,12 @@ func (o *optionBoolean) UnmarshalJSON(raw []byte) error {
253273

254274
switch boolish {
255275
case "1", "yes", "on", "true", "enabled":
276+
o.original = boolish
256277
o.value = true
257278
return nil
258279

259280
case "0", "no", "off", "false", "disabled":
281+
o.original = boolish
260282
o.value = false
261283
return nil
262284

@@ -289,7 +311,7 @@ func (o *optionInteger) AsListString() ([]string, error) {
289311
}
290312

291313
func (o *optionInteger) AsString() (string, error) {
292-
return "", NewOptionTypeMismatchError("a string", "an integer")
314+
return strconv.Itoa(o.value), nil
293315
}
294316

295317
func (o *optionInteger) Equal(other *optionInteger) bool {

lucirpc/options_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,84 @@ func TestOptionsUnmarshalJSON(t *testing.T) {
277277
assert.NilError(t, err)
278278
assert.DeepEqual(t, options, want)
279279
})
280+
281+
t.Run("coerces stringy values", func(t *testing.T) {
282+
// Given
283+
var options lucirpc.Options
284+
rawJSON := `{
285+
"option1": "1",
286+
"option2": "yes",
287+
"option3": "on",
288+
"option4": "true",
289+
"option5": "enabled",
290+
"option6": "0",
291+
"option7": "no",
292+
"option8": "off",
293+
"option9": "false",
294+
"option10": "disabled",
295+
"option11": "31"
296+
}`
297+
298+
// When
299+
err := json.Unmarshal([]byte(rawJSON), &options)
300+
301+
// Then
302+
assert.NilError(t, err)
303+
got1Integer, err := options.GetInteger("option1")
304+
want1Integer := 1
305+
assert.NilError(t, err)
306+
assert.DeepEqual(t, got1Integer, want1Integer)
307+
got1String, err := options.GetString("option1")
308+
want1String := "1"
309+
assert.NilError(t, err)
310+
assert.DeepEqual(t, got1String, want1String)
311+
got2String, err := options.GetString("option2")
312+
want2String := "yes"
313+
assert.NilError(t, err)
314+
assert.DeepEqual(t, got2String, want2String)
315+
got3String, err := options.GetString("option3")
316+
want3String := "on"
317+
assert.NilError(t, err)
318+
assert.DeepEqual(t, got3String, want3String)
319+
got4String, err := options.GetString("option4")
320+
want4String := "true"
321+
assert.NilError(t, err)
322+
assert.DeepEqual(t, got4String, want4String)
323+
got5String, err := options.GetString("option5")
324+
want5String := "enabled"
325+
assert.NilError(t, err)
326+
assert.DeepEqual(t, got5String, want5String)
327+
got6Integer, err := options.GetInteger("option6")
328+
want6Integer := 0
329+
assert.NilError(t, err)
330+
assert.DeepEqual(t, got6Integer, want6Integer)
331+
got6String, err := options.GetString("option6")
332+
want6String := "0"
333+
assert.NilError(t, err)
334+
assert.DeepEqual(t, got6String, want6String)
335+
got7String, err := options.GetString("option7")
336+
want7String := "no"
337+
assert.NilError(t, err)
338+
assert.DeepEqual(t, got7String, want7String)
339+
got8String, err := options.GetString("option8")
340+
want8String := "off"
341+
assert.NilError(t, err)
342+
assert.DeepEqual(t, got8String, want8String)
343+
got9String, err := options.GetString("option9")
344+
want9String := "false"
345+
assert.NilError(t, err)
346+
assert.DeepEqual(t, got9String, want9String)
347+
got10String, err := options.GetString("option10")
348+
want10String := "disabled"
349+
assert.NilError(t, err)
350+
assert.DeepEqual(t, got10String, want10String)
351+
got11Integer, err := options.GetInteger("option11")
352+
want11Integer := 31
353+
assert.NilError(t, err)
354+
assert.DeepEqual(t, got11Integer, want11Integer)
355+
got11String, err := options.GetString("option11")
356+
want11String := "31"
357+
assert.NilError(t, err)
358+
assert.DeepEqual(t, got11String, want11String)
359+
})
280360
}

0 commit comments

Comments
 (0)