-
Notifications
You must be signed in to change notification settings - Fork 1
/
util.go
130 lines (108 loc) · 3.86 KB
/
util.go
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
package addrFmt
import (
"errors"
"reflect"
"sort"
"strings"
)
var addressMemberNameMapping = map[string]string{
"Attention": "attention",
"HouseNumber": "house_number",
"House": "house",
"Road": "road",
"Village": "village",
"Suburb": "suburb",
"City": "city",
"County": "county",
"CountyCode": "county_code",
"Postcode": "postcode",
"StateDistrict": "state_district",
"State": "state",
"StateCode": "state_code",
"Region": "region",
"Island": "island",
"Country": "country",
"CountryCode": "country_code",
"Continent": "continent",
"Archipelago": "archipelago",
"Municipality": "municipality",
"Town": "town",
"Hamlet": "hamlet",
"PostalCity": "postal_city",
"Neighbourhood": "neighbourhood",
"Quarter": "quarter",
"Residential": "residential",
"CityDistrict": "city_district",
} // (struct Name, template Name)
// convert Address to a map of names used in OpenCageData templates and their value
func addressToMap(address *Address) (addressMap, error) {
v := reflect.ValueOf(*address)
addressMap := make(map[string]string, v.NumField())
addressType := v.Type()
for i := 0; i < v.NumField(); i++ {
if !v.Field(i).IsZero() {
fi := addressType.Field(i)
mapFieldName, hasMapping := addressMemberNameMapping[fi.Name]
if hasMapping {
addressMap[mapFieldName] = v.Field(i).String()
} else {
return nil, errors.New(fi.Name + " has no corresponding Name")
}
}
}
return addressMap, nil
}
// MapToAddress Convert map of address components used in OpenCageData templates and their aliases into an Address struct
func MapToAddress(addressMap map[string]string, componentAliases map[string]componentAlias, unknownAsAttention bool) *Address {
// replace common aliases with their main keys used in templates
addressMap = applyComponentAliases(addressMap, componentAliases)
// invert addressMemberNameMapping to map component names to Address struct fields
componentNameAddressFieldMapping := getNameAddressFieldMapping()
var address Address
av := reflect.ValueOf(&address).Elem()
unknownFieldValues := make([]string, 0)
for k, v := range addressMap {
name, hasCorrespondingField := componentNameAddressFieldMapping[k]
if hasCorrespondingField {
av.FieldByName(name).Set(reflect.ValueOf(v))
} else // has no corresponding field and is also not an alias => attention
if _, hasAlias := componentAliases[k]; unknownAsAttention && !hasAlias {
unknownFieldValues = append(unknownFieldValues, v)
}
}
if attention, hasAttention := addressMap["attention"]; hasAttention {
address.Attention = attention
} else if unknownAsAttention {
sort.Strings(unknownFieldValues)
address.Attention = strings.Join(unknownFieldValues, ", ")
}
return &address
}
// this duplicates values from the alias to the given component name mapping
func applyComponentAliases(addressMap addressMap, componentAliases map[string]componentAlias) addressMap {
// helper to remain order provided in config file
aliasRanking := make(map[string]int)
for k, v := range addressMap {
if alias, hasAlias := componentAliases[k]; hasAlias {
if addressMap[alias.componentName] == "" || aliasRanking[alias.componentName] > alias.aliasOrderRank {
addressMap[alias.componentName] = v
aliasRanking[alias.componentName] = alias.aliasOrderRank
}
}
}
return addressMap
}
func getNameAddressFieldMapping() map[string]string {
componentNameAddressFieldMapping := make(map[string]string, len(addressMemberNameMapping))
for k, v := range addressMemberNameMapping {
componentNameAddressFieldMapping[v] = k
}
return componentNameAddressFieldMapping
}
func findTemplate(countryCode string, templates map[string]template) template {
template, hasTemplate := templates[countryCode]
if hasTemplate {
return template
}
return templates["default"]
}