File: toml.go

package info (click to toggle)
python-tomlkit 0.13.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,096 kB
  • sloc: python: 5,985; makefile: 20; sh: 5
file content (131 lines) | stat: -rw-r--r-- 3,162 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package tomltest

import (
	"math"
	"reflect"
)

// cmpTOML consumes the recursive structure of both want and have
// simultaneously. If anything is unequal the result has failed and comparison
// stops.
//
// reflect.DeepEqual could work here, but it won't tell us how the two
// structures are different.
func (r Test) cmpTOML(want, have interface{}) Test {
	if isTomlValue(want) {
		if !isTomlValue(have) {
			return r.fail("Type for key '%s' differs:\n"+
				"  Expected:     %[2]v (%[2]T)\n"+
				"  Your encoder: %[3]v (%[3]T)",
				r.Key, want, have)
		}

		if !deepEqual(want, have) {
			return r.fail("Values for key '%s' differ:\n"+
				"  Expected:     %[2]v (%[2]T)\n"+
				"  Your encoder: %[3]v (%[3]T)",
				r.Key, want, have)
		}
		return r
	}

	switch w := want.(type) {
	case map[string]interface{}:
		return r.cmpTOMLMap(w, have)
	case []interface{}:
		return r.cmpTOMLArrays(w, have)
	default:
		return r.fail("Unrecognized TOML structure: %T", want)
	}
}

func (r Test) cmpTOMLMap(want map[string]interface{}, have interface{}) Test {
	haveMap, ok := have.(map[string]interface{})
	if !ok {
		return r.mismatch("table", want, haveMap)
	}

	// Check that the keys of each map are equivalent.
	for k := range want {
		if _, ok := haveMap[k]; !ok {
			bunk := r.kjoin(k)
			return bunk.fail("Could not find key '%s' in encoder output", bunk.Key)
		}
	}
	for k := range haveMap {
		if _, ok := want[k]; !ok {
			bunk := r.kjoin(k)
			return bunk.fail("Could not find key '%s' in expected output", bunk.Key)
		}
	}

	// Okay, now make sure that each value is equivalent.
	for k := range want {
		if sub := r.kjoin(k).cmpTOML(want[k], haveMap[k]); sub.Failed() {
			return sub
		}
	}
	return r
}

func (r Test) cmpTOMLArrays(want []interface{}, have interface{}) Test {
	// Slice can be decoded to []interface{} for an array of primitives, or
	// []map[string]interface{} for an array of tables.
	//
	// TODO: it would be nicer if it could always decode to []interface{}?
	haveSlice, ok := have.([]interface{})
	if !ok {
		tblArray, ok := have.([]map[string]interface{})
		if !ok {
			return r.mismatch("array", want, have)
		}

		haveSlice = make([]interface{}, len(tblArray))
		for i := range tblArray {
			haveSlice[i] = tblArray[i]
		}
	}

	if len(want) != len(haveSlice) {
		return r.fail("Array lengths differ for key '%s'"+
			"  Expected:     %[2]v (len=%[4]d)\n"+
			"  Your encoder: %[3]v (len=%[5]d)",
			r.Key, want, haveSlice, len(want), len(haveSlice))
	}
	for i := 0; i < len(want); i++ {
		if sub := r.cmpTOML(want[i], haveSlice[i]); sub.Failed() {
			return sub
		}
	}
	return r
}

// reflect.DeepEqual() that deals with NaN != NaN
func deepEqual(want, have interface{}) bool {
	var wantF, haveF float64
	switch f := want.(type) {
	case float32:
		wantF = float64(f)
	case float64:
		wantF = f
	}
	switch f := have.(type) {
	case float32:
		haveF = float64(f)
	case float64:
		haveF = f
	}
	if math.IsNaN(wantF) && math.IsNaN(haveF) {
		return true
	}

	return reflect.DeepEqual(want, have)
}

func isTomlValue(v interface{}) bool {
	switch v.(type) {
	case map[string]interface{}, []interface{}:
		return false
	}
	return true
}