363 lines
8.2 KiB
Go
363 lines
8.2 KiB
Go
|
/*
|
||
|
GoUtils - a library to assist with various Golang-related functions
|
||
|
Copyright (C) 2020 Brent Saner
|
||
|
|
||
|
This program is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
package structutils
|
||
|
|
||
|
import (
|
||
|
`reflect`
|
||
|
`strings`
|
||
|
)
|
||
|
|
||
|
/*
|
||
|
TagToBoolMap takes struct field `field` and tag name `tagName`,
|
||
|
optionally with options `opts`, and returns a map of the tag values.
|
||
|
The tag value string is assumed to be in the form of:
|
||
|
option[,option,option...]
|
||
|
and returns a map[string]bool (map[option]true).
|
||
|
|
||
|
If field does not have tag tagName, m will be nil.
|
||
|
|
||
|
See the TagMap* constants for opts.
|
||
|
*/
|
||
|
func TagToBoolMap(field reflect.StructField, tagName string, opts ...tagMapOpt) (m map[string]bool) {
|
||
|
|
||
|
var s string
|
||
|
var optSplit []string
|
||
|
var tagOpts map[tagMapOpt]bool = getTagMapOpts(opts)
|
||
|
|
||
|
s = field.Tag.Get(tagName)
|
||
|
|
||
|
if strings.TrimSpace(s) == "" {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
optSplit = strings.Split(s, ",")
|
||
|
if optSplit == nil || len(optSplit) == 0 {
|
||
|
return
|
||
|
}
|
||
|
m = make(map[string]bool)
|
||
|
for _, o := range optSplit {
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
o = strings.TrimSpace(o)
|
||
|
}
|
||
|
m[o] = true
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
TagToBoolMapWithValue is like TagToBoolMap but additionally assumes the first value is an "identifier".
|
||
|
The tag value string is assumed to be in the form of:
|
||
|
value,option[,option,option...]
|
||
|
and returns a map[string]bool (map[option]true) with the value.
|
||
|
*/
|
||
|
func TagToBoolMapWithValue(field reflect.StructField, tagName string, opts ...tagMapOpt) (value string, m map[string]bool) {
|
||
|
|
||
|
var s string
|
||
|
var optSplit []string
|
||
|
var tagOpts map[tagMapOpt]bool = getTagMapOpts(opts)
|
||
|
|
||
|
s = field.Tag.Get(tagName)
|
||
|
|
||
|
if strings.TrimSpace(s) == "" {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
optSplit = strings.Split(s, ",")
|
||
|
if optSplit == nil || len(optSplit) == 0 {
|
||
|
return
|
||
|
}
|
||
|
m = make(map[string]bool)
|
||
|
for idx, o := range optSplit {
|
||
|
if idx == 0 {
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
o = strings.TrimSpace(o)
|
||
|
}
|
||
|
value = o
|
||
|
continue
|
||
|
}
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
o = strings.TrimSpace(o)
|
||
|
}
|
||
|
m[o] = true
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
TagToMixedMap combines TagToBoolMap and TagToStringMap.
|
||
|
It takes struct field `field` and tag name `tagName`,
|
||
|
and returns all single-value options in mapBool, and all key/value options in mapString.
|
||
|
|
||
|
If field does not have tag tagName, m will be nil.
|
||
|
|
||
|
See the TagMap* constants for opts.
|
||
|
*/
|
||
|
func TagToMixedMap(field reflect.StructField, tagName string, opts ...tagMapOpt) (mapBool map[string]bool, mapString map[string]string) {
|
||
|
|
||
|
var s string
|
||
|
var valStr string
|
||
|
var split []string
|
||
|
var kvSplit []string
|
||
|
var valSplit []string
|
||
|
var k string
|
||
|
var v string
|
||
|
var tagOpts map[tagMapOpt]bool = getTagMapOpts(opts)
|
||
|
|
||
|
s = field.Tag.Get(tagName)
|
||
|
|
||
|
if strings.TrimSpace(s) == "" {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
split = strings.Split(s, ",")
|
||
|
if split == nil || len(split) == 0 {
|
||
|
return
|
||
|
}
|
||
|
mapBool = make(map[string]bool)
|
||
|
mapString = make(map[string]string)
|
||
|
for _, valStr = range split {
|
||
|
if strings.Contains(valStr, "=") {
|
||
|
kvSplit = strings.SplitN(valStr, "=", 2)
|
||
|
if kvSplit == nil || len(kvSplit) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
k = valSplit[0]
|
||
|
switch len(valSplit) {
|
||
|
case 1:
|
||
|
v = ""
|
||
|
case 2:
|
||
|
v = kvSplit[1]
|
||
|
}
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
k = strings.TrimSpace(k)
|
||
|
v = strings.TrimSpace(v)
|
||
|
}
|
||
|
mapString[k] = v
|
||
|
} else {
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
valStr = strings.TrimSpace(valStr)
|
||
|
}
|
||
|
mapBool[valStr] = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
TagToMixedMapWithValue combines TagToBoolMapWithValue and TagToStringMapWithValue.
|
||
|
It takes struct field `field` and tag name `tagName`,
|
||
|
and returns all single-value options in mapBool, and all key/value options in mapString
|
||
|
along with the first single-value option as value..
|
||
|
|
||
|
If field does not have tag tagName, m will be nil.
|
||
|
|
||
|
See the TagMap* constants for opts.
|
||
|
*/
|
||
|
func TagToMixedMapWithValue(field reflect.StructField, tagName string, opts ...tagMapOpt) (value string, mapBool map[string]bool, mapString map[string]string) {
|
||
|
|
||
|
var s string
|
||
|
var idx int
|
||
|
var valStr string
|
||
|
var split []string
|
||
|
var kvSplit []string
|
||
|
var valSplit []string
|
||
|
var k string
|
||
|
var v string
|
||
|
var tagOpts map[tagMapOpt]bool = getTagMapOpts(opts)
|
||
|
|
||
|
s = field.Tag.Get(tagName)
|
||
|
|
||
|
if strings.TrimSpace(s) == "" {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
split = strings.Split(s, ",")
|
||
|
if split == nil || len(split) == 0 {
|
||
|
return
|
||
|
}
|
||
|
mapBool = make(map[string]bool)
|
||
|
mapString = make(map[string]string)
|
||
|
for idx, valStr = range split {
|
||
|
if idx == 0 {
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
valStr = strings.TrimSpace(valStr)
|
||
|
}
|
||
|
value = valStr
|
||
|
continue
|
||
|
}
|
||
|
if strings.Contains(valStr, "=") {
|
||
|
kvSplit = strings.SplitN(valStr, "=", 2)
|
||
|
if kvSplit == nil || len(kvSplit) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
k = valSplit[0]
|
||
|
switch len(valSplit) {
|
||
|
case 1:
|
||
|
v = ""
|
||
|
case 2:
|
||
|
v = kvSplit[1]
|
||
|
}
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
k = strings.TrimSpace(k)
|
||
|
v = strings.TrimSpace(v)
|
||
|
}
|
||
|
mapString[k] = v
|
||
|
} else {
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
valStr = strings.TrimSpace(valStr)
|
||
|
}
|
||
|
mapBool[valStr] = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
TagToStringMap takes struct field `field` and tag name `tagName`,
|
||
|
optionally with options `opts`, and returns a map of the tag values.
|
||
|
The tag value string is assumed to be in the form of:
|
||
|
key=value[,key=value,key=value...]
|
||
|
and returns a map[string]string (map[key]value).
|
||
|
It is proccessed in order; later duplicate keys overwrite previous ones.
|
||
|
|
||
|
If field does not have tag tagName, m will be nil.
|
||
|
|
||
|
If only a key is provided with no value, the value in the map will be an empty string.
|
||
|
(e.g. "foo,bar=baz" => map[string]string{"foo": "", "bar: "baz"}
|
||
|
|
||
|
See the TagMap* constants for opts.
|
||
|
*/
|
||
|
func TagToStringMap(field reflect.StructField, tagName string, opts ...tagMapOpt) (m map[string]string) {
|
||
|
|
||
|
var s string
|
||
|
var kvSplit []string
|
||
|
var valSplit []string
|
||
|
var k string
|
||
|
var v string
|
||
|
var tagOpts map[tagMapOpt]bool = getTagMapOpts(opts)
|
||
|
|
||
|
s = field.Tag.Get(tagName)
|
||
|
|
||
|
if strings.TrimSpace(s) == "" {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
kvSplit = strings.Split(s, ",")
|
||
|
if kvSplit == nil || len(kvSplit) == 0 {
|
||
|
return
|
||
|
}
|
||
|
for _, kv := range kvSplit {
|
||
|
valSplit = strings.SplitN(kv, "=", 2)
|
||
|
if valSplit == nil || len(valSplit) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
k = valSplit[0]
|
||
|
switch len(valSplit) {
|
||
|
case 1:
|
||
|
v = ""
|
||
|
case 2:
|
||
|
v = valSplit[1]
|
||
|
// It's not possible to have more than 2.
|
||
|
}
|
||
|
if m == nil {
|
||
|
m = make(map[string]string)
|
||
|
}
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
k = strings.TrimSpace(k)
|
||
|
v = strings.TrimSpace(v)
|
||
|
}
|
||
|
m[k] = v
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
TagToStringMapWithValue is like TagToStringMap but additionally assumes the first value is an "identifier".
|
||
|
The tag value string is assumed to be in the form of:
|
||
|
value,key=value[,key=value,key=value...]
|
||
|
and returns a map[string]string (map[key]value) with the value.
|
||
|
*/
|
||
|
func TagToStringMapWithValue(field reflect.StructField, tagName string, opts ...tagMapOpt) (value string, m map[string]string) {
|
||
|
|
||
|
var s string
|
||
|
var kvSplit []string
|
||
|
var valSplit []string
|
||
|
var k string
|
||
|
var v string
|
||
|
var tagOpts map[tagMapOpt]bool = getTagMapOpts(opts)
|
||
|
|
||
|
s = field.Tag.Get(tagName)
|
||
|
|
||
|
if strings.TrimSpace(s) == "" {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
kvSplit = strings.Split(s, ",")
|
||
|
if kvSplit == nil || len(kvSplit) == 0 {
|
||
|
return
|
||
|
}
|
||
|
for idx, kv := range kvSplit {
|
||
|
if idx == 0 {
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
kv = strings.TrimSpace(kv)
|
||
|
}
|
||
|
value = kv
|
||
|
continue
|
||
|
}
|
||
|
valSplit = strings.SplitN(kv, "=", 2)
|
||
|
if valSplit == nil || len(valSplit) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
k = valSplit[0]
|
||
|
switch len(valSplit) {
|
||
|
case 1:
|
||
|
v = ""
|
||
|
case 2:
|
||
|
v = valSplit[1]
|
||
|
// It's not possible to have more than 2.
|
||
|
}
|
||
|
if m == nil {
|
||
|
m = make(map[string]string)
|
||
|
}
|
||
|
if tagOpts[TagMapTrim] {
|
||
|
k = strings.TrimSpace(k)
|
||
|
v = strings.TrimSpace(v)
|
||
|
}
|
||
|
m[k] = v
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func getTagMapOpts(opts []tagMapOpt) (optMap map[tagMapOpt]bool) {
|
||
|
|
||
|
optMap = make(map[tagMapOpt]bool)
|
||
|
|
||
|
if opts == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|