Commits · ampify

tav  ·  18f2ff5509093cbcf4117a1dc3a50613873e39f6  ·  ampify  ·  github 1312127156

Implemented amp.Header with auto-reflection support. Fuck yeah!

Changes

src/amp/base/header.go
......@@ -0,0 +1,159 @@
1
+// Public Domain (-) 2011 The Ampify Authors.
2
+// See the Ampify UNLICENSE file for details.
3
+
4
+package amp
5
+
6
+import (
7
+	"fmt"
8
+	"os"
9
+	"reflect"
10
+	"unicode"
11
+	"utf8"
12
+)
13
+
14
+type Error struct {
15
+	Message string
16
+}
17
+
18
+func (err *Error) String() string {
19
+	return err.Message
20
+}
21
+
22
+var (
23
+	ErrNotFound     os.Error = &Error{"amp header: key not found"}
24
+	ErrPtrExpected  os.Error = &Error{"amp header: expected pointer type"}
25
+	ErrTypeMismatch os.Error = &Error{"amp header: type mismatch"}
26
+)
27
+
28
+type Header map[string]interface{}
29
+
30
+func (header Header) Get(key string, value interface{}) (err os.Error) {
31
+	if resp, ok := header[key]; ok {
32
+		ev := reflect.ValueOf(value)
33
+		if ev.Type().Kind() != reflect.Ptr {
34
+			return ErrPtrExpected
35
+		}
36
+		ev = ev.Elem()
37
+		et := ev.Type()
38
+		rv := reflect.ValueOf(resp)
39
+		rt := rv.Type()
40
+		if et == rt {
41
+			ev.Set(rv)
42
+			return
43
+		}
44
+		ek := et.Kind()
45
+		rk := rt.Kind()
46
+		if ek == rk && ek == reflect.Slice && rt.Elem().Kind() == reflect.Interface {
47
+			l := rv.Len()
48
+			it := et.Elem()
49
+			sl := reflect.MakeSlice(et, l, l)
50
+			for i := 0; i < l; i++ {
51
+				ii := rv.Index(i)
52
+				iv := ii.Elem()
53
+				if iv.Type() != it {
54
+					return ErrTypeMismatch
55
+				}
56
+				sl.Index(i).Set(iv)
57
+			}
58
+			ev.Set(sl)
59
+			return
60
+		}
61
+		if et.Kind() == reflect.Struct && rt.Kind() == reflect.Map && rt.Key().Kind() == reflect.String && rt.Elem().Kind() == reflect.Interface {
62
+			return setValue(rv, ev)
63
+		}
64
+		fmt.Printf("TYPE: %v\n", rt)
65
+		return ErrTypeMismatch
66
+	}
67
+	return ErrNotFound
68
+}
69
+
70
+func (header Header) GetString(key string) (value string, ok bool) {
71
+	if val, ok := header[key]; ok {
72
+		if value, ok := val.(string); ok {
73
+			return value, true
74
+		}
75
+		if value, ok := val.([]byte); ok {
76
+			return string(value), true
77
+		}
78
+	}
79
+	return
80
+}
81
+
82
+func setValue(src, dst reflect.Value) (err os.Error) {
83
+
84
+	ks := ""
85
+	kv := reflect.ValueOf(&ks).Elem()
86
+
87
+	var fv reflect.Value
88
+
89
+	for _, mapkey := range src.MapKeys() {
90
+		kv.Set(mapkey)
91
+		rune, _ := utf8.DecodeRuneInString(ks)
92
+		if !unicode.IsUpper(rune) {
93
+			continue
94
+		}
95
+		field := dst.FieldByName(ks)
96
+		if !field.IsValid() {
97
+			continue
98
+		}
99
+		ft := field.Type()
100
+		ptr := false
101
+		if ft.Kind() == reflect.Ptr {
102
+			if field.Elem().IsValid() {
103
+			} else {
104
+				fv = reflect.New(ft.Elem())
105
+				field.Set(fv)
106
+				ptr = true
107
+			}
108
+			ft = ft.Elem()
109
+		}
110
+		value := src.MapIndex(mapkey).Elem()
111
+		vt := value.Type()
112
+		if ft.Kind() == vt.Kind() {
113
+			if ft == vt {
114
+				if ptr {
115
+					fv.Elem().Set(value)
116
+				} else {
117
+					field.Set(value)
118
+				}
119
+			} else {
120
+				switch ft.Kind() {
121
+				case reflect.Bool:
122
+					field.SetBool(value.Internal.(bool))
123
+				case reflect.String:
124
+					field.SetString(value.Internal.(string))
125
+				case reflect.Slice:
126
+					if vt.Elem().Kind() != reflect.Interface {
127
+						return ErrTypeMismatch
128
+					}
129
+					l := value.Len()
130
+					it := ft.Elem()
131
+					sl := reflect.MakeSlice(ft, l, l)
132
+					for i := 0; i < l; i++ {
133
+						ii := value.Index(i)
134
+						iv := ii.Elem()
135
+						if iv.Type() != it {
136
+							return ErrTypeMismatch
137
+						}
138
+						sl.Index(i).Set(iv)
139
+					}
140
+					field.Set(sl)
141
+				default:
142
+					return &Error{"amp header: unsupported type: " + ft.Kind().String()}
143
+				}
144
+			}
145
+		} else if ft.Kind() == reflect.Struct && vt.Kind() == reflect.Map && vt.Key().Kind() == reflect.String && vt.Elem().Kind() == reflect.Interface {
146
+			if ptr {
147
+				err = setValue(value, fv.Elem())
148
+			} else {
149
+				err = setValue(value, field)
150
+			}
151
+			if err != nil {
152
+				return err
153
+			}
154
+		}
155
+	}
156
+
157
+	return
158
+
159
+}
src/amp/base/header_test.go
......@@ -0,0 +1,179 @@
1
+// Public Domain (-) 2011 The Ampify Authors.
2
+// See the Ampify UNLICENSE file for details.
3
+
4
+package amp
5
+
6
+import (
7
+	"json"
8
+	"testing"
9
+)
10
+
11
+type city string
12
+
13
+type address struct {
14
+	Street  string
15
+	Town    string
16
+	Country string
17
+}
18
+
19
+type metaStruct struct {
20
+	Address  *address
21
+	City     city
22
+	Duration float64
23
+	Partner  string
24
+	Stylists []string
25
+	Month    *string
26
+	Sexy     string
27
+}
28
+
29
+func TestHeader(t *testing.T) {
30
+
31
+	header := Header{
32
+		"name":       "tav",
33
+		"age":        float64(29),
34
+		"housemates": []string{"Dave", "Mamading", "Sofia", "Sylvana"},
35
+		"meta": map[string]interface{}{
36
+			"City":     "London",
37
+			"Duration": 6,
38
+			"Month":    "March",
39
+			"Address": map[string]interface{}{
40
+				"Town":    "Brixton",
41
+				"Country": "U.K.",
42
+			},
43
+			"Sexy":     "jeffarch",
44
+			"Stylists": []string{"Phillipe", "Diane"},
45
+			"Birth":    float64(1982),
46
+		},
47
+	}
48
+
49
+	name, ok := header.GetString("name")
50
+	if !ok {
51
+		t.Errorf("Couldn't get the header value for 'name'.")
52
+		return
53
+	}
54
+
55
+	if name != "tav" {
56
+		t.Errorf("Go unexpected value for the 'name' header: %q", name)
57
+		return
58
+	}
59
+
60
+	var housemates []string
61
+
62
+	err := header.Get("housemates", &housemates)
63
+	if err != nil {
64
+		t.Errorf("Got an error getting the header value for 'housemates': %s", err)
65
+		return
66
+	}
67
+
68
+	if len(housemates) != 4 {
69
+		t.Errorf("Got an invalid value for the housemates header: %#v", housemates)
70
+		return
71
+	}
72
+
73
+	var age float64
74
+
75
+	header.Get("age", &age)
76
+
77
+	if age != 29 {
78
+		t.Errorf("Got an invalid value for the age header: %f", age)
79
+		return
80
+	}
81
+
82
+	var meta metaStruct
83
+
84
+	err = header.Get("meta", &meta)
85
+	if err != nil {
86
+		t.Errorf("Got an error getting the header value for 'meta': %s", err)
87
+		return
88
+	}
89
+
90
+	if meta.City != "London" {
91
+		t.Errorf("Got an invalid value for meta.City: %v", meta.City)
92
+		return
93
+	}
94
+
95
+	if *meta.Month != "March" {
96
+		t.Errorf("Got an invalid value for *meta.Month: %v", *meta.Month)
97
+		return
98
+	}
99
+
100
+}
101
+
102
+func TestHeaderFromJSON(t *testing.T) {
103
+
104
+	header := &Header{}
105
+
106
+	jsonerr := json.Unmarshal([]byte(`{
107
+		"name":       "tav",
108
+		"age":        29,
109
+		"housemates": ["Dave", "Mamading", "Sofia", "Sylvana"],
110
+		"meta": {
111
+			"City":     "London",
112
+			"Duration": 6,
113
+			"Month":    "March",
114
+			"Address": {
115
+				"Town":    "Brixton",
116
+				"Country": "U.K."
117
+			},
118
+			"Sexy":     "jeffarch",
119
+			"Stylists": ["Phillipe", "Diane"],
120
+			"Birth":    1982
121
+		}
122
+	}`), header)
123
+
124
+	if jsonerr != nil {
125
+		t.Errorf("Error parsing JSON data as a header: %s", jsonerr)
126
+		return
127
+	}
128
+
129
+	name, ok := header.GetString("name")
130
+	if !ok {
131
+		t.Errorf("Couldn't get the header value for 'name'.")
132
+		return
133
+	}
134
+
135
+	if name != "tav" {
136
+		t.Errorf("Go unexpected value for the 'name' header: %q", name)
137
+		return
138
+	}
139
+
140
+	var housemates []string
141
+
142
+	err := header.Get("housemates", &housemates)
143
+	if err != nil {
144
+		t.Errorf("Got an error getting the header value for 'housemates': %s", err)
145
+		return
146
+	}
147
+
148
+	if len(housemates) != 4 {
149
+		t.Errorf("Got an invalid value for the housemates header: %#v", housemates)
150
+		return
151
+	}
152
+
153
+	var age float64
154
+
155
+	header.Get("age", &age)
156
+
157
+	if age != 29 {
158
+		t.Errorf("Got an invalid value for the age header: %d", age)
159
+		return
160
+	}
161
+
162
+	var meta metaStruct
163
+
164
+	err = header.Get("meta", &meta)
165
+	if err != nil {
166
+		t.Errorf("Got an error getting the header value for 'meta': %s", err)
167
+		return
168
+	}
169
+
170
+	if meta.City != "London" {
171
+		t.Errorf("Got an invalid value for meta.City: %v", meta.City)
172
+		return
173
+	}
174
+
175
+	if *meta.Month != "March" {
176
+		t.Errorf("Got an invalid value for *meta.Month: %v", *meta.Month)
177
+	}
178
+
179
+}