/* 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 . */ 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 }