vendor: github.com/dylanmei/winrmtest@99b7fe2fddf1
This commit is contained in:
parent
98353dbe60
commit
fdb126f27e
5
go.mod
5
go.mod
|
@ -8,8 +8,6 @@ require (
|
||||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect
|
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect
|
||||||
github.com/agext/levenshtein v1.2.2
|
github.com/agext/levenshtein v1.2.2
|
||||||
github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6 // indirect
|
github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6 // indirect
|
||||||
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd // indirect
|
|
||||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 // indirect
|
|
||||||
github.com/apparentlymart/go-cidr v1.0.0
|
github.com/apparentlymart/go-cidr v1.0.0
|
||||||
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0
|
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0
|
||||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
|
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
|
||||||
|
@ -28,7 +26,7 @@ require (
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31 // indirect
|
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31 // indirect
|
||||||
github.com/dylanmei/iso8601 v0.1.0 // indirect
|
github.com/dylanmei/iso8601 v0.1.0 // indirect
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08
|
github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1
|
||||||
github.com/go-test/deep v1.0.1
|
github.com/go-test/deep v1.0.1
|
||||||
github.com/gogo/protobuf v1.2.0 // indirect
|
github.com/gogo/protobuf v1.2.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect
|
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect
|
||||||
|
@ -95,7 +93,6 @@ require (
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect
|
||||||
github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17 // indirect
|
github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17 // indirect
|
||||||
github.com/posener/complete v1.2.1
|
github.com/posener/complete v1.2.1
|
||||||
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07 // indirect
|
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||||
github.com/sirupsen/logrus v1.1.1 // indirect
|
github.com/sirupsen/logrus v1.1.1 // indirect
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect
|
||||||
|
|
16
go.sum
16
go.sum
|
@ -29,10 +29,10 @@ github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki
|
||||||
github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6 h1:LoeFxdq5zUCBQPhbQKE6zvoGwHMxCBlqwbH9+9kHoHA=
|
github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6 h1:LoeFxdq5zUCBQPhbQKE6zvoGwHMxCBlqwbH9+9kHoHA=
|
||||||
github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
|
github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd h1:S3Fr6QnkpW9VRjiEY4psQHhhbbahASuNVj52YIce7lI=
|
github.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e h1:ptBAamGVd6CfRsUtyHD+goy2JGhv1QC32v3gqM8mYAM=
|
||||||
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
github.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
||||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 h1:BFFG6KP8ASFBg2ptWsJn8p8RDufBjBDKIxLU7BTYGOM=
|
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0 h1:JaCC8jz0zdMLk2m+qCCVLLLM/PL93p84w4pK3aJWj60=
|
||||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
||||||
github.com/apparentlymart/go-cidr v1.0.0 h1:lGDvXx8Lv9QHjrAVP7jyzleG4F9+FkRhJcEsDFxeb8w=
|
github.com/apparentlymart/go-cidr v1.0.0 h1:lGDvXx8Lv9QHjrAVP7jyzleG4F9+FkRhJcEsDFxeb8w=
|
||||||
github.com/apparentlymart/go-cidr v1.0.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
|
github.com/apparentlymart/go-cidr v1.0.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
|
||||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA=
|
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA=
|
||||||
|
@ -93,8 +93,8 @@ github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31/go.mod h1:aBB1+wY4s9
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI=
|
github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI=
|
||||||
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
|
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTDtSXe9YYGCwf8jp5Fb/b+4a6MTRm4qzY=
|
github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1 h1:r1oACdS2XYiAWcfF8BJXkoU8l1J71KehGR+d99yWEDA=
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08/go.mod h1:VBVDFSBXCIW8JaHQpI8lldSKfYaLMzP9oyq6IJ4fhzY=
|
github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1/go.mod h1:lcy9/2gH1jn/VCLouHA6tOEwLoNVd4GW6zhuKLmHC2Y=
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
|
@ -326,8 +326,8 @@ github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7q
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273 h1:agujYaXJSxSo18YNX3jzl+4G6Bstwt+kqv47GS12uL0=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273 h1:agujYaXJSxSo18YNX3jzl+4G6Bstwt+kqv47GS12uL0=
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07 h1:DEZDfcCVq3xDJrjqdCgyN/dHYVoqR92MCsdqCdxmnhM=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
|
|
|
@ -2,8 +2,8 @@ language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.6
|
- 1.6
|
||||||
- 1.7
|
- 1.9
|
||||||
- 1.8
|
- '1.10'
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- go get github.com/mattn/goveralls
|
- go get github.com/mattn/goveralls
|
||||||
|
|
|
@ -7,9 +7,17 @@ XPath
|
||||||
|
|
||||||
XPath is Go package provides selecting nodes from XML, HTML or other documents using XPath expression.
|
XPath is Go package provides selecting nodes from XML, HTML or other documents using XPath expression.
|
||||||
|
|
||||||
[XQuery](https://github.com/antchfx/xquery) : lets you extract data from HTML/XML documents using XPath package.
|
Implementation
|
||||||
|
===
|
||||||
|
|
||||||
### Features
|
- [htmlquery](https://github.com/antchfx/htmlquery) - an XPath query package for HTML document
|
||||||
|
|
||||||
|
- [xmlquery](https://github.com/antchfx/xmlquery) - an XPath query package for XML document.
|
||||||
|
|
||||||
|
- [jsonquery](https://github.com/antchfx/jsonquery) - an XPath query package for JSON document
|
||||||
|
|
||||||
|
Supported Features
|
||||||
|
===
|
||||||
|
|
||||||
#### The basic XPath patterns.
|
#### The basic XPath patterns.
|
||||||
|
|
||||||
|
@ -45,7 +53,10 @@ XPath is Go package provides selecting nodes from XML, HTML or other documents u
|
||||||
|
|
||||||
- `//b` : Returns elements in the entire document matching b.
|
- `//b` : Returns elements in the entire document matching b.
|
||||||
|
|
||||||
- `a|b` : All nodes matching a or b.
|
- `a|b` : All nodes matching a or b, union operation(not boolean or).
|
||||||
|
|
||||||
|
- `(a, b, c)` : Evaluates each of its operands and concatenates the resulting sequences, in order, into a single result sequence
|
||||||
|
|
||||||
|
|
||||||
#### Node Axes
|
#### Node Axes
|
||||||
|
|
||||||
|
@ -97,23 +108,60 @@ XPath is Go package provides selecting nodes from XML, HTML or other documents u
|
||||||
* a div b Divide
|
* a div b Divide
|
||||||
* a mod b Floating point mod, like Java.
|
* a mod b Floating point mod, like Java.
|
||||||
|
|
||||||
|
- `a or b` : Boolean `or` operation.
|
||||||
|
|
||||||
|
- `a and b` : Boolean `and` operation.
|
||||||
|
|
||||||
- `(expr)` : Parenthesized expressions.
|
- `(expr)` : Parenthesized expressions.
|
||||||
|
|
||||||
- `fun(arg1, ..., argn)` : Function calls.
|
- `fun(arg1, ..., argn)` : Function calls:
|
||||||
|
|
||||||
* position()
|
| Function | Supported |
|
||||||
* last()
|
| --- | --- |
|
||||||
* count( node-set )
|
`boolean()`| ✓ |
|
||||||
* name()
|
`ceiling()`| ✓ |
|
||||||
* starts-with( string, string )
|
`choose()`| ✗ |
|
||||||
* normalize-space( string )
|
`concat()`| ✓ |
|
||||||
* substring( string , start [, length] )
|
`contains()`| ✓ |
|
||||||
* not( expression )
|
`count()`| ✓ |
|
||||||
* string-length( [string] )
|
`current()`| ✗ |
|
||||||
* contains( string, string )
|
`document()`| ✗ |
|
||||||
* sum( node-set )
|
`element-available()`| ✗ |
|
||||||
* concat( string1 , string2 [, stringn]* )
|
`ends-with()`| ✓ |
|
||||||
|
`false()`| ✓ |
|
||||||
|
`floor()`| ✓ |
|
||||||
|
`format-number()`| ✗ |
|
||||||
|
`function-available()`| ✗ |
|
||||||
|
`generate-id()`| ✗ |
|
||||||
|
`id()`| ✗ |
|
||||||
|
`key()`| ✗ |
|
||||||
|
`lang()`| ✗ |
|
||||||
|
`last()`| ✓ |
|
||||||
|
`local-name()`| ✓ |
|
||||||
|
`name()`| ✓ |
|
||||||
|
`namespace-uri()`| ✓ |
|
||||||
|
`normalize-space()`| ✓ |
|
||||||
|
`not()`| ✓ |
|
||||||
|
`number()`| ✓ |
|
||||||
|
`position()`| ✓ |
|
||||||
|
`round()`| ✓ |
|
||||||
|
`starts-with()`| ✓ |
|
||||||
|
`string()`| ✓ |
|
||||||
|
`string-length()`| ✓ |
|
||||||
|
`substring()`| ✓ |
|
||||||
|
`substring-after()`| ✓ |
|
||||||
|
`substring-before()`| ✓ |
|
||||||
|
`sum()`| ✓ |
|
||||||
|
`system-property()`| ✗ |
|
||||||
|
`translate()`| ✓ |
|
||||||
|
`true()`| ✓ |
|
||||||
|
`unparsed-entity-url()` | ✗ |
|
||||||
|
|
||||||
- `a or b` : Boolean or.
|
Changelogs
|
||||||
|
===
|
||||||
|
|
||||||
- `a and b` : Boolean and.
|
2019-01-29
|
||||||
|
- improvement `normalize-space` function. [#32](https://github.com/antchfx/xpath/issues/32)
|
||||||
|
|
||||||
|
2018-12-07
|
||||||
|
- supports XPath 2.0 Sequence expressions. [#30](https://github.com/antchfx/xpath/pull/30) by [@minherz](https://github.com/minherz).
|
|
@ -23,9 +23,12 @@ type builder struct {
|
||||||
func axisPredicate(root *axisNode) func(NodeNavigator) bool {
|
func axisPredicate(root *axisNode) func(NodeNavigator) bool {
|
||||||
// get current axix node type.
|
// get current axix node type.
|
||||||
typ := ElementNode
|
typ := ElementNode
|
||||||
if root.AxeType == "attribute" {
|
switch root.AxeType {
|
||||||
|
case "attribute":
|
||||||
typ = AttributeNode
|
typ = AttributeNode
|
||||||
} else {
|
case "self", "parent":
|
||||||
|
typ = allNode
|
||||||
|
default:
|
||||||
switch root.Prop {
|
switch root.Prop {
|
||||||
case "comment":
|
case "comment":
|
||||||
typ = CommentNode
|
typ = CommentNode
|
||||||
|
@ -34,12 +37,17 @@ func axisPredicate(root *axisNode) func(NodeNavigator) bool {
|
||||||
// case "processing-instruction":
|
// case "processing-instruction":
|
||||||
// typ = ProcessingInstructionNode
|
// typ = ProcessingInstructionNode
|
||||||
case "node":
|
case "node":
|
||||||
typ = ElementNode
|
typ = allNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
nametest := root.LocalName != "" || root.Prefix != ""
|
||||||
predicate := func(n NodeNavigator) bool {
|
predicate := func(n NodeNavigator) bool {
|
||||||
if typ == n.NodeType() || typ == TextNode {
|
if typ == n.NodeType() || typ == allNode || typ == TextNode {
|
||||||
if root.LocalName == "" || (root.LocalName == n.LocalName() && root.Prefix == n.Prefix()) {
|
if nametest {
|
||||||
|
if root.LocalName == n.LocalName() && root.Prefix == n.Prefix() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,18 +69,16 @@ func (b *builder) processAxisNode(root *axisNode) (query, error) {
|
||||||
if root.Input == nil {
|
if root.Input == nil {
|
||||||
qyInput = &contextQuery{}
|
qyInput = &contextQuery{}
|
||||||
} else {
|
} else {
|
||||||
if b.flag&filterFlag == 0 {
|
if root.AxeType == "child" && (root.Input.Type() == nodeAxis) {
|
||||||
if root.AxeType == "child" && (root.Input.Type() == nodeAxis) {
|
if input := root.Input.(*axisNode); input.AxeType == "descendant-or-self" {
|
||||||
if input := root.Input.(*axisNode); input.AxeType == "descendant-or-self" {
|
var qyGrandInput query
|
||||||
var qyGrandInput query
|
if input.Input != nil {
|
||||||
if input.Input != nil {
|
qyGrandInput, _ = b.processNode(input.Input)
|
||||||
qyGrandInput, _ = b.processNode(input.Input)
|
} else {
|
||||||
} else {
|
qyGrandInput = &contextQuery{}
|
||||||
qyGrandInput = &contextQuery{}
|
|
||||||
}
|
|
||||||
qyOutput = &descendantQuery{Input: qyGrandInput, Predicate: predicate, Self: true}
|
|
||||||
return qyOutput, nil
|
|
||||||
}
|
}
|
||||||
|
qyOutput = &descendantQuery{Input: qyGrandInput, Predicate: predicate, Self: true}
|
||||||
|
return qyOutput, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qyInput, err = b.processNode(root.Input)
|
qyInput, err = b.processNode(root.Input)
|
||||||
|
@ -157,6 +163,16 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
qyOutput = &functionQuery{Input: b.firstInput, Func: startwithFunc(arg1, arg2)}
|
qyOutput = &functionQuery{Input: b.firstInput, Func: startwithFunc(arg1, arg2)}
|
||||||
|
case "ends-with":
|
||||||
|
arg1, err := b.processNode(root.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
arg2, err := b.processNode(root.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
qyOutput = &functionQuery{Input: b.firstInput, Func: endwithFunc(arg1, arg2)}
|
||||||
case "contains":
|
case "contains":
|
||||||
arg1, err := b.processNode(root.Args[0])
|
arg1, err := b.processNode(root.Args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -189,6 +205,25 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qyOutput = &functionQuery{Input: b.firstInput, Func: substringFunc(arg1, arg2, arg3)}
|
qyOutput = &functionQuery{Input: b.firstInput, Func: substringFunc(arg1, arg2, arg3)}
|
||||||
|
case "substring-before", "substring-after":
|
||||||
|
//substring-xxxx( haystack, needle )
|
||||||
|
if len(root.Args) != 2 {
|
||||||
|
return nil, errors.New("xpath: substring-before function must have two parameters")
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
arg1, arg2 query
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if arg1, err = b.processNode(root.Args[0]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if arg2, err = b.processNode(root.Args[1]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
qyOutput = &functionQuery{
|
||||||
|
Input: b.firstInput,
|
||||||
|
Func: substringIndFunc(arg1, arg2, root.FuncName == "substring-after"),
|
||||||
|
}
|
||||||
case "string-length":
|
case "string-length":
|
||||||
// string-length( [string] )
|
// string-length( [string] )
|
||||||
if len(root.Args) < 1 {
|
if len(root.Args) < 1 {
|
||||||
|
@ -208,6 +243,25 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
qyOutput = &functionQuery{Input: argQuery, Func: normalizespaceFunc}
|
qyOutput = &functionQuery{Input: argQuery, Func: normalizespaceFunc}
|
||||||
|
case "translate":
|
||||||
|
//translate( string , string, string )
|
||||||
|
if len(root.Args) != 3 {
|
||||||
|
return nil, errors.New("xpath: translate function must have three parameters")
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
arg1, arg2, arg3 query
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if arg1, err = b.processNode(root.Args[0]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if arg2, err = b.processNode(root.Args[1]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if arg3, err = b.processNode(root.Args[2]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
qyOutput = &functionQuery{Input: b.firstInput, Func: translateFunc(arg1, arg2, arg3)}
|
||||||
case "not":
|
case "not":
|
||||||
if len(root.Args) == 0 {
|
if len(root.Args) == 0 {
|
||||||
return nil, errors.New("xpath: not function must have at least one parameter")
|
return nil, errors.New("xpath: not function must have at least one parameter")
|
||||||
|
@ -217,12 +271,62 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
qyOutput = &functionQuery{Input: argQuery, Func: notFunc}
|
qyOutput = &functionQuery{Input: argQuery, Func: notFunc}
|
||||||
case "name":
|
case "name", "local-name", "namespace-uri":
|
||||||
qyOutput = &functionQuery{Input: b.firstInput, Func: nameFunc}
|
inp := b.firstInput
|
||||||
|
if len(root.Args) > 1 {
|
||||||
|
return nil, fmt.Errorf("xpath: %s function must have at most one parameter", root.FuncName)
|
||||||
|
}
|
||||||
|
if len(root.Args) == 1 {
|
||||||
|
argQuery, err := b.processNode(root.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inp = argQuery
|
||||||
|
}
|
||||||
|
f := &functionQuery{Input: inp}
|
||||||
|
switch root.FuncName {
|
||||||
|
case "name":
|
||||||
|
f.Func = nameFunc
|
||||||
|
case "local-name":
|
||||||
|
f.Func = localNameFunc
|
||||||
|
case "namespace-uri":
|
||||||
|
f.Func = namespaceFunc
|
||||||
|
}
|
||||||
|
qyOutput = f
|
||||||
|
case "true", "false":
|
||||||
|
val := root.FuncName == "true"
|
||||||
|
qyOutput = &functionQuery{
|
||||||
|
Input: b.firstInput,
|
||||||
|
Func: func(_ query, _ iterator) interface{} {
|
||||||
|
return val
|
||||||
|
},
|
||||||
|
}
|
||||||
case "last":
|
case "last":
|
||||||
qyOutput = &functionQuery{Input: b.firstInput, Func: lastFunc}
|
qyOutput = &functionQuery{Input: b.firstInput, Func: lastFunc}
|
||||||
case "position":
|
case "position":
|
||||||
qyOutput = &functionQuery{Input: b.firstInput, Func: positionFunc}
|
qyOutput = &functionQuery{Input: b.firstInput, Func: positionFunc}
|
||||||
|
case "boolean", "number", "string":
|
||||||
|
inp := b.firstInput
|
||||||
|
if len(root.Args) > 1 {
|
||||||
|
return nil, fmt.Errorf("xpath: %s function must have at most one parameter", root.FuncName)
|
||||||
|
}
|
||||||
|
if len(root.Args) == 1 {
|
||||||
|
argQuery, err := b.processNode(root.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inp = argQuery
|
||||||
|
}
|
||||||
|
f := &functionQuery{Input: inp}
|
||||||
|
switch root.FuncName {
|
||||||
|
case "boolean":
|
||||||
|
f.Func = booleanFunc
|
||||||
|
case "string":
|
||||||
|
f.Func = stringFunc
|
||||||
|
case "number":
|
||||||
|
f.Func = numberFunc
|
||||||
|
}
|
||||||
|
qyOutput = f
|
||||||
case "count":
|
case "count":
|
||||||
//if b.firstInput == nil {
|
//if b.firstInput == nil {
|
||||||
// return nil, errors.New("xpath: expression must evaluate to node-set")
|
// return nil, errors.New("xpath: expression must evaluate to node-set")
|
||||||
|
@ -244,6 +348,24 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
qyOutput = &functionQuery{Input: argQuery, Func: sumFunc}
|
qyOutput = &functionQuery{Input: argQuery, Func: sumFunc}
|
||||||
|
case "ceiling", "floor", "round":
|
||||||
|
if len(root.Args) == 0 {
|
||||||
|
return nil, fmt.Errorf("xpath: ceiling(node-sets) function must with have parameters node-sets")
|
||||||
|
}
|
||||||
|
argQuery, err := b.processNode(root.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f := &functionQuery{Input: argQuery}
|
||||||
|
switch root.FuncName {
|
||||||
|
case "ceiling":
|
||||||
|
f.Func = ceilingFunc
|
||||||
|
case "floor":
|
||||||
|
f.Func = floorFunc
|
||||||
|
case "round":
|
||||||
|
f.Func = roundFunc
|
||||||
|
}
|
||||||
|
qyOutput = f
|
||||||
case "concat":
|
case "concat":
|
||||||
if len(root.Args) < 2 {
|
if len(root.Args) < 2 {
|
||||||
return nil, fmt.Errorf("xpath: concat() must have at least two arguments")
|
return nil, fmt.Errorf("xpath: concat() must have at least two arguments")
|
||||||
|
@ -304,12 +426,14 @@ func (b *builder) processOperatorNode(root *operatorNode) (query, error) {
|
||||||
exprFunc = neFunc
|
exprFunc = neFunc
|
||||||
}
|
}
|
||||||
qyOutput = &logicalQuery{Left: left, Right: right, Do: exprFunc}
|
qyOutput = &logicalQuery{Left: left, Right: right, Do: exprFunc}
|
||||||
case "or", "and", "|":
|
case "or", "and":
|
||||||
isOr := false
|
isOr := false
|
||||||
if root.Op == "or" || root.Op == "|" {
|
if root.Op == "or" {
|
||||||
isOr = true
|
isOr = true
|
||||||
}
|
}
|
||||||
qyOutput = &booleanQuery{Left: left, Right: right, IsOr: isOr}
|
qyOutput = &booleanQuery{Left: left, Right: right, IsOr: isOr}
|
||||||
|
case "|":
|
||||||
|
qyOutput = &unionQuery{Left: left, Right: right}
|
||||||
}
|
}
|
||||||
return qyOutput, nil
|
return qyOutput, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@ package xpath
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -80,16 +83,146 @@ func sumFunc(q query, t iterator) interface{} {
|
||||||
case float64:
|
case float64:
|
||||||
sum = typ
|
sum = typ
|
||||||
case string:
|
case string:
|
||||||
if v, err := strconv.ParseFloat(typ, 64); err != nil {
|
v, err := strconv.ParseFloat(typ, 64)
|
||||||
sum = v
|
if err != nil {
|
||||||
|
panic(errors.New("sum() function argument type must be a node-set or number"))
|
||||||
}
|
}
|
||||||
|
sum = v
|
||||||
}
|
}
|
||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func asNumber(t iterator, o interface{}) float64 {
|
||||||
|
switch typ := o.(type) {
|
||||||
|
case query:
|
||||||
|
node := typ.Select(t)
|
||||||
|
if node == nil {
|
||||||
|
return float64(0)
|
||||||
|
}
|
||||||
|
if v, err := strconv.ParseFloat(node.Value(), 64); err == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
return typ
|
||||||
|
case string:
|
||||||
|
v, err := strconv.ParseFloat(typ, 64)
|
||||||
|
if err != nil {
|
||||||
|
panic(errors.New("ceiling() function argument type must be a node-set or number"))
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ceilingFunc is a XPath Node Set functions ceiling(node-set).
|
||||||
|
func ceilingFunc(q query, t iterator) interface{} {
|
||||||
|
val := asNumber(t, q.Evaluate(t))
|
||||||
|
return math.Ceil(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// floorFunc is a XPath Node Set functions floor(node-set).
|
||||||
|
func floorFunc(q query, t iterator) interface{} {
|
||||||
|
val := asNumber(t, q.Evaluate(t))
|
||||||
|
return math.Floor(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// roundFunc is a XPath Node Set functions round(node-set).
|
||||||
|
func roundFunc(q query, t iterator) interface{} {
|
||||||
|
val := asNumber(t, q.Evaluate(t))
|
||||||
|
//return math.Round(val)
|
||||||
|
return round(val)
|
||||||
|
}
|
||||||
|
|
||||||
// nameFunc is a XPath functions name([node-set]).
|
// nameFunc is a XPath functions name([node-set]).
|
||||||
func nameFunc(q query, t iterator) interface{} {
|
func nameFunc(q query, t iterator) interface{} {
|
||||||
return t.Current().LocalName()
|
v := q.Select(t)
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
ns := v.Prefix()
|
||||||
|
if ns == "" {
|
||||||
|
return v.LocalName()
|
||||||
|
}
|
||||||
|
return ns + ":" + v.LocalName()
|
||||||
|
}
|
||||||
|
|
||||||
|
// localNameFunc is a XPath functions local-name([node-set]).
|
||||||
|
func localNameFunc(q query, t iterator) interface{} {
|
||||||
|
v := q.Select(t)
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return v.LocalName()
|
||||||
|
}
|
||||||
|
|
||||||
|
// namespaceFunc is a XPath functions namespace-uri([node-set]).
|
||||||
|
func namespaceFunc(q query, t iterator) interface{} {
|
||||||
|
v := q.Select(t)
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return v.Prefix()
|
||||||
|
}
|
||||||
|
|
||||||
|
func asBool(t iterator, v interface{}) bool {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case nil:
|
||||||
|
return false
|
||||||
|
case *NodeIterator:
|
||||||
|
return v.MoveNext()
|
||||||
|
case bool:
|
||||||
|
return bool(v)
|
||||||
|
case float64:
|
||||||
|
return v != 0
|
||||||
|
case string:
|
||||||
|
return v != ""
|
||||||
|
case query:
|
||||||
|
return v.Select(t) != nil
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unexpected type: %T", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func asString(t iterator, v interface{}) string {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case nil:
|
||||||
|
return ""
|
||||||
|
case bool:
|
||||||
|
if v {
|
||||||
|
return "true"
|
||||||
|
}
|
||||||
|
return "false"
|
||||||
|
case float64:
|
||||||
|
return strconv.FormatFloat(v, 'g', -1, 64)
|
||||||
|
case string:
|
||||||
|
return v
|
||||||
|
case query:
|
||||||
|
node := v.Select(t)
|
||||||
|
if node == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return node.Value()
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unexpected type: %T", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// booleanFunc is a XPath functions boolean([node-set]).
|
||||||
|
func booleanFunc(q query, t iterator) interface{} {
|
||||||
|
v := q.Evaluate(t)
|
||||||
|
return asBool(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// numberFunc is a XPath functions number([node-set]).
|
||||||
|
func numberFunc(q query, t iterator) interface{} {
|
||||||
|
v := q.Evaluate(t)
|
||||||
|
return asNumber(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringFunc is a XPath functions string([node-set]).
|
||||||
|
func stringFunc(q query, t iterator) interface{} {
|
||||||
|
v := q.Evaluate(t)
|
||||||
|
return asString(t, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// startwithFunc is a XPath functions starts-with(string, string).
|
// startwithFunc is a XPath functions starts-with(string, string).
|
||||||
|
@ -119,6 +252,33 @@ func startwithFunc(arg1, arg2 query) func(query, iterator) interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endwithFunc is a XPath functions ends-with(string, string).
|
||||||
|
func endwithFunc(arg1, arg2 query) func(query, iterator) interface{} {
|
||||||
|
return func(q query, t iterator) interface{} {
|
||||||
|
var (
|
||||||
|
m, n string
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
switch typ := arg1.Evaluate(t).(type) {
|
||||||
|
case string:
|
||||||
|
m = typ
|
||||||
|
case query:
|
||||||
|
node := typ.Select(t)
|
||||||
|
if node == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
m = node.Value()
|
||||||
|
default:
|
||||||
|
panic(errors.New("ends-with() function argument type must be string"))
|
||||||
|
}
|
||||||
|
n, ok = arg2.Evaluate(t).(string)
|
||||||
|
if !ok {
|
||||||
|
panic(errors.New("ends-with() function argument type must be string"))
|
||||||
|
}
|
||||||
|
return strings.HasSuffix(m, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// containsFunc is a XPath functions contains(string or @attr, string).
|
// containsFunc is a XPath functions contains(string or @attr, string).
|
||||||
func containsFunc(arg1, arg2 query) func(query, iterator) interface{} {
|
func containsFunc(arg1, arg2 query) func(query, iterator) interface{} {
|
||||||
return func(q query, t iterator) interface{} {
|
return func(q query, t iterator) interface{} {
|
||||||
|
@ -149,6 +309,11 @@ func containsFunc(arg1, arg2 query) func(query, iterator) interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
regnewline = regexp.MustCompile(`[\r\n\t]`)
|
||||||
|
regseqspace = regexp.MustCompile(`\s{2,}`)
|
||||||
|
)
|
||||||
|
|
||||||
// normalizespaceFunc is XPath functions normalize-space(string?)
|
// normalizespaceFunc is XPath functions normalize-space(string?)
|
||||||
func normalizespaceFunc(q query, t iterator) interface{} {
|
func normalizespaceFunc(q query, t iterator) interface{} {
|
||||||
var m string
|
var m string
|
||||||
|
@ -158,11 +323,14 @@ func normalizespaceFunc(q query, t iterator) interface{} {
|
||||||
case query:
|
case query:
|
||||||
node := typ.Select(t)
|
node := typ.Select(t)
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return ""
|
||||||
}
|
}
|
||||||
m = node.Value()
|
m = node.Value()
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(m)
|
m = strings.TrimSpace(m)
|
||||||
|
m = regnewline.ReplaceAllString(m, " ")
|
||||||
|
m = regseqspace.ReplaceAllString(m, " ")
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// substringFunc is XPath functions substring function returns a part of a given string.
|
// substringFunc is XPath functions substring function returns a part of a given string.
|
||||||
|
@ -175,7 +343,7 @@ func substringFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} {
|
||||||
case query:
|
case query:
|
||||||
node := typ.Select(t)
|
node := typ.Select(t)
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return ""
|
||||||
}
|
}
|
||||||
m = node.Value()
|
m = node.Value()
|
||||||
}
|
}
|
||||||
|
@ -185,7 +353,10 @@ func substringFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} {
|
||||||
|
|
||||||
if start, ok = arg2.Evaluate(t).(float64); !ok {
|
if start, ok = arg2.Evaluate(t).(float64); !ok {
|
||||||
panic(errors.New("substring() function first argument type must be int"))
|
panic(errors.New("substring() function first argument type must be int"))
|
||||||
|
} else if start < 1 {
|
||||||
|
panic(errors.New("substring() function first argument type must be >= 1"))
|
||||||
}
|
}
|
||||||
|
start--
|
||||||
if arg3 != nil {
|
if arg3 != nil {
|
||||||
if length, ok = arg3.Evaluate(t).(float64); !ok {
|
if length, ok = arg3.Evaluate(t).(float64); !ok {
|
||||||
panic(errors.New("substring() function second argument type must be int"))
|
panic(errors.New("substring() function second argument type must be int"))
|
||||||
|
@ -201,6 +372,46 @@ func substringFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// substringIndFunc is XPath functions substring-before/substring-after function returns a part of a given string.
|
||||||
|
func substringIndFunc(arg1, arg2 query, after bool) func(query, iterator) interface{} {
|
||||||
|
return func(q query, t iterator) interface{} {
|
||||||
|
var str string
|
||||||
|
switch v := arg1.Evaluate(t).(type) {
|
||||||
|
case string:
|
||||||
|
str = v
|
||||||
|
case query:
|
||||||
|
node := v.Select(t)
|
||||||
|
if node == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
str = node.Value()
|
||||||
|
}
|
||||||
|
var word string
|
||||||
|
switch v := arg2.Evaluate(t).(type) {
|
||||||
|
case string:
|
||||||
|
word = v
|
||||||
|
case query:
|
||||||
|
node := v.Select(t)
|
||||||
|
if node == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
word = node.Value()
|
||||||
|
}
|
||||||
|
if word == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
i := strings.Index(str, word)
|
||||||
|
if i < 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if after {
|
||||||
|
return str[i+len(word):]
|
||||||
|
}
|
||||||
|
return str[:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// stringLengthFunc is XPATH string-length( [string] ) function that returns a number
|
// stringLengthFunc is XPATH string-length( [string] ) function that returns a number
|
||||||
// equal to the number of characters in a given string.
|
// equal to the number of characters in a given string.
|
||||||
func stringLengthFunc(arg1 query) func(query, iterator) interface{} {
|
func stringLengthFunc(arg1 query) func(query, iterator) interface{} {
|
||||||
|
@ -219,6 +430,25 @@ func stringLengthFunc(arg1 query) func(query, iterator) interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// translateFunc is XPath functions translate() function returns a replaced string.
|
||||||
|
func translateFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} {
|
||||||
|
return func(q query, t iterator) interface{} {
|
||||||
|
str := asString(t, arg1.Evaluate(t))
|
||||||
|
src := asString(t, arg2.Evaluate(t))
|
||||||
|
dst := asString(t, arg3.Evaluate(t))
|
||||||
|
|
||||||
|
var replace []string
|
||||||
|
for i, s := range src {
|
||||||
|
d := ""
|
||||||
|
if i < len(dst) {
|
||||||
|
d = string(dst[i])
|
||||||
|
}
|
||||||
|
replace = append(replace, string(s), d)
|
||||||
|
}
|
||||||
|
return strings.NewReplacer(replace...).Replace(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// notFunc is XPATH functions not(expression) function operation.
|
// notFunc is XPATH functions not(expression) function operation.
|
||||||
func notFunc(q query, t iterator) interface{} {
|
func notFunc(q query, t iterator) interface{} {
|
||||||
switch v := q.Evaluate(t).(type) {
|
switch v := q.Evaluate(t).(type) {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
// +build go1.10
|
||||||
|
|
||||||
|
package xpath
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
func round(f float64) int {
|
||||||
|
return int(math.Round(f))
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
// +build !go1.10
|
||||||
|
|
||||||
|
package xpath
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// math.Round() is supported by Go 1.10+,
|
||||||
|
// This method just compatible for version <1.10.
|
||||||
|
// https://github.com/golang/go/issues/20100
|
||||||
|
func round(f float64) int {
|
||||||
|
if math.Abs(f) < 0.5 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(f + math.Copysign(0.5, f))
|
||||||
|
}
|
|
@ -42,7 +42,7 @@ const (
|
||||||
itemString // Quoted string constant
|
itemString // Quoted string constant
|
||||||
itemNumber // Number constant
|
itemNumber // Number constant
|
||||||
itemAxe // Axe (like child::)
|
itemAxe // Axe (like child::)
|
||||||
itemEof // END
|
itemEOF // END
|
||||||
)
|
)
|
||||||
|
|
||||||
// A node is an XPath node in the parse tree.
|
// A node is an XPath node in the parse tree.
|
||||||
|
@ -389,7 +389,7 @@ Loop:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep
|
// Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep
|
||||||
func (p *parser) parseStep(n node) node {
|
func (p *parser) parseStep(n node) (opnd node) {
|
||||||
axeTyp := "child" // default axes value.
|
axeTyp := "child" // default axes value.
|
||||||
if p.r.typ == itemDot || p.r.typ == itemDotDot {
|
if p.r.typ == itemDot || p.r.typ == itemDotDot {
|
||||||
if p.r.typ == itemDot {
|
if p.r.typ == itemDot {
|
||||||
|
@ -398,23 +398,45 @@ func (p *parser) parseStep(n node) node {
|
||||||
axeTyp = "parent"
|
axeTyp = "parent"
|
||||||
}
|
}
|
||||||
p.next()
|
p.next()
|
||||||
return newAxisNode(axeTyp, "", "", "", n)
|
opnd = newAxisNode(axeTyp, "", "", "", n)
|
||||||
|
if p.r.typ != itemLBracket {
|
||||||
|
return opnd
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch p.r.typ {
|
||||||
|
case itemAt:
|
||||||
|
p.next()
|
||||||
|
axeTyp = "attribute"
|
||||||
|
case itemAxe:
|
||||||
|
axeTyp = p.r.name
|
||||||
|
p.next()
|
||||||
|
case itemLParens:
|
||||||
|
return p.parseSequence(n)
|
||||||
|
}
|
||||||
|
opnd = p.parseNodeTest(n, axeTyp)
|
||||||
}
|
}
|
||||||
switch p.r.typ {
|
|
||||||
case itemAt:
|
|
||||||
p.next()
|
|
||||||
axeTyp = "attribute"
|
|
||||||
case itemAxe:
|
|
||||||
axeTyp = p.r.name
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
opnd := p.parseNodeTest(n, axeTyp)
|
|
||||||
for p.r.typ == itemLBracket {
|
for p.r.typ == itemLBracket {
|
||||||
opnd = newFilterNode(opnd, p.parsePredicate(opnd))
|
opnd = newFilterNode(opnd, p.parsePredicate(opnd))
|
||||||
}
|
}
|
||||||
return opnd
|
return opnd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expr ::= '(' Step ("," Step)* ')'
|
||||||
|
func (p *parser) parseSequence(n node) (opnd node) {
|
||||||
|
p.skipItem(itemLParens)
|
||||||
|
opnd = p.parseStep(n)
|
||||||
|
for {
|
||||||
|
if p.r.typ != itemComma {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
opnd2 := p.parseStep(n)
|
||||||
|
opnd = newOperatorNode("|", opnd, opnd2)
|
||||||
|
}
|
||||||
|
p.skipItem(itemRParens)
|
||||||
|
return opnd
|
||||||
|
}
|
||||||
|
|
||||||
// NodeTest ::= NameTest | nodeType '(' ')' | 'processing-instruction' '(' Literal ')'
|
// NodeTest ::= NameTest | nodeType '(' ')' | 'processing-instruction' '(' Literal ')'
|
||||||
func (p *parser) parseNodeTest(n node, axeTyp string) (opnd node) {
|
func (p *parser) parseNodeTest(n node, axeTyp string) (opnd node) {
|
||||||
switch p.r.typ {
|
switch p.r.typ {
|
||||||
|
@ -628,7 +650,7 @@ func (s *scanner) nextChar() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
s.curr = rune(s.text[s.pos])
|
s.curr = rune(s.text[s.pos])
|
||||||
s.pos += 1
|
s.pos++
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,7 +658,7 @@ func (s *scanner) nextItem() bool {
|
||||||
s.skipSpace()
|
s.skipSpace()
|
||||||
switch s.curr {
|
switch s.curr {
|
||||||
case 0:
|
case 0:
|
||||||
s.typ = itemEof
|
s.typ = itemEOF
|
||||||
return false
|
return false
|
||||||
case ',', '@', '(', ')', '|', '*', '[', ']', '+', '-', '=', '#', '$':
|
case ',', '@', '(', ')', '|', '*', '[', ']', '+', '-', '=', '#', '$':
|
||||||
s.typ = asItemType(s.curr)
|
s.typ = asItemType(s.curr)
|
||||||
|
|
|
@ -71,7 +71,7 @@ func (a *ancestorQuery) Select(t iterator) NodeNavigator {
|
||||||
}
|
}
|
||||||
for node.MoveToParent() {
|
for node.MoveToParent() {
|
||||||
if !a.Predicate(node) {
|
if !a.Predicate(node) {
|
||||||
break
|
continue
|
||||||
}
|
}
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
@ -707,16 +707,79 @@ func (b *booleanQuery) Select(t iterator) NodeNavigator {
|
||||||
|
|
||||||
func (b *booleanQuery) Evaluate(t iterator) interface{} {
|
func (b *booleanQuery) Evaluate(t iterator) interface{} {
|
||||||
m := b.Left.Evaluate(t)
|
m := b.Left.Evaluate(t)
|
||||||
if m.(bool) == b.IsOr {
|
left := asBool(t, m)
|
||||||
return m
|
if b.IsOr && left {
|
||||||
|
return true
|
||||||
|
} else if !b.IsOr && !left {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return b.Right.Evaluate(t)
|
m = b.Right.Evaluate(t)
|
||||||
|
return asBool(t, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *booleanQuery) Clone() query {
|
func (b *booleanQuery) Clone() query {
|
||||||
return &booleanQuery{IsOr: b.IsOr, Left: b.Left.Clone(), Right: b.Right.Clone()}
|
return &booleanQuery{IsOr: b.IsOr, Left: b.Left.Clone(), Right: b.Right.Clone()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type unionQuery struct {
|
||||||
|
Left, Right query
|
||||||
|
iterator func() NodeNavigator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *unionQuery) Select(t iterator) NodeNavigator {
|
||||||
|
if u.iterator == nil {
|
||||||
|
var list []NodeNavigator
|
||||||
|
var i int
|
||||||
|
root := t.Current().Copy()
|
||||||
|
for {
|
||||||
|
node := u.Left.Select(t)
|
||||||
|
if node == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
node = node.Copy()
|
||||||
|
list = append(list, node)
|
||||||
|
}
|
||||||
|
t.Current().MoveTo(root)
|
||||||
|
for {
|
||||||
|
node := u.Right.Select(t)
|
||||||
|
if node == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
node = node.Copy()
|
||||||
|
var exists bool
|
||||||
|
for _, x := range list {
|
||||||
|
if reflect.DeepEqual(x, node) {
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
list = append(list, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u.iterator = func() NodeNavigator {
|
||||||
|
if i >= len(list) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
node := list[i]
|
||||||
|
i++
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return u.iterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *unionQuery) Evaluate(t iterator) interface{} {
|
||||||
|
u.iterator = nil
|
||||||
|
u.Left.Evaluate(t)
|
||||||
|
u.Right.Evaluate(t)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *unionQuery) Clone() query {
|
||||||
|
return &unionQuery{Left: u.Left.Clone(), Right: u.Right.Clone()}
|
||||||
|
}
|
||||||
|
|
||||||
func getNodePosition(q query) int {
|
func getNodePosition(q query) int {
|
||||||
type Position interface {
|
type Position interface {
|
||||||
position() int
|
position() int
|
||||||
|
|
|
@ -22,6 +22,9 @@ const (
|
||||||
|
|
||||||
// CommentNode is a comment node, such as <!-- my comment -->
|
// CommentNode is a comment node, such as <!-- my comment -->
|
||||||
CommentNode
|
CommentNode
|
||||||
|
|
||||||
|
// allNode is any types of node, used by xpath package only to predicate match.
|
||||||
|
allNode
|
||||||
)
|
)
|
||||||
|
|
||||||
// NodeNavigator provides cursor model for navigating XML data.
|
// NodeNavigator provides cursor model for navigating XML data.
|
||||||
|
|
|
@ -66,10 +66,15 @@ func (n *Node) InnerText() string {
|
||||||
|
|
||||||
func outputXML(buf *bytes.Buffer, n *Node) {
|
func outputXML(buf *bytes.Buffer, n *Node) {
|
||||||
if n.Type == TextNode || n.Type == CommentNode {
|
if n.Type == TextNode || n.Type == CommentNode {
|
||||||
buf.WriteString(strings.TrimSpace(n.Data))
|
xml.EscapeText(buf, []byte(strings.TrimSpace(n.Data)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buf.WriteString("<" + n.Data)
|
if n.Type == DeclarationNode {
|
||||||
|
buf.WriteString("<?" + n.Data)
|
||||||
|
} else {
|
||||||
|
buf.WriteString("<" + n.Data)
|
||||||
|
}
|
||||||
|
|
||||||
for _, attr := range n.Attr {
|
for _, attr := range n.Attr {
|
||||||
if attr.Name.Space != "" {
|
if attr.Name.Space != "" {
|
||||||
buf.WriteString(fmt.Sprintf(` %s:%s="%s"`, attr.Name.Space, attr.Name.Local, attr.Value))
|
buf.WriteString(fmt.Sprintf(` %s:%s="%s"`, attr.Name.Space, attr.Name.Local, attr.Value))
|
||||||
|
@ -77,11 +82,17 @@ func outputXML(buf *bytes.Buffer, n *Node) {
|
||||||
buf.WriteString(fmt.Sprintf(` %s="%s"`, attr.Name.Local, attr.Value))
|
buf.WriteString(fmt.Sprintf(` %s="%s"`, attr.Name.Local, attr.Value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf.WriteString(">")
|
if n.Type == DeclarationNode {
|
||||||
|
buf.WriteString("?>")
|
||||||
|
} else {
|
||||||
|
buf.WriteString(">")
|
||||||
|
}
|
||||||
for child := n.FirstChild; child != nil; child = child.NextSibling {
|
for child := n.FirstChild; child != nil; child = child.NextSibling {
|
||||||
outputXML(buf, child)
|
outputXML(buf, child)
|
||||||
}
|
}
|
||||||
buf.WriteString(fmt.Sprintf("</%s>", n.Data))
|
if n.Type != DeclarationNode {
|
||||||
|
buf.WriteString(fmt.Sprintf("</%s>", n.Data))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OutputXML returns the text that including tags name.
|
// OutputXML returns the text that including tags name.
|
||||||
|
@ -128,6 +139,9 @@ func addChild(parent, n *Node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addSibling(sibling, n *Node) {
|
func addSibling(sibling, n *Node) {
|
||||||
|
for t := sibling.NextSibling; t != nil; t = t.NextSibling {
|
||||||
|
sibling = t
|
||||||
|
}
|
||||||
n.Parent = sibling.Parent
|
n.Parent = sibling.Parent
|
||||||
sibling.NextSibling = n
|
sibling.NextSibling = n
|
||||||
n.PrevSibling = sibling
|
n.PrevSibling = sibling
|
||||||
|
@ -245,8 +259,3 @@ quit:
|
||||||
func Parse(r io.Reader) (*Node, error) {
|
func Parse(r io.Reader) (*Node, error) {
|
||||||
return parse(r)
|
return parse(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseXML returns the parse tree for the XML from the given Reader.Deprecated.
|
|
||||||
func ParseXML(r io.Reader) (*Node, error) {
|
|
||||||
return parse(r)
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
module github.com/dylanmei/winrmtest
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e // indirect
|
||||||
|
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0
|
||||||
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
|
github.com/satori/go.uuid v1.2.0
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd // indirect
|
||||||
|
golang.org/x/text v0.3.0 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,17 @@
|
||||||
|
github.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e h1:ptBAamGVd6CfRsUtyHD+goy2JGhv1QC32v3gqM8mYAM=
|
||||||
|
github.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
||||||
|
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0 h1:JaCC8jz0zdMLk2m+qCCVLLLM/PL93p84w4pK3aJWj60=
|
||||||
|
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
@ -7,6 +7,8 @@ go:
|
||||||
- 1.5
|
- 1.5
|
||||||
- 1.6
|
- 1.6
|
||||||
- 1.7
|
- 1.7
|
||||||
|
- 1.8
|
||||||
|
- 1.9
|
||||||
- tip
|
- tip
|
||||||
matrix:
|
matrix:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (C) 2013-2016 by Maxim Bublis <b@codemonkey.ru>
|
Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
|
|
@ -59,7 +59,7 @@ func main() {
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|
||||||
Copyright (C) 2013-2016 by Maxim Bublis <b@codemonkey.ru>.
|
Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>.
|
||||||
|
|
||||||
UUID package released under MIT License.
|
UUID package released under MIT License.
|
||||||
See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details.
|
See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details.
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FromBytes returns UUID converted from raw byte slice input.
|
||||||
|
// It will return error if the slice isn't 16 bytes long.
|
||||||
|
func FromBytes(input []byte) (u UUID, err error) {
|
||||||
|
err = u.UnmarshalBinary(input)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromBytesOrNil returns UUID converted from raw byte slice input.
|
||||||
|
// Same behavior as FromBytes, but returns a Nil UUID on error.
|
||||||
|
func FromBytesOrNil(input []byte) UUID {
|
||||||
|
uuid, err := FromBytes(input)
|
||||||
|
if err != nil {
|
||||||
|
return Nil
|
||||||
|
}
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromString returns UUID parsed from string input.
|
||||||
|
// Input is expected in a form accepted by UnmarshalText.
|
||||||
|
func FromString(input string) (u UUID, err error) {
|
||||||
|
err = u.UnmarshalText([]byte(input))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStringOrNil returns UUID parsed from string input.
|
||||||
|
// Same behavior as FromString, but returns a Nil UUID on error.
|
||||||
|
func FromStringOrNil(input string) UUID {
|
||||||
|
uuid, err := FromString(input)
|
||||||
|
if err != nil {
|
||||||
|
return Nil
|
||||||
|
}
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText implements the encoding.TextMarshaler interface.
|
||||||
|
// The encoding is the same as returned by String.
|
||||||
|
func (u UUID) MarshalText() (text []byte, err error) {
|
||||||
|
text = []byte(u.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||||
|
// Following formats are supported:
|
||||||
|
// "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
||||||
|
// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
|
||||||
|
// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||||
|
// "6ba7b8109dad11d180b400c04fd430c8"
|
||||||
|
// ABNF for supported UUID text representation follows:
|
||||||
|
// uuid := canonical | hashlike | braced | urn
|
||||||
|
// plain := canonical | hashlike
|
||||||
|
// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct
|
||||||
|
// hashlike := 12hexoct
|
||||||
|
// braced := '{' plain '}'
|
||||||
|
// urn := URN ':' UUID-NID ':' plain
|
||||||
|
// URN := 'urn'
|
||||||
|
// UUID-NID := 'uuid'
|
||||||
|
// 12hexoct := 6hexoct 6hexoct
|
||||||
|
// 6hexoct := 4hexoct 2hexoct
|
||||||
|
// 4hexoct := 2hexoct 2hexoct
|
||||||
|
// 2hexoct := hexoct hexoct
|
||||||
|
// hexoct := hexdig hexdig
|
||||||
|
// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' |
|
||||||
|
// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' |
|
||||||
|
// 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
|
||||||
|
func (u *UUID) UnmarshalText(text []byte) (err error) {
|
||||||
|
switch len(text) {
|
||||||
|
case 32:
|
||||||
|
return u.decodeHashLike(text)
|
||||||
|
case 36:
|
||||||
|
return u.decodeCanonical(text)
|
||||||
|
case 38:
|
||||||
|
return u.decodeBraced(text)
|
||||||
|
case 41:
|
||||||
|
fallthrough
|
||||||
|
case 45:
|
||||||
|
return u.decodeURN(text)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("uuid: incorrect UUID length: %s", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeCanonical decodes UUID string in format
|
||||||
|
// "6ba7b810-9dad-11d1-80b4-00c04fd430c8".
|
||||||
|
func (u *UUID) decodeCanonical(t []byte) (err error) {
|
||||||
|
if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' {
|
||||||
|
return fmt.Errorf("uuid: incorrect UUID format %s", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
src := t[:]
|
||||||
|
dst := u[:]
|
||||||
|
|
||||||
|
for i, byteGroup := range byteGroups {
|
||||||
|
if i > 0 {
|
||||||
|
src = src[1:] // skip dash
|
||||||
|
}
|
||||||
|
_, err = hex.Decode(dst[:byteGroup/2], src[:byteGroup])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
src = src[byteGroup:]
|
||||||
|
dst = dst[byteGroup/2:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeHashLike decodes UUID string in format
|
||||||
|
// "6ba7b8109dad11d180b400c04fd430c8".
|
||||||
|
func (u *UUID) decodeHashLike(t []byte) (err error) {
|
||||||
|
src := t[:]
|
||||||
|
dst := u[:]
|
||||||
|
|
||||||
|
if _, err = hex.Decode(dst, src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeBraced decodes UUID string in format
|
||||||
|
// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" or in format
|
||||||
|
// "{6ba7b8109dad11d180b400c04fd430c8}".
|
||||||
|
func (u *UUID) decodeBraced(t []byte) (err error) {
|
||||||
|
l := len(t)
|
||||||
|
|
||||||
|
if t[0] != '{' || t[l-1] != '}' {
|
||||||
|
return fmt.Errorf("uuid: incorrect UUID format %s", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.decodePlain(t[1 : l-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeURN decodes UUID string in format
|
||||||
|
// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in format
|
||||||
|
// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8".
|
||||||
|
func (u *UUID) decodeURN(t []byte) (err error) {
|
||||||
|
total := len(t)
|
||||||
|
|
||||||
|
urn_uuid_prefix := t[:9]
|
||||||
|
|
||||||
|
if !bytes.Equal(urn_uuid_prefix, urnPrefix) {
|
||||||
|
return fmt.Errorf("uuid: incorrect UUID format: %s", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.decodePlain(t[9:total])
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodePlain decodes UUID string in canonical format
|
||||||
|
// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format
|
||||||
|
// "6ba7b8109dad11d180b400c04fd430c8".
|
||||||
|
func (u *UUID) decodePlain(t []byte) (err error) {
|
||||||
|
switch len(t) {
|
||||||
|
case 32:
|
||||||
|
return u.decodeHashLike(t)
|
||||||
|
case 36:
|
||||||
|
return u.decodeCanonical(t)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("uuid: incorrrect UUID length: %s", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
||||||
|
func (u UUID) MarshalBinary() (data []byte, err error) {
|
||||||
|
data = u.Bytes()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
|
||||||
|
// It will return error if the slice isn't 16 bytes long.
|
||||||
|
func (u *UUID) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
if len(data) != Size {
|
||||||
|
err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(u[:], data)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,239 @@
|
||||||
|
// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/binary"
|
||||||
|
"hash"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Difference in 100-nanosecond intervals between
|
||||||
|
// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
|
||||||
|
const epochStart = 122192928000000000
|
||||||
|
|
||||||
|
var (
|
||||||
|
global = newDefaultGenerator()
|
||||||
|
|
||||||
|
epochFunc = unixTimeFunc
|
||||||
|
posixUID = uint32(os.Getuid())
|
||||||
|
posixGID = uint32(os.Getgid())
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewV1 returns UUID based on current timestamp and MAC address.
|
||||||
|
func NewV1() UUID {
|
||||||
|
return global.NewV1()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV2 returns DCE Security UUID based on POSIX UID/GID.
|
||||||
|
func NewV2(domain byte) UUID {
|
||||||
|
return global.NewV2(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
|
||||||
|
func NewV3(ns UUID, name string) UUID {
|
||||||
|
return global.NewV3(ns, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV4 returns random generated UUID.
|
||||||
|
func NewV4() UUID {
|
||||||
|
return global.NewV4()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
|
||||||
|
func NewV5(ns UUID, name string) UUID {
|
||||||
|
return global.NewV5(ns, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generator provides interface for generating UUIDs.
|
||||||
|
type Generator interface {
|
||||||
|
NewV1() UUID
|
||||||
|
NewV2(domain byte) UUID
|
||||||
|
NewV3(ns UUID, name string) UUID
|
||||||
|
NewV4() UUID
|
||||||
|
NewV5(ns UUID, name string) UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default generator implementation.
|
||||||
|
type generator struct {
|
||||||
|
storageOnce sync.Once
|
||||||
|
storageMutex sync.Mutex
|
||||||
|
|
||||||
|
lastTime uint64
|
||||||
|
clockSequence uint16
|
||||||
|
hardwareAddr [6]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultGenerator() Generator {
|
||||||
|
return &generator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV1 returns UUID based on current timestamp and MAC address.
|
||||||
|
func (g *generator) NewV1() UUID {
|
||||||
|
u := UUID{}
|
||||||
|
|
||||||
|
timeNow, clockSeq, hardwareAddr := g.getStorage()
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
|
||||||
|
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
|
||||||
|
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
|
||||||
|
binary.BigEndian.PutUint16(u[8:], clockSeq)
|
||||||
|
|
||||||
|
copy(u[10:], hardwareAddr)
|
||||||
|
|
||||||
|
u.SetVersion(V1)
|
||||||
|
u.SetVariant(VariantRFC4122)
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV2 returns DCE Security UUID based on POSIX UID/GID.
|
||||||
|
func (g *generator) NewV2(domain byte) UUID {
|
||||||
|
u := UUID{}
|
||||||
|
|
||||||
|
timeNow, clockSeq, hardwareAddr := g.getStorage()
|
||||||
|
|
||||||
|
switch domain {
|
||||||
|
case DomainPerson:
|
||||||
|
binary.BigEndian.PutUint32(u[0:], posixUID)
|
||||||
|
case DomainGroup:
|
||||||
|
binary.BigEndian.PutUint32(u[0:], posixGID)
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
|
||||||
|
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
|
||||||
|
binary.BigEndian.PutUint16(u[8:], clockSeq)
|
||||||
|
u[9] = domain
|
||||||
|
|
||||||
|
copy(u[10:], hardwareAddr)
|
||||||
|
|
||||||
|
u.SetVersion(V2)
|
||||||
|
u.SetVariant(VariantRFC4122)
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
|
||||||
|
func (g *generator) NewV3(ns UUID, name string) UUID {
|
||||||
|
u := newFromHash(md5.New(), ns, name)
|
||||||
|
u.SetVersion(V3)
|
||||||
|
u.SetVariant(VariantRFC4122)
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV4 returns random generated UUID.
|
||||||
|
func (g *generator) NewV4() UUID {
|
||||||
|
u := UUID{}
|
||||||
|
g.safeRandom(u[:])
|
||||||
|
u.SetVersion(V4)
|
||||||
|
u.SetVariant(VariantRFC4122)
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
|
||||||
|
func (g *generator) NewV5(ns UUID, name string) UUID {
|
||||||
|
u := newFromHash(sha1.New(), ns, name)
|
||||||
|
u.SetVersion(V5)
|
||||||
|
u.SetVariant(VariantRFC4122)
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *generator) initStorage() {
|
||||||
|
g.initClockSequence()
|
||||||
|
g.initHardwareAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *generator) initClockSequence() {
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
g.safeRandom(buf)
|
||||||
|
g.clockSequence = binary.BigEndian.Uint16(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *generator) initHardwareAddr() {
|
||||||
|
interfaces, err := net.Interfaces()
|
||||||
|
if err == nil {
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if len(iface.HardwareAddr) >= 6 {
|
||||||
|
copy(g.hardwareAddr[:], iface.HardwareAddr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize hardwareAddr randomly in case
|
||||||
|
// of real network interfaces absence
|
||||||
|
g.safeRandom(g.hardwareAddr[:])
|
||||||
|
|
||||||
|
// Set multicast bit as recommended in RFC 4122
|
||||||
|
g.hardwareAddr[0] |= 0x01
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *generator) safeRandom(dest []byte) {
|
||||||
|
if _, err := rand.Read(dest); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns UUID v1/v2 storage state.
|
||||||
|
// Returns epoch timestamp, clock sequence, and hardware address.
|
||||||
|
func (g *generator) getStorage() (uint64, uint16, []byte) {
|
||||||
|
g.storageOnce.Do(g.initStorage)
|
||||||
|
|
||||||
|
g.storageMutex.Lock()
|
||||||
|
defer g.storageMutex.Unlock()
|
||||||
|
|
||||||
|
timeNow := epochFunc()
|
||||||
|
// Clock changed backwards since last UUID generation.
|
||||||
|
// Should increase clock sequence.
|
||||||
|
if timeNow <= g.lastTime {
|
||||||
|
g.clockSequence++
|
||||||
|
}
|
||||||
|
g.lastTime = timeNow
|
||||||
|
|
||||||
|
return timeNow, g.clockSequence, g.hardwareAddr[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns difference in 100-nanosecond intervals between
|
||||||
|
// UUID epoch (October 15, 1582) and current time.
|
||||||
|
// This is default epoch calculation function.
|
||||||
|
func unixTimeFunc() uint64 {
|
||||||
|
return epochStart + uint64(time.Now().UnixNano()/100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns UUID based on hashing of namespace UUID and name.
|
||||||
|
func newFromHash(h hash.Hash, ns UUID, name string) UUID {
|
||||||
|
u := UUID{}
|
||||||
|
h.Write(ns[:])
|
||||||
|
h.Write([]byte(name))
|
||||||
|
copy(u[:], h.Sum(nil))
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Value implements the driver.Valuer interface.
|
||||||
|
func (u UUID) Value() (driver.Value, error) {
|
||||||
|
return u.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the sql.Scanner interface.
|
||||||
|
// A 16-byte slice is handled by UnmarshalBinary, while
|
||||||
|
// a longer byte slice or a string is handled by UnmarshalText.
|
||||||
|
func (u *UUID) Scan(src interface{}) error {
|
||||||
|
switch src := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
if len(src) == Size {
|
||||||
|
return u.UnmarshalBinary(src)
|
||||||
|
}
|
||||||
|
return u.UnmarshalText(src)
|
||||||
|
|
||||||
|
case string:
|
||||||
|
return u.UnmarshalText([]byte(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("uuid: cannot convert %T to UUID", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NullUUID can be used with the standard sql package to represent a
|
||||||
|
// UUID value that can be NULL in the database
|
||||||
|
type NullUUID struct {
|
||||||
|
UUID UUID
|
||||||
|
Valid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver.Valuer interface.
|
||||||
|
func (u NullUUID) Value() (driver.Value, error) {
|
||||||
|
if !u.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// Delegate to UUID Value function
|
||||||
|
return u.UUID.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the sql.Scanner interface.
|
||||||
|
func (u *NullUUID) Scan(src interface{}) error {
|
||||||
|
if src == nil {
|
||||||
|
u.UUID, u.Valid = Nil, false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate to UUID Scan function
|
||||||
|
u.Valid = true
|
||||||
|
return u.UUID.Scan(src)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (C) 2013-2015 by Maxim Bublis <b@codemonkey.ru>
|
// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
// a copy of this software and associated documentation files (the
|
// a copy of this software and associated documentation files (the
|
||||||
|
@ -26,23 +26,29 @@ package uuid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1"
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
)
|
||||||
"hash"
|
|
||||||
"net"
|
// Size of a UUID in bytes.
|
||||||
"os"
|
const Size = 16
|
||||||
"sync"
|
|
||||||
"time"
|
// UUID representation compliant with specification
|
||||||
|
// described in RFC 4122.
|
||||||
|
type UUID [Size]byte
|
||||||
|
|
||||||
|
// UUID versions
|
||||||
|
const (
|
||||||
|
_ byte = iota
|
||||||
|
V1
|
||||||
|
V2
|
||||||
|
V3
|
||||||
|
V4
|
||||||
|
V5
|
||||||
)
|
)
|
||||||
|
|
||||||
// UUID layout variants.
|
// UUID layout variants.
|
||||||
const (
|
const (
|
||||||
VariantNCS = iota
|
VariantNCS byte = iota
|
||||||
VariantRFC4122
|
VariantRFC4122
|
||||||
VariantMicrosoft
|
VariantMicrosoft
|
||||||
VariantFuture
|
VariantFuture
|
||||||
|
@ -55,136 +61,48 @@ const (
|
||||||
DomainOrg
|
DomainOrg
|
||||||
)
|
)
|
||||||
|
|
||||||
// Difference in 100-nanosecond intervals between
|
|
||||||
// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
|
|
||||||
const epochStart = 122192928000000000
|
|
||||||
|
|
||||||
// Used in string method conversion
|
|
||||||
const dash byte = '-'
|
|
||||||
|
|
||||||
// UUID v1/v2 storage.
|
|
||||||
var (
|
|
||||||
storageMutex sync.Mutex
|
|
||||||
storageOnce sync.Once
|
|
||||||
epochFunc = unixTimeFunc
|
|
||||||
clockSequence uint16
|
|
||||||
lastTime uint64
|
|
||||||
hardwareAddr [6]byte
|
|
||||||
posixUID = uint32(os.Getuid())
|
|
||||||
posixGID = uint32(os.Getgid())
|
|
||||||
)
|
|
||||||
|
|
||||||
// String parse helpers.
|
// String parse helpers.
|
||||||
var (
|
var (
|
||||||
urnPrefix = []byte("urn:uuid:")
|
urnPrefix = []byte("urn:uuid:")
|
||||||
byteGroups = []int{8, 4, 4, 4, 12}
|
byteGroups = []int{8, 4, 4, 4, 12}
|
||||||
)
|
)
|
||||||
|
|
||||||
func initClockSequence() {
|
// Nil is special form of UUID that is specified to have all
|
||||||
buf := make([]byte, 2)
|
|
||||||
safeRandom(buf)
|
|
||||||
clockSequence = binary.BigEndian.Uint16(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initHardwareAddr() {
|
|
||||||
interfaces, err := net.Interfaces()
|
|
||||||
if err == nil {
|
|
||||||
for _, iface := range interfaces {
|
|
||||||
if len(iface.HardwareAddr) >= 6 {
|
|
||||||
copy(hardwareAddr[:], iface.HardwareAddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize hardwareAddr randomly in case
|
|
||||||
// of real network interfaces absence
|
|
||||||
safeRandom(hardwareAddr[:])
|
|
||||||
|
|
||||||
// Set multicast bit as recommended in RFC 4122
|
|
||||||
hardwareAddr[0] |= 0x01
|
|
||||||
}
|
|
||||||
|
|
||||||
func initStorage() {
|
|
||||||
initClockSequence()
|
|
||||||
initHardwareAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func safeRandom(dest []byte) {
|
|
||||||
if _, err := rand.Read(dest); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns difference in 100-nanosecond intervals between
|
|
||||||
// UUID epoch (October 15, 1582) and current time.
|
|
||||||
// This is default epoch calculation function.
|
|
||||||
func unixTimeFunc() uint64 {
|
|
||||||
return epochStart + uint64(time.Now().UnixNano()/100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UUID representation compliant with specification
|
|
||||||
// described in RFC 4122.
|
|
||||||
type UUID [16]byte
|
|
||||||
|
|
||||||
// NullUUID can be used with the standard sql package to represent a
|
|
||||||
// UUID value that can be NULL in the database
|
|
||||||
type NullUUID struct {
|
|
||||||
UUID UUID
|
|
||||||
Valid bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// The nil UUID is special form of UUID that is specified to have all
|
|
||||||
// 128 bits set to zero.
|
// 128 bits set to zero.
|
||||||
var Nil = UUID{}
|
var Nil = UUID{}
|
||||||
|
|
||||||
// Predefined namespace UUIDs.
|
// Predefined namespace UUIDs.
|
||||||
var (
|
var (
|
||||||
NamespaceDNS, _ = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
|
||||||
NamespaceURL, _ = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
|
NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
|
||||||
NamespaceOID, _ = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
|
NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
|
||||||
NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
|
||||||
)
|
)
|
||||||
|
|
||||||
// And returns result of binary AND of two UUIDs.
|
|
||||||
func And(u1 UUID, u2 UUID) UUID {
|
|
||||||
u := UUID{}
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
u[i] = u1[i] & u2[i]
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// Or returns result of binary OR of two UUIDs.
|
|
||||||
func Or(u1 UUID, u2 UUID) UUID {
|
|
||||||
u := UUID{}
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
u[i] = u1[i] | u2[i]
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal returns true if u1 and u2 equals, otherwise returns false.
|
// Equal returns true if u1 and u2 equals, otherwise returns false.
|
||||||
func Equal(u1 UUID, u2 UUID) bool {
|
func Equal(u1 UUID, u2 UUID) bool {
|
||||||
return bytes.Equal(u1[:], u2[:])
|
return bytes.Equal(u1[:], u2[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version returns algorithm version used to generate UUID.
|
// Version returns algorithm version used to generate UUID.
|
||||||
func (u UUID) Version() uint {
|
func (u UUID) Version() byte {
|
||||||
return uint(u[6] >> 4)
|
return u[6] >> 4
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variant returns UUID layout variant.
|
// Variant returns UUID layout variant.
|
||||||
func (u UUID) Variant() uint {
|
func (u UUID) Variant() byte {
|
||||||
switch {
|
switch {
|
||||||
case (u[8] & 0x80) == 0x00:
|
case (u[8] >> 7) == 0x00:
|
||||||
return VariantNCS
|
return VariantNCS
|
||||||
case (u[8]&0xc0)|0x80 == 0x80:
|
case (u[8] >> 6) == 0x02:
|
||||||
return VariantRFC4122
|
return VariantRFC4122
|
||||||
case (u[8]&0xe0)|0xc0 == 0xc0:
|
case (u[8] >> 5) == 0x06:
|
||||||
return VariantMicrosoft
|
return VariantMicrosoft
|
||||||
|
case (u[8] >> 5) == 0x07:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
return VariantFuture
|
||||||
}
|
}
|
||||||
return VariantFuture
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes returns bytes slice representation of UUID.
|
// Bytes returns bytes slice representation of UUID.
|
||||||
|
@ -198,13 +116,13 @@ func (u UUID) String() string {
|
||||||
buf := make([]byte, 36)
|
buf := make([]byte, 36)
|
||||||
|
|
||||||
hex.Encode(buf[0:8], u[0:4])
|
hex.Encode(buf[0:8], u[0:4])
|
||||||
buf[8] = dash
|
buf[8] = '-'
|
||||||
hex.Encode(buf[9:13], u[4:6])
|
hex.Encode(buf[9:13], u[4:6])
|
||||||
buf[13] = dash
|
buf[13] = '-'
|
||||||
hex.Encode(buf[14:18], u[6:8])
|
hex.Encode(buf[14:18], u[6:8])
|
||||||
buf[18] = dash
|
buf[18] = '-'
|
||||||
hex.Encode(buf[19:23], u[8:10])
|
hex.Encode(buf[19:23], u[8:10])
|
||||||
buf[23] = dash
|
buf[23] = '-'
|
||||||
hex.Encode(buf[24:], u[10:])
|
hex.Encode(buf[24:], u[10:])
|
||||||
|
|
||||||
return string(buf)
|
return string(buf)
|
||||||
|
@ -215,267 +133,29 @@ func (u *UUID) SetVersion(v byte) {
|
||||||
u[6] = (u[6] & 0x0f) | (v << 4)
|
u[6] = (u[6] & 0x0f) | (v << 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetVariant sets variant bits as described in RFC 4122.
|
// SetVariant sets variant bits.
|
||||||
func (u *UUID) SetVariant() {
|
func (u *UUID) SetVariant(v byte) {
|
||||||
u[8] = (u[8] & 0xbf) | 0x80
|
switch v {
|
||||||
}
|
case VariantNCS:
|
||||||
|
u[8] = (u[8]&(0xff>>1) | (0x00 << 7))
|
||||||
// MarshalText implements the encoding.TextMarshaler interface.
|
case VariantRFC4122:
|
||||||
// The encoding is the same as returned by String.
|
u[8] = (u[8]&(0xff>>2) | (0x02 << 6))
|
||||||
func (u UUID) MarshalText() (text []byte, err error) {
|
case VariantMicrosoft:
|
||||||
text = []byte(u.String())
|
u[8] = (u[8]&(0xff>>3) | (0x06 << 5))
|
||||||
return
|
case VariantFuture:
|
||||||
}
|
fallthrough
|
||||||
|
default:
|
||||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
u[8] = (u[8]&(0xff>>3) | (0x07 << 5))
|
||||||
// Following formats are supported:
|
|
||||||
// "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
||||||
// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
|
|
||||||
// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
||||||
func (u *UUID) UnmarshalText(text []byte) (err error) {
|
|
||||||
if len(text) < 32 {
|
|
||||||
err = fmt.Errorf("uuid: UUID string too short: %s", text)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t := text[:]
|
|
||||||
braced := false
|
|
||||||
|
|
||||||
if bytes.Equal(t[:9], urnPrefix) {
|
|
||||||
t = t[9:]
|
|
||||||
} else if t[0] == '{' {
|
|
||||||
braced = true
|
|
||||||
t = t[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
b := u[:]
|
|
||||||
|
|
||||||
for i, byteGroup := range byteGroups {
|
|
||||||
if i > 0 {
|
|
||||||
if t[0] != '-' {
|
|
||||||
err = fmt.Errorf("uuid: invalid string format")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t = t[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(t) < byteGroup {
|
|
||||||
err = fmt.Errorf("uuid: UUID string too short: %s", text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == 4 && len(t) > byteGroup &&
|
|
||||||
((braced && t[byteGroup] != '}') || len(t[byteGroup:]) > 1 || !braced) {
|
|
||||||
err = fmt.Errorf("uuid: UUID string too long: %s", text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = hex.Decode(b[:byteGroup/2], t[:byteGroup])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t = t[byteGroup:]
|
|
||||||
b = b[byteGroup/2:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
// Must is a helper that wraps a call to a function returning (UUID, error)
|
||||||
func (u UUID) MarshalBinary() (data []byte, err error) {
|
// and panics if the error is non-nil. It is intended for use in variable
|
||||||
data = u.Bytes()
|
// initializations such as
|
||||||
return
|
// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000"));
|
||||||
}
|
func Must(u UUID, err error) UUID {
|
||||||
|
|
||||||
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
|
|
||||||
// It will return error if the slice isn't 16 bytes long.
|
|
||||||
func (u *UUID) UnmarshalBinary(data []byte) (err error) {
|
|
||||||
if len(data) != 16 {
|
|
||||||
err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
copy(u[:], data)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the driver.Valuer interface.
|
|
||||||
func (u UUID) Value() (driver.Value, error) {
|
|
||||||
return u.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan implements the sql.Scanner interface.
|
|
||||||
// A 16-byte slice is handled by UnmarshalBinary, while
|
|
||||||
// a longer byte slice or a string is handled by UnmarshalText.
|
|
||||||
func (u *UUID) Scan(src interface{}) error {
|
|
||||||
switch src := src.(type) {
|
|
||||||
case []byte:
|
|
||||||
if len(src) == 16 {
|
|
||||||
return u.UnmarshalBinary(src)
|
|
||||||
}
|
|
||||||
return u.UnmarshalText(src)
|
|
||||||
|
|
||||||
case string:
|
|
||||||
return u.UnmarshalText([]byte(src))
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("uuid: cannot convert %T to UUID", src)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the driver.Valuer interface.
|
|
||||||
func (u NullUUID) Value() (driver.Value, error) {
|
|
||||||
if !u.Valid {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
// Delegate to UUID Value function
|
|
||||||
return u.UUID.Value()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan implements the sql.Scanner interface.
|
|
||||||
func (u *NullUUID) Scan(src interface{}) error {
|
|
||||||
if src == nil {
|
|
||||||
u.UUID, u.Valid = Nil, false
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate to UUID Scan function
|
|
||||||
u.Valid = true
|
|
||||||
return u.UUID.Scan(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromBytes returns UUID converted from raw byte slice input.
|
|
||||||
// It will return error if the slice isn't 16 bytes long.
|
|
||||||
func FromBytes(input []byte) (u UUID, err error) {
|
|
||||||
err = u.UnmarshalBinary(input)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromBytesOrNil returns UUID converted from raw byte slice input.
|
|
||||||
// Same behavior as FromBytes, but returns a Nil UUID on error.
|
|
||||||
func FromBytesOrNil(input []byte) UUID {
|
|
||||||
uuid, err := FromBytes(input)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Nil
|
panic(err)
|
||||||
}
|
}
|
||||||
return uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromString returns UUID parsed from string input.
|
|
||||||
// Input is expected in a form accepted by UnmarshalText.
|
|
||||||
func FromString(input string) (u UUID, err error) {
|
|
||||||
err = u.UnmarshalText([]byte(input))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromStringOrNil returns UUID parsed from string input.
|
|
||||||
// Same behavior as FromString, but returns a Nil UUID on error.
|
|
||||||
func FromStringOrNil(input string) UUID {
|
|
||||||
uuid, err := FromString(input)
|
|
||||||
if err != nil {
|
|
||||||
return Nil
|
|
||||||
}
|
|
||||||
return uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns UUID v1/v2 storage state.
|
|
||||||
// Returns epoch timestamp, clock sequence, and hardware address.
|
|
||||||
func getStorage() (uint64, uint16, []byte) {
|
|
||||||
storageOnce.Do(initStorage)
|
|
||||||
|
|
||||||
storageMutex.Lock()
|
|
||||||
defer storageMutex.Unlock()
|
|
||||||
|
|
||||||
timeNow := epochFunc()
|
|
||||||
// Clock changed backwards since last UUID generation.
|
|
||||||
// Should increase clock sequence.
|
|
||||||
if timeNow <= lastTime {
|
|
||||||
clockSequence++
|
|
||||||
}
|
|
||||||
lastTime = timeNow
|
|
||||||
|
|
||||||
return timeNow, clockSequence, hardwareAddr[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV1 returns UUID based on current timestamp and MAC address.
|
|
||||||
func NewV1() UUID {
|
|
||||||
u := UUID{}
|
|
||||||
|
|
||||||
timeNow, clockSeq, hardwareAddr := getStorage()
|
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
|
|
||||||
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
|
|
||||||
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
|
|
||||||
binary.BigEndian.PutUint16(u[8:], clockSeq)
|
|
||||||
|
|
||||||
copy(u[10:], hardwareAddr)
|
|
||||||
|
|
||||||
u.SetVersion(1)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV2 returns DCE Security UUID based on POSIX UID/GID.
|
|
||||||
func NewV2(domain byte) UUID {
|
|
||||||
u := UUID{}
|
|
||||||
|
|
||||||
timeNow, clockSeq, hardwareAddr := getStorage()
|
|
||||||
|
|
||||||
switch domain {
|
|
||||||
case DomainPerson:
|
|
||||||
binary.BigEndian.PutUint32(u[0:], posixUID)
|
|
||||||
case DomainGroup:
|
|
||||||
binary.BigEndian.PutUint32(u[0:], posixGID)
|
|
||||||
}
|
|
||||||
|
|
||||||
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
|
|
||||||
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
|
|
||||||
binary.BigEndian.PutUint16(u[8:], clockSeq)
|
|
||||||
u[9] = domain
|
|
||||||
|
|
||||||
copy(u[10:], hardwareAddr)
|
|
||||||
|
|
||||||
u.SetVersion(2)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
|
|
||||||
func NewV3(ns UUID, name string) UUID {
|
|
||||||
u := newFromHash(md5.New(), ns, name)
|
|
||||||
u.SetVersion(3)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV4 returns random generated UUID.
|
|
||||||
func NewV4() UUID {
|
|
||||||
u := UUID{}
|
|
||||||
safeRandom(u[:])
|
|
||||||
u.SetVersion(4)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
|
|
||||||
func NewV5(ns UUID, name string) UUID {
|
|
||||||
u := newFromHash(sha1.New(), ns, name)
|
|
||||||
u.SetVersion(5)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns UUID based on hashing of namespace UUID and name.
|
|
||||||
func newFromHash(h hash.Hash, ns UUID, name string) UUID {
|
|
||||||
u := UUID{}
|
|
||||||
h.Write(ns[:])
|
|
||||||
h.Write([]byte(name))
|
|
||||||
copy(u[:], h.Sum(nil))
|
|
||||||
|
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,9 +47,9 @@ github.com/agext/levenshtein
|
||||||
# github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6
|
# github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6
|
||||||
github.com/agl/ed25519
|
github.com/agl/ed25519
|
||||||
github.com/agl/ed25519/edwards25519
|
github.com/agl/ed25519/edwards25519
|
||||||
# github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd
|
# github.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e
|
||||||
github.com/antchfx/xpath
|
github.com/antchfx/xpath
|
||||||
# github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607
|
# github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0
|
||||||
github.com/antchfx/xquery/xml
|
github.com/antchfx/xquery/xml
|
||||||
# github.com/apparentlymart/go-cidr v1.0.0
|
# github.com/apparentlymart/go-cidr v1.0.0
|
||||||
github.com/apparentlymart/go-cidr/cidr
|
github.com/apparentlymart/go-cidr/cidr
|
||||||
|
@ -134,7 +134,7 @@ github.com/dgrijalva/jwt-go
|
||||||
github.com/dimchansky/utfbom
|
github.com/dimchansky/utfbom
|
||||||
# github.com/dylanmei/iso8601 v0.1.0
|
# github.com/dylanmei/iso8601 v0.1.0
|
||||||
github.com/dylanmei/iso8601
|
github.com/dylanmei/iso8601
|
||||||
# github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08
|
# github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1
|
||||||
github.com/dylanmei/winrmtest
|
github.com/dylanmei/winrmtest
|
||||||
# github.com/fatih/color v1.7.0
|
# github.com/fatih/color v1.7.0
|
||||||
github.com/fatih/color
|
github.com/fatih/color
|
||||||
|
@ -170,10 +170,10 @@ github.com/gophercloud/gophercloud
|
||||||
github.com/gophercloud/gophercloud/openstack
|
github.com/gophercloud/gophercloud/openstack
|
||||||
github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers
|
github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers
|
||||||
github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects
|
github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects
|
||||||
|
github.com/gophercloud/gophercloud/pagination
|
||||||
github.com/gophercloud/gophercloud/openstack/identity/v2/tokens
|
github.com/gophercloud/gophercloud/openstack/identity/v2/tokens
|
||||||
github.com/gophercloud/gophercloud/openstack/identity/v3/tokens
|
github.com/gophercloud/gophercloud/openstack/identity/v3/tokens
|
||||||
github.com/gophercloud/gophercloud/openstack/utils
|
github.com/gophercloud/gophercloud/openstack/utils
|
||||||
github.com/gophercloud/gophercloud/pagination
|
|
||||||
github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts
|
github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts
|
||||||
github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions
|
github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions
|
||||||
github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes
|
github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes
|
||||||
|
@ -408,7 +408,7 @@ github.com/posener/complete
|
||||||
github.com/posener/complete/cmd/install
|
github.com/posener/complete/cmd/install
|
||||||
github.com/posener/complete/cmd
|
github.com/posener/complete/cmd
|
||||||
github.com/posener/complete/match
|
github.com/posener/complete/match
|
||||||
# github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07
|
# github.com/satori/go.uuid v1.2.0
|
||||||
github.com/satori/go.uuid
|
github.com/satori/go.uuid
|
||||||
# github.com/spf13/afero v1.2.1
|
# github.com/spf13/afero v1.2.1
|
||||||
github.com/spf13/afero
|
github.com/spf13/afero
|
||||||
|
|
Loading…
Reference in New Issue