From 1cf977be255107dbc85bf6447eda2cd508aa40af Mon Sep 17 00:00:00 2001 From: geek Date: Tue, 3 Apr 2018 17:37:26 +0900 Subject: [PATCH] project init --- .gitignore | 68 ++ .vscode/launch.json | 32 + .vscode/settings.json | 11 + channel/action.go | 13 + context/context.go | 111 +++ encoding/json/json.go | 50 ++ encoding/json/number.go | 14 + glide.yaml | 2 + net/cidr/range-v4.go | 128 +++ net/cidr/range-v6.go | 43 + net/cidr/range.go | 44 + net/converter/ip-v4.go | 16 + net/converter/ip-v6.go | 15 + net/converter/ip.go | 17 + net/converter/mac.go | 18 + net/gateway/gateway-common.go | 162 ++++ net/gateway/gateway-darwin.go | 16 + net/gateway/gateway-freebsd.go | 16 + net/gateway/gateway-linux.go | 34 + net/gateway/gateway-solaris.go | 16 + net/gateway/gateway-test.go | 248 ++++++ net/gateway/gateway-unimplemented.go | 14 + net/gateway/gateway-windows.go | 16 + net/url/url.go | 20 + reflect/convert.go | 1153 ++++++++++++++++++++++++++ reflect/error.go | 41 + reflect/indirect.go | 31 + reflect/kind.go | 35 + reflect/type.go | 72 ++ 29 files changed, 2456 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 channel/action.go create mode 100644 context/context.go create mode 100644 encoding/json/json.go create mode 100644 encoding/json/number.go create mode 100644 glide.yaml create mode 100644 net/cidr/range-v4.go create mode 100644 net/cidr/range-v6.go create mode 100644 net/cidr/range.go create mode 100644 net/converter/ip-v4.go create mode 100644 net/converter/ip-v6.go create mode 100644 net/converter/ip.go create mode 100644 net/converter/mac.go create mode 100644 net/gateway/gateway-common.go create mode 100644 net/gateway/gateway-darwin.go create mode 100644 net/gateway/gateway-freebsd.go create mode 100644 net/gateway/gateway-linux.go create mode 100644 net/gateway/gateway-solaris.go create mode 100644 net/gateway/gateway-test.go create mode 100644 net/gateway/gateway-unimplemented.go create mode 100644 net/gateway/gateway-windows.go create mode 100644 net/url/url.go create mode 100644 reflect/convert.go create mode 100644 reflect/error.go create mode 100644 reflect/indirect.go create mode 100644 reflect/kind.go create mode 100644 reflect/type.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3733e36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# Created by .ignore support plugin (hsz.mobi) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Go template +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ +.idea/ +*.iml + +vendor/ +glide.lock +.DS_Store +dist/ +debug diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2ca2b1d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug", + "type": "go", + "request": "launch", + "mode": "debug", + "remotePath": "", + "port": 2345, + "host": "127.0.0.1", + "program": "${workspaceRoot}/main.go", + "env": {}, + "args": [], + "showLog": true + }, + { + "name": "File Debug", + "type": "go", + "request": "launch", + "mode": "debug", + "remotePath": "", + "port": 2345, + "host": "127.0.0.1", + "program": "${fileDirname}", + "env": {}, + "args": [], + "showLog": true + } + + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..46c249c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +// Place your settings in this file to overwrite default and user settings. +{ + // Specifies Lint tool name. + "go.lintTool": "gometalinter", + + // Flags to pass to Lint tool (e.g. ["-min_confidence=.8"]) + "go.lintFlags": [ + "--config=${workspaceRoot}/golint.json" + ] + +} \ No newline at end of file diff --git a/channel/action.go b/channel/action.go new file mode 100644 index 0000000..8abc157 --- /dev/null +++ b/channel/action.go @@ -0,0 +1,13 @@ +package channel + +type ActionType int + +const ( + ActionTypeCreate ActionType = iota + ActionTypeDelete + ActionTypeUpdate +) + +type Action struct { + Type ActionType +} diff --git a/context/context.go b/context/context.go new file mode 100644 index 0000000..a549ba2 --- /dev/null +++ b/context/context.go @@ -0,0 +1,111 @@ +package context + +import ( + "reflect" + "sync" +) + +type ContextKey string + +func (c ContextKey) String() string { + return string(c) +} + +func NewContext(parent Context) Context { + c := &defaultContext{ + Context: parent, + } + c.attributes = make(map[interface{}]interface{}) + return c +} + +type Context interface { + Parent() Context + SetAttribute(key interface{}, value interface{}) + GetAttribute(key interface{}) (value interface{}) + RemoveAttribute(key interface{}) + ContainsAttribute(key interface{}) (exist bool) +} + +type defaultContext struct { + Context + attributes map[interface{}]interface{} + + mtx sync.RWMutex +} + +func (dc *defaultContext) Parent() Context { + return dc.Context +} + +func (dc *defaultContext) SetAttribute(key interface{}, value interface{}) { + dc.checkInitialized() + + if key == nil { + panic("nil key") + } + if !reflect.TypeOf(key).Comparable() { + panic("key is not comparable") + } + + dc.mtx.Lock() + defer dc.mtx.Unlock() + + dc.attributes[key] = value +} + +func (dc *defaultContext) GetAttribute(key interface{}) (value interface{}) { + dc.checkInitialized() + + dc.mtx.RLock() + defer dc.mtx.RUnlock() + + if _, ok := dc.attributes[key]; ok { + return dc.attributes[key] + } + + if nil == dc.Context { + return nil + } + return dc.Context.GetAttribute(key) +} + +func (dc *defaultContext) RemoveAttribute(key interface{}) { + dc.checkInitialized() + + dc.mtx.Lock() + defer dc.mtx.Unlock() + + if _, ok := dc.attributes[key]; ok { + delete(dc.attributes, key) + return + } + + if nil == dc.Context { + return + } + + dc.Context.RemoveAttribute(key) +} + +func (dc *defaultContext) ContainsAttribute(key interface{}) (exist bool) { + dc.checkInitialized() + + dc.mtx.RLock() + defer dc.mtx.RUnlock() + + if _, ok := dc.attributes[key]; ok { + return true + } + + if nil == dc.Context { + return false + } + return dc.Context.ContainsAttribute(key) +} + +func (dc *defaultContext) checkInitialized() { + if nil == dc.attributes { + panic("Attribute Manager: must be initialized") + } +} diff --git a/encoding/json/json.go b/encoding/json/json.go new file mode 100644 index 0000000..df6258c --- /dev/null +++ b/encoding/json/json.go @@ -0,0 +1,50 @@ +package json + +import ( + "encoding/json" + "fmt" + "reflect" + + cur "git.loafle.net/commons/util-go/reflect" +) + +// SetValueWithJSONStringArray set the value of json string array +// raw([]byte) is ["1", {"a": 1}, [1, 2]] +// targets([]interface{}) is array of pointer ex) *int, *string, *[], *map, *struct +func SetValueWithJSONStringArray(raw []byte, targets []interface{}) error { + var values []string + if err := json.Unmarshal(raw, &values); err != nil { + return err + } + + if len(targets) != len(values) { + return fmt.Errorf("Count of raw[%d] and targets[%d] is not same", len(values), len(targets)) + } + + for indexI := 0; indexI < len(values); indexI++ { + target := targets[indexI] + value := values[indexI] + + if reflect.Ptr != reflect.TypeOf(target).Kind() { + return fmt.Errorf("Type of target[%d] must be ptr but is %s, value=%s", indexI, reflect.TypeOf(target).Kind(), value) + } + + switch reflect.TypeOf(target).Elem().Kind() { + case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct: + if err := json.Unmarshal([]byte(value), &target); nil != err { + return err + } + case reflect.Ptr: + return fmt.Errorf("Type of target[%d] cannot be double ptr, value=%s", indexI, value) + default: + cv, err := cur.ConvertToType(value, reflect.TypeOf(target).Elem()) + if nil != err { + return fmt.Errorf("Type conversion of value[%s] has been failed to %s[%d]", value, reflect.TypeOf(target).Elem().Kind(), indexI) + } + + reflect.ValueOf(target).Elem().Set(reflect.ValueOf(cv)) + } + } + + return nil +} diff --git a/encoding/json/number.go b/encoding/json/number.go new file mode 100644 index 0000000..936ba86 --- /dev/null +++ b/encoding/json/number.go @@ -0,0 +1,14 @@ +package json + +import ( + "encoding/json" + "strconv" +) + +func NumberToInt(n json.Number) (int, error) { + n64, err := strconv.ParseInt(n.String(), 10, 32) + if nil != err { + return 0, err + } + return int(n64), nil +} diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..1db37ca --- /dev/null +++ b/glide.yaml @@ -0,0 +1,2 @@ +package: git.loafle.net/commons/util-go +import: [] diff --git a/net/cidr/range-v4.go b/net/cidr/range-v4.go new file mode 100644 index 0000000..d7f5f6d --- /dev/null +++ b/net/cidr/range-v4.go @@ -0,0 +1,128 @@ +package cidr + +import ( + "fmt" + "net" + + "git.loafle.net/commons/util-go/net/converter" +) + +type cidrRangeIPv4 struct { + cidrNet *net.IPNet +} + +func (cr *cidrRangeIPv4) Contains(ip net.IP) bool { + return cr.cidrNet.Contains(ip) +} + +func (cr *cidrRangeIPv4) First() net.IP { + nIP := cr.Network() + return cr.Next(nIP) +} + +func (cr *cidrRangeIPv4) Last() net.IP { + bIP := cr.Broadcast() + return cr.Previous(bIP) +} + +func (cr *cidrRangeIPv4) Range() []net.IP { + fIP := cr.First() + if nil == fIP { + return nil + } + lIP := cr.Last() + if nil == lIP { + return nil + } + fNum := converter.IPv4ToInt(fIP.To4()) + lNum := converter.IPv4ToInt(lIP.To4()) + + r := make([]net.IP, 0) + for i := fNum; i <= lNum; i++ { + r = append(r, converter.IntToIPv4(i)) + } + return r +} + +// (!Contains(startIP) || !Contains(endIP)) return nil +// (startIP > endIP) return nil +// (nil != startIP && nil != endIP) return (startIP ~ endIP) + include - exclude +// (nil == startIP || nil == endIP) return include - exclude +func (cr *cidrRangeIPv4) Ranges(startIP net.IP, endIP net.IP, include []net.IP, exclude []net.IP) ([]net.IP, error) { + + res := make(map[int32]bool) + + if nil != startIP && nil != endIP { + if !cr.Contains(startIP) { + return nil, fmt.Errorf("CIDR Range: CIDR not contains start ip[%v]", startIP) + } + if !cr.Contains(endIP) { + return nil, fmt.Errorf("CIDR Range: CIDR not contains end ip[%v]", endIP) + } + sNum := converter.IPv4ToInt(startIP.To4()) + eNum := converter.IPv4ToInt(endIP.To4()) + if sNum > eNum { + return nil, fmt.Errorf("CIDR Range: Start IP[%v] must smaller then End IP[%v]", startIP, endIP) + } + for i := sNum; i <= eNum; i++ { + res[i] = true + } + } + + if nil != include { + for _, in := range include { + iNum := converter.IPv4ToInt(in.To4()) + if _, ok := res[iNum]; !ok { + res[iNum] = true + } + } + } + + if nil != exclude { + for _, ex := range exclude { + iNum := converter.IPv4ToInt(ex.To4()) + if _, ok := res[iNum]; ok { + delete(res, iNum) + } + } + } + + r := make([]net.IP, 0) + for k, _ := range res { + r = append(r, converter.IntToIPv4(k)) + } + + return r, nil +} + +func (cr *cidrRangeIPv4) Broadcast() net.IP { + ip := cr.cidrNet.IP.To4() + bIP := net.IPv4(0, 0, 0, 0).To4() + for i := 0; i < len(bIP); i++ { + bIP[i] = ip[i] | ^cr.cidrNet.Mask[i] + } + return bIP +} + +func (cr *cidrRangeIPv4) Network() net.IP { + ip := cr.cidrNet.IP.To4() + return ip.Mask(cr.cidrNet.Mask) +} + +func (cr *cidrRangeIPv4) Next(ip net.IP) net.IP { + nNum := converter.IPv4ToInt(ip.To4()) + 1 + nIP := converter.IntToIPv4(nNum) + if cr.Contains(nIP) { + return nIP + } + return nil +} + +func (cr *cidrRangeIPv4) Previous(ip net.IP) net.IP { + nNum := converter.IPv4ToInt(ip.To4()) - 1 + nIP := converter.IntToIPv4(nNum) + if cr.Contains(nIP) { + return nIP + } + return nil +} diff --git a/net/cidr/range-v6.go b/net/cidr/range-v6.go new file mode 100644 index 0000000..440ad0b --- /dev/null +++ b/net/cidr/range-v6.go @@ -0,0 +1,43 @@ +package cidr + +import "net" + +type cidrRangeIPv6 struct { + cidrNet *net.IPNet +} + +func (cr *cidrRangeIPv6) Contains(ip net.IP) bool { + return cr.cidrNet.Contains(ip) +} + +func (cr *cidrRangeIPv6) First() net.IP { + return nil +} + +func (cr *cidrRangeIPv6) Last() net.IP { + return nil +} + +func (cr *cidrRangeIPv6) Range() []net.IP { + return nil +} + +func (cr *cidrRangeIPv6) Ranges(startIP net.IP, endIP net.IP, include []net.IP, exclude []net.IP) ([]net.IP, error) { + return nil, nil +} + +func (cr *cidrRangeIPv6) Broadcast() net.IP { + return nil +} + +func (cr *cidrRangeIPv6) Network() net.IP { + return nil +} + +func (cr *cidrRangeIPv6) Next(ip net.IP) net.IP { + return nil +} + +func (cr *cidrRangeIPv6) Previous(ip net.IP) net.IP { + return nil +} diff --git a/net/cidr/range.go b/net/cidr/range.go new file mode 100644 index 0000000..9d908ee --- /dev/null +++ b/net/cidr/range.go @@ -0,0 +1,44 @@ +package cidr + +import ( + "fmt" + "net" +) + +func NewCIDRRanger(cidr string) (CIDRRanger, error) { + _, nIPNet, err := net.ParseCIDR(cidr) + if nil != err { + return nil, err + } + + switch len(nIPNet.IP) { + case net.IPv4len: + cr := &cidrRangeIPv4{ + cidrNet: nIPNet, + } + return cr, nil + case net.IPv6len: + cr := &cidrRangeIPv6{ + cidrNet: nIPNet, + } + return cr, nil + default: + return nil, fmt.Errorf("Net: not supported IP length") + } +} + +type CIDRRanger interface { + Contains(ip net.IP) bool + First() net.IP + Last() net.IP + Range() []net.IP + // (!Contains(startIP) || !Contains(endIP)) return error + // (startIP > endIP) return error + // (nil != startIP && nil != endIP) return (startIP ~ endIP) + include - exclude + // (nil == startIP || nil == endIP) return include - exclude + Ranges(startIP net.IP, endIP net.IP, include []net.IP, exclude []net.IP) ([]net.IP, error) + Broadcast() net.IP + Network() net.IP + Next(ip net.IP) net.IP + Previous(ip net.IP) net.IP +} diff --git a/net/converter/ip-v4.go b/net/converter/ip-v4.go new file mode 100644 index 0000000..694e778 --- /dev/null +++ b/net/converter/ip-v4.go @@ -0,0 +1,16 @@ +package converter + +import ( + "encoding/binary" + "net" +) + +func IPv4ToInt(ip net.IP) int32 { + return int32(binary.BigEndian.Uint32(ip.To4())) +} + +func IntToIPv4(n int32) net.IP { + b := make([]byte, 4) + binary.BigEndian.PutUint32(b, uint32(n)) + return net.IP(b) +} diff --git a/net/converter/ip-v6.go b/net/converter/ip-v6.go new file mode 100644 index 0000000..676ad64 --- /dev/null +++ b/net/converter/ip-v6.go @@ -0,0 +1,15 @@ +package converter + +import ( + "net" +) + +// Not implemented +func IPv6ToInt(ip net.IP) int32 { + return 0 +} + +// Not implemented +func IntToIPv6(n int32) net.IP { + return nil +} diff --git a/net/converter/ip.go b/net/converter/ip.go new file mode 100644 index 0000000..5768225 --- /dev/null +++ b/net/converter/ip.go @@ -0,0 +1,17 @@ +package converter + +import ( + "fmt" + "net" +) + +func IPToInt(ip net.IP) (int32, error) { + switch len(ip) { + case net.IPv4len: + return IPv4ToInt(ip), nil + case net.IPv6len: + return IPv6ToInt(ip), nil + default: + return 0, fmt.Errorf("Net: not supported IP length") + } +} diff --git a/net/converter/mac.go b/net/converter/mac.go new file mode 100644 index 0000000..45777b5 --- /dev/null +++ b/net/converter/mac.go @@ -0,0 +1,18 @@ +package converter + +import ( + "net" + "regexp" + "strconv" +) + +var ( + macStripRegexp = regexp.MustCompile(`[^a-fA-F0-9]`) +) + +func MacToUint64(hwAddr net.HardwareAddr) (uint64, error) { + mac := hwAddr.String() + hex := macStripRegexp.ReplaceAllLiteralString(mac, "") + + return strconv.ParseUint(hex, 16, 64) +} diff --git a/net/gateway/gateway-common.go b/net/gateway/gateway-common.go new file mode 100644 index 0000000..532ee32 --- /dev/null +++ b/net/gateway/gateway-common.go @@ -0,0 +1,162 @@ +package gateway + +import ( + "errors" + "net" + "strings" +) + +var errNoGateway = errors.New("no gateway found") + +func parseWindowsRoutePrint(output []byte) (net.IP, string, error) { + // Windows route output format is always like this: + // =========================================================================== + // Active Routes: + // Network Destination Netmask Gateway Interface Metric + // 0.0.0.0 0.0.0.0 192.168.1.1 192.168.1.100 20 + // =========================================================================== + // I'm trying to pick the active route, + // then jump 2 lines and pick the third IP + // Not using regex because output is quite standard from Windows XP to 8 (NEEDS TESTING) + lines := strings.Split(string(output), "\n") + for idx, line := range lines { + if strings.HasPrefix(line, "Active Routes:") { + if len(lines) <= idx+2 { + return nil, "", errNoGateway + } + + fields := strings.Fields(lines[idx+2]) + if len(fields) < 3 { + return nil, "", errNoGateway + } + + ip := net.ParseIP(fields[2]) + if ip != nil { + return ip, fields[3], nil + } + } + } + return nil, "", errNoGateway +} + +func parseLinuxIPRoute(output []byte) (net.IP, string, error) { + // Linux '/usr/bin/ip route show' format looks like this: + // default via 192.168.178.1 dev wlp3s0 metric 303 + // 192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303 + lines := strings.Split(string(output), "\n") + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) >= 3 && fields[0] == "default" { + ip := net.ParseIP(fields[2]) + if ip != nil { + return ip, fields[4], nil + } + } + } + + return nil, "", errNoGateway +} + +func parseLinuxRoute(output []byte) (net.IP, string, error) { + // Linux route out format is always like this: + // Kernel IP routing table + // Destination Gateway Genmask Flags Metric Ref Use Iface + // 0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0 + lines := strings.Split(string(output), "\n") + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) >= 2 && fields[0] == "0.0.0.0" { + ip := net.ParseIP(fields[1]) + if ip != nil { + return ip, fields[7], nil + } + } + } + + return nil, "", errNoGateway +} + +func parseDarwinRouteGet(output []byte) (net.IP, string, error) { + // Darwin route out format is always like this: + // route to: default + // destination: default + // mask: default + // gateway: 192.168.1.1 + // interface: tun0 + // flags: + // lines := strings.Split(string(output), "\n") + // for _, line := range lines { + // fields := strings.Fields(line) + // if len(fields) >= 2 && fields[0] == "gateway:" { + // ip := net.ParseIP(fields[1]) + // if ip != nil { + // return ip, "", nil + // } + // } + // } + + // Darwin route out format is always like this: + // Internet: + // Destination Gateway Flags Refs Use Netif Expire + // default 192.168.10.254 UGSc 194 0 en3 + // 127 127.0.0.1 UCS 0 429 lo0 + // 127.0.0.1 127.0.0.1 UH 1 587632 lo0 + // 169.254 link#7 UCS 0 0 en3 + // 192.168.10 link#7 UCS 4 0 en3 + // 192.168.10.1 0:11:32:7f:20:61 UHLWIi 1 202 en3 1065 + // 224.0.0/4 link#7 UmCS 3 0 en3 + // 224.0.0.251 1:0:5e:0:0:fb UHmLWI 0 2325 en3 + // 239.192.152.143 1:0:5e:40:98:8f UHmLWI 0 22892 en3 + // 239.255.255.250 1:0:5e:7f:ff:fa UHmLWI 0 15988 en3 + // 255.255.255.255/32 link#7 UCS 0 0 en3 + + // Internet6: + // Destination Gateway Flags Netif Expire + // default fe80::%utun0 UGcI utun0 + // default fe80::%utun1 UGcI utun1 + // default fe80::%utun2 UGcI utun2 + // default fe80::%utun3 UGcI utun3 + outputLines := strings.Split(string(output), "\n") + for _, line := range outputLines { + fields := strings.Fields(line) + if len(fields) >= 2 && fields[0] == "default" { + ip := net.ParseIP(fields[1]) + if ip != nil { + return ip, fields[5], nil + } + } + } + return nil, "", errNoGateway +} + +func parseBSDSolarisNetstat(output []byte) (net.IP, string, error) { + // netstat -rn produces the following on FreeBSD: + // Routing tables + // + // Internet: + // Destination Gateway Flags Netif Expire + // default 10.88.88.2 UGS em0 + // 10.88.88.0/24 link#1 U em0 + // 10.88.88.148 link#1 UHS lo0 + // 127.0.0.1 link#2 UH lo0 + // + // Internet6: + // Destination Gateway Flags Netif Expire + // ::/96 ::1 UGRS lo0 + // ::1 link#2 UH lo0 + // ::ffff:0.0.0.0/96 ::1 UGRS lo0 + // fe80::/10 ::1 UGRS lo0 + // ... + outputLines := strings.Split(string(output), "\n") + for _, line := range outputLines { + fields := strings.Fields(line) + if len(fields) >= 2 && fields[0] == "default" { + ip := net.ParseIP(fields[1]) + if ip != nil { + return ip, fields[3], nil + } + } + } + + return nil, "", errNoGateway +} diff --git a/net/gateway/gateway-darwin.go b/net/gateway/gateway-darwin.go new file mode 100644 index 0000000..aad613d --- /dev/null +++ b/net/gateway/gateway-darwin.go @@ -0,0 +1,16 @@ +package gateway + +import ( + "net" + "os/exec" +) + +func DiscoverGateway() (ip net.IP, iface string, err error) { + routeCmd := exec.Command("netstat", "-rn") + output, err := routeCmd.CombinedOutput() + if err != nil { + return nil, "", err + } + + return parseDarwinRouteGet(output) +} diff --git a/net/gateway/gateway-freebsd.go b/net/gateway/gateway-freebsd.go new file mode 100644 index 0000000..035c22e --- /dev/null +++ b/net/gateway/gateway-freebsd.go @@ -0,0 +1,16 @@ +package gateway + +import ( + "net" + "os/exec" +) + +func DiscoverGateway() (ip net.IP, iface string, err error) { + routeCmd := exec.Command("netstat", "-rn") + output, err := routeCmd.CombinedOutput() + if err != nil { + return nil, "", err + } + + return parseBSDSolarisNetstat(output) +} diff --git a/net/gateway/gateway-linux.go b/net/gateway/gateway-linux.go new file mode 100644 index 0000000..daec1c7 --- /dev/null +++ b/net/gateway/gateway-linux.go @@ -0,0 +1,34 @@ +package gateway + +import ( + "net" + "os/exec" +) + +func DiscoverGateway() (ip net.IP, iface string, err error) { + ip, iface, err = discoverGatewayUsingRoute() + if err != nil { + ip, iface, err = discoverGatewayUsingIp() + } + return +} + +func discoverGatewayUsingIp() (net.IP, string, error) { + routeCmd := exec.Command("ip", "route", "show") + output, err := routeCmd.CombinedOutput() + if err != nil { + return nil, "", err + } + + return parseLinuxIPRoute(output) +} + +func discoverGatewayUsingRoute() (net.IP, string, error) { + routeCmd := exec.Command("route", "-n") + output, err := routeCmd.CombinedOutput() + if err != nil { + return nil, "", err + } + + return parseLinuxRoute(output) +} diff --git a/net/gateway/gateway-solaris.go b/net/gateway/gateway-solaris.go new file mode 100644 index 0000000..035c22e --- /dev/null +++ b/net/gateway/gateway-solaris.go @@ -0,0 +1,16 @@ +package gateway + +import ( + "net" + "os/exec" +) + +func DiscoverGateway() (ip net.IP, iface string, err error) { + routeCmd := exec.Command("netstat", "-rn") + output, err := routeCmd.CombinedOutput() + if err != nil { + return nil, "", err + } + + return parseBSDSolarisNetstat(output) +} diff --git a/net/gateway/gateway-test.go b/net/gateway/gateway-test.go new file mode 100644 index 0000000..e519173 --- /dev/null +++ b/net/gateway/gateway-test.go @@ -0,0 +1,248 @@ +package gateway + +import ( + "net" + "testing" +) + +type testcase struct { + output []byte + ok bool + gateway string +} + +func TestParseWindowsRoutePrint(t *testing.T) { + correctData := []byte(` +IPv4 Route Table +=========================================================================== +Active Routes: +Network Destination Netmask Gateway Interface Metric + 0.0.0.0 0.0.0.0 10.88.88.2 10.88.88.149 10 +=========================================================================== +Persistent Routes: +`) + randomData := []byte(` +Lorem ipsum dolor sit amet, consectetur adipiscing elit, +sed do eiusmod tempor incididunt ut labore et dolore magna +aliqua. Ut enim ad minim veniam, quis nostrud exercitation +`) + noRoute := []byte(` +IPv4 Route Table +=========================================================================== +Active Routes: +`) + badRoute1 := []byte(` +IPv4 Route Table +=========================================================================== +Active Routes: +=========================================================================== +Persistent Routes: +`) + badRoute2 := []byte(` +IPv4 Route Table +=========================================================================== +Active Routes: +Network Destination Netmask Gateway Interface Metric + 0.0.0.0 0.0.0.0 foo 10.88.88.149 10 +=========================================================================== +Persistent Routes: +`) + + testcases := []testcase{ + {correctData, true, "10.88.88.2"}, + {randomData, false, ""}, + {noRoute, false, ""}, + {badRoute1, false, ""}, + {badRoute2, false, ""}, + } + + test(t, testcases, parseWindowsRoutePrint) +} + +func TestParseLinuxIPRoutePrint(t *testing.T) { + correctData := []byte(` +default via 192.168.178.1 dev wlp3s0 metric 303 +192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303 +`) + randomData := []byte(` +test +Lorem ipsum dolor sit amet, consectetur adipiscing elit, +sed do eiusmod tempor incididunt ut labore et dolore magna +aliqua. Ut enim ad minim veniam, quis nostrud exercitation +`) + noRoute := []byte(` +192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303 +`) + badRoute := []byte(` +default via foo dev wlp3s0 metric 303 +192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303 +`) + + testcases := []testcase{ + {correctData, true, "192.168.178.1"}, + {randomData, false, ""}, + {noRoute, false, ""}, + {badRoute, false, ""}, + } + + test(t, testcases, parseLinuxIPRoute) +} + +func TestParseLinuxRoutePrint(t *testing.T) { + correctData := []byte(` +Kernel IP routing table +Destination Gateway Genmask Flags Metric Ref Use Iface +0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0 +`) + randomData := []byte(` +test +Lorem ipsum dolor sit amet, consectetur adipiscing elit, +sed do eiusmod tempor incididunt ut labore et dolore magna +aliqua. Ut enim ad minim veniam, quis nostrud exercitation +`) + noRoute := []byte(` +Kernel IP routing table +Destination Gateway Genmask Flags Metric Ref Use Iface +`) + badRoute := []byte(` +Kernel IP routing table +Destination Gateway Genmask Flags Metric Ref Use Iface +0.0.0.0 foo 0.0.0.0 UG 0 0 0 eth0 +`) + + testcases := []testcase{ + {correctData, true, "192.168.1.1"}, + {randomData, false, ""}, + {noRoute, false, ""}, + {badRoute, false, ""}, + } + + test(t, testcases, parseLinuxRoute) +} + +func TestParseDarwinRouteGet(t *testing.T) { + correctData := []byte(` + route to: 0.0.0.0 +destination: default + mask: default + gateway: 172.16.32.1 + interface: en0 + flags: + recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire + 0 0 0 0 0 0 1500 0 +`) + randomData := []byte(` +test +Lorem ipsum dolor sit amet, consectetur adipiscing elit, +sed do eiusmod tempor incididunt ut labore et dolore magna +aliqua. Ut enim ad minim veniam, quis nostrud exercitation +`) + noRoute := []byte(` + route to: 0.0.0.0 +destination: default + mask: default +`) + badRoute := []byte(` + route to: 0.0.0.0 +destination: default + mask: default + gateway: foo + interface: en0 + flags: + recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire + 0 0 0 0 0 0 1500 0 +`) + + testcases := []testcase{ + {correctData, true, "172.16.32.1"}, + {randomData, false, ""}, + {noRoute, false, ""}, + {badRoute, false, ""}, + } + + test(t, testcases, parseDarwinRouteGet) +} + +func TestParseBSDSolarisNetstat(t *testing.T) { + correctDataFreeBSD := []byte(` +Routing tables + +Internet: +Destination Gateway Flags Netif Expire +default 10.88.88.2 UGS em0 +10.88.88.0/24 link#1 U em0 +10.88.88.148 link#1 UHS lo0 +127.0.0.1 link#2 UH lo0 + +Internet6: +Destination Gateway Flags Netif Expire +::/96 ::1 UGRS lo0 +::1 link#2 UH lo0 +::ffff:0.0.0.0/96 ::1 UGRS lo0 +fe80::/10 ::1 UGRS lo0 +`) + correctDataSolaris := []byte(` +Routing Table: IPv4 + Destination Gateway Flags Ref Use Interface +-------------------- -------------------- ----- ----- ---------- --------- +default 172.16.32.1 UG 2 76419 net0 +127.0.0.1 127.0.0.1 UH 2 36 lo0 +172.16.32.0 172.16.32.17 U 4 8100 net0 + +Routing Table: IPv6 + Destination/Mask Gateway Flags Ref Use If +--------------------------- --------------------------- ----- --- ------- ----- +::1 ::1 UH 3 75382 lo0 +2001:470:deeb:32::/64 2001:470:deeb:32::17 U 3 2744 net0 +fe80::/10 fe80::6082:52ff:fedc:7df0 U 3 8430 net0 +`) + randomData := []byte(` +Lorem ipsum dolor sit amet, consectetur adipiscing elit, +sed do eiusmod tempor incididunt ut labore et dolore magna +aliqua. Ut enim ad minim veniam, quis nostrud exercitation +`) + noRoute := []byte(` +Internet: +Destination Gateway Flags Netif Expire +10.88.88.0/24 link#1 U em0 +10.88.88.148 link#1 UHS lo0 +127.0.0.1 link#2 UH lo0 +`) + badRoute := []byte(` +Internet: +Destination Gateway Flags Netif Expire +default foo UGS em0 +10.88.88.0/24 link#1 U em0 +10.88.88.148 link#1 UHS lo0 +127.0.0.1 link#2 UH lo0 +`) + + testcases := []testcase{ + {correctDataFreeBSD, true, "10.88.88.2"}, + {correctDataSolaris, true, "172.16.32.1"}, + {randomData, false, ""}, + {noRoute, false, ""}, + {badRoute, false, ""}, + } + + test(t, testcases, parseBSDSolarisNetstat) +} + +func test(t *testing.T, testcases []testcase, fn func([]byte) (net.IP, string, error)) { + for i, tc := range testcases { + net, iface, err := fn(tc.output) + if tc.ok { + if err != nil { + t.Errorf("Unexpected error in test #%d: %v", i, err) + } + if net.String() != tc.gateway { + t.Errorf("Unexpected gateway address %v != %s", net, tc.gateway) + } + if "" == iface { + t.Errorf("Unexpected interface") + } + } else if err == nil { + t.Errorf("Unexpected nil error in test #%d", i) + } + } +} diff --git a/net/gateway/gateway-unimplemented.go b/net/gateway/gateway-unimplemented.go new file mode 100644 index 0000000..676553e --- /dev/null +++ b/net/gateway/gateway-unimplemented.go @@ -0,0 +1,14 @@ +// +build !darwin,!linux,!windows,!solaris,!freebsd + +package gateway + +import ( + "fmt" + "net" + "runtime" +) + +func DiscoverGateway() (ip net.IP, iface string, err error) { + err = fmt.Errorf("DiscoverGateway not implemented for OS %s", runtime.GOOS) + return +} diff --git a/net/gateway/gateway-windows.go b/net/gateway/gateway-windows.go new file mode 100644 index 0000000..685f2c2 --- /dev/null +++ b/net/gateway/gateway-windows.go @@ -0,0 +1,16 @@ +package gateway + +import ( + "net" + "os/exec" +) + +func DiscoverGateway() (ip net.IP, iface string, err error) { + routeCmd := exec.Command("route", "print", "0.0.0.0") + output, err := routeCmd.CombinedOutput() + if err != nil { + return nil, "", err + } + + return parseWindowsRoutePrint(output) +} diff --git a/net/url/url.go b/net/url/url.go new file mode 100644 index 0000000..0ea04a6 --- /dev/null +++ b/net/url/url.go @@ -0,0 +1,20 @@ +package url + +import ( + "net/url" + "path" +) + +// Join is concat URL string and path +// ex) http://127.0.0.1/ and /entry +func Join(u string, p string) (string, error) { + var err error + var rURL *url.URL + + if rURL, err = url.Parse(u); nil != err { + return "", err + } + + rURL.Path = path.Join(rURL.Path, p) + return rURL.String(), nil +} diff --git a/reflect/convert.go b/reflect/convert.go new file mode 100644 index 0000000..3e43fb1 --- /dev/null +++ b/reflect/convert.go @@ -0,0 +1,1153 @@ +package reflect + +import ( + "reflect" + "strconv" +) + +func ConvertToType(v interface{}, t reflect.Type) (interface{}, error) { + var ( + tt = reflect.TypeOf(v) + vv = reflect.ValueOf(v) + r interface{} + k int + err error + ) + + if t.Kind() == Interface { + return v, nil + } + + switch tt.Kind() { + case Slice: + switch t.Kind() { + case Slice: + var ( + res = reflect.MakeSlice( + t, + vv.Len(), + vv.Cap(), + ) + ) + + for k = 0; k < vv.Len(); k++ { + r, err = ConvertToType( + vv.Index(k).Interface(), + t.Elem(), + ) + if err != nil { + return nil, err + } + res.Index(k).Set(reflect.ValueOf(r)) + } + + return res.Interface(), nil + } + case Map: + switch t.Kind() { + case Map: + var ( + res = reflect.MakeMap(t) + ck interface{} + ) + for _, k := range vv.MapKeys() { + ck, err = ConvertToType( + k.Interface(), + t.Key(), + ) + if err != nil { + return nil, err + } + + r, err = ConvertToType( + vv.MapIndex(k).Interface(), + t.Elem(), + ) + if err != nil { + return nil, err + } + + res.SetMapIndex( + reflect.ValueOf(ck), + reflect.ValueOf(r), + ) + } + + return res.Interface(), nil + } + } + + switch t { + case TypeBool: + return ConvertToBool(v) + case TypeInt: + return ConvertToInt(v) + case TypeInt8: + return ConvertToInt8(v) + case TypeInt16: + return ConvertToInt16(v) + case TypeInt32: + return ConvertToInt32(v) + case TypeInt64: + return ConvertToInt64(v) + case TypeUint: + return ConvertToUint(v) + case TypeUint8: + return ConvertToUint8(v) + case TypeUint16: + return ConvertToUint16(v) + case TypeUint32: + return ConvertToUint32(v) + case TypeUint64: + return ConvertToUint64(v) + case TypeFloat32: + return ConvertToFloat32(v) + case TypeFloat64: + return ConvertToFloat64(v) + case TypeComplex64: + return ConvertToComplex64(v) + case TypeComplex128: + return ConvertToComplex128(v) + case TypeString: + return ConvertToString(v) + default: + return nil, NewErrCanNotConvertType( + v, + tt, + t, + ) + } +} + +func ConvertToBool(vv interface{}) (bool, error) { + var ( + v = Indirect(vv) + ) + + switch value := v.(type) { + case bool: + return value, nil + case nil: + return false, nil + case int: + if value != 0 { + return true, nil + } + return false, nil + case string: + if value == "" { + return false, nil + } + return strconv.ParseBool(value) + default: + return false, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeBool, + ) + } +} + +func ConvertToInt(vv interface{}) (int, error) { + var ( + v = Indirect(vv) + res int64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return value, nil + case int8: + return int(value), nil + case int16: + return int(value), nil + case int32: + return int(value), nil + case int64: + return int(value), nil + case uint: + return int(value), nil + case uint8: + return int(value), nil + case uint16: + return int(value), nil + case uint32: + return int(value), nil + case uint64: + return int(value), nil + case float32: + return int(value), nil + case float64: + return int(value), nil + case complex64: + return int(real(value)), nil + case complex128: + return int(real(value)), nil + case uintptr: + return int(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseInt(value, 0, 0) + if err == nil { + return int(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeInt, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeInt, + ) + } +} + +func ConvertToInt8(vv interface{}) (int8, error) { + var ( + v = Indirect(vv) + res int64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return int8(value), nil + case int8: + return value, nil + case int16: + return int8(value), nil + case int32: + return int8(value), nil + case int64: + return int8(value), nil + case uint: + return int8(value), nil + case uint8: + return int8(value), nil + case uint16: + return int8(value), nil + case uint32: + return int8(value), nil + case uint64: + return int8(value), nil + case float32: + return int8(value), nil + case float64: + return int8(value), nil + case complex64: + return int8(real(value)), nil + case complex128: + return int8(real(value)), nil + case uintptr: + return int8(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseInt(value, 0, 8) + if err == nil { + return int8(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeInt8, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeInt8, + ) + } +} + +func ConvertToInt16(vv interface{}) (int16, error) { + var ( + v = Indirect(vv) + res int64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return int16(value), nil + case int8: + return int16(value), nil + case int16: + return value, nil + case int32: + return int16(value), nil + case int64: + return int16(value), nil + case uint: + return int16(value), nil + case uint8: + return int16(value), nil + case uint16: + return int16(value), nil + case uint32: + return int16(value), nil + case uint64: + return int16(value), nil + case float32: + return int16(value), nil + case float64: + return int16(value), nil + case complex64: + return int16(real(value)), nil + case complex128: + return int16(real(value)), nil + case uintptr: + return int16(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseInt(value, 0, 16) + if err == nil { + return int16(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeInt16, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeInt16, + ) + } +} + +func ConvertToInt32(vv interface{}) (int32, error) { + var ( + v = Indirect(vv) + res int64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return int32(value), nil + case int8: + return int32(value), nil + case int16: + return int32(value), nil + case int32: + return value, nil + case int64: + return int32(value), nil + case uint: + return int32(value), nil + case uint8: + return int32(value), nil + case uint16: + return int32(value), nil + case uint32: + return int32(value), nil + case uint64: + return int32(value), nil + case float32: + return int32(value), nil + case float64: + return int32(value), nil + case complex64: + return int32(real(value)), nil + case complex128: + return int32(real(value)), nil + case uintptr: + return int32(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseInt(value, 0, 32) + if err == nil { + return int32(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeInt32, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeInt32, + ) + } +} + +func ConvertToInt64(vv interface{}) (int64, error) { + var ( + v = Indirect(vv) + res int64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return int64(value), nil + case int8: + return int64(value), nil + case int16: + return int64(value), nil + case int32: + return int64(value), nil + case int64: + return value, nil + case uint: + return int64(value), nil + case uint8: + return int64(value), nil + case uint16: + return int64(value), nil + case uint32: + return int64(value), nil + case uint64: + return int64(value), nil + case float32: + return int64(value), nil + case float64: + return int64(value), nil + case complex64: + return int64(real(value)), nil + case complex128: + return int64(real(value)), nil + case uintptr: + return int64(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseInt(value, 0, 64) + if err == nil { + return int64(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeInt64, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeInt64, + ) + } +} + +func ConvertToUint(vv interface{}) (uint, error) { + var ( + v = Indirect(vv) + res uint64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return uint(value), nil + case int8: + return uint(value), nil + case int16: + return uint(value), nil + case int32: + return uint(value), nil + case int64: + return uint(value), nil + case uint: + return value, nil + case uint8: + return uint(value), nil + case uint16: + return uint(value), nil + case uint32: + return uint(value), nil + case uint64: + return uint(value), nil + case float32: + return uint(value), nil + case float64: + return uint(value), nil + case complex64: + return uint(real(value)), nil + case complex128: + return uint(real(value)), nil + case uintptr: + return uint(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseUint(value, 0, 0) + if err == nil { + return uint(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeUint, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeUint, + ) + } +} + +func ConvertToUint8(vv interface{}) (uint8, error) { + var ( + v = Indirect(vv) + res uint64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return uint8(value), nil + case int8: + return uint8(value), nil + case int16: + return uint8(value), nil + case int32: + return uint8(value), nil + case int64: + return uint8(value), nil + case uint: + return uint8(value), nil + case uint8: + return value, nil + case uint16: + return uint8(value), nil + case uint32: + return uint8(value), nil + case uint64: + return uint8(value), nil + case float32: + return uint8(value), nil + case float64: + return uint8(value), nil + case complex64: + return uint8(real(value)), nil + case complex128: + return uint8(real(value)), nil + case uintptr: + return uint8(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseUint(value, 0, 8) + if err == nil { + return uint8(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeUint8, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeUint8, + ) + } +} + +func ConvertToUint16(vv interface{}) (uint16, error) { + var ( + v = Indirect(vv) + res uint64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return uint16(value), nil + case int8: + return uint16(value), nil + case int16: + return uint16(value), nil + case int32: + return uint16(value), nil + case int64: + return uint16(value), nil + case uint: + return uint16(value), nil + case uint8: + return uint16(value), nil + case uint16: + return value, nil + case uint32: + return uint16(value), nil + case uint64: + return uint16(value), nil + case float32: + return uint16(value), nil + case float64: + return uint16(value), nil + case complex64: + return uint16(real(value)), nil + case complex128: + return uint16(real(value)), nil + case uintptr: + return uint16(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseUint(value, 0, 16) + if err == nil { + return uint16(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeUint16, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeUint16, + ) + } + +} + +func ConvertToUint32(vv interface{}) (uint32, error) { + var ( + v = Indirect(vv) + res uint64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return uint32(value), nil + case int8: + return uint32(value), nil + case int16: + return uint32(value), nil + case int32: + return uint32(value), nil + case int64: + return uint32(value), nil + case uint: + return uint32(value), nil + case uint8: + return uint32(value), nil + case uint16: + return uint32(value), nil + case uint32: + return value, nil + case uint64: + return uint32(value), nil + case float32: + return uint32(value), nil + case float64: + return uint32(value), nil + case complex64: + return uint32(real(value)), nil + case complex128: + return uint32(real(value)), nil + case uintptr: + return uint32(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseUint(value, 0, 32) + if err == nil { + return uint32(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeUint32, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeUint32, + ) + } +} + +func ConvertToUint64(vv interface{}) (uint64, error) { + var ( + v = Indirect(vv) + res uint64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return uint64(value), nil + case int8: + return uint64(value), nil + case int16: + return uint64(value), nil + case int32: + return uint64(value), nil + case int64: + return uint64(value), nil + case uint: + return uint64(value), nil + case uint8: + return uint64(value), nil + case uint16: + return uint64(value), nil + case uint32: + return uint64(value), nil + case uint64: + return value, nil + case float32: + return uint64(value), nil + case float64: + return uint64(value), nil + case complex64: + return uint64(real(value)), nil + case complex128: + return uint64(real(value)), nil + case uintptr: + return uint64(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseUint(value, 0, 64) + if err == nil { + return uint64(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeUint64, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeUint64, + ) + } +} + +func ConvertToFloat32(vv interface{}) (float32, error) { + var ( + v = Indirect(vv) + res float64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return float32(value), nil + case int8: + return float32(value), nil + case int16: + return float32(value), nil + case int32: + return float32(value), nil + case int64: + return float32(value), nil + case uint: + return float32(value), nil + case uint8: + return float32(value), nil + case uint16: + return float32(value), nil + case uint32: + return float32(value), nil + case uint64: + return float32(value), nil + case float32: + return value, nil + case float64: + return float32(value), nil + case complex64: + return float32(real(value)), nil + case complex128: + return float32(real(value)), nil + case uintptr: + return float32(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseFloat(value, 32) + if err == nil { + return float32(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeFloat32, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeFloat32, + ) + } +} + +func ConvertToFloat64(vv interface{}) (float64, error) { + var ( + v = Indirect(vv) + res float64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return float64(value), nil + case int8: + return float64(value), nil + case int16: + return float64(value), nil + case int32: + return float64(value), nil + case int64: + return float64(value), nil + case uint: + return float64(value), nil + case uint8: + return float64(value), nil + case uint16: + return float64(value), nil + case uint32: + return float64(value), nil + case uint64: + return float64(value), nil + case float32: + return float64(value), nil + case float64: + return value, nil + case complex64: + return float64(real(value)), nil + case complex128: + return float64(real(value)), nil + case uintptr: + return float64(value), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseFloat(value, 32) + if err == nil { + return float64(res), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeFloat64, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeFloat64, + ) + } +} + +func ConvertToComplex64(vv interface{}) (complex64, error) { + var ( + v = Indirect(vv) + res float64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return complex64(complex(float32(value), 0)), nil + case int8: + return complex64(complex(float32(value), 0)), nil + case int16: + return complex64(complex(float32(value), 0)), nil + case int32: + return complex64(complex(float32(value), 0)), nil + case int64: + return complex64(complex(float32(value), 0)), nil + case uint: + return complex64(complex(float32(value), 0)), nil + case uint8: + return complex64(complex(float32(value), 0)), nil + case uint16: + return complex64(complex(float32(value), 0)), nil + case uint32: + return complex64(complex(float32(value), 0)), nil + case uint64: + return complex64(complex(float32(value), 0)), nil + case float32: + return complex64(complex(float32(value), 0)), nil + case float64: + return complex64(complex(float32(value), 0)), nil + case complex64: + return value, nil + case complex128: + return complex64(value), nil + case uintptr: + return complex64(complex(float32(value), 0)), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseFloat(value, 32) + if err == nil { + return complex64(complex(float32(res), 0)), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeComplex64, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeComplex64, + ) + } +} + +func ConvertToComplex128(vv interface{}) (complex128, error) { + var ( + v = Indirect(vv) + res float64 + err error + ) + + switch value := v.(type) { + case bool: + if value { + return 1, nil + } + return 0, nil + case int: + return complex128(complex(float32(value), 0)), nil + case int8: + return complex128(complex(float32(value), 0)), nil + case int16: + return complex128(complex(float32(value), 0)), nil + case int32: + return complex128(complex(float32(value), 0)), nil + case int64: + return complex128(complex(float32(value), 0)), nil + case uint: + return complex128(complex(float32(value), 0)), nil + case uint8: + return complex128(complex(float32(value), 0)), nil + case uint16: + return complex128(complex(float32(value), 0)), nil + case uint32: + return complex128(complex(float32(value), 0)), nil + case uint64: + return complex128(complex(float32(value), 0)), nil + case float32: + return complex128(complex(float32(value), 0)), nil + case float64: + return complex128(complex(float32(value), 0)), nil + case complex64: + return complex128(value), nil + case complex128: + return value, nil + case uintptr: + return complex128(complex(float32(value), 0)), nil + case string: + if value == "" { + return 0, nil + } + res, err = strconv.ParseFloat(value, 32) + if err == nil { + return complex128(complex(float32(res), 0)), nil + } + return 0, NewErrCanNotConvertType( + v, + reflect.TypeOf(vv), + TypeComplex128, + err.Error(), + ) + case nil: + return 0, nil + default: + return 0, NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeComplex128, + ) + } +} + +func ConvertToString(vv interface{}) (string, error) { + var ( + v = Indirect(vv) + ) + + switch value := v.(type) { + case bool: + return strconv.FormatBool(value), nil + case int: + return strconv.FormatInt(int64(value), 10), nil + case int8: + return strconv.FormatInt(int64(value), 10), nil + case int16: + return strconv.FormatInt(int64(value), 10), nil + case int32: + return strconv.FormatInt(int64(value), 10), nil + case int64: + return strconv.FormatInt(value, 0), nil + case uint: + return strconv.FormatInt(int64(value), 10), nil + case uint8: + return strconv.FormatInt(int64(value), 10), nil + case uint16: + return strconv.FormatInt(int64(value), 10), nil + case uint32: + return strconv.FormatInt(int64(value), 10), nil + case uint64: + return strconv.FormatInt(int64(value), 10), nil + case float32: + return strconv.FormatFloat(float64(value), 'f', -1, 32), nil + case float64: + return strconv.FormatFloat(value, 'f', -1, 64), nil + case complex64: + return strconv.FormatFloat(float64(real(value)), 'f', -1, 32), nil + case complex128: + return strconv.FormatFloat(real(value), 'f', -1, 64), nil + case uintptr: + return strconv.FormatInt(int64(value), 10), nil + case string: + return value, nil + case nil: + return "", nil + default: + return "", NewErrCanNotConvertType( + vv, + reflect.TypeOf(vv), + TypeString, + ) + } +} diff --git a/reflect/error.go b/reflect/error.go new file mode 100644 index 0000000..117d18a --- /dev/null +++ b/reflect/error.go @@ -0,0 +1,41 @@ +package reflect + +import ( + "fmt" + "reflect" + "strings" +) + +type ErrCanNotConvertType struct { + value interface{} + from reflect.Type + to reflect.Type + reason []string +} + +func (e ErrCanNotConvertType) Error() string { + var ( + reason = strings.Join(e.reason, ", ") + ) + + if reason != "" { + reason = ", reason: " + reason + } + + return fmt.Sprintf( + "Can not convert '%#v' of type '%s' to '%s'%s", + e.value, + e.from, + e.to, + reason, + ) +} + +func NewErrCanNotConvertType(value interface{}, from reflect.Type, to reflect.Type, reason ...string) ErrCanNotConvertType { + return ErrCanNotConvertType{ + value: value, + from: from, + to: to, + reason: reason, + } +} diff --git a/reflect/indirect.go b/reflect/indirect.go new file mode 100644 index 0000000..8156141 --- /dev/null +++ b/reflect/indirect.go @@ -0,0 +1,31 @@ +package reflect + +import ( + "reflect" +) + +func Indirect(v interface{}) interface{} { + if v == nil { + return nil + } + + return IndirectValue(reflect.ValueOf(v)).Interface() +} + +func IndirectValue(reflectValue reflect.Value) reflect.Value { + if reflectValue.Kind() == reflect.Ptr { + return reflectValue.Elem() + } + return reflectValue +} + +func IndirectType(reflectType reflect.Type) reflect.Type { + if reflectType == TypeInvalid { + return TypeInvalid + } + + if reflectType.Kind() == reflect.Ptr { + return reflectType.Elem() + } + return reflectType +} diff --git a/reflect/kind.go b/reflect/kind.go new file mode 100644 index 0000000..8ce5eef --- /dev/null +++ b/reflect/kind.go @@ -0,0 +1,35 @@ +package reflect + +import ( + "reflect" +) + +const ( + Invalid = reflect.Invalid + Bool = reflect.Bool + Int = reflect.Int + Int8 = reflect.Int8 + Int16 = reflect.Int16 + Int32 = reflect.Int32 + Int64 = reflect.Int64 + Uint = reflect.Uint + Uint8 = reflect.Uint8 + Uint16 = reflect.Uint16 + Uint32 = reflect.Uint32 + Uint64 = reflect.Uint64 + Uintptr = reflect.Uintptr + Float32 = reflect.Float32 + Float64 = reflect.Float64 + Complex64 = reflect.Complex64 + Complex128 = reflect.Complex128 + Array = reflect.Array + Chan = reflect.Chan + Func = reflect.Func + Interface = reflect.Interface + Map = reflect.Map + Ptr = reflect.Ptr + Slice = reflect.Slice + String = reflect.String + Struct = reflect.Struct + UnsafePointer = reflect.UnsafePointer +) diff --git a/reflect/type.go b/reflect/type.go new file mode 100644 index 0000000..1e070c9 --- /dev/null +++ b/reflect/type.go @@ -0,0 +1,72 @@ +package reflect + +import ( + "reflect" +) + +var ( + TypeOf = reflect.TypeOf +) + +var ( + Types = []reflect.Type{ + TypeBool, + TypeInt, + TypeInt8, + TypeInt16, + TypeInt32, + TypeInt64, + TypeUint, + TypeUint8, + TypeUint16, + TypeUint32, + TypeUint64, + TypeFloat32, + TypeFloat64, + TypeComplex64, + TypeComplex128, + TypeUintptr, + TypeString, + } + + TypeInvalid = reflect.Type(nil) + TypeBool = reflect.TypeOf(false) + TypeInt = reflect.TypeOf(int(0)) + TypeInt8 = reflect.TypeOf(int8(0)) + TypeInt16 = reflect.TypeOf(int16(0)) + TypeInt32 = reflect.TypeOf(int32(0)) + TypeInt64 = reflect.TypeOf(int64(0)) + TypeUint = reflect.TypeOf(uint(0)) + TypeUint8 = reflect.TypeOf(uint8(0)) + TypeUint16 = reflect.TypeOf(uint16(0)) + TypeUint32 = reflect.TypeOf(uint32(0)) + TypeUint64 = reflect.TypeOf(uint64(0)) + TypeFloat32 = reflect.TypeOf(float32(0)) + TypeFloat64 = reflect.TypeOf(float64(0)) + TypeComplex64 = reflect.TypeOf(complex64(0)) + TypeComplex128 = reflect.TypeOf(complex128(0)) + TypeUintptr = reflect.TypeOf(uintptr(0)) + TypeString = reflect.TypeOf(string("")) +) + +func IsTypeKind(t reflect.Type, kind reflect.Kind, removePtr bool) bool { + if reflect.Ptr == t.Kind() { + if removePtr { + return IsTypeKind(t.Elem(), kind, removePtr) + } + } + + return kind == t.Kind() +} + +func GetTypeInfo(t reflect.Type) (realType reflect.Type, pkgName string, name string) { + if reflect.Ptr == t.Kind() { + return GetTypeInfo(t.Elem()) + } + + return t, t.PkgPath(), t.Name() +} + +func ConvertStringToType() { + +}