v1.5.0
Added structutils
This commit is contained in:
		
							parent
							
								
									70d6c2cbb3
								
							
						
					
					
						commit
						ec686d84c2
					
				@ -12,3 +12,5 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
- DOCS.
 | 
					- DOCS.
 | 
				
			||||||
-- Done, but flesh out.
 | 
					-- Done, but flesh out.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Implement io.Writer interfaces
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										5
									
								
								structutils/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								structutils/consts.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					package structutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						TagMapTrim tagMapOpt = iota
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										362
									
								
								structutils/funcs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								structutils/funcs.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,362 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					   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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								structutils/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								structutils/types.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					package structutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						tagMapOpt uint8
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user