mirror of
				https://github.com/adnanh/webhook.git
				synced 2025-10-25 02:30:58 +00:00 
			
		
		
		
	Merge ac897f59aa into dbc6565c35
				
					
				
			This commit is contained in:
		
						commit
						296746ad82
					
				
					 457 changed files with 161 additions and 276813 deletions
				
			
		
							
								
								
									
										10
									
								
								.github/workflows/build.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/build.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -4,20 +4,20 @@ jobs: | |||
|   build: | ||||
|     strategy: | ||||
|       matrix: | ||||
|         go-version: [1.14.x, 1.15.x] | ||||
|         go-version: [1.19.x, 1.20.x] | ||||
|         os: [ubuntu-latest, macos-latest, windows-latest] | ||||
| 
 | ||||
|     runs-on: ${{ matrix.os }} | ||||
| 
 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - uses: actions/setup-go@v2 | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/setup-go@v4 | ||||
|         with: | ||||
|           go-version: ${{ matrix.go-version }} | ||||
|         id: go | ||||
| 
 | ||||
|       - name: Build | ||||
|         run: go build -v | ||||
|         run: make build | ||||
| 
 | ||||
|       - name: Test | ||||
|         run: go test -v ./... | ||||
|         run: make test | ||||
|  |  | |||
							
								
								
									
										2
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -10,7 +10,7 @@ help: | |||
| all: build release release-windows | ||||
| 
 | ||||
| build: deps ## Build the project
 | ||||
| 	go build | ||||
| 	go build -ldflags "-linkmode external -extldflags=-static" | ||||
| 
 | ||||
| release: clean deps ## Generate releases for unix systems
 | ||||
| 	@for arch in $(ARCHS);\
 | ||||
|  |  | |||
							
								
								
									
										20
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										20
									
								
								go.mod
									
										
									
									
									
								
							|  | @ -4,16 +4,14 @@ go 1.14 | |||
| 
 | ||||
| require ( | ||||
| 	github.com/clbanning/mxj v1.8.4 | ||||
| 	github.com/dustin/go-humanize v1.0.0 | ||||
| 	github.com/fsnotify/fsnotify v1.4.7 // indirect | ||||
| 	github.com/dustin/go-humanize v1.0.1 | ||||
| 	github.com/fsnotify/fsnotify v1.6.0 // indirect | ||||
| 	github.com/ghodss/yaml v1.0.0 | ||||
| 	github.com/go-chi/chi v4.0.2+incompatible | ||||
| 	github.com/gofrs/uuid v3.2.0+incompatible | ||||
| 	github.com/gorilla/mux v1.7.3 | ||||
| 	github.com/kr/pretty v0.1.0 // indirect | ||||
| 	golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect | ||||
| 	golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 | ||||
| 	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect | ||||
| 	gopkg.in/fsnotify.v1 v1.4.2 | ||||
| 	gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7 // indirect | ||||
| 	github.com/go-chi/chi v4.1.2+incompatible | ||||
| 	github.com/gofrs/uuid v4.4.0+incompatible | ||||
| 	github.com/gorilla/mux v1.8.0 | ||||
| 	golang.org/x/net v0.14.0 // indirect | ||||
| 	golang.org/x/sys v0.11.0 | ||||
| 	gopkg.in/fsnotify.v1 v1.4.7 | ||||
| 	gopkg.in/yaml.v2 v2.4.0 // indirect | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										83
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						
									
										83
									
								
								go.sum
									
										
									
									
									
								
							|  | @ -1,34 +1,63 @@ | |||
| github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= | ||||
| github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= | ||||
| github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= | ||||
| github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= | ||||
| github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= | ||||
| github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | ||||
| github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= | ||||
| github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= | ||||
| github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= | ||||
| github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= | ||||
| github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= | ||||
| github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= | ||||
| github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= | ||||
| github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= | ||||
| github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= | ||||
| github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= | ||||
| github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= | ||||
| github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= | ||||
| 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/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= | ||||
| github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= | ||||
| github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= | ||||
| github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= | ||||
| github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= | ||||
| github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= | ||||
| github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= | ||||
| golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= | ||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||
| golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= | ||||
| golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | ||||
| golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||||
| golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | ||||
| golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= | ||||
| golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= | ||||
| golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM= | ||||
| golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= | ||||
| golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
| golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= | ||||
| golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= | ||||
| golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= | ||||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/fsnotify.v1 v1.4.2 h1:AwZiD/bIUttYJ+n/k1UwlSUsM+VSE6id7UAnSKqQ+Tc= | ||||
| gopkg.in/fsnotify.v1 v1.4.2/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | ||||
| gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7 h1:+t9dhfO+GNOIGJof6kPOAenx7YgrZMTdRPV+EsnPabk= | ||||
| gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||
| golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | ||||
| golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= | ||||
| golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||||
| golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= | ||||
| gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | ||||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | ||||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||||
|  |  | |||
|  | @ -438,6 +438,7 @@ type Argument struct { | |||
| 	Name                    string   `json:"name,omitempty"` | ||||
| 	EnvName                 string   `json:"envname,omitempty"` | ||||
| 	Base64Decode            bool     `json:"base64decode,omitempty"` | ||||
| 	SignaturePayloadHeaders []string `json:"headers-to-add-to-signature-payload,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // Get Argument method returns the value for the Argument's key name | ||||
|  | @ -919,6 +920,7 @@ func (r MatchRule) Evaluate(req *Request) (bool, error) { | |||
| 
 | ||||
| 	arg, err := r.Parameter.Get(req) | ||||
| 	if err == nil { | ||||
| 		payload := r.signaturePayload(req.Body, req.Headers) | ||||
| 		switch r.Type { | ||||
| 		case MatchValue: | ||||
| 			return compare(arg, r.Value), nil | ||||
|  | @ -928,25 +930,38 @@ func (r MatchRule) Evaluate(req *Request) (bool, error) { | |||
| 			log.Print(`warn: use of deprecated option payload-hash-sha1; use payload-hmac-sha1 instead`) | ||||
| 			fallthrough | ||||
| 		case MatchHMACSHA1: | ||||
| 			_, err := CheckPayloadSignature(req.Body, r.Secret, arg) | ||||
| 			_, err := CheckPayloadSignature(payload, r.Secret, arg) | ||||
| 			return err == nil, err | ||||
| 		case MatchHashSHA256: | ||||
| 			log.Print(`warn: use of deprecated option payload-hash-sha256: use payload-hmac-sha256 instead`) | ||||
| 			fallthrough | ||||
| 		case MatchHMACSHA256: | ||||
| 			_, err := CheckPayloadSignature256(req.Body, r.Secret, arg) | ||||
| 			_, err := CheckPayloadSignature256(payload, r.Secret, arg) | ||||
| 			return err == nil, err | ||||
| 		case MatchHashSHA512: | ||||
| 			log.Print(`warn: use of deprecated option payload-hash-sha512: use payload-hmac-sha512 instead`) | ||||
| 			fallthrough | ||||
| 		case MatchHMACSHA512: | ||||
| 			_, err := CheckPayloadSignature512(req.Body, r.Secret, arg) | ||||
| 			_, err := CheckPayloadSignature512(payload, r.Secret, arg) | ||||
| 			return err == nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	return false, err | ||||
| } | ||||
| 
 | ||||
| // signaturePayload will concatenate the request body with header values from headers specified in r.Parameter.SignaturePayloadHeaders. | ||||
| func (r MatchRule) signaturePayload(body []byte, headers map[string]interface{}) []byte { | ||||
| 	for _, elem := range r.Parameter.SignaturePayloadHeaders { | ||||
| 		h, found := headers[elem] | ||||
| 		header := fmt.Sprintf("%s", h) | ||||
| 		if found && header != "" { | ||||
| 			body = append(body, []byte(header)...) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return body | ||||
| } | ||||
| 
 | ||||
| // compare is a helper function for constant time string comparisons. | ||||
| func compare(a, b string) bool { | ||||
| 	return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1 | ||||
|  |  | |||
|  | @ -274,7 +274,7 @@ var argumentGetTests = []struct { | |||
| 
 | ||||
| func TestArgumentGet(t *testing.T) { | ||||
| 	for _, tt := range argumentGetTests { | ||||
| 		a := Argument{tt.source, tt.name, "", false} | ||||
| 		a := Argument{tt.source, tt.name, "", false, nil} | ||||
| 		r := &Request{ | ||||
| 			Headers:    tt.headers, | ||||
| 			Query:      tt.query, | ||||
|  | @ -294,14 +294,14 @@ var hookParseJSONParametersTests = []struct { | |||
| 	rheaders, rquery, rpayload map[string]interface{} | ||||
| 	ok                         bool | ||||
| }{ | ||||
| 	{[]Argument{Argument{"header", "a", "", false}}, map[string]interface{}{"A": `{"b": "y"}`}, nil, nil, map[string]interface{}{"A": map[string]interface{}{"b": "y"}}, nil, nil, true}, | ||||
| 	{[]Argument{Argument{"url", "a", "", false}}, nil, map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true}, | ||||
| 	{[]Argument{Argument{"payload", "a", "", false}}, nil, nil, map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true}, | ||||
| 	{[]Argument{Argument{"header", "z", "", false}}, map[string]interface{}{"Z": `{}`}, nil, nil, map[string]interface{}{"Z": map[string]interface{}{}}, nil, nil, true}, | ||||
| 	{[]Argument{Argument{"header", "a", "", false, nil}}, map[string]interface{}{"A": `{"b": "y"}`}, nil, nil, map[string]interface{}{"A": map[string]interface{}{"b": "y"}}, nil, nil, true}, | ||||
| 	{[]Argument{Argument{"url", "a", "", false, nil}}, nil, map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true}, | ||||
| 	{[]Argument{Argument{"payload", "a", "", false, nil}}, nil, nil, map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true}, | ||||
| 	{[]Argument{Argument{"header", "z", "", false, nil}}, map[string]interface{}{"Z": `{}`}, nil, nil, map[string]interface{}{"Z": map[string]interface{}{}}, nil, nil, true}, | ||||
| 	// failures | ||||
| 	{[]Argument{Argument{"header", "z", "", false}}, map[string]interface{}{"Z": ``}, nil, nil, map[string]interface{}{"Z": ``}, nil, nil, false},     // empty string | ||||
| 	{[]Argument{Argument{"header", "y", "", false}}, map[string]interface{}{"X": `{}`}, nil, nil, map[string]interface{}{"X": `{}`}, nil, nil, false}, // missing parameter | ||||
| 	{[]Argument{Argument{"string", "z", "", false}}, map[string]interface{}{"Z": ``}, nil, nil, map[string]interface{}{"Z": ``}, nil, nil, false},     // invalid argument source | ||||
| 	{[]Argument{Argument{"header", "z", "", false, nil}}, map[string]interface{}{"Z": ``}, nil, nil, map[string]interface{}{"Z": ``}, nil, nil, false},     // empty string | ||||
| 	{[]Argument{Argument{"header", "y", "", false, nil}}, map[string]interface{}{"X": `{}`}, nil, nil, map[string]interface{}{"X": `{}`}, nil, nil, false}, // missing parameter | ||||
| 	{[]Argument{Argument{"string", "z", "", false, nil}}, map[string]interface{}{"Z": ``}, nil, nil, map[string]interface{}{"Z": ``}, nil, nil, false},     // invalid argument source | ||||
| } | ||||
| 
 | ||||
| func TestHookParseJSONParameters(t *testing.T) { | ||||
|  | @ -326,9 +326,9 @@ var hookExtractCommandArgumentsTests = []struct { | |||
| 	value                   []string | ||||
| 	ok                      bool | ||||
| }{ | ||||
| 	{"test", []Argument{Argument{"header", "a", "", false}}, map[string]interface{}{"A": "z"}, nil, nil, []string{"test", "z"}, true}, | ||||
| 	{"test", []Argument{Argument{"header", "a", "", false, nil}}, map[string]interface{}{"A": "z"}, nil, nil, []string{"test", "z"}, true}, | ||||
| 	// failures | ||||
| 	{"fail", []Argument{Argument{"payload", "a", "", false}}, map[string]interface{}{"A": "z"}, nil, nil, []string{"fail", ""}, false}, | ||||
| 	{"fail", []Argument{Argument{"payload", "a", "", false, nil}}, map[string]interface{}{"A": "z"}, nil, nil, []string{"fail", ""}, false}, | ||||
| } | ||||
| 
 | ||||
| func TestHookExtractCommandArguments(t *testing.T) { | ||||
|  | @ -351,6 +351,7 @@ func TestHookExtractCommandArguments(t *testing.T) { | |||
| // we test both cases where the name of the data is used as the name of the | ||||
| // env key & the case where the hook definition sets the env var name to a | ||||
| // fixed value using the envname construct like so:: | ||||
| // | ||||
| //	[ | ||||
| //	  { | ||||
| //	    "id": "push", | ||||
|  | @ -375,14 +376,14 @@ var hookExtractCommandArgumentsForEnvTests = []struct { | |||
| 	// successes | ||||
| 	{ | ||||
| 		"test", | ||||
| 		[]Argument{Argument{"header", "a", "", false}}, | ||||
| 		[]Argument{Argument{"header", "a", "", false, nil}}, | ||||
| 		map[string]interface{}{"A": "z"}, nil, nil, | ||||
| 		[]string{"HOOK_a=z"}, | ||||
| 		true, | ||||
| 	}, | ||||
| 	{ | ||||
| 		"test", | ||||
| 		[]Argument{Argument{"header", "a", "MYKEY", false}}, | ||||
| 		[]Argument{Argument{"header", "a", "MYKEY", false, nil}}, | ||||
| 		map[string]interface{}{"A": "z"}, nil, nil, | ||||
| 		[]string{"MYKEY=z"}, | ||||
| 		true, | ||||
|  | @ -390,7 +391,7 @@ var hookExtractCommandArgumentsForEnvTests = []struct { | |||
| 	// failures | ||||
| 	{ | ||||
| 		"fail", | ||||
| 		[]Argument{Argument{"payload", "a", "", false}}, | ||||
| 		[]Argument{Argument{"payload", "a", "", false, nil}}, | ||||
| 		map[string]interface{}{"A": "z"}, nil, nil, | ||||
| 		[]string{}, | ||||
| 		false, | ||||
|  | @ -489,24 +490,27 @@ var matchRuleTests = []struct { | |||
| 	ok                                 bool | ||||
| 	err                                bool | ||||
| }{ | ||||
| 	{"value", "", "", "z", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", true, false}, | ||||
| 	{"regex", "^z", "", "z", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", true, false}, | ||||
| 	{"payload-hmac-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	{"payload-hmac-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	{"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	{"value", "", "", "z", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", true, false}, | ||||
| 	{"regex", "^z", "", "z", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", true, false}, | ||||
| 	{"payload-hmac-sha1", "", "secret", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false, []string{"X-Header"}}, map[string]interface{}{"A": "63bfb5c18198b324c22a2349796ac1657cec24e5", "X-Header": "custom-value"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	{"payload-hmac-sha256", "", "secret", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	{"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	{"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false, []string{"X-Header"}}, map[string]interface{}{"A": "0a817ee9c28421c89e30792534fbe2dff5c246fe3a2e31d624ddab20f0227268", "X-Header": "custom-value"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	{"payload-hash-sha512", "", "secret", "", "", Argument{"header", "a", "", false, []string{"X-Header"}}, map[string]interface{}{"A": "08ed79bb565b6848666d40c874e2818552f897923cd43765dcd4983bfa6fa4d2443c3be0d3addda1cd416bcc538c48b0d328a9c4af6ce7a7fecd4ba7641a6412", "X-Header": "custom-value"}, nil, nil, []byte(`{"a": "z"}`), "", true, false}, | ||||
| 	// failures | ||||
| 	{"value", "", "", "X", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, false}, | ||||
| 	{"regex", "^X", "", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, false}, | ||||
| 	{"value", "", "2", "X", "", Argument{"header", "a", "", false}, map[string]interface{}{"Y": "z"}, nil, nil, []byte{}, "", false, true}, // reference invalid header | ||||
| 	{"value", "", "", "X", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, false}, | ||||
| 	{"regex", "^X", "", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, false}, | ||||
| 	{"value", "", "2", "X", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"Y": "z"}, nil, nil, []byte{}, "", false, true}, // reference invalid header | ||||
| 	// errors | ||||
| 	{"regex", "*", "", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, true},                   // invalid regex | ||||
| 	{"payload-hmac-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true},   // invalid hmac | ||||
| 	{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true},   // invalid hmac | ||||
| 	{"payload-hmac-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac | ||||
| 	{"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac | ||||
| 	{"payload-hmac-sha512", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac | ||||
| 	{"payload-hash-sha512", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac | ||||
| 	{"regex", "*", "", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, true},                   // invalid regex | ||||
| 	{"payload-hmac-sha1", "", "secret", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true},   // invalid hmac | ||||
| 	{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true},   // invalid hmac | ||||
| 	{"payload-hmac-sha256", "", "secret", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac | ||||
| 	{"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac | ||||
| 	{"payload-hmac-sha512", "", "secret", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac | ||||
| 	{"payload-hash-sha512", "", "secret", "", "", Argument{"header", "a", "", false, nil}, map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac | ||||
| 	// IP whitelisting, valid cases | ||||
| 	{"ip-whitelist", "", "", "", "192.168.0.1/24", Argument{}, nil, nil, nil, []byte{}, "192.168.0.2:9000", true, false}, // valid IPv4, with range | ||||
| 	{"ip-whitelist", "", "", "", "192.168.0.1/24", Argument{}, nil, nil, nil, []byte{}, "192.168.0.2:9000", true, false}, // valid IPv4, with range | ||||
|  | @ -552,8 +556,8 @@ var andRuleTests = []struct { | |||
| 	{ | ||||
| 		"(a=z, b=y): a=z && b=y", | ||||
| 		AndRule{ | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false, nil}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false, nil}, ""}}, | ||||
| 		}, | ||||
| 		map[string]interface{}{"A": "z", "B": "y"}, nil, nil, | ||||
| 		[]byte{}, | ||||
|  | @ -562,8 +566,8 @@ var andRuleTests = []struct { | |||
| 	{ | ||||
| 		"(a=z, b=Y): a=z && b=y", | ||||
| 		AndRule{ | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false, nil}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false, nil}, ""}}, | ||||
| 		}, | ||||
| 		map[string]interface{}{"A": "z", "B": "Y"}, nil, nil, | ||||
| 		[]byte{}, | ||||
|  | @ -573,22 +577,22 @@ var andRuleTests = []struct { | |||
| 	{ | ||||
| 		"(a=z, b=y, c=x, d=w=, e=X, f=X): a=z && (b=y && c=x) && (d=w || e=v) && !f=u", | ||||
| 		AndRule{ | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false, nil}, ""}}, | ||||
| 			{ | ||||
| 				And: &AndRule{ | ||||
| 					{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}}, | ||||
| 					{Match: &MatchRule{"value", "", "", "x", Argument{"header", "c", "", false}, ""}}, | ||||
| 					{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false, nil}, ""}}, | ||||
| 					{Match: &MatchRule{"value", "", "", "x", Argument{"header", "c", "", false, nil}, ""}}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			{ | ||||
| 				Or: &OrRule{ | ||||
| 					{Match: &MatchRule{"value", "", "", "w", Argument{"header", "d", "", false}, ""}}, | ||||
| 					{Match: &MatchRule{"value", "", "", "v", Argument{"header", "e", "", false}, ""}}, | ||||
| 					{Match: &MatchRule{"value", "", "", "w", Argument{"header", "d", "", false, nil}, ""}}, | ||||
| 					{Match: &MatchRule{"value", "", "", "v", Argument{"header", "e", "", false, nil}, ""}}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			{ | ||||
| 				Not: &NotRule{ | ||||
| 					Match: &MatchRule{"value", "", "", "u", Argument{"header", "f", "", false}, ""}, | ||||
| 					Match: &MatchRule{"value", "", "", "u", Argument{"header", "f", "", false, nil}, ""}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
|  | @ -600,7 +604,7 @@ var andRuleTests = []struct { | |||
| 	// failures | ||||
| 	{ | ||||
| 		"invalid rule", | ||||
| 		AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", "", false}, ""}}}, | ||||
| 		AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", "", false, nil}, ""}}}, | ||||
| 		map[string]interface{}{"Y": "z"}, nil, nil, nil, | ||||
| 		false, true, | ||||
| 	}, | ||||
|  | @ -632,8 +636,8 @@ var orRuleTests = []struct { | |||
| 	{ | ||||
| 		"(a=z, b=X): a=z || b=y", | ||||
| 		OrRule{ | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false, nil}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false, nil}, ""}}, | ||||
| 		}, | ||||
| 		map[string]interface{}{"A": "z", "B": "X"}, nil, nil, | ||||
| 		[]byte{}, | ||||
|  | @ -642,8 +646,8 @@ var orRuleTests = []struct { | |||
| 	{ | ||||
| 		"(a=X, b=y): a=z || b=y", | ||||
| 		OrRule{ | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false, nil}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false, nil}, ""}}, | ||||
| 		}, | ||||
| 		map[string]interface{}{"A": "X", "B": "y"}, nil, nil, | ||||
| 		[]byte{}, | ||||
|  | @ -652,8 +656,8 @@ var orRuleTests = []struct { | |||
| 	{ | ||||
| 		"(a=Z, b=Y): a=z || b=y", | ||||
| 		OrRule{ | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false, nil}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false, nil}, ""}}, | ||||
| 		}, | ||||
| 		map[string]interface{}{"A": "Z", "B": "Y"}, nil, nil, | ||||
| 		[]byte{}, | ||||
|  | @ -663,7 +667,7 @@ var orRuleTests = []struct { | |||
| 	{ | ||||
| 		"missing parameter node", | ||||
| 		OrRule{ | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, | ||||
| 			{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false, nil}, ""}}, | ||||
| 		}, | ||||
| 		map[string]interface{}{"Y": "Z"}, nil, nil, | ||||
| 		[]byte{}, | ||||
|  | @ -694,8 +698,8 @@ var notRuleTests = []struct { | |||
| 	ok                      bool | ||||
| 	err                     bool | ||||
| }{ | ||||
| 	{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", "", false}, ""}}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false}, | ||||
| 	{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false}, | ||||
| 	{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", "", false, nil}, ""}}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false}, | ||||
| 	{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false, nil}, ""}}, map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false}, | ||||
| } | ||||
| 
 | ||||
| func TestNotRule(t *testing.T) { | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ type LogEntry struct { | |||
| } | ||||
| 
 | ||||
| // Write constructs and writes the final log entry. | ||||
| func (l *LogEntry) Write(status, totalBytes int, elapsed time.Duration) { | ||||
| func (l *LogEntry) Write(status, totalBytes int, header http.Header, elapsed time.Duration, extra interface{}) { | ||||
| 	rid := GetReqID(l.req.Context()) | ||||
| 	if rid != "" { | ||||
| 		fmt.Fprintf(l.buf, "[%s] ", rid) | ||||
|  |  | |||
							
								
								
									
										55
									
								
								vendor/github.com/clbanning/mxj/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/clbanning/mxj/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,55 +0,0 @@ | |||
| Copyright (c) 2012-2016 Charles Banning <clbanning@gmail.com>.  All rights reserved. | ||||
| 
 | ||||
| The MIT License (MIT) | ||||
| 
 | ||||
| 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. | ||||
| 
 | ||||
| =============================================================================== | ||||
| 
 | ||||
| Go Language Copyright & License -  | ||||
| 
 | ||||
| Copyright 2009 The Go Authors. All rights reserved. | ||||
| Use of this source code is governed by a BSD-style | ||||
| license that can be found in the LICENSE file. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
| 
 | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										189
									
								
								vendor/github.com/clbanning/mxj/anyxml.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										189
									
								
								vendor/github.com/clbanning/mxj/anyxml.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,189 +0,0 @@ | |||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/xml" | ||||
| 	"reflect" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	DefaultElementTag = "element" | ||||
| ) | ||||
| 
 | ||||
| // Encode arbitrary value as XML. | ||||
| // | ||||
| // Note: unmarshaling the resultant | ||||
| // XML may not return the original value, since tag labels may have been injected | ||||
| // to create the XML representation of the value. | ||||
| /* | ||||
|  Encode an arbitrary JSON object. | ||||
| 	package main | ||||
| 
 | ||||
| 	import ( | ||||
| 		"encoding/json" | ||||
| 		"fmt" | ||||
| 		"github.com/clbanning/mxj" | ||||
| 	) | ||||
| 
 | ||||
| 	func main() { | ||||
| 		jsondata := []byte(`[ | ||||
| 			{ "somekey":"somevalue" }, | ||||
| 			"string", | ||||
| 			3.14159265, | ||||
| 			true | ||||
| 		]`) | ||||
| 		var i interface{} | ||||
| 		err := json.Unmarshal(jsondata, &i) | ||||
| 		if err != nil { | ||||
| 			// do something | ||||
| 		} | ||||
| 		x, err := mxj.AnyXmlIndent(i, "", "  ", "mydoc") | ||||
| 		if err != nil { | ||||
| 			// do something else | ||||
| 		} | ||||
| 		fmt.Println(string(x)) | ||||
| 	} | ||||
| 
 | ||||
| 	output: | ||||
| 		<mydoc> | ||||
| 		  <somekey>somevalue</somekey> | ||||
| 		  <element>string</element> | ||||
| 		  <element>3.14159265</element> | ||||
| 		  <element>true</element> | ||||
| 		</mydoc> | ||||
| */ | ||||
| // Alternative values for DefaultRootTag and DefaultElementTag can be set as: | ||||
| // AnyXml( v, myRootTag, myElementTag). | ||||
| func AnyXml(v interface{}, tags ...string) ([]byte, error) { | ||||
| 	var rt, et string | ||||
| 	if len(tags) == 1 || len(tags) == 2 { | ||||
| 		rt = tags[0] | ||||
| 	} else { | ||||
| 		rt = DefaultRootTag | ||||
| 	} | ||||
| 	if len(tags) == 2 { | ||||
| 		et = tags[1] | ||||
| 	} else { | ||||
| 		et = DefaultElementTag | ||||
| 	} | ||||
| 
 | ||||
| 	if v == nil { | ||||
| 		if useGoXmlEmptyElemSyntax { | ||||
| 			return []byte("<" + rt + "></" + rt + ">"), nil | ||||
| 		} | ||||
| 		return []byte("<" + rt + "/>"), nil | ||||
| 	} | ||||
| 	if reflect.TypeOf(v).Kind() == reflect.Struct { | ||||
| 		return xml.Marshal(v) | ||||
| 	} | ||||
| 
 | ||||
| 	var err error | ||||
| 	s := new(string) | ||||
| 	p := new(pretty) | ||||
| 
 | ||||
| 	var ss string | ||||
| 	var b []byte | ||||
| 	switch v.(type) { | ||||
| 	case []interface{}: | ||||
| 		ss = "<" + rt + ">" | ||||
| 		for _, vv := range v.([]interface{}) { | ||||
| 			switch vv.(type) { | ||||
| 			case map[string]interface{}: | ||||
| 				m := vv.(map[string]interface{}) | ||||
| 				if len(m) == 1 { | ||||
| 					for tag, val := range m { | ||||
| 						err = mapToXmlIndent(false, s, tag, val, p) | ||||
| 					} | ||||
| 				} else { | ||||
| 					err = mapToXmlIndent(false, s, et, vv, p) | ||||
| 				} | ||||
| 			default: | ||||
| 				err = mapToXmlIndent(false, s, et, vv, p) | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		ss += *s + "</" + rt + ">" | ||||
| 		b = []byte(ss) | ||||
| 	case map[string]interface{}: | ||||
| 		m := Map(v.(map[string]interface{})) | ||||
| 		b, err = m.Xml(rt) | ||||
| 	default: | ||||
| 		err = mapToXmlIndent(false, s, rt, v, p) | ||||
| 		b = []byte(*s) | ||||
| 	} | ||||
| 
 | ||||
| 	return b, err | ||||
| } | ||||
| 
 | ||||
| // Encode an arbitrary value as a pretty XML string. | ||||
| // Alternative values for DefaultRootTag and DefaultElementTag can be set as: | ||||
| // AnyXmlIndent( v, "", "  ", myRootTag, myElementTag). | ||||
| func AnyXmlIndent(v interface{}, prefix, indent string, tags ...string) ([]byte, error) { | ||||
| 	var rt, et string | ||||
| 	if len(tags) == 1 || len(tags) == 2 { | ||||
| 		rt = tags[0] | ||||
| 	} else { | ||||
| 		rt = DefaultRootTag | ||||
| 	} | ||||
| 	if len(tags) == 2 { | ||||
| 		et = tags[1] | ||||
| 	} else { | ||||
| 		et = DefaultElementTag | ||||
| 	} | ||||
| 
 | ||||
| 	if v == nil { | ||||
| 		if useGoXmlEmptyElemSyntax { | ||||
| 			return []byte(prefix + "<" + rt + "></" + rt + ">"), nil | ||||
| 		} | ||||
| 		return []byte(prefix + "<" + rt + "/>"), nil | ||||
| 	} | ||||
| 	if reflect.TypeOf(v).Kind() == reflect.Struct { | ||||
| 		return xml.MarshalIndent(v, prefix, indent) | ||||
| 	} | ||||
| 
 | ||||
| 	var err error | ||||
| 	s := new(string) | ||||
| 	p := new(pretty) | ||||
| 	p.indent = indent | ||||
| 	p.padding = prefix | ||||
| 
 | ||||
| 	var ss string | ||||
| 	var b []byte | ||||
| 	switch v.(type) { | ||||
| 	case []interface{}: | ||||
| 		ss = "<" + rt + ">\n" | ||||
| 		p.Indent() | ||||
| 		for _, vv := range v.([]interface{}) { | ||||
| 			switch vv.(type) { | ||||
| 			case map[string]interface{}: | ||||
| 				m := vv.(map[string]interface{}) | ||||
| 				if len(m) == 1 { | ||||
| 					for tag, val := range m { | ||||
| 						err = mapToXmlIndent(true, s, tag, val, p) | ||||
| 					} | ||||
| 				} else { | ||||
| 					p.start = 1 // we 1 tag in | ||||
| 					err = mapToXmlIndent(true, s, et, vv, p) | ||||
| 					*s += "\n" | ||||
| 				} | ||||
| 			default: | ||||
| 				p.start = 0 // in case trailing p.start = 1 | ||||
| 				err = mapToXmlIndent(true, s, et, vv, p) | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		ss += *s + "</" + rt + ">" | ||||
| 		b = []byte(ss) | ||||
| 	case map[string]interface{}: | ||||
| 		m := Map(v.(map[string]interface{})) | ||||
| 		b, err = m.XmlIndent(prefix, indent, rt) | ||||
| 	default: | ||||
| 		err = mapToXmlIndent(true, s, rt, v, p) | ||||
| 		b = []byte(*s) | ||||
| 	} | ||||
| 
 | ||||
| 	return b, err | ||||
| } | ||||
							
								
								
									
										54
									
								
								vendor/github.com/clbanning/mxj/atomFeedString.xml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/clbanning/mxj/atomFeedString.xml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,54 +0,0 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us" updated="2009-10-04T01:35:58+00:00"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><link href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></link><id>http://codereview.appspot.com/</id><author><name>rietveld<></name></author><entry><title>rietveld: an attempt at pubsubhubbub | ||||
| </title><link href="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html"> | ||||
|   An attempt at adding pubsubhubbub support to Rietveld. | ||||
| http://code.google.com/p/pubsubhubbub | ||||
| http://code.google.com/p/rietveld/issues/detail?id=155 | ||||
| 
 | ||||
| The server side of the protocol is trivial: | ||||
|   1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all | ||||
|      feeds that will be pubsubhubbubbed. | ||||
|   2. every time one of those feeds changes, tell the hub | ||||
|      with a simple POST request. | ||||
| 
 | ||||
| I have tested this by adding debug prints to a local hub | ||||
| server and checking that the server got the right publish | ||||
| requests. | ||||
| 
 | ||||
| I can&#39;t quite get the server to work, but I think the bug | ||||
| is not in my code.  I think that the server expects to be | ||||
| able to grab the feed and see the feed&#39;s actual URL in | ||||
| the link rel=&quot;self&quot;, but the default value for that drops | ||||
| the :port from the URL, and I cannot for the life of me | ||||
| figure out how to get the Atom generator deep inside | ||||
| django not to do that, or even where it is doing that, | ||||
| or even what code is running to generate the Atom feed. | ||||
| (I thought I knew but I added some assert False statements | ||||
| and it kept running!) | ||||
| 
 | ||||
| Ignoring that particular problem, I would appreciate | ||||
| feedback on the right way to get the two values at | ||||
| the top of feeds.py marked NOTE(rsc). | ||||
| 
 | ||||
| 
 | ||||
| </summary></entry><entry><title>rietveld: correct tab handling | ||||
| </title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html"> | ||||
|   This fixes the buggy tab rendering that can be seen at | ||||
| http://codereview.appspot.com/116075/diff/1/2 | ||||
| 
 | ||||
| The fundamental problem was that the tab code was | ||||
| not being told what column the text began in, so it | ||||
| didn&#39;t know where to put the tab stops.  Another problem | ||||
| was that some of the code assumed that string byte | ||||
| offsets were the same as column offsets, which is only | ||||
| true if there are no tabs. | ||||
| 
 | ||||
| In the process of fixing this, I cleaned up the arguments | ||||
| to Fold and ExpandTabs and renamed them Break and | ||||
| _ExpandTabs so that I could be sure that I found all the | ||||
| call sites.  I also wanted to verify that ExpandTabs was | ||||
| not being used from outside intra_region_diff.py. | ||||
| 
 | ||||
| 
 | ||||
| </summary></entry></feed> 	   ` | ||||
| 
 | ||||
							
								
								
									
										134
									
								
								vendor/github.com/clbanning/mxj/doc.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										134
									
								
								vendor/github.com/clbanning/mxj/doc.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,134 +0,0 @@ | |||
| // mxj - A collection of map[string]interface{} and associated XML and JSON utilities. | ||||
| // Copyright 2012-2015, 2018 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| /* | ||||
| Marshal/Unmarshal XML to/from map[string]interface{} values (and JSON); extract/modify values from maps by key or key-path, including wildcards. | ||||
| 
 | ||||
| mxj supplants the legacy x2j and j2x packages. The subpackage x2j-wrapper is provided to facilitate migrating from the x2j package.  The x2j and j2x subpackages provide similar functionality of the old packages but are not function-name compatible with them. | ||||
| 
 | ||||
| Note: this library was designed for processing ad hoc anonymous messages.  Bulk processing large data sets may be much more efficiently performed using the encoding/xml or encoding/json packages from Go's standard library directly. | ||||
| 
 | ||||
| Related Packages: | ||||
| 	checkxml: github.com/clbanning/checkxml provides functions for validating XML data. | ||||
| 
 | ||||
| Notes: | ||||
| 	2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc. | ||||
| 	2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps. | ||||
| 	2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package. | ||||
| 	2017.02.22: LeafNode paths can use ".N" syntax rather than "[N]" for list member indexing. | ||||
| 	2017.02.21: github.com/clbanning/checkxml provides functions for validating XML data. | ||||
| 	2017.02.10: SetFieldSeparator changes field separator for args in UpdateValuesForPath, ValuesFor... methods. | ||||
| 	2017.02.06: Support XMPP stream processing - HandleXMPPStreamTag(). | ||||
| 	2016.11.07: Preserve name space prefix syntax in XmlSeq parser - NewMapXmlSeq(), etc. | ||||
| 	2016.06.25: Support overriding default XML attribute prefix, "-", in Map keys - SetAttrPrefix(). | ||||
| 	2016.05.26: Support customization of xml.Decoder by exposing CustomDecoder variable. | ||||
| 	2016.03.19: Escape invalid chars when encoding XML attribute and element values - XMLEscapeChars(). | ||||
| 	2016.03.02: By default decoding XML with float64 and bool value casting will not cast "NaN", "Inf", and "-Inf". | ||||
| 	            To cast them to float64, first set flag with CastNanInf(true). | ||||
| 	2016.02.22: New mv.Root(), mv.Elements(), mv.Attributes methods let you examine XML document structure. | ||||
| 	2016.02.16: Add CoerceKeysToLower() option to handle tags with mixed capitalization. | ||||
| 	2016.02.12: Seek for first xml.StartElement token; only return error if io.EOF is reached first (handles BOM). | ||||
| 	2015-12-02: NewMapXmlSeq() with mv.XmlSeq() & co. will try to preserve structure of XML doc when re-encoding. | ||||
| 	2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML. | ||||
| 
 | ||||
| SUMMARY | ||||
| 
 | ||||
|    type Map map[string]interface{} | ||||
| 
 | ||||
|    Create a Map value, 'mv', from any map[string]interface{} value, 'v': | ||||
|       mv := Map(v) | ||||
| 
 | ||||
|    Unmarshal / marshal XML as a Map value, 'mv': | ||||
|       mv, err := NewMapXml(xmlValue) // unmarshal | ||||
|       xmlValue, err := mv.Xml()      // marshal | ||||
| 
 | ||||
|    Unmarshal XML from an io.Reader as a Map value, 'mv': | ||||
|       mv, err := NewMapXmlReader(xmlReader)         // repeated calls, as with an os.File Reader, will process stream | ||||
|       mv, raw, err := NewMapXmlReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded | ||||
| 
 | ||||
|    Marshal Map value, 'mv', to an XML Writer (io.Writer): | ||||
|       err := mv.XmlWriter(xmlWriter) | ||||
|       raw, err := mv.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter | ||||
| 
 | ||||
|    Also, for prettified output: | ||||
|       xmlValue, err := mv.XmlIndent(prefix, indent, ...) | ||||
|       err := mv.XmlIndentWriter(xmlWriter, prefix, indent, ...) | ||||
|       raw, err := mv.XmlIndentWriterRaw(xmlWriter, prefix, indent, ...) | ||||
| 
 | ||||
|    Bulk process XML with error handling (note: handlers must return a boolean value): | ||||
|       err := HandleXmlReader(xmlReader, mapHandler(Map), errHandler(error)) | ||||
|       err := HandleXmlReaderRaw(xmlReader, mapHandler(Map, []byte), errHandler(error, []byte)) | ||||
| 
 | ||||
|    Converting XML to JSON: see Examples for NewMapXml and HandleXmlReader. | ||||
| 
 | ||||
|    There are comparable functions and methods for JSON processing. | ||||
| 
 | ||||
|    Arbitrary structure values can be decoded to / encoded from Map values: | ||||
|       mv, err := NewMapStruct(structVal) | ||||
|       err := mv.Struct(structPointer) | ||||
| 
 | ||||
|    To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON | ||||
|    or structure to a Map value, 'mv', or cast a map[string]interface{} value to a Map value, 'mv', then: | ||||
|       paths := mv.PathsForKey(key) | ||||
|       path := mv.PathForKeyShortest(key) | ||||
|       values, err := mv.ValuesForKey(key, subkeys) | ||||
|       values, err := mv.ValuesForPath(path, subkeys) // 'path' can be dot-notation with wildcards and indexed arrays. | ||||
|       count, err := mv.UpdateValuesForPath(newVal, path, subkeys) | ||||
| 
 | ||||
|    Get everything at once, irrespective of path depth: | ||||
|       leafnodes := mv.LeafNodes() | ||||
|       leafvalues := mv.LeafValues() | ||||
| 
 | ||||
|    A new Map with whatever keys are desired can be created from the current Map and then encoded in XML | ||||
|    or JSON. (Note: keys can use dot-notation. 'oldKey' can also use wildcards and indexed arrays.) | ||||
|       newMap, err := mv.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N") | ||||
|       newMap, err := mv.NewMap("oldKey1", "oldKey3", "oldKey5") // a subset of 'mv'; see "examples/partial.go" | ||||
|       newXml, err := newMap.Xml()   // for example | ||||
|       newJson, err := newMap.Json() // ditto | ||||
| 
 | ||||
| XML PARSING CONVENTIONS | ||||
| 
 | ||||
|    Using NewMapXml() | ||||
| 
 | ||||
|    - Attributes are parsed to `map[string]interface{}` values by prefixing a hyphen, `-`, | ||||
|      to the attribute label. (Unless overridden by `PrependAttrWithHyphen(false)` or | ||||
|      `SetAttrPrefix()`.) | ||||
|    - If the element is a simple element and has attributes, the element value | ||||
|      is given the key `#text` for its `map[string]interface{}` representation.  (See | ||||
|      the 'atomFeedString.xml' test data, below.) | ||||
|    - XML comments, directives, and process instructions are ignored. | ||||
|    - If CoerceKeysToLower() has been called, then the resultant keys will be lower case. | ||||
| 
 | ||||
|    Using NewMapXmlSeq() | ||||
| 
 | ||||
|    - Attributes are parsed to `map["#attr"]map[<attr_label>]map[string]interface{}`values | ||||
|      where the `<attr_label>` value has "#text" and "#seq" keys - the "#text" key holds the  | ||||
|      value for `<attr_label>`. | ||||
|    - All elements, except for the root, have a "#seq" key. | ||||
|    - Comments, directives, and process instructions are unmarshalled into the Map using the | ||||
|      keys "#comment", "#directive", and "#procinst", respectively. (See documentation for more | ||||
|      specifics.) | ||||
|    - Name space syntax is preserved:  | ||||
|       - <ns:key>something</ns.key> parses to map["ns:key"]interface{}{"something"} | ||||
|       - xmlns:ns="http://myns.com/ns" parses to map["xmlns:ns"]interface{}{"http://myns.com/ns"} | ||||
| 
 | ||||
|    Both | ||||
| 
 | ||||
|    - By default, "Nan", "Inf", and "-Inf" values are not cast to float64.  If you want them | ||||
|      to be cast, set a flag to cast them  using CastNanInf(true). | ||||
| 
 | ||||
| XML ENCODING CONVENTIONS | ||||
|     | ||||
|    - 'nil' Map values, which may represent 'null' JSON values, are encoded as "<tag/>". | ||||
|      NOTE: the operation is not symmetric as "<tag/>" elements are decoded as 'tag:""' Map values, | ||||
|            which, then, encode in JSON as '"tag":""' values.. | ||||
|    - ALSO: there is no guarantee that the encoded XML doc will be the same as the decoded one.  (Go | ||||
|            randomizes the walk through map[string]interface{} values.) If you plan to re-encode the | ||||
|            Map value to XML and want the same sequencing of elements look at NewMapXmlSeq() and | ||||
|            mv.XmlSeq() - these try to preserve the element sequencing but with added complexity when | ||||
|            working with the Map representation. | ||||
| 
 | ||||
| */ | ||||
| package mxj | ||||
							
								
								
									
										54
									
								
								vendor/github.com/clbanning/mxj/escapechars.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/clbanning/mxj/escapechars.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,54 +0,0 @@ | |||
| // Copyright 2016 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| ) | ||||
| 
 | ||||
| var xmlEscapeChars bool | ||||
| 
 | ||||
| // XMLEscapeChars(true) forces escaping invalid characters in attribute and element values. | ||||
| // NOTE: this is brute force with NO interrogation of '&' being escaped already; if it is | ||||
| // then '&' will be re-escaped as '&amp;'. | ||||
| //   | ||||
| /* | ||||
| 	The values are: | ||||
| 	"   " | ||||
| 	'   ' | ||||
| 	<   < | ||||
| 	>   > | ||||
| 	&   & | ||||
| */ | ||||
| func XMLEscapeChars(b bool) { | ||||
| 	xmlEscapeChars = b | ||||
| } | ||||
| 
 | ||||
| // Scan for '&' first, since 's' may contain "&" that is parsed to "&amp;"  | ||||
| // - or "<" that is parsed to "&lt;". | ||||
| var escapechars = [][2][]byte{ | ||||
| 	{[]byte(`&`), []byte(`&`)}, | ||||
| 	{[]byte(`<`), []byte(`<`)}, | ||||
| 	{[]byte(`>`), []byte(`>`)}, | ||||
| 	{[]byte(`"`), []byte(`"`)}, | ||||
| 	{[]byte(`'`), []byte(`'`)}, | ||||
| } | ||||
| 
 | ||||
| func escapeChars(s string) string { | ||||
| 	if len(s) == 0 { | ||||
| 		return s | ||||
| 	} | ||||
| 
 | ||||
| 	b := []byte(s) | ||||
| 	for _, v := range escapechars { | ||||
| 		n := bytes.Count(b, v[0]) | ||||
| 		if n == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		b = bytes.Replace(b, v[0], v[1], n) | ||||
| 	} | ||||
| 	return string(b) | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										7
									
								
								vendor/github.com/clbanning/mxj/exists.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/clbanning/mxj/exists.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,7 +0,0 @@ | |||
| package mxj | ||||
| 
 | ||||
| // Checks whether the path exists | ||||
| func (mv Map) Exists(path string, subkeys ...string) bool { | ||||
| 	v, err := mv.ValuesForPath(path, subkeys...) | ||||
| 	return err == nil && len(v) > 0 | ||||
| } | ||||
							
								
								
									
										287
									
								
								vendor/github.com/clbanning/mxj/files.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										287
									
								
								vendor/github.com/clbanning/mxj/files.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,287 +0,0 @@ | |||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| type Maps []Map | ||||
| 
 | ||||
| func NewMaps() Maps { | ||||
| 	return make(Maps, 0) | ||||
| } | ||||
| 
 | ||||
| type MapRaw struct { | ||||
| 	M Map | ||||
| 	R []byte | ||||
| } | ||||
| 
 | ||||
| // NewMapsFromXmlFile - creates an array from a file of JSON values. | ||||
| func NewMapsFromJsonFile(name string) (Maps, error) { | ||||
| 	fi, err := os.Stat(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if !fi.Mode().IsRegular() { | ||||
| 		return nil, fmt.Errorf("file %s is not a regular file", name) | ||||
| 	} | ||||
| 
 | ||||
| 	fh, err := os.Open(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer fh.Close() | ||||
| 
 | ||||
| 	am := make([]Map, 0) | ||||
| 	for { | ||||
| 		m, raw, err := NewMapJsonReaderRaw(fh) | ||||
| 		if err != nil && err != io.EOF { | ||||
| 			return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(raw)) | ||||
| 		} | ||||
| 		if len(m) > 0 { | ||||
| 			am = append(am, m) | ||||
| 		} | ||||
| 		if err == io.EOF { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return am, nil | ||||
| } | ||||
| 
 | ||||
| // ReadMapsFromJsonFileRaw - creates an array of MapRaw from a file of JSON values. | ||||
| func NewMapsFromJsonFileRaw(name string) ([]MapRaw, error) { | ||||
| 	fi, err := os.Stat(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if !fi.Mode().IsRegular() { | ||||
| 		return nil, fmt.Errorf("file %s is not a regular file", name) | ||||
| 	} | ||||
| 
 | ||||
| 	fh, err := os.Open(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer fh.Close() | ||||
| 
 | ||||
| 	am := make([]MapRaw, 0) | ||||
| 	for { | ||||
| 		mr := new(MapRaw) | ||||
| 		mr.M, mr.R, err = NewMapJsonReaderRaw(fh) | ||||
| 		if err != nil && err != io.EOF { | ||||
| 			return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(mr.R)) | ||||
| 		} | ||||
| 		if len(mr.M) > 0 { | ||||
| 			am = append(am, *mr) | ||||
| 		} | ||||
| 		if err == io.EOF { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return am, nil | ||||
| } | ||||
| 
 | ||||
| // NewMapsFromXmlFile - creates an array from a file of XML values. | ||||
| func NewMapsFromXmlFile(name string) (Maps, error) { | ||||
| 	fi, err := os.Stat(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if !fi.Mode().IsRegular() { | ||||
| 		return nil, fmt.Errorf("file %s is not a regular file", name) | ||||
| 	} | ||||
| 
 | ||||
| 	fh, err := os.Open(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer fh.Close() | ||||
| 
 | ||||
| 	am := make([]Map, 0) | ||||
| 	for { | ||||
| 		m, raw, err := NewMapXmlReaderRaw(fh) | ||||
| 		if err != nil && err != io.EOF { | ||||
| 			return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(raw)) | ||||
| 		} | ||||
| 		if len(m) > 0 { | ||||
| 			am = append(am, m) | ||||
| 		} | ||||
| 		if err == io.EOF { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return am, nil | ||||
| } | ||||
| 
 | ||||
| // NewMapsFromXmlFileRaw - creates an array of MapRaw from a file of XML values. | ||||
| // NOTE: the slice with the raw XML is clean with no extra capacity - unlike NewMapXmlReaderRaw(). | ||||
| // It is slow at parsing a file from disk and is intended for relatively small utility files. | ||||
| func NewMapsFromXmlFileRaw(name string) ([]MapRaw, error) { | ||||
| 	fi, err := os.Stat(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if !fi.Mode().IsRegular() { | ||||
| 		return nil, fmt.Errorf("file %s is not a regular file", name) | ||||
| 	} | ||||
| 
 | ||||
| 	fh, err := os.Open(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer fh.Close() | ||||
| 
 | ||||
| 	am := make([]MapRaw, 0) | ||||
| 	for { | ||||
| 		mr := new(MapRaw) | ||||
| 		mr.M, mr.R, err = NewMapXmlReaderRaw(fh) | ||||
| 		if err != nil && err != io.EOF { | ||||
| 			return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(mr.R)) | ||||
| 		} | ||||
| 		if len(mr.M) > 0 { | ||||
| 			am = append(am, *mr) | ||||
| 		} | ||||
| 		if err == io.EOF { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return am, nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------ Maps writing ------------------------- | ||||
| // These are handy-dandy methods for dumping configuration data, etc. | ||||
| 
 | ||||
| // JsonString - analogous to mv.Json() | ||||
| func (mvs Maps) JsonString(safeEncoding ...bool) (string, error) { | ||||
| 	var s string | ||||
| 	for _, v := range mvs { | ||||
| 		j, err := v.Json() | ||||
| 		if err != nil { | ||||
| 			return s, err | ||||
| 		} | ||||
| 		s += string(j) | ||||
| 	} | ||||
| 	return s, nil | ||||
| } | ||||
| 
 | ||||
| // JsonStringIndent - analogous to mv.JsonIndent() | ||||
| func (mvs Maps) JsonStringIndent(prefix, indent string, safeEncoding ...bool) (string, error) { | ||||
| 	var s string | ||||
| 	var haveFirst bool | ||||
| 	for _, v := range mvs { | ||||
| 		j, err := v.JsonIndent(prefix, indent) | ||||
| 		if err != nil { | ||||
| 			return s, err | ||||
| 		} | ||||
| 		if haveFirst { | ||||
| 			s += "\n" | ||||
| 		} else { | ||||
| 			haveFirst = true | ||||
| 		} | ||||
| 		s += string(j) | ||||
| 	} | ||||
| 	return s, nil | ||||
| } | ||||
| 
 | ||||
| // XmlString - analogous to mv.Xml() | ||||
| func (mvs Maps) XmlString() (string, error) { | ||||
| 	var s string | ||||
| 	for _, v := range mvs { | ||||
| 		x, err := v.Xml() | ||||
| 		if err != nil { | ||||
| 			return s, err | ||||
| 		} | ||||
| 		s += string(x) | ||||
| 	} | ||||
| 	return s, nil | ||||
| } | ||||
| 
 | ||||
| // XmlStringIndent - analogous to mv.XmlIndent() | ||||
| func (mvs Maps) XmlStringIndent(prefix, indent string) (string, error) { | ||||
| 	var s string | ||||
| 	for _, v := range mvs { | ||||
| 		x, err := v.XmlIndent(prefix, indent) | ||||
| 		if err != nil { | ||||
| 			return s, err | ||||
| 		} | ||||
| 		s += string(x) | ||||
| 	} | ||||
| 	return s, nil | ||||
| } | ||||
| 
 | ||||
| // JsonFile - write Maps to named file as JSON | ||||
| // Note: the file will be created, if necessary; if it exists it will be truncated. | ||||
| // If you need to append to a file, open it and use JsonWriter method. | ||||
| func (mvs Maps) JsonFile(file string, safeEncoding ...bool) error { | ||||
| 	var encoding bool | ||||
| 	if len(safeEncoding) == 1 { | ||||
| 		encoding = safeEncoding[0] | ||||
| 	} | ||||
| 	s, err := mvs.JsonString(encoding) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	fh, err := os.Create(file) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer fh.Close() | ||||
| 	fh.WriteString(s) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // JsonFileIndent - write Maps to named file as pretty JSON | ||||
| // Note: the file will be created, if necessary; if it exists it will be truncated. | ||||
| // If you need to append to a file, open it and use JsonIndentWriter method. | ||||
| func (mvs Maps) JsonFileIndent(file, prefix, indent string, safeEncoding ...bool) error { | ||||
| 	var encoding bool | ||||
| 	if len(safeEncoding) == 1 { | ||||
| 		encoding = safeEncoding[0] | ||||
| 	} | ||||
| 	s, err := mvs.JsonStringIndent(prefix, indent, encoding) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	fh, err := os.Create(file) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer fh.Close() | ||||
| 	fh.WriteString(s) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // XmlFile - write Maps to named file as XML | ||||
| // Note: the file will be created, if necessary; if it exists it will be truncated. | ||||
| // If you need to append to a file, open it and use XmlWriter method. | ||||
| func (mvs Maps) XmlFile(file string) error { | ||||
| 	s, err := mvs.XmlString() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	fh, err := os.Create(file) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer fh.Close() | ||||
| 	fh.WriteString(s) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // XmlFileIndent - write Maps to named file as pretty XML | ||||
| // Note: the file will be created,if necessary; if it exists it will be truncated. | ||||
| // If you need to append to a file, open it and use XmlIndentWriter method. | ||||
| func (mvs Maps) XmlFileIndent(file, prefix, indent string) error { | ||||
| 	s, err := mvs.XmlStringIndent(prefix, indent) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	fh, err := os.Create(file) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer fh.Close() | ||||
| 	fh.WriteString(s) | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										2
									
								
								vendor/github.com/clbanning/mxj/files_test.badjson
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/clbanning/mxj/files_test.badjson
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,2 +0,0 @@ | |||
| { "this":"is", "a":"test", "file":"for", "files_test.go":"case" } | ||||
| { "with":"some", "bad":JSON, "in":"it" } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/clbanning/mxj/files_test.badxml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/clbanning/mxj/files_test.badxml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,9 +0,0 @@ | |||
| <doc> | ||||
| 	<some>test</some> | ||||
| 	<data>for files.go</data> | ||||
| </doc> | ||||
| <msg> | ||||
| 	<just>some</just> | ||||
| 	<another>doc</other> | ||||
| 	<for>test case</for> | ||||
| </msg> | ||||
							
								
								
									
										2
									
								
								vendor/github.com/clbanning/mxj/files_test.json
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/clbanning/mxj/files_test.json
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,2 +0,0 @@ | |||
| { "this":"is", "a":"test", "file":"for", "files_test.go":"case" } | ||||
| { "with":"just", "two":2, "JSON":"values", "true":true } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/clbanning/mxj/files_test.xml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/clbanning/mxj/files_test.xml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,9 +0,0 @@ | |||
| <doc> | ||||
| 	<some>test</some> | ||||
| 	<data>for files.go</data> | ||||
| </doc> | ||||
| <msg> | ||||
| 	<just>some</just> | ||||
| 	<another>doc</another> | ||||
| 	<for>test case</for> | ||||
| </msg> | ||||
							
								
								
									
										1
									
								
								vendor/github.com/clbanning/mxj/files_test_dup.json
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/clbanning/mxj/files_test_dup.json
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1 +0,0 @@ | |||
| {"a":"test","file":"for","files_test.go":"case","this":"is"}{"JSON":"values","true":true,"two":2,"with":"just"} | ||||
							
								
								
									
										1
									
								
								vendor/github.com/clbanning/mxj/files_test_dup.xml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/clbanning/mxj/files_test_dup.xml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1 +0,0 @@ | |||
| <doc><data>for files.go</data><some>test</some></doc><msg><another>doc</another><for>test case</for><just>some</just></msg> | ||||
							
								
								
									
										12
									
								
								vendor/github.com/clbanning/mxj/files_test_indent.json
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/clbanning/mxj/files_test_indent.json
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,12 +0,0 @@ | |||
| { | ||||
|   "a": "test", | ||||
|   "file": "for", | ||||
|   "files_test.go": "case", | ||||
|   "this": "is" | ||||
| } | ||||
| { | ||||
|   "JSON": "values", | ||||
|   "true": true, | ||||
|   "two": 2, | ||||
|   "with": "just" | ||||
| } | ||||
							
								
								
									
										8
									
								
								vendor/github.com/clbanning/mxj/files_test_indent.xml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/clbanning/mxj/files_test_indent.xml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,8 +0,0 @@ | |||
| <doc> | ||||
|   <data>for files.go</data> | ||||
|   <some>test</some> | ||||
| </doc><msg> | ||||
|   <another>doc</another> | ||||
|   <for>test case</for> | ||||
|   <just>some</just> | ||||
| </msg> | ||||
							
								
								
									
										35
									
								
								vendor/github.com/clbanning/mxj/gob.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/clbanning/mxj/gob.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,35 +0,0 @@ | |||
| // gob.go - Encode/Decode a Map into a gob object. | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/gob" | ||||
| ) | ||||
| 
 | ||||
| // NewMapGob returns a Map value for a gob object that has been | ||||
| // encoded from a map[string]interface{} (or compatible type) value. | ||||
| // It is intended to provide symmetric handling of Maps that have | ||||
| // been encoded using mv.Gob. | ||||
| func NewMapGob(gobj []byte) (Map, error) { | ||||
| 	m := make(map[string]interface{}, 0) | ||||
| 	if len(gobj) == 0 { | ||||
| 		return m, nil | ||||
| 	} | ||||
| 	r := bytes.NewReader(gobj) | ||||
| 	dec := gob.NewDecoder(r) | ||||
| 	if err := dec.Decode(&m); err != nil { | ||||
| 		return m, err | ||||
| 	} | ||||
| 	return m, nil | ||||
| } | ||||
| 
 | ||||
| // Gob returns a gob-encoded value for the Map 'mv'. | ||||
| func (mv Map) Gob() ([]byte, error) { | ||||
| 	var buf bytes.Buffer | ||||
| 	enc := gob.NewEncoder(&buf) | ||||
| 	if err := enc.Encode(map[string]interface{}(mv)); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return buf.Bytes(), nil | ||||
| } | ||||
							
								
								
									
										323
									
								
								vendor/github.com/clbanning/mxj/json.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										323
									
								
								vendor/github.com/clbanning/mxj/json.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,323 +0,0 @@ | |||
| // Copyright 2012-2014 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // ------------------------------ write JSON ----------------------- | ||||
| 
 | ||||
| // Just a wrapper on json.Marshal. | ||||
| // If option safeEncoding is'true' then safe encoding of '<', '>' and '&' | ||||
| // is preserved. (see encoding/json#Marshal, encoding/json#Encode) | ||||
| func (mv Map) Json(safeEncoding ...bool) ([]byte, error) { | ||||
| 	var s bool | ||||
| 	if len(safeEncoding) == 1 { | ||||
| 		s = safeEncoding[0] | ||||
| 	} | ||||
| 
 | ||||
| 	b, err := json.Marshal(mv) | ||||
| 
 | ||||
| 	if !s { | ||||
| 		b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1) | ||||
| 		b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1) | ||||
| 		b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1) | ||||
| 	} | ||||
| 	return b, err | ||||
| } | ||||
| 
 | ||||
| // Just a wrapper on json.MarshalIndent. | ||||
| // If option safeEncoding is'true' then safe encoding of '<' , '>' and '&' | ||||
| // is preserved. (see encoding/json#Marshal, encoding/json#Encode) | ||||
| func (mv Map) JsonIndent(prefix, indent string, safeEncoding ...bool) ([]byte, error) { | ||||
| 	var s bool | ||||
| 	if len(safeEncoding) == 1 { | ||||
| 		s = safeEncoding[0] | ||||
| 	} | ||||
| 
 | ||||
| 	b, err := json.MarshalIndent(mv, prefix, indent) | ||||
| 	if !s { | ||||
| 		b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1) | ||||
| 		b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1) | ||||
| 		b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1) | ||||
| 	} | ||||
| 	return b, err | ||||
| } | ||||
| 
 | ||||
| // The following implementation is provided for symmetry with NewMapJsonReader[Raw] | ||||
| // The names will also provide a key for the number of return arguments. | ||||
| 
 | ||||
| // Writes the Map as JSON on the Writer. | ||||
| // If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved. | ||||
| func (mv Map) JsonWriter(jsonWriter io.Writer, safeEncoding ...bool) error { | ||||
| 	b, err := mv.Json(safeEncoding...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = jsonWriter.Write(b) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // Writes the Map as JSON on the Writer. []byte is the raw JSON that was written. | ||||
| // If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved. | ||||
| func (mv Map) JsonWriterRaw(jsonWriter io.Writer, safeEncoding ...bool) ([]byte, error) { | ||||
| 	b, err := mv.Json(safeEncoding...) | ||||
| 	if err != nil { | ||||
| 		return b, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = jsonWriter.Write(b) | ||||
| 	return b, err | ||||
| } | ||||
| 
 | ||||
| // Writes the Map as pretty JSON on the Writer. | ||||
| // If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved. | ||||
| func (mv Map) JsonIndentWriter(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) error { | ||||
| 	b, err := mv.JsonIndent(prefix, indent, safeEncoding...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = jsonWriter.Write(b) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // Writes the Map as pretty JSON on the Writer. []byte is the raw JSON that was written. | ||||
| // If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved. | ||||
| func (mv Map) JsonIndentWriterRaw(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) ([]byte, error) { | ||||
| 	b, err := mv.JsonIndent(prefix, indent, safeEncoding...) | ||||
| 	if err != nil { | ||||
| 		return b, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = jsonWriter.Write(b) | ||||
| 	return b, err | ||||
| } | ||||
| 
 | ||||
| // --------------------------- read JSON ----------------------------- | ||||
| 
 | ||||
| // Decode numericvalues as json.Number type Map values - see encoding/json#Number. | ||||
| // NOTE: this is for decoding JSON into a Map with NewMapJson(), NewMapJsonReader(),  | ||||
| // etc.; it does not affect NewMapXml(), etc.  The XML encoders mv.Xml() and mv.XmlIndent() | ||||
| // do recognize json.Number types; a JSON object can be decoded to a Map with json.Number | ||||
| // value types and the resulting Map can be correctly encoded into a XML object. | ||||
| var JsonUseNumber bool | ||||
| 
 | ||||
| // Just a wrapper on json.Unmarshal | ||||
| //	Converting JSON to XML is a simple as: | ||||
| //		... | ||||
| //		mapVal, merr := mxj.NewMapJson(jsonVal) | ||||
| //		if merr != nil { | ||||
| //			// handle error | ||||
| //		} | ||||
| //		xmlVal, xerr := mapVal.Xml() | ||||
| //		if xerr != nil { | ||||
| //			// handle error | ||||
| //		} | ||||
| // NOTE: as a special case, passing a list, e.g., [{"some-null-value":"", "a-non-null-value":"bar"}], | ||||
| // will be interpreted as having the root key 'object' prepended - {"object":[ ... ]} - to unmarshal to a Map. | ||||
| // See mxj/j2x/j2x_test.go. | ||||
| func NewMapJson(jsonVal []byte) (Map, error) { | ||||
| 	// empty or nil begets empty | ||||
| 	if len(jsonVal) == 0 { | ||||
| 		m := make(map[string]interface{}, 0) | ||||
| 		return m, nil | ||||
| 	} | ||||
| 	// handle a goofy case ... | ||||
| 	if jsonVal[0] == '[' { | ||||
| 		jsonVal = []byte(`{"object":` + string(jsonVal) + `}`) | ||||
| 	} | ||||
| 	m := make(map[string]interface{}) | ||||
| 	// err := json.Unmarshal(jsonVal, &m) | ||||
| 	buf := bytes.NewReader(jsonVal) | ||||
| 	dec := json.NewDecoder(buf) | ||||
| 	if JsonUseNumber { | ||||
| 		dec.UseNumber() | ||||
| 	} | ||||
| 	err := dec.Decode(&m) | ||||
| 	return m, err | ||||
| } | ||||
| 
 | ||||
| // Retrieve a Map value from an io.Reader. | ||||
| //  NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an | ||||
| //        os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte | ||||
| //        value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal | ||||
| //        a JSON object. | ||||
| func NewMapJsonReader(jsonReader io.Reader) (Map, error) { | ||||
| 	jb, err := getJson(jsonReader) | ||||
| 	if err != nil || len(*jb) == 0 { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Unmarshal the 'presumed' JSON string | ||||
| 	return NewMapJson(*jb) | ||||
| } | ||||
| 
 | ||||
| // Retrieve a Map value and raw JSON - []byte - from an io.Reader. | ||||
| //  NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an | ||||
| //        os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte | ||||
| //        value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal | ||||
| //        a JSON object and retrieve the raw JSON in a single call. | ||||
| func NewMapJsonReaderRaw(jsonReader io.Reader) (Map, []byte, error) { | ||||
| 	jb, err := getJson(jsonReader) | ||||
| 	if err != nil || len(*jb) == 0 { | ||||
| 		return nil, *jb, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Unmarshal the 'presumed' JSON string | ||||
| 	m, merr := NewMapJson(*jb) | ||||
| 	return m, *jb, merr | ||||
| } | ||||
| 
 | ||||
| // Pull the next JSON string off the stream: just read from first '{' to its closing '}'. | ||||
| // Returning a pointer to the slice saves 16 bytes - maybe unnecessary, but internal to package. | ||||
| func getJson(rdr io.Reader) (*[]byte, error) { | ||||
| 	bval := make([]byte, 1) | ||||
| 	jb := make([]byte, 0) | ||||
| 	var inQuote, inJson bool | ||||
| 	var parenCnt int | ||||
| 	var previous byte | ||||
| 
 | ||||
| 	// scan the input for a matched set of {...} | ||||
| 	// json.Unmarshal will handle syntax checking. | ||||
| 	for { | ||||
| 		_, err := rdr.Read(bval) | ||||
| 		if err != nil { | ||||
| 			if err == io.EOF && inJson && parenCnt > 0 { | ||||
| 				return &jb, fmt.Errorf("no closing } for JSON string: %s", string(jb)) | ||||
| 			} | ||||
| 			return &jb, err | ||||
| 		} | ||||
| 		switch bval[0] { | ||||
| 		case '{': | ||||
| 			if !inQuote { | ||||
| 				parenCnt++ | ||||
| 				inJson = true | ||||
| 			} | ||||
| 		case '}': | ||||
| 			if !inQuote { | ||||
| 				parenCnt-- | ||||
| 			} | ||||
| 			if parenCnt < 0 { | ||||
| 				return nil, fmt.Errorf("closing } without opening {: %s", string(jb)) | ||||
| 			} | ||||
| 		case '"': | ||||
| 			if inQuote { | ||||
| 				if previous == '\\' { | ||||
| 					break | ||||
| 				} | ||||
| 				inQuote = false | ||||
| 			} else { | ||||
| 				inQuote = true | ||||
| 			} | ||||
| 		case '\n', '\r', '\t', ' ': | ||||
| 			if !inQuote { | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		if inJson { | ||||
| 			jb = append(jb, bval[0]) | ||||
| 			if parenCnt == 0 { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		previous = bval[0] | ||||
| 	} | ||||
| 
 | ||||
| 	return &jb, nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------- JSON Reader handler via Map values  ----------------------- | ||||
| 
 | ||||
| // Default poll delay to keep Handler from spinning on an open stream | ||||
| // like sitting on os.Stdin waiting for imput. | ||||
| var jhandlerPollInterval = time.Duration(1e6) | ||||
| 
 | ||||
| // While unnecessary, we make HandleJsonReader() have the same signature as HandleXmlReader(). | ||||
| // This avoids treating one or other as a special case and discussing the underlying stdlib logic. | ||||
| 
 | ||||
| // Bulk process JSON using handlers that process a Map value. | ||||
| //	'rdr' is an io.Reader for the JSON (stream). | ||||
| //	'mapHandler' is the Map processing handler. Return of 'false' stops io.Reader processing. | ||||
| //	'errHandler' is the error processor. Return of 'false' stops io.Reader  processing and returns the error. | ||||
| //	Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized. | ||||
| //	      This means that you can stop reading the file on error or after processing a particular message. | ||||
| //	      To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'. | ||||
| func HandleJsonReader(jsonReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error { | ||||
| 	var n int | ||||
| 	for { | ||||
| 		m, merr := NewMapJsonReader(jsonReader) | ||||
| 		n++ | ||||
| 
 | ||||
| 		// handle error condition with errhandler | ||||
| 		if merr != nil && merr != io.EOF { | ||||
| 			merr = fmt.Errorf("[jsonReader: %d] %s", n, merr.Error()) | ||||
| 			if ok := errHandler(merr); !ok { | ||||
| 				// caused reader termination | ||||
| 				return merr | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// pass to maphandler | ||||
| 		if len(m) != 0 { | ||||
| 			if ok := mapHandler(m); !ok { | ||||
| 				break | ||||
| 			} | ||||
| 		} else if merr != io.EOF { | ||||
| 			<-time.After(jhandlerPollInterval) | ||||
| 		} | ||||
| 
 | ||||
| 		if merr == io.EOF { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Bulk process JSON using handlers that process a Map value and the raw JSON. | ||||
| //	'rdr' is an io.Reader for the JSON (stream). | ||||
| //	'mapHandler' is the Map and raw JSON - []byte - processor. Return of 'false' stops io.Reader processing. | ||||
| //	'errHandler' is the error and raw JSON processor. Return of 'false' stops io.Reader processing and returns the error. | ||||
| //	Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized. | ||||
| //	      This means that you can stop reading the file on error or after processing a particular message. | ||||
| //	      To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'. | ||||
| func HandleJsonReaderRaw(jsonReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error { | ||||
| 	var n int | ||||
| 	for { | ||||
| 		m, raw, merr := NewMapJsonReaderRaw(jsonReader) | ||||
| 		n++ | ||||
| 
 | ||||
| 		// handle error condition with errhandler | ||||
| 		if merr != nil && merr != io.EOF { | ||||
| 			merr = fmt.Errorf("[jsonReader: %d] %s", n, merr.Error()) | ||||
| 			if ok := errHandler(merr, raw); !ok { | ||||
| 				// caused reader termination | ||||
| 				return merr | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// pass to maphandler | ||||
| 		if len(m) != 0 { | ||||
| 			if ok := mapHandler(m, raw); !ok { | ||||
| 				break | ||||
| 			} | ||||
| 		} else if merr != io.EOF { | ||||
| 			<-time.After(jhandlerPollInterval) | ||||
| 		} | ||||
| 
 | ||||
| 		if merr == io.EOF { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										671
									
								
								vendor/github.com/clbanning/mxj/keyvalues.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										671
									
								
								vendor/github.com/clbanning/mxj/keyvalues.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,671 +0,0 @@ | |||
| // Copyright 2012-2014 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| //	keyvalues.go: Extract values from an arbitrary XML doc. Tag path can include wildcard characters. | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // ----------------------------- get everything FOR a single key ------------------------- | ||||
| 
 | ||||
| const ( | ||||
| 	minArraySize = 32 | ||||
| ) | ||||
| 
 | ||||
| var defaultArraySize int = minArraySize | ||||
| 
 | ||||
| // Adjust the buffers for expected number of values to return from ValuesForKey() and ValuesForPath(). | ||||
| // This can have the effect of significantly reducing memory allocation-copy functions for large data sets. | ||||
| // Returns the initial buffer size. | ||||
| func SetArraySize(size int) int { | ||||
| 	if size > minArraySize { | ||||
| 		defaultArraySize = size | ||||
| 	} else { | ||||
| 		defaultArraySize = minArraySize | ||||
| 	} | ||||
| 	return defaultArraySize | ||||
| } | ||||
| 
 | ||||
| // Return all values in Map, 'mv', associated with a 'key'. If len(returned_values) == 0, then no match. | ||||
| // On error, the returned slice is 'nil'. NOTE: 'key' can be wildcard, "*". | ||||
| //   'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list. | ||||
| //             - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them. | ||||
| //             - For attributes prefix the label with a hyphen, '-', e.g., "-seq:3". | ||||
| //             - If the 'key' refers to a list, then "key:value" could select a list member of the list. | ||||
| //             - The subkey can be wildcarded - "key:*" - to require that it's there with some value. | ||||
| //             - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an | ||||
| //               exclusion critera - e.g., "!author:William T. Gaddis". | ||||
| //             - If val contains ":" symbol, use SetFieldSeparator to a unused symbol, perhaps "|". | ||||
| func (mv Map) ValuesForKey(key string, subkeys ...string) ([]interface{}, error) { | ||||
| 	m := map[string]interface{}(mv) | ||||
| 	var subKeyMap map[string]interface{} | ||||
| 	if len(subkeys) > 0 { | ||||
| 		var err error | ||||
| 		subKeyMap, err = getSubKeyMap(subkeys...) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ret := make([]interface{}, 0, defaultArraySize) | ||||
| 	var cnt int | ||||
| 	hasKey(m, key, &ret, &cnt, subKeyMap) | ||||
| 	return ret[:cnt], nil | ||||
| } | ||||
| 
 | ||||
| var KeyNotExistError = errors.New("Key does not exist") | ||||
| 
 | ||||
| // ValueForKey is a wrapper on ValuesForKey.  It returns the first member of []interface{}, if any. | ||||
| // If there is no value, "nil, nil" is returned. | ||||
| func (mv Map) ValueForKey(key string, subkeys ...string) (interface{}, error) { | ||||
| 	vals, err := mv.ValuesForKey(key, subkeys...) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(vals) == 0 { | ||||
| 		return nil, KeyNotExistError | ||||
| 	} | ||||
| 	return vals[0], nil | ||||
| } | ||||
| 
 | ||||
| // hasKey - if the map 'key' exists append it to array | ||||
| //          if it doesn't do nothing except scan array and map values | ||||
| func hasKey(iv interface{}, key string, ret *[]interface{}, cnt *int, subkeys map[string]interface{}) { | ||||
| 	// func hasKey(iv interface{}, key string, ret *[]interface{}, subkeys map[string]interface{}) { | ||||
| 	switch iv.(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		vv := iv.(map[string]interface{}) | ||||
| 		// see if the current value is of interest | ||||
| 		if v, ok := vv[key]; ok { | ||||
| 			switch v.(type) { | ||||
| 			case map[string]interface{}: | ||||
| 				if hasSubKeys(v, subkeys) { | ||||
| 					*ret = append(*ret, v) | ||||
| 					*cnt++ | ||||
| 				} | ||||
| 			case []interface{}: | ||||
| 				for _, av := range v.([]interface{}) { | ||||
| 					if hasSubKeys(av, subkeys) { | ||||
| 						*ret = append(*ret, av) | ||||
| 						*cnt++ | ||||
| 					} | ||||
| 				} | ||||
| 			default: | ||||
| 				if len(subkeys) == 0 { | ||||
| 					*ret = append(*ret, v) | ||||
| 					*cnt++ | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// wildcard case | ||||
| 		if key == "*" { | ||||
| 			for _, v := range vv { | ||||
| 				switch v.(type) { | ||||
| 				case map[string]interface{}: | ||||
| 					if hasSubKeys(v, subkeys) { | ||||
| 						*ret = append(*ret, v) | ||||
| 						*cnt++ | ||||
| 					} | ||||
| 				case []interface{}: | ||||
| 					for _, av := range v.([]interface{}) { | ||||
| 						if hasSubKeys(av, subkeys) { | ||||
| 							*ret = append(*ret, av) | ||||
| 							*cnt++ | ||||
| 						} | ||||
| 					} | ||||
| 				default: | ||||
| 					if len(subkeys) == 0 { | ||||
| 						*ret = append(*ret, v) | ||||
| 						*cnt++ | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// scan the rest | ||||
| 		for _, v := range vv { | ||||
| 			hasKey(v, key, ret, cnt, subkeys) | ||||
| 		} | ||||
| 	case []interface{}: | ||||
| 		for _, v := range iv.([]interface{}) { | ||||
| 			hasKey(v, key, ret, cnt, subkeys) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // -----------------------  get everything for a node in the Map --------------------------- | ||||
| 
 | ||||
| // Allow indexed arrays in "path" specification. (Request from Abhijit Kadam - abhijitk100@gmail.com.) | ||||
| // 2014.04.28 - implementation note. | ||||
| // Implemented as a wrapper of (old)ValuesForPath() because we need look-ahead logic to handle expansion | ||||
| // of wildcards and unindexed arrays.  Embedding such logic into valuesForKeyPath() would have made the | ||||
| // code much more complicated; this wrapper is straightforward, easy to debug, and doesn't add significant overhead. | ||||
| 
 | ||||
| // Retrieve all values for a path from the Map.  If len(returned_values) == 0, then no match. | ||||
| // On error, the returned array is 'nil'. | ||||
| //   'path' is a dot-separated path of key values. | ||||
| //          - If a node in the path is '*', then everything beyond is walked. | ||||
| //          - 'path' can contain indexed array references, such as, "*.data[1]" and "msgs[2].data[0].field" - | ||||
| //            even "*[2].*[0].field". | ||||
| //   'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list. | ||||
| //             - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them. | ||||
| //             - For attributes prefix the label with a hyphen, '-', e.g., "-seq:3". | ||||
| //             - If the 'path' refers to a list, then "tag:value" would return member of the list. | ||||
| //             - The subkey can be wildcarded - "key:*" - to require that it's there with some value. | ||||
| //             - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an | ||||
| //               exclusion critera - e.g., "!author:William T. Gaddis". | ||||
| //             - If val contains ":" symbol, use SetFieldSeparator to a unused symbol, perhaps "|". | ||||
| func (mv Map) ValuesForPath(path string, subkeys ...string) ([]interface{}, error) { | ||||
| 	// If there are no array indexes in path, use legacy ValuesForPath() logic. | ||||
| 	if strings.Index(path, "[") < 0 { | ||||
| 		return mv.oldValuesForPath(path, subkeys...) | ||||
| 	} | ||||
| 
 | ||||
| 	var subKeyMap map[string]interface{} | ||||
| 	if len(subkeys) > 0 { | ||||
| 		var err error | ||||
| 		subKeyMap, err = getSubKeyMap(subkeys...) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	keys, kerr := parsePath(path) | ||||
| 	if kerr != nil { | ||||
| 		return nil, kerr | ||||
| 	} | ||||
| 
 | ||||
| 	vals, verr := valuesForArray(keys, mv) | ||||
| 	if verr != nil { | ||||
| 		return nil, verr // Vals may be nil, but return empty array. | ||||
| 	} | ||||
| 
 | ||||
| 	// Need to handle subkeys ... only return members of vals that satisfy conditions. | ||||
| 	retvals := make([]interface{}, 0) | ||||
| 	for _, v := range vals { | ||||
| 		if hasSubKeys(v, subKeyMap) { | ||||
| 			retvals = append(retvals, v) | ||||
| 		} | ||||
| 	} | ||||
| 	return retvals, nil | ||||
| } | ||||
| 
 | ||||
| func valuesForArray(keys []*key, m Map) ([]interface{}, error) { | ||||
| 	var tmppath string | ||||
| 	var haveFirst bool | ||||
| 	var vals []interface{} | ||||
| 	var verr error | ||||
| 
 | ||||
| 	lastkey := len(keys) - 1 | ||||
| 	for i := 0; i <= lastkey; i++ { | ||||
| 		if !haveFirst { | ||||
| 			tmppath = keys[i].name | ||||
| 			haveFirst = true | ||||
| 		} else { | ||||
| 			tmppath += "." + keys[i].name | ||||
| 		} | ||||
| 
 | ||||
| 		// Look-ahead: explode wildcards and unindexed arrays. | ||||
| 		// Need to handle un-indexed list recursively: | ||||
| 		// e.g., path is "stuff.data[0]" rather than "stuff[0].data[0]". | ||||
| 		// Need to treat it as "stuff[0].data[0]", "stuff[1].data[0]", ... | ||||
| 		if !keys[i].isArray && i < lastkey && keys[i+1].isArray { | ||||
| 			// Can't pass subkeys because we may not be at literal end of path. | ||||
| 			vv, vverr := m.oldValuesForPath(tmppath) | ||||
| 			if vverr != nil { | ||||
| 				return nil, vverr | ||||
| 			} | ||||
| 			for _, v := range vv { | ||||
| 				// See if we can walk the value. | ||||
| 				am, ok := v.(map[string]interface{}) | ||||
| 				if !ok { | ||||
| 					continue | ||||
| 				} | ||||
| 				// Work the backend. | ||||
| 				nvals, nvalserr := valuesForArray(keys[i+1:], Map(am)) | ||||
| 				if nvalserr != nil { | ||||
| 					return nil, nvalserr | ||||
| 				} | ||||
| 				vals = append(vals, nvals...) | ||||
| 			} | ||||
| 			break // have recursed the whole path - return | ||||
| 		} | ||||
| 
 | ||||
| 		if keys[i].isArray || i == lastkey { | ||||
| 			// Don't pass subkeys because may not be at literal end of path. | ||||
| 			vals, verr = m.oldValuesForPath(tmppath) | ||||
| 		} else { | ||||
| 			continue | ||||
| 		} | ||||
| 		if verr != nil { | ||||
| 			return nil, verr | ||||
| 		} | ||||
| 
 | ||||
| 		if i == lastkey && !keys[i].isArray { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		// Now we're looking at an array - supposedly. | ||||
| 		// Is index in range of vals? | ||||
| 		if len(vals) <= keys[i].position { | ||||
| 			vals = nil | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		// Return the array member of interest, if at end of path. | ||||
| 		if i == lastkey { | ||||
| 			vals = vals[keys[i].position:(keys[i].position + 1)] | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		// Extract the array member of interest. | ||||
| 		am := vals[keys[i].position:(keys[i].position + 1)] | ||||
| 
 | ||||
| 		// must be a map[string]interface{} value so we can keep walking the path | ||||
| 		amm, ok := am[0].(map[string]interface{}) | ||||
| 		if !ok { | ||||
| 			vals = nil | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		m = Map(amm) | ||||
| 		haveFirst = false | ||||
| 	} | ||||
| 
 | ||||
| 	return vals, nil | ||||
| } | ||||
| 
 | ||||
| type key struct { | ||||
| 	name     string | ||||
| 	isArray  bool | ||||
| 	position int | ||||
| } | ||||
| 
 | ||||
| func parsePath(s string) ([]*key, error) { | ||||
| 	keys := strings.Split(s, ".") | ||||
| 
 | ||||
| 	ret := make([]*key, 0) | ||||
| 
 | ||||
| 	for i := 0; i < len(keys); i++ { | ||||
| 		if keys[i] == "" { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		newkey := new(key) | ||||
| 		if strings.Index(keys[i], "[") < 0 { | ||||
| 			newkey.name = keys[i] | ||||
| 			ret = append(ret, newkey) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		p := strings.Split(keys[i], "[") | ||||
| 		newkey.name = p[0] | ||||
| 		p = strings.Split(p[1], "]") | ||||
| 		if p[0] == "" { // no right bracket | ||||
| 			return nil, fmt.Errorf("no right bracket on key index: %s", keys[i]) | ||||
| 		} | ||||
| 		// convert p[0] to a int value | ||||
| 		pos, nerr := strconv.ParseInt(p[0], 10, 32) | ||||
| 		if nerr != nil { | ||||
| 			return nil, fmt.Errorf("cannot convert index to int value: %s", p[0]) | ||||
| 		} | ||||
| 		newkey.position = int(pos) | ||||
| 		newkey.isArray = true | ||||
| 		ret = append(ret, newkey) | ||||
| 	} | ||||
| 
 | ||||
| 	return ret, nil | ||||
| } | ||||
| 
 | ||||
| // legacy ValuesForPath() - now wrapped to handle special case of indexed arrays in 'path'. | ||||
| func (mv Map) oldValuesForPath(path string, subkeys ...string) ([]interface{}, error) { | ||||
| 	m := map[string]interface{}(mv) | ||||
| 	var subKeyMap map[string]interface{} | ||||
| 	if len(subkeys) > 0 { | ||||
| 		var err error | ||||
| 		subKeyMap, err = getSubKeyMap(subkeys...) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	keys := strings.Split(path, ".") | ||||
| 	if keys[len(keys)-1] == "" { | ||||
| 		keys = keys[:len(keys)-1] | ||||
| 	} | ||||
| 	ivals := make([]interface{}, 0, defaultArraySize) | ||||
| 	var cnt int | ||||
| 	valuesForKeyPath(&ivals, &cnt, m, keys, subKeyMap) | ||||
| 	return ivals[:cnt], nil | ||||
| } | ||||
| 
 | ||||
| func valuesForKeyPath(ret *[]interface{}, cnt *int, m interface{}, keys []string, subkeys map[string]interface{}) { | ||||
| 	lenKeys := len(keys) | ||||
| 
 | ||||
| 	// load 'm' values into 'ret' | ||||
| 	// expand any lists | ||||
| 	if lenKeys == 0 { | ||||
| 		switch m.(type) { | ||||
| 		case map[string]interface{}: | ||||
| 			if subkeys != nil { | ||||
| 				if ok := hasSubKeys(m, subkeys); !ok { | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 			*ret = append(*ret, m) | ||||
| 			*cnt++ | ||||
| 		case []interface{}: | ||||
| 			for i, v := range m.([]interface{}) { | ||||
| 				if subkeys != nil { | ||||
| 					if ok := hasSubKeys(v, subkeys); !ok { | ||||
| 						continue // only load list members with subkeys | ||||
| 					} | ||||
| 				} | ||||
| 				*ret = append(*ret, (m.([]interface{}))[i]) | ||||
| 				*cnt++ | ||||
| 			} | ||||
| 		default: | ||||
| 			if subkeys != nil { | ||||
| 				return // must be map[string]interface{} if there are subkeys | ||||
| 			} | ||||
| 			*ret = append(*ret, m) | ||||
| 			*cnt++ | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// key of interest | ||||
| 	key := keys[0] | ||||
| 	switch key { | ||||
| 	case "*": // wildcard - scan all values | ||||
| 		switch m.(type) { | ||||
| 		case map[string]interface{}: | ||||
| 			for _, v := range m.(map[string]interface{}) { | ||||
| 				// valuesForKeyPath(ret, v, keys[1:], subkeys) | ||||
| 				valuesForKeyPath(ret, cnt, v, keys[1:], subkeys) | ||||
| 			} | ||||
| 		case []interface{}: | ||||
| 			for _, v := range m.([]interface{}) { | ||||
| 				switch v.(type) { | ||||
| 				// flatten out a list of maps - keys are processed | ||||
| 				case map[string]interface{}: | ||||
| 					for _, vv := range v.(map[string]interface{}) { | ||||
| 						// valuesForKeyPath(ret, vv, keys[1:], subkeys) | ||||
| 						valuesForKeyPath(ret, cnt, vv, keys[1:], subkeys) | ||||
| 					} | ||||
| 				default: | ||||
| 					// valuesForKeyPath(ret, v, keys[1:], subkeys) | ||||
| 					valuesForKeyPath(ret, cnt, v, keys[1:], subkeys) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	default: // key - must be map[string]interface{} | ||||
| 		switch m.(type) { | ||||
| 		case map[string]interface{}: | ||||
| 			if v, ok := m.(map[string]interface{})[key]; ok { | ||||
| 				// valuesForKeyPath(ret, v, keys[1:], subkeys) | ||||
| 				valuesForKeyPath(ret, cnt, v, keys[1:], subkeys) | ||||
| 			} | ||||
| 		case []interface{}: // may be buried in list | ||||
| 			for _, v := range m.([]interface{}) { | ||||
| 				switch v.(type) { | ||||
| 				case map[string]interface{}: | ||||
| 					if vv, ok := v.(map[string]interface{})[key]; ok { | ||||
| 						// valuesForKeyPath(ret, vv, keys[1:], subkeys) | ||||
| 						valuesForKeyPath(ret, cnt, vv, keys[1:], subkeys) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // hasSubKeys() - interface{} equality works for string, float64, bool | ||||
| // 'v' must be a map[string]interface{} value to have subkeys | ||||
| // 'a' can have k:v pairs with v.(string) == "*", which is treated like a wildcard. | ||||
| func hasSubKeys(v interface{}, subkeys map[string]interface{}) bool { | ||||
| 	if len(subkeys) == 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	switch v.(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		// do all subKey name:value pairs match? | ||||
| 		mv := v.(map[string]interface{}) | ||||
| 		for skey, sval := range subkeys { | ||||
| 			isNotKey := false | ||||
| 			if skey[:1] == "!" { // a NOT-key | ||||
| 				skey = skey[1:] | ||||
| 				isNotKey = true | ||||
| 			} | ||||
| 			vv, ok := mv[skey] | ||||
| 			if !ok { // key doesn't exist | ||||
| 				if isNotKey { // key not there, but that's what we want | ||||
| 					if kv, ok := sval.(string); ok && kv == "*" { | ||||
| 						continue | ||||
| 					} | ||||
| 				} | ||||
| 				return false | ||||
| 			} | ||||
| 			// wildcard check | ||||
| 			if kv, ok := sval.(string); ok && kv == "*" { | ||||
| 				if isNotKey { // key is there, and we don't want it | ||||
| 					return false | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
| 			switch sval.(type) { | ||||
| 			case string: | ||||
| 				if s, ok := vv.(string); ok && s == sval.(string) { | ||||
| 					if isNotKey { | ||||
| 						return false | ||||
| 					} | ||||
| 					continue | ||||
| 				} | ||||
| 			case bool: | ||||
| 				if b, ok := vv.(bool); ok && b == sval.(bool) { | ||||
| 					if isNotKey { | ||||
| 						return false | ||||
| 					} | ||||
| 					continue | ||||
| 				} | ||||
| 			case float64: | ||||
| 				if f, ok := vv.(float64); ok && f == sval.(float64) { | ||||
| 					if isNotKey { | ||||
| 						return false | ||||
| 					} | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
| 			// key there but didn't match subkey value | ||||
| 			if isNotKey { // that's what we want | ||||
| 				continue | ||||
| 			} | ||||
| 			return false | ||||
| 		} | ||||
| 		// all subkeys matched | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	// not a map[string]interface{} value, can't have subkeys | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // Generate map of key:value entries as map[string]string. | ||||
| //	'kv' arguments are "name:value" pairs: attribute keys are designated with prepended hyphen, '-'. | ||||
| //	If len(kv) == 0, the return is (nil, nil). | ||||
| func getSubKeyMap(kv ...string) (map[string]interface{}, error) { | ||||
| 	if len(kv) == 0 { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	m := make(map[string]interface{}, 0) | ||||
| 	for _, v := range kv { | ||||
| 		vv := strings.Split(v, fieldSep) | ||||
| 		switch len(vv) { | ||||
| 		case 2: | ||||
| 			m[vv[0]] = interface{}(vv[1]) | ||||
| 		case 3: | ||||
| 			switch vv[2] { | ||||
| 			case "string", "char", "text": | ||||
| 				m[vv[0]] = interface{}(vv[1]) | ||||
| 			case "bool", "boolean": | ||||
| 				// ParseBool treats "1"==true & "0"==false | ||||
| 				b, err := strconv.ParseBool(vv[1]) | ||||
| 				if err != nil { | ||||
| 					return nil, fmt.Errorf("can't convert subkey value to bool: %s", vv[1]) | ||||
| 				} | ||||
| 				m[vv[0]] = interface{}(b) | ||||
| 			case "float", "float64", "num", "number", "numeric": | ||||
| 				f, err := strconv.ParseFloat(vv[1], 64) | ||||
| 				if err != nil { | ||||
| 					return nil, fmt.Errorf("can't convert subkey value to float: %s", vv[1]) | ||||
| 				} | ||||
| 				m[vv[0]] = interface{}(f) | ||||
| 			default: | ||||
| 				return nil, fmt.Errorf("unknown subkey conversion spec: %s", v) | ||||
| 			} | ||||
| 		default: | ||||
| 			return nil, fmt.Errorf("unknown subkey spec: %s", v) | ||||
| 		} | ||||
| 	} | ||||
| 	return m, nil | ||||
| } | ||||
| 
 | ||||
| // -------------------------------  END of valuesFor ... ---------------------------- | ||||
| 
 | ||||
| // ----------------------- locate where a key value is in the tree ------------------- | ||||
| 
 | ||||
| //----------------------------- find all paths to a key -------------------------------- | ||||
| 
 | ||||
| // Get all paths through Map, 'mv', (in dot-notation) that terminate with the specified key. | ||||
| // Results can be used with ValuesForPath. | ||||
| func (mv Map) PathsForKey(key string) []string { | ||||
| 	m := map[string]interface{}(mv) | ||||
| 	breadbasket := make(map[string]bool, 0) | ||||
| 	breadcrumbs := "" | ||||
| 
 | ||||
| 	hasKeyPath(breadcrumbs, m, key, breadbasket) | ||||
| 	if len(breadbasket) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// unpack map keys to return | ||||
| 	res := make([]string, len(breadbasket)) | ||||
| 	var i int | ||||
| 	for k := range breadbasket { | ||||
| 		res[i] = k | ||||
| 		i++ | ||||
| 	} | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| // Extract the shortest path from all possible paths - from PathsForKey() - in Map, 'mv'.. | ||||
| // Paths are strings using dot-notation. | ||||
| func (mv Map) PathForKeyShortest(key string) string { | ||||
| 	paths := mv.PathsForKey(key) | ||||
| 
 | ||||
| 	lp := len(paths) | ||||
| 	if lp == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	if lp == 1 { | ||||
| 		return paths[0] | ||||
| 	} | ||||
| 
 | ||||
| 	shortest := paths[0] | ||||
| 	shortestLen := len(strings.Split(shortest, ".")) | ||||
| 
 | ||||
| 	for i := 1; i < len(paths); i++ { | ||||
| 		vlen := len(strings.Split(paths[i], ".")) | ||||
| 		if vlen < shortestLen { | ||||
| 			shortest = paths[i] | ||||
| 			shortestLen = vlen | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return shortest | ||||
| } | ||||
| 
 | ||||
| // hasKeyPath - if the map 'key' exists append it to KeyPath.path and increment KeyPath.depth | ||||
| // This is really just a breadcrumber that saves all trails that hit the prescribed 'key'. | ||||
| func hasKeyPath(crumbs string, iv interface{}, key string, basket map[string]bool) { | ||||
| 	switch iv.(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		vv := iv.(map[string]interface{}) | ||||
| 		if _, ok := vv[key]; ok { | ||||
| 			// create a new breadcrumb, intialized with the one we have | ||||
| 			var nbc string | ||||
| 			if crumbs == "" { | ||||
| 				nbc = key | ||||
| 			} else { | ||||
| 				nbc = crumbs + "." + key | ||||
| 			} | ||||
| 			basket[nbc] = true | ||||
| 		} | ||||
| 		// walk on down the path, key could occur again at deeper node | ||||
| 		for k, v := range vv { | ||||
| 			// create a new breadcrumb, intialized with the one we have | ||||
| 			var nbc string | ||||
| 			if crumbs == "" { | ||||
| 				nbc = k | ||||
| 			} else { | ||||
| 				nbc = crumbs + "." + k | ||||
| 			} | ||||
| 			hasKeyPath(nbc, v, key, basket) | ||||
| 		} | ||||
| 	case []interface{}: | ||||
| 		// crumb-trail doesn't change, pass it on | ||||
| 		for _, v := range iv.([]interface{}) { | ||||
| 			hasKeyPath(crumbs, v, key, basket) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var PathNotExistError = errors.New("Path does not exist") | ||||
| 
 | ||||
| // ValueForPath wrap ValuesFor Path and returns the first value returned. | ||||
| // If no value is found it returns 'nil' and PathNotExistError. | ||||
| func (mv Map) ValueForPath(path string) (interface{}, error) { | ||||
| 	vals, err := mv.ValuesForPath(path) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(vals) == 0 { | ||||
| 		return nil, PathNotExistError | ||||
| 	} | ||||
| 	return vals[0], nil | ||||
| } | ||||
| 
 | ||||
| // Returns the first found value for the path as a string. | ||||
| func (mv Map) ValueForPathString(path string) (string, error) { | ||||
| 	vals, err := mv.ValuesForPath(path) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if len(vals) == 0 { | ||||
| 		return "", errors.New("ValueForPath: path not found") | ||||
| 	} | ||||
| 	val := vals[0] | ||||
| 	switch str := val.(type) { | ||||
| 	case string: | ||||
| 		return str, nil | ||||
| 	default: | ||||
| 		return "", fmt.Errorf("ValueForPath: unsupported type: %T", str) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Returns the first found value for the path as a string. | ||||
| // If the path is not found then it returns an empty string. | ||||
| func (mv Map) ValueOrEmptyForPathString(path string) string { | ||||
| 	str, _ := mv.ValueForPathString(path) | ||||
| 	return str | ||||
| } | ||||
							
								
								
									
										112
									
								
								vendor/github.com/clbanning/mxj/leafnode.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/clbanning/mxj/leafnode.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,112 +0,0 @@ | |||
| package mxj | ||||
| 
 | ||||
| // leafnode.go - return leaf nodes with paths and values for the Map | ||||
| // inspired by: https://groups.google.com/forum/#!topic/golang-nuts/3JhuVKRuBbw | ||||
| 
 | ||||
| import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	NoAttributes = true // suppress LeafNode values that are attributes | ||||
| ) | ||||
| 
 | ||||
| // LeafNode - a terminal path value in a Map. | ||||
| // For XML Map values it represents an attribute or simple element value  - of type | ||||
| // string unless Map was created using Cast flag. For JSON Map values it represents | ||||
| // a string, numeric, boolean, or null value. | ||||
| type LeafNode struct { | ||||
| 	Path  string      // a dot-notation representation of the path with array subscripting | ||||
| 	Value interface{} // the value at the path termination | ||||
| } | ||||
| 
 | ||||
| // LeafNodes - returns an array of all LeafNode values for the Map. | ||||
| // The option no_attr argument suppresses attribute values (keys with prepended hyphen, '-') | ||||
| // as well as the "#text" key for the associated simple element value. | ||||
| // | ||||
| // PrependAttrWithHypen(false) will result in attributes having .attr-name as  | ||||
| // terminal node in 'path' while the path for the element value, itself, will be  | ||||
| // the base path w/o "#text".  | ||||
| // | ||||
| // LeafUseDotNotation(true) causes list members to be identified using ".N" syntax | ||||
| // rather than "[N]" syntax. | ||||
| func (mv Map) LeafNodes(no_attr ...bool) []LeafNode { | ||||
| 	var a bool | ||||
| 	if len(no_attr) == 1 { | ||||
| 		a = no_attr[0] | ||||
| 	} | ||||
| 
 | ||||
| 	l := make([]LeafNode, 0) | ||||
| 	getLeafNodes("", "", map[string]interface{}(mv), &l, a) | ||||
| 	return l | ||||
| } | ||||
| 
 | ||||
| func getLeafNodes(path, node string, mv interface{}, l *[]LeafNode, noattr bool) { | ||||
| 	// if stripping attributes, then also strip "#text" key | ||||
| 	if !noattr || node != "#text" { | ||||
| 		if path != "" && node[:1] != "[" { | ||||
| 			path += "." | ||||
| 		} | ||||
| 		path += node | ||||
| 	} | ||||
| 	switch mv.(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		for k, v := range mv.(map[string]interface{}) { | ||||
| 			// if noattr && k[:1] == "-" { | ||||
| 			if noattr && len(attrPrefix) > 0 && strings.Index(k, attrPrefix) == 0 { | ||||
| 				continue | ||||
| 			} | ||||
| 			getLeafNodes(path, k, v, l, noattr) | ||||
| 		} | ||||
| 	case []interface{}: | ||||
| 		for i, v := range mv.([]interface{}) { | ||||
| 			if useDotNotation { | ||||
| 				getLeafNodes(path, strconv.Itoa(i), v, l, noattr) | ||||
| 			} else { | ||||
| 				getLeafNodes(path, "["+strconv.Itoa(i)+"]", v, l, noattr) | ||||
| 			} | ||||
| 		} | ||||
| 	default: | ||||
| 		// can't walk any further, so create leaf | ||||
| 		n := LeafNode{path, mv} | ||||
| 		*l = append(*l, n) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // LeafPaths - all paths that terminate in LeafNode values. | ||||
| func (mv Map) LeafPaths(no_attr ...bool) []string { | ||||
| 	ln := mv.LeafNodes() | ||||
| 	ss := make([]string, len(ln)) | ||||
| 	for i := 0; i < len(ln); i++ { | ||||
| 		ss[i] = ln[i].Path | ||||
| 	} | ||||
| 	return ss | ||||
| } | ||||
| 
 | ||||
| // LeafValues - all terminal values in the Map. | ||||
| func (mv Map) LeafValues(no_attr ...bool) []interface{} { | ||||
| 	ln := mv.LeafNodes() | ||||
| 	vv := make([]interface{}, len(ln)) | ||||
| 	for i := 0; i < len(ln); i++ { | ||||
| 		vv[i] = ln[i].Value | ||||
| 	} | ||||
| 	return vv | ||||
| } | ||||
| 
 | ||||
| // ====================== utilities ====================== | ||||
| 
 | ||||
| // https://groups.google.com/forum/#!topic/golang-nuts/pj0C5IrZk4I | ||||
| var useDotNotation bool | ||||
| 
 | ||||
| // LeafUseDotNotation sets a flag that list members in LeafNode paths | ||||
| // should be identified using ".N" syntax rather than the default "[N]" | ||||
| // syntax.  Calling LeafUseDotNotation with no arguments toggles the  | ||||
| // flag on/off; otherwise, the argument sets the flag value 'true'/'false'. | ||||
| func LeafUseDotNotation(b ...bool) { | ||||
| 	if len(b) == 0 { | ||||
| 		useDotNotation = !useDotNotation | ||||
| 		return | ||||
| 	} | ||||
| 	useDotNotation = b[0] | ||||
| } | ||||
							
								
								
									
										86
									
								
								vendor/github.com/clbanning/mxj/misc.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										86
									
								
								vendor/github.com/clbanning/mxj/misc.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,86 +0,0 @@ | |||
| // Copyright 2016 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| // misc.go - mimic functions (+others) called out in: | ||||
| //          https://groups.google.com/forum/#!topic/golang-nuts/jm_aGsJNbdQ | ||||
| // Primarily these methods let you retrive XML structure information. | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Return the root element of the Map. If there is not a single key in Map, | ||||
| // then an error is returned. | ||||
| func (mv Map) Root() (string, error) { | ||||
| 	mm := map[string]interface{}(mv) | ||||
| 	if len(mm) != 1 { | ||||
| 		return "", fmt.Errorf("Map does not have singleton root. Len: %d.", len(mm)) | ||||
| 	} | ||||
| 	for k, _ := range mm { | ||||
| 		return k, nil | ||||
| 	} | ||||
| 	return "", nil | ||||
| } | ||||
| 
 | ||||
| // If the path is an element with sub-elements, return a list of the sub-element | ||||
| // keys.  (The list is alphabeticly sorted.)  NOTE: Map keys that are prefixed with | ||||
| // '-', a hyphen, are considered attributes; see m.Attributes(path). | ||||
| func (mv Map) Elements(path string) ([]string, error) { | ||||
| 	e, err := mv.ValueForPath(path) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	switch e.(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		ee := e.(map[string]interface{}) | ||||
| 		elems := make([]string, len(ee)) | ||||
| 		var i int | ||||
| 		for k, _ := range ee { | ||||
| 			if len(attrPrefix) > 0 && strings.Index(k, attrPrefix) == 0 { | ||||
| 				continue // skip attributes | ||||
| 			} | ||||
| 			elems[i] = k | ||||
| 			i++ | ||||
| 		} | ||||
| 		elems = elems[:i] | ||||
| 		// alphabetic sort keeps things tidy | ||||
| 		sort.Strings(elems) | ||||
| 		return elems, nil | ||||
| 	} | ||||
| 	return nil, fmt.Errorf("no elements for path: %s", path) | ||||
| } | ||||
| 
 | ||||
| // If the path is an element with attributes, return a list of the attribute | ||||
| // keys.  (The list is alphabeticly sorted.)  NOTE: Map keys that are not prefixed with | ||||
| // '-', a hyphen, are not treated as attributes; see m.Elements(path). Also, if the | ||||
| // attribute prefix is "" - SetAttrPrefix("") or PrependAttrWithHyphen(false) - then | ||||
| // there are no identifiable attributes. | ||||
| func (mv Map) Attributes(path string) ([]string, error) { | ||||
| 	a, err := mv.ValueForPath(path) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	switch a.(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		aa := a.(map[string]interface{}) | ||||
| 		attrs := make([]string, len(aa)) | ||||
| 		var i int | ||||
| 		for k, _ := range aa { | ||||
| 			if len(attrPrefix) == 0 || strings.Index(k, attrPrefix) != 0 { | ||||
| 				continue // skip non-attributes | ||||
| 			} | ||||
| 			attrs[i] = k[len(attrPrefix):] | ||||
| 			i++ | ||||
| 		} | ||||
| 		attrs = attrs[:i] | ||||
| 		// alphabetic sort keeps things tidy | ||||
| 		sort.Strings(attrs) | ||||
| 		return attrs, nil | ||||
| 	} | ||||
| 	return nil, fmt.Errorf("no attributes for path: %s", path) | ||||
| } | ||||
							
								
								
									
										128
									
								
								vendor/github.com/clbanning/mxj/mxj.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										128
									
								
								vendor/github.com/clbanning/mxj/mxj.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,128 +0,0 @@ | |||
| // mxj - A collection of map[string]interface{} and associated XML and JSON utilities. | ||||
| // Copyright 2012-2014 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"sort" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	Cast         = true // for clarity - e.g., mxj.NewMapXml(doc, mxj.Cast) | ||||
| 	SafeEncoding = true // ditto - e.g., mv.Json(mxj.SafeEncoding) | ||||
| ) | ||||
| 
 | ||||
| type Map map[string]interface{} | ||||
| 
 | ||||
| // Allocate a Map. | ||||
| func New() Map { | ||||
| 	m := make(map[string]interface{}, 0) | ||||
| 	return m | ||||
| } | ||||
| 
 | ||||
| // Cast a Map to map[string]interface{} | ||||
| func (mv Map) Old() map[string]interface{} { | ||||
| 	return mv | ||||
| } | ||||
| 
 | ||||
| // Return a copy of mv as a newly allocated Map.  If the Map only contains string, | ||||
| // numeric, map[string]interface{}, and []interface{} values, then it can be thought | ||||
| // of as a "deep copy."  Copying a structure (or structure reference) value is subject | ||||
| // to the noted restrictions. | ||||
| //	NOTE: If 'mv' includes structure values with, possibly, JSON encoding tags | ||||
| //	      then only public fields of the structure are in the new Map - and with | ||||
| //	      keys that conform to any encoding tag instructions. The structure itself will | ||||
| //	      be represented as a map[string]interface{} value. | ||||
| func (mv Map) Copy() (Map, error) { | ||||
| 	// this is the poor-man's deep copy | ||||
| 	// not efficient, but it works | ||||
| 	j, jerr := mv.Json() | ||||
| 	// must handle, we don't know how mv got built | ||||
| 	if jerr != nil { | ||||
| 		return nil, jerr | ||||
| 	} | ||||
| 	return NewMapJson(j) | ||||
| } | ||||
| 
 | ||||
| // --------------- StringIndent ... from x2j.WriteMap ------------- | ||||
| 
 | ||||
| // Pretty print a Map. | ||||
| func (mv Map) StringIndent(offset ...int) string { | ||||
| 	return writeMap(map[string]interface{}(mv), true, true, offset...) | ||||
| } | ||||
| 
 | ||||
| // Pretty print a Map without the value type information - just key:value entries. | ||||
| func (mv Map) StringIndentNoTypeInfo(offset ...int) string { | ||||
| 	return writeMap(map[string]interface{}(mv), false, true, offset...) | ||||
| } | ||||
| 
 | ||||
| // writeMap - dumps the map[string]interface{} for examination. | ||||
| // 'typeInfo' causes value type to be printed. | ||||
| //	'offset' is initial indentation count; typically: Write(m). | ||||
| func writeMap(m interface{}, typeInfo, root bool, offset ...int) string { | ||||
| 	var indent int | ||||
| 	if len(offset) == 1 { | ||||
| 		indent = offset[0] | ||||
| 	} | ||||
| 
 | ||||
| 	var s string | ||||
| 	switch m.(type) { | ||||
| 	case []interface{}: | ||||
| 		if typeInfo { | ||||
| 			s += "[[]interface{}]" | ||||
| 		} | ||||
| 		for _, v := range m.([]interface{}) { | ||||
| 			s += "\n" | ||||
| 			for i := 0; i < indent; i++ { | ||||
| 				s += "  " | ||||
| 			} | ||||
| 			s += writeMap(v, typeInfo, false, indent+1) | ||||
| 		} | ||||
| 	case map[string]interface{}: | ||||
| 		list := make([][2]string, len(m.(map[string]interface{}))) | ||||
| 		var n int | ||||
| 		for k, v := range m.(map[string]interface{}) { | ||||
| 			list[n][0] = k | ||||
| 			list[n][1] = writeMap(v, typeInfo, false, indent+1) | ||||
| 			n++ | ||||
| 		} | ||||
| 		sort.Sort(mapList(list)) | ||||
| 		for _, v := range list { | ||||
| 			if root { | ||||
| 				root = false | ||||
| 			} else { | ||||
| 				s += "\n" | ||||
| 			} | ||||
| 			for i := 0; i < indent; i++ { | ||||
| 				s += "  " | ||||
| 			} | ||||
| 			s += v[0] + " : " + v[1] | ||||
| 		} | ||||
| 	default: | ||||
| 		if typeInfo { | ||||
| 			s += fmt.Sprintf("[%T] %+v", m, m) | ||||
| 		} else { | ||||
| 			s += fmt.Sprintf("%+v", m) | ||||
| 		} | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| // ======================== utility =============== | ||||
| 
 | ||||
| type mapList [][2]string | ||||
| 
 | ||||
| func (ml mapList) Len() int { | ||||
| 	return len(ml) | ||||
| } | ||||
| 
 | ||||
| func (ml mapList) Swap(i, j int) { | ||||
| 	ml[i], ml[j] = ml[j], ml[i] | ||||
| } | ||||
| 
 | ||||
| func (ml mapList) Less(i, j int) bool { | ||||
| 	return ml[i][0] <= ml[j][0] | ||||
| } | ||||
							
								
								
									
										184
									
								
								vendor/github.com/clbanning/mxj/newmap.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										184
									
								
								vendor/github.com/clbanning/mxj/newmap.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,184 +0,0 @@ | |||
| // mxj - A collection of map[string]interface{} and associated XML and JSON utilities. | ||||
| // Copyright 2012-2014, 2018 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| // remap.go - build a new Map from the current Map based on keyOld:keyNew mapppings | ||||
| //            keys can use dot-notation, keyOld can use wildcard, '*' | ||||
| // | ||||
| // Computational strategy - | ||||
| // Using the key path - []string - traverse a new map[string]interface{} and | ||||
| // insert the oldVal as the newVal when we arrive at the end of the path. | ||||
| // If the type at the end is nil, then that is newVal | ||||
| // If the type at the end is a singleton (string, float64, bool) an array is created. | ||||
| // If the type at the end is an array, newVal is just appended. | ||||
| // If the type at the end is a map, it is inserted if possible or the map value | ||||
| //    is converted into an array if necessary. | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // (Map)NewMap - create a new Map from data in the current Map. | ||||
| //	'keypairs' are key mappings "oldKey:newKey" and specify that the current value of 'oldKey' | ||||
| //	should be the value for 'newKey' in the returned Map. | ||||
| //		- 'oldKey' supports dot-notation as described for (Map)ValuesForPath() | ||||
| //		- 'newKey' supports dot-notation but with no wildcards, '*', or indexed arrays | ||||
| //		- "oldKey" is shorthand for the keypair value "oldKey:oldKey" | ||||
| //		- "oldKey:" and ":newKey" are invalid keypair values | ||||
| //		- if 'oldKey' does not exist in the current Map, it is not written to the new Map. | ||||
| //		  "null" is not supported unless it is the current Map. | ||||
| //		- see newmap_test.go for several syntax examples | ||||
| // 	- mv.NewMap() == mxj.New() | ||||
| // | ||||
| //	NOTE: "examples/partial.go" shows how to create arbitrary sub-docs of an XML doc. | ||||
| func (mv Map) NewMap(keypairs ...string) (Map, error) { | ||||
| 	n := make(map[string]interface{}, 0) | ||||
| 	if len(keypairs) == 0 { | ||||
| 		return n, nil | ||||
| 	} | ||||
| 
 | ||||
| 	// loop through the pairs | ||||
| 	var oldKey, newKey string | ||||
| 	var path []string | ||||
| 	for _, v := range keypairs { | ||||
| 		if len(v) == 0 { | ||||
| 			continue // just skip over empty keypair arguments | ||||
| 		} | ||||
| 
 | ||||
| 		// initialize oldKey, newKey and check | ||||
| 		vv := strings.Split(v, ":") | ||||
| 		if len(vv) > 2 { | ||||
| 			return n, errors.New("oldKey:newKey keypair value not valid - " + v) | ||||
| 		} | ||||
| 		if len(vv) == 1 { | ||||
| 			oldKey, newKey = vv[0], vv[0] | ||||
| 		} else { | ||||
| 			oldKey, newKey = vv[0], vv[1] | ||||
| 		} | ||||
| 		strings.TrimSpace(oldKey) | ||||
| 		strings.TrimSpace(newKey) | ||||
| 		if i := strings.Index(newKey, "*"); i > -1 { | ||||
| 			return n, errors.New("newKey value cannot contain wildcard character - " + v) | ||||
| 		} | ||||
| 		if i := strings.Index(newKey, "["); i > -1 { | ||||
| 			return n, errors.New("newKey value cannot contain indexed arrays - " + v) | ||||
| 		} | ||||
| 		if oldKey == "" || newKey == "" { | ||||
| 			return n, errors.New("oldKey or newKey is not specified - " + v) | ||||
| 		} | ||||
| 
 | ||||
| 		// get oldKey value | ||||
| 		oldVal, err := mv.ValuesForPath(oldKey) | ||||
| 		if err != nil { | ||||
| 			return n, err | ||||
| 		} | ||||
| 		if len(oldVal) == 0 { | ||||
| 			continue // oldKey has no value, may not exist in mv | ||||
| 		} | ||||
| 
 | ||||
| 		// break down path | ||||
| 		path = strings.Split(newKey, ".") | ||||
| 		if path[len(path)-1] == "" { // ignore a trailing dot in newKey spec | ||||
| 			path = path[:len(path)-1] | ||||
| 		} | ||||
| 
 | ||||
| 		addNewVal(&n, path, oldVal) | ||||
| 	} | ||||
| 
 | ||||
| 	return n, nil | ||||
| } | ||||
| 
 | ||||
| // navigate 'n' to end of path and add val | ||||
| func addNewVal(n *map[string]interface{}, path []string, val []interface{}) { | ||||
| 	// newVal - either singleton or array | ||||
| 	var newVal interface{} | ||||
| 	if len(val) == 1 { | ||||
| 		newVal = val[0] // is type interface{} | ||||
| 	} else { | ||||
| 		newVal = interface{}(val) | ||||
| 	} | ||||
| 
 | ||||
| 	// walk to the position of interest, create it if necessary | ||||
| 	m := (*n)           // initialize map walker | ||||
| 	var k string        // key for m | ||||
| 	lp := len(path) - 1 // when to stop looking | ||||
| 	for i := 0; i < len(path); i++ { | ||||
| 		k = path[i] | ||||
| 		if i == lp { | ||||
| 			break | ||||
| 		} | ||||
| 		var nm map[string]interface{} // holds position of next-map | ||||
| 		switch m[k].(type) { | ||||
| 		case nil: // need a map for next node in path, so go there | ||||
| 			nm = make(map[string]interface{}, 0) | ||||
| 			m[k] = interface{}(nm) | ||||
| 			m = m[k].(map[string]interface{}) | ||||
| 		case map[string]interface{}: | ||||
| 			// OK - got somewhere to walk to, go there | ||||
| 			m = m[k].(map[string]interface{}) | ||||
| 		case []interface{}: | ||||
| 			// add a map and nm points to new map unless there's already | ||||
| 			// a map in the array, then nm points there | ||||
| 			// The placement of the next value in the array is dependent | ||||
| 			// on the sequence of members - could land on a map or a nil | ||||
| 			// value first.  TODO: how to test this. | ||||
| 			a := make([]interface{}, 0) | ||||
| 			var foundmap bool | ||||
| 			for _, vv := range m[k].([]interface{}) { | ||||
| 				switch vv.(type) { | ||||
| 				case nil: // doesn't appear that this occurs, need a test case | ||||
| 					if foundmap { // use the first one in array | ||||
| 						a = append(a, vv) | ||||
| 						continue | ||||
| 					} | ||||
| 					nm = make(map[string]interface{}, 0) | ||||
| 					a = append(a, interface{}(nm)) | ||||
| 					foundmap = true | ||||
| 				case map[string]interface{}: | ||||
| 					if foundmap { // use the first one in array | ||||
| 						a = append(a, vv) | ||||
| 						continue | ||||
| 					} | ||||
| 					nm = vv.(map[string]interface{}) | ||||
| 					a = append(a, vv) | ||||
| 					foundmap = true | ||||
| 				default: | ||||
| 					a = append(a, vv) | ||||
| 				} | ||||
| 			} | ||||
| 			// no map found in array | ||||
| 			if !foundmap { | ||||
| 				nm = make(map[string]interface{}, 0) | ||||
| 				a = append(a, interface{}(nm)) | ||||
| 			} | ||||
| 			m[k] = interface{}(a) // must insert in map | ||||
| 			m = nm | ||||
| 		default: // it's a string, float, bool, etc. | ||||
| 			aa := make([]interface{}, 0) | ||||
| 			nm = make(map[string]interface{}, 0) | ||||
| 			aa = append(aa, m[k], nm) | ||||
| 			m[k] = interface{}(aa) | ||||
| 			m = nm | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// value is nil, array or a singleton of some kind | ||||
| 	// initially m.(type) == map[string]interface{} | ||||
| 	v := m[k] | ||||
| 	switch v.(type) { | ||||
| 	case nil: // initialized | ||||
| 		m[k] = newVal | ||||
| 	case []interface{}: | ||||
| 		a := m[k].([]interface{}) | ||||
| 		a = append(a, newVal) | ||||
| 		m[k] = interface{}(a) | ||||
| 	default: // v exists:string, float64, bool, map[string]interface, etc. | ||||
| 		a := make([]interface{}, 0) | ||||
| 		a = append(a, v, newVal) | ||||
| 		m[k] = interface{}(a) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										179
									
								
								vendor/github.com/clbanning/mxj/readme.md
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										179
									
								
								vendor/github.com/clbanning/mxj/readme.md
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,179 +0,0 @@ | |||
| <h2>mxj - to/from maps, XML and JSON</h2> | ||||
| Decode/encode XML to/from map[string]interface{} (or JSON) values, and extract/modify values from maps by key or key-path, including wildcards. | ||||
| 
 | ||||
| mxj supplants the legacy x2j and j2x packages. If you want the old syntax, use mxj/x2j and mxj/j2x packages. | ||||
| 
 | ||||
| <h4>Related Packages</h4> | ||||
| 
 | ||||
| https://github.com/clbanning/checkxml provides functions for validating XML data. | ||||
| 
 | ||||
| <h4>Refactor Decoder - 2015.11.15</h4> | ||||
| For over a year I've wanted to refactor the XML-to-map[string]interface{} decoder to make it more performant.  I recently took the time to do that, since we were using github.com/clbanning/mxj in a production system that could be deployed on a Raspberry Pi.  Now the decoder is comparable to the stdlib JSON-to-map[string]interface{} decoder in terms of its additional processing overhead relative to decoding to a structure value.  As shown by: | ||||
| 
 | ||||
| 	BenchmarkNewMapXml-4         	  100000	     18043 ns/op | ||||
| 	BenchmarkNewStructXml-4      	  100000	     14892 ns/op | ||||
| 	BenchmarkNewMapJson-4        	  300000	      4633 ns/op | ||||
| 	BenchmarkNewStructJson-4     	  300000	      3427 ns/op | ||||
| 	BenchmarkNewMapXmlBooks-4    	   20000	     82850 ns/op | ||||
| 	BenchmarkNewStructXmlBooks-4 	   20000	     67822 ns/op | ||||
| 	BenchmarkNewMapJsonBooks-4   	  100000	     17222 ns/op | ||||
| 	BenchmarkNewStructJsonBooks-4	  100000	     15309 ns/op | ||||
| 
 | ||||
| <h4>Notices</h4> | ||||
| 
 | ||||
| 	2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc. | ||||
| 	2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps. | ||||
| 	2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package. | ||||
| 	2017.02.22: LeafNode paths can use ".N" syntax rather than "[N]" for list member indexing. | ||||
| 	2017.02.10: SetFieldSeparator changes field separator for args in UpdateValuesForPath, ValuesFor... methods. | ||||
| 	2017.02.06: Support XMPP stream processing - HandleXMPPStreamTag(). | ||||
| 	2016.11.07: Preserve name space prefix syntax in XmlSeq parser - NewMapXmlSeq(), etc. | ||||
| 	2016.06.25: Support overriding default XML attribute prefix, "-", in Map keys - SetAttrPrefix(). | ||||
| 	2016.05.26: Support customization of xml.Decoder by exposing CustomDecoder variable. | ||||
| 	2016.03.19: Escape invalid chars when encoding XML attribute and element values - XMLEscapeChars(). | ||||
| 	2016.03.02: By default decoding XML with float64 and bool value casting will not cast "NaN", "Inf", and "-Inf". | ||||
| 	            To cast them to float64, first set flag with CastNanInf(true). | ||||
| 	2016.02.22: New mv.Root(), mv.Elements(), mv.Attributes methods let you examine XML document structure. | ||||
| 	2016.02.16: Add CoerceKeysToLower() option to handle tags with mixed capitalization. | ||||
| 	2016.02.12: Seek for first xml.StartElement token; only return error if io.EOF is reached first (handles BOM). | ||||
| 	2015.12.02: XML decoding/encoding that preserves original structure of document. See NewMapXmlSeq() | ||||
| 	            and mv.XmlSeq() / mv.XmlSeqIndent(). | ||||
| 	2015-05-20: New: mv.StringIndentNoTypeInfo(). | ||||
| 	            Also, alphabetically sort map[string]interface{} values by key to prettify output for mv.Xml(), | ||||
| 	            mv.XmlIndent(), mv.StringIndent(), mv.StringIndentNoTypeInfo(). | ||||
| 	2014-11-09: IncludeTagSeqNum() adds "_seq" key with XML doc positional information. | ||||
| 	            (NOTE: PreserveXmlList() is similar and will be here soon.) | ||||
| 	2014-09-18: inspired by NYTimes fork, added PrependAttrWithHyphen() to allow stripping hyphen from attribute tag. | ||||
| 	2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML. | ||||
| 	2014-04-28: ValuesForPath() and NewMap() now accept path with indexed array references. | ||||
| 
 | ||||
| <h4>Basic Unmarshal XML to map[string]interface{}</h4> | ||||
| <pre>type Map map[string]interface{}</pre> | ||||
| 
 | ||||
| Create a `Map` value, 'mv', from any `map[string]interface{}` value, 'v': | ||||
| <pre>mv := Map(v)</pre> | ||||
| 
 | ||||
| Unmarshal / marshal XML as a `Map` value, 'mv': | ||||
| <pre>mv, err := NewMapXml(xmlValue) // unmarshal | ||||
| xmlValue, err := mv.Xml()      // marshal</pre> | ||||
| 
 | ||||
| Unmarshal XML from an `io.Reader` as a `Map` value, 'mv': | ||||
| <pre>mv, err := NewMapXmlReader(xmlReader)         // repeated calls, as with an os.File Reader, will process stream | ||||
| mv, raw, err := NewMapXmlReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded</pre> | ||||
| 
 | ||||
| Marshal `Map` value, 'mv', to an XML Writer (`io.Writer`): | ||||
| <pre>err := mv.XmlWriter(xmlWriter) | ||||
| raw, err := mv.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter</pre> | ||||
|     | ||||
| Also, for prettified output: | ||||
| <pre>xmlValue, err := mv.XmlIndent(prefix, indent, ...) | ||||
| err := mv.XmlIndentWriter(xmlWriter, prefix, indent, ...) | ||||
| raw, err := mv.XmlIndentWriterRaw(xmlWriter, prefix, indent, ...)</pre> | ||||
| 
 | ||||
| Bulk process XML with error handling (note: handlers must return a boolean value): | ||||
| <pre>err := HandleXmlReader(xmlReader, mapHandler(Map), errHandler(error)) | ||||
| err := HandleXmlReaderRaw(xmlReader, mapHandler(Map, []byte), errHandler(error, []byte))</pre> | ||||
| 
 | ||||
| Converting XML to JSON: see Examples for `NewMapXml` and `HandleXmlReader`. | ||||
| 
 | ||||
| There are comparable functions and methods for JSON processing. | ||||
| 
 | ||||
| Arbitrary structure values can be decoded to / encoded from `Map` values: | ||||
| <pre>mv, err := NewMapStruct(structVal) | ||||
| err := mv.Struct(structPointer)</pre> | ||||
| 
 | ||||
| <h4>Extract / modify Map values</h4> | ||||
| To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON | ||||
| or structure to a `Map` value, 'mv', or cast a `map[string]interface{}` value to a `Map` value, 'mv', then: | ||||
| <pre>paths := mv.PathsForKey(key) | ||||
| path := mv.PathForKeyShortest(key) | ||||
| values, err := mv.ValuesForKey(key, subkeys) | ||||
| values, err := mv.ValuesForPath(path, subkeys) | ||||
| count, err := mv.UpdateValuesForPath(newVal, path, subkeys)</pre> | ||||
| 
 | ||||
| Get everything at once, irrespective of path depth: | ||||
| <pre>leafnodes := mv.LeafNodes() | ||||
| leafvalues := mv.LeafValues()</pre> | ||||
| 
 | ||||
| A new `Map` with whatever keys are desired can be created from the current `Map` and then encoded in XML | ||||
| or JSON. (Note: keys can use dot-notation.) | ||||
| <pre>newMap, err := mv.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N") | ||||
| newMap, err := mv.NewMap("oldKey1", "oldKey3", "oldKey5") // a subset of 'mv'; see "examples/partial.go" | ||||
| newXml, err := newMap.Xml()   // for example | ||||
| newJson, err := newMap.Json() // ditto</pre> | ||||
| 
 | ||||
| <h4>Usage</h4> | ||||
| 
 | ||||
| The package is fairly well [self-documented with examples](http://godoc.org/github.com/clbanning/mxj). | ||||
| 
 | ||||
| Also, the subdirectory "examples" contains a wide range of examples, several taken from golang-nuts discussions. | ||||
| 
 | ||||
| <h4>XML parsing conventions</h4> | ||||
| 
 | ||||
| Using NewMapXml() | ||||
| 
 | ||||
|    - Attributes are parsed to `map[string]interface{}` values by prefixing a hyphen, `-`, | ||||
|      to the attribute label. (Unless overridden by `PrependAttrWithHyphen(false)` or | ||||
|      `SetAttrPrefix()`.) | ||||
|    - If the element is a simple element and has attributes, the element value | ||||
|      is given the key `#text` for its `map[string]interface{}` representation.  (See | ||||
|      the 'atomFeedString.xml' test data, below.) | ||||
|    - XML comments, directives, and process instructions are ignored. | ||||
|    - If CoerceKeysToLower() has been called, then the resultant keys will be lower case. | ||||
| 
 | ||||
| Using NewMapXmlSeq() | ||||
| 
 | ||||
|    - Attributes are parsed to `map["#attr"]map[<attr_label>]map[string]interface{}`values | ||||
|      where the `<attr_label>` value has "#text" and "#seq" keys - the "#text" key holds the  | ||||
|      value for `<attr_label>`. | ||||
|    - All elements, except for the root, have a "#seq" key. | ||||
|    - Comments, directives, and process instructions are unmarshalled into the Map using the | ||||
|      keys "#comment", "#directive", and "#procinst", respectively. (See documentation for more | ||||
|      specifics.) | ||||
|    - Name space syntax is preserved:  | ||||
|       - `<ns:key>something</ns.key>` parses to `map["ns:key"]interface{}{"something"}` | ||||
|       - `xmlns:ns="http://myns.com/ns"` parses to `map["xmlns:ns"]interface{}{"http://myns.com/ns"}` | ||||
| 
 | ||||
| Both | ||||
| 
 | ||||
|    - By default, "Nan", "Inf", and "-Inf" values are not cast to float64.  If you want them | ||||
|      to be cast, set a flag to cast them  using CastNanInf(true). | ||||
| 
 | ||||
| <h4>XML encoding conventions</h4> | ||||
| 
 | ||||
|    - 'nil' `Map` values, which may represent 'null' JSON values, are encoded as `<tag/>`. | ||||
|      NOTE: the operation is not symmetric as `<tag/>` elements are decoded as `tag:""` `Map` values, | ||||
|            which, then, encode in JSON as `"tag":""` values. | ||||
|    - ALSO: there is no guarantee that the encoded XML doc will be the same as the decoded one.  (Go | ||||
|            randomizes the walk through map[string]interface{} values.) If you plan to re-encode the | ||||
|            Map value to XML and want the same sequencing of elements look at NewMapXmlSeq() and | ||||
|            mv.XmlSeq() - these try to preserve the element sequencing but with added complexity when | ||||
|            working with the Map representation. | ||||
| 
 | ||||
| <h4>Running "go test"</h4> | ||||
| 
 | ||||
| Because there are no guarantees on the sequence map elements are retrieved, the tests have been  | ||||
| written for visual verification in most cases.  One advantage is that you can easily use the  | ||||
| output from running "go test" as examples of calling the various functions and methods. | ||||
| 
 | ||||
| <h4>Motivation</h4> | ||||
| 
 | ||||
| I make extensive use of JSON for messaging and typically unmarshal the messages into | ||||
| `map[string]interface{}` values.  This is easily done using `json.Unmarshal` from the | ||||
| standard Go libraries.  Unfortunately, many legacy solutions use structured | ||||
| XML messages; in those environments the applications would have to be refactored to | ||||
| interoperate with my components. | ||||
| 
 | ||||
| The better solution is to just provide an alternative HTTP handler that receives | ||||
| XML messages and parses it into a `map[string]interface{}` value and then reuse | ||||
| all the JSON-based code.  The Go `xml.Unmarshal()` function does not provide the same | ||||
| option of unmarshaling XML messages into `map[string]interface{}` values. So I wrote | ||||
| a couple of small functions to fill this gap and released them as the x2j package. | ||||
| 
 | ||||
| Over the next year and a half additional features were added, and the companion j2x | ||||
| package was released to address XML encoding of arbitrary JSON and `map[string]interface{}` | ||||
| values.  As part of a refactoring of our production system and looking at how we had been | ||||
| using the x2j and j2x packages we found that we rarely performed direct XML-to-JSON or | ||||
| JSON-to_XML conversion and that working with the XML or JSON as `map[string]interface{}` | ||||
| values was the primary value.  Thus, everything was refactored into the mxj package. | ||||
| 
 | ||||
							
								
								
									
										37
									
								
								vendor/github.com/clbanning/mxj/remove.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								vendor/github.com/clbanning/mxj/remove.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,37 +0,0 @@ | |||
| package mxj | ||||
| 
 | ||||
| import "strings" | ||||
| 
 | ||||
| // Removes the path. | ||||
| func (mv Map) Remove(path string) error { | ||||
| 	m := map[string]interface{}(mv) | ||||
| 	return remove(m, path) | ||||
| } | ||||
| 
 | ||||
| func remove(m interface{}, path string) error { | ||||
| 	val, err := prevValueByPath(m, path) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	lastKey := lastKey(path) | ||||
| 	delete(val, lastKey) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // returns the last key of the path. | ||||
| // lastKey("a.b.c") would had returned "c" | ||||
| func lastKey(path string) string { | ||||
| 	keys := strings.Split(path, ".") | ||||
| 	key := keys[len(keys)-1] | ||||
| 	return key | ||||
| } | ||||
| 
 | ||||
| // returns the path without the last key | ||||
| // parentPath("a.b.c") whould had returned "a.b" | ||||
| func parentPath(path string) string { | ||||
| 	keys := strings.Split(path, ".") | ||||
| 	parentPath := strings.Join(keys[0:len(keys)-1], ".") | ||||
| 	return parentPath | ||||
| } | ||||
							
								
								
									
										54
									
								
								vendor/github.com/clbanning/mxj/rename.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/clbanning/mxj/rename.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,54 +0,0 @@ | |||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // RenameKey renames a key in a Map. | ||||
| // It works only for nested maps. It doesn't work for cases when it buried in a list. | ||||
| func (mv Map) RenameKey(path string, newName string) error { | ||||
| 	if !mv.Exists(path) { | ||||
| 		return errors.New("RenameKey: path not found: " + path) | ||||
| 	} | ||||
| 	if mv.Exists(parentPath(path) + "." + newName) { | ||||
| 		return errors.New("RenameKey: key already exists: " + newName) | ||||
| 	} | ||||
| 
 | ||||
| 	m := map[string]interface{}(mv) | ||||
| 	return renameKey(m, path, newName) | ||||
| } | ||||
| 
 | ||||
| func renameKey(m interface{}, path string, newName string) error { | ||||
| 	val, err := prevValueByPath(m, path) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	oldName := lastKey(path) | ||||
| 	val[newName] = val[oldName] | ||||
| 	delete(val, oldName) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // returns a value which contains a last key in the path | ||||
| // For example: prevValueByPath("a.b.c", {a{b{c: 3}}}) returns {c: 3} | ||||
| func prevValueByPath(m interface{}, path string) (map[string]interface{}, error) { | ||||
| 	keys := strings.Split(path, ".") | ||||
| 
 | ||||
| 	switch mValue := m.(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		for key, value := range mValue { | ||||
| 			if key == keys[0] { | ||||
| 				if len(keys) == 1 { | ||||
| 					return mValue, nil | ||||
| 				} else { | ||||
| 					// keep looking for the full path to the key | ||||
| 					return prevValueByPath(value, strings.Join(keys[1:], ".")) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, errors.New("prevValueByPath: didn't find path – " + path) | ||||
| } | ||||
							
								
								
									
										26
									
								
								vendor/github.com/clbanning/mxj/set.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/clbanning/mxj/set.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,26 +0,0 @@ | |||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Sets the value for the path | ||||
| func (mv Map) SetValueForPath(value interface{}, path string) error { | ||||
| 	pathAry := strings.Split(path, ".") | ||||
| 	parentPathAry := pathAry[0 : len(pathAry)-1] | ||||
| 	parentPath := strings.Join(parentPathAry, ".") | ||||
| 
 | ||||
| 	val, err := mv.ValueForPath(parentPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if val == nil { | ||||
| 		return nil // we just ignore the request if there's no val | ||||
| 	} | ||||
| 
 | ||||
| 	key := pathAry[len(pathAry)-1] | ||||
| 	cVal := val.(map[string]interface{}) | ||||
| 	cVal[key] = value | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										20
									
								
								vendor/github.com/clbanning/mxj/setfieldsep.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/clbanning/mxj/setfieldsep.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,20 +0,0 @@ | |||
| package mxj | ||||
| 
 | ||||
| // Per: https://github.com/clbanning/mxj/issues/37#issuecomment-278651862 | ||||
| var fieldSep string = ":" | ||||
| 
 | ||||
| // SetFieldSeparator changes the default field separator, ":", for the | ||||
| // newVal argument in mv.UpdateValuesForPath and the optional 'subkey' arguments | ||||
| // in mv.ValuesForKey and mv.ValuesForPath.  | ||||
| //  | ||||
| // E.g., if the newVal value is "http://blah/blah", setting the field separator | ||||
| // to "|" will allow the newVal specification, "<key>|http://blah/blah" to parse | ||||
| // properly.  If called with no argument or an empty string value, the field | ||||
| // separator is set to the default, ":". | ||||
| func SetFieldSeparator(s ...string) { | ||||
| 	if len(s) == 0 || s[0] == "" { | ||||
| 		fieldSep = ":" // the default | ||||
| 		return | ||||
| 	} | ||||
| 	fieldSep = s[0] | ||||
| } | ||||
							
								
								
									
										29
									
								
								vendor/github.com/clbanning/mxj/songtext.xml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/clbanning/mxj/songtext.xml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,29 +0,0 @@ | |||
| <msg mtype="alert" mpriority="1"> | ||||
| 	<text>help me!</text> | ||||
| 	<song title="A Long Time" author="Mayer Hawthorne"> | ||||
| 		<verses> | ||||
| 			<verse name="verse 1" no="1"> | ||||
| 				<line no="1">Henry was a renegade</line> | ||||
| 				<line no="2">Didn't like to play it safe</line> | ||||
| 				<line no="3">One component at a time</line> | ||||
| 				<line no="4">There's got to be a better way</line> | ||||
| 				<line no="5">Oh, people came from miles around</line> | ||||
| 				<line no="6">Searching for a steady job</line> | ||||
| 				<line no="7">Welcome to the Motor Town</line> | ||||
| 				<line no="8">Booming like an atom bomb</line> | ||||
| 			</verse> | ||||
| 			<verse name="verse 2" no="2"> | ||||
| 				<line no="1">Oh, Henry was the end of the story</line> | ||||
| 				<line no="2">Then everything went wrong</line> | ||||
| 				<line no="3">And we'll return it to its former glory</line> | ||||
| 				<line no="4">But it just takes so long</line> | ||||
| 			</verse> | ||||
| 		</verses> | ||||
| 		<chorus> | ||||
| 			<line no="1">It's going to take a long time</line> | ||||
| 			<line no="2">It's going to take it, but we'll make it one day</line> | ||||
| 			<line no="3">It's going to take a long time</line> | ||||
| 			<line no="4">It's going to take it, but we'll make it one day</line> | ||||
| 		</chorus> | ||||
| 	</song> | ||||
| </msg> | ||||
							
								
								
									
										30
									
								
								vendor/github.com/clbanning/mxj/strict.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/clbanning/mxj/strict.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,30 +0,0 @@ | |||
| // Copyright 2016 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| // strict.go actually addresses setting xml.Decoder attribute | ||||
| // values.  This'll let you parse non-standard XML. | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/xml" | ||||
| ) | ||||
| 
 | ||||
| // CustomDecoder can be used to specify xml.Decoder attribute | ||||
| // values, e.g., Strict:false, to be used.  By default CustomDecoder | ||||
| // is nil.  If CustomeDecoder != nil, then mxj.XmlCharsetReader variable is | ||||
| // ignored and must be set as part of the CustomDecoder value, if needed. | ||||
| //	Usage: | ||||
| //		mxj.CustomDecoder = &xml.Decoder{Strict:false} | ||||
| var CustomDecoder *xml.Decoder | ||||
| 
 | ||||
| // useCustomDecoder copy over public attributes from customDecoder | ||||
| func useCustomDecoder(d *xml.Decoder) { | ||||
| 	d.Strict = CustomDecoder.Strict | ||||
| 	d.AutoClose = CustomDecoder.AutoClose | ||||
| 	d.Entity = CustomDecoder.Entity | ||||
| 	d.CharsetReader = CustomDecoder.CharsetReader | ||||
| 	d.DefaultSpace = CustomDecoder.DefaultSpace | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										54
									
								
								vendor/github.com/clbanning/mxj/struct.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/clbanning/mxj/struct.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,54 +0,0 @@ | |||
| // Copyright 2012-2017 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"reflect" | ||||
| 
 | ||||
| 	// "github.com/fatih/structs" | ||||
| ) | ||||
| 
 | ||||
| // Create a new Map value from a structure.  Error returned if argument is not a structure. | ||||
| // Only public structure fields are decoded in the Map value. See github.com/fatih/structs#Map | ||||
| // for handling of "structs" tags. | ||||
| 
 | ||||
| // DEPRECATED - import github.com/fatih/structs and cast result of structs.Map to mxj.Map. | ||||
| //	import "github.com/fatih/structs" | ||||
| //	... | ||||
| //	   sm, err := structs.Map(<some struct>) | ||||
| //	   if err != nil { | ||||
| //	      // handle error | ||||
| //	   } | ||||
| //	   m := mxj.Map(sm) | ||||
| // Alernatively uncomment the old source and import in struct.go. | ||||
| func NewMapStruct(structVal interface{}) (Map, error) { | ||||
| 	return nil, errors.New("deprecated - see package documentation") | ||||
| 	/* | ||||
| 		if !structs.IsStruct(structVal) { | ||||
| 			return nil, errors.New("NewMapStruct() error: argument is not type Struct") | ||||
| 		} | ||||
| 		return structs.Map(structVal), nil | ||||
| 	*/ | ||||
| } | ||||
| 
 | ||||
| // Marshal a map[string]interface{} into a structure referenced by 'structPtr'. Error returned | ||||
| // if argument is not a pointer or if json.Unmarshal returns an error. | ||||
| //	json.Unmarshal structure encoding rules are followed to encode public structure fields. | ||||
| func (mv Map) Struct(structPtr interface{}) error { | ||||
| 	// should check that we're getting a pointer. | ||||
| 	if reflect.ValueOf(structPtr).Kind() != reflect.Ptr { | ||||
| 		return errors.New("mv.Struct() error: argument is not type Ptr") | ||||
| 	} | ||||
| 
 | ||||
| 	m := map[string]interface{}(mv) | ||||
| 	j, err := json.Marshal(m) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return json.Unmarshal(j, structPtr) | ||||
| } | ||||
							
								
								
									
										256
									
								
								vendor/github.com/clbanning/mxj/updatevalues.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										256
									
								
								vendor/github.com/clbanning/mxj/updatevalues.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,256 +0,0 @@ | |||
| // Copyright 2012-2014, 2017 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| // updatevalues.go - modify a value based on path and possibly sub-keys | ||||
| // TODO(clb): handle simple elements with attributes and NewMapXmlSeq Map values. | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Update value based on path and possible sub-key values. | ||||
| // A count of the number of values changed and any error are returned. | ||||
| // If the count == 0, then no path (and subkeys) matched. | ||||
| //	'newVal' can be a Map or map[string]interface{} value with a single 'key' that is the key to be modified | ||||
| //	             or a string value "key:value[:type]" where type is "bool" or "num" to cast the value. | ||||
| //	'path' is dot-notation list of keys to traverse; last key in path can be newVal key | ||||
| //	       NOTE: 'path' spec does not currently support indexed array references. | ||||
| //	'subkeys' are "key:value[:type]" entries that must match for path node | ||||
| //	            The subkey can be wildcarded - "key:*" - to require that it's there with some value. | ||||
| //	            If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an | ||||
| //	            exclusion critera - e.g., "!author:William T. Gaddis". | ||||
| // | ||||
| //	NOTES: | ||||
| //		1. Simple elements with attributes need a path terminated as ".#text" to modify the actual value. | ||||
| //		2. Values in Maps created using NewMapXmlSeq are map[string]interface{} values with a "#text" key. | ||||
| //		3. If values in 'newVal' or 'subkeys' args contain ":", use SetFieldSeparator to an unused symbol, | ||||
| //	      perhaps "|". | ||||
| func (mv Map) UpdateValuesForPath(newVal interface{}, path string, subkeys ...string) (int, error) { | ||||
| 	m := map[string]interface{}(mv) | ||||
| 
 | ||||
| 	// extract the subkeys | ||||
| 	var subKeyMap map[string]interface{} | ||||
| 	if len(subkeys) > 0 { | ||||
| 		var err error | ||||
| 		subKeyMap, err = getSubKeyMap(subkeys...) | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// extract key and value from newVal | ||||
| 	var key string | ||||
| 	var val interface{} | ||||
| 	switch newVal.(type) { | ||||
| 	case map[string]interface{}, Map: | ||||
| 		switch newVal.(type) { // "fallthrough is not permitted in type switch" (Spec) | ||||
| 		case Map: | ||||
| 			newVal = newVal.(Map).Old() | ||||
| 		} | ||||
| 		if len(newVal.(map[string]interface{})) != 1 { | ||||
| 			return 0, fmt.Errorf("newVal map can only have len == 1 - %+v", newVal) | ||||
| 		} | ||||
| 		for key, val = range newVal.(map[string]interface{}) { | ||||
| 		} | ||||
| 	case string: // split it as a key:value pair | ||||
| 		ss := strings.Split(newVal.(string), fieldSep) | ||||
| 		n := len(ss) | ||||
| 		if n < 2 || n > 3 { | ||||
| 			return 0, fmt.Errorf("unknown newVal spec - %+v", newVal) | ||||
| 		} | ||||
| 		key = ss[0] | ||||
| 		if n == 2 { | ||||
| 			val = interface{}(ss[1]) | ||||
| 		} else if n == 3 { | ||||
| 			switch ss[2] { | ||||
| 			case "bool", "boolean": | ||||
| 				nv, err := strconv.ParseBool(ss[1]) | ||||
| 				if err != nil { | ||||
| 					return 0, fmt.Errorf("can't convert newVal to bool - %+v", newVal) | ||||
| 				} | ||||
| 				val = interface{}(nv) | ||||
| 			case "num", "numeric", "float", "int": | ||||
| 				nv, err := strconv.ParseFloat(ss[1], 64) | ||||
| 				if err != nil { | ||||
| 					return 0, fmt.Errorf("can't convert newVal to float64 - %+v", newVal) | ||||
| 				} | ||||
| 				val = interface{}(nv) | ||||
| 			default: | ||||
| 				return 0, fmt.Errorf("unknown type for newVal value - %+v", newVal) | ||||
| 			} | ||||
| 		} | ||||
| 	default: | ||||
| 		return 0, fmt.Errorf("invalid newVal type - %+v", newVal) | ||||
| 	} | ||||
| 
 | ||||
| 	// parse path | ||||
| 	keys := strings.Split(path, ".") | ||||
| 
 | ||||
| 	var count int | ||||
| 	updateValuesForKeyPath(key, val, m, keys, subKeyMap, &count) | ||||
| 
 | ||||
| 	return count, nil | ||||
| } | ||||
| 
 | ||||
| // navigate the path | ||||
| func updateValuesForKeyPath(key string, value interface{}, m interface{}, keys []string, subkeys map[string]interface{}, cnt *int) { | ||||
| 	// ----- at end node: looking at possible node to get 'key' ---- | ||||
| 	if len(keys) == 1 { | ||||
| 		updateValue(key, value, m, keys[0], subkeys, cnt) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// ----- here we are navigating the path thru the penultimate node -------- | ||||
| 	// key of interest is keys[0] - the next in the path | ||||
| 	switch keys[0] { | ||||
| 	case "*": // wildcard - scan all values | ||||
| 		switch m.(type) { | ||||
| 		case map[string]interface{}: | ||||
| 			for _, v := range m.(map[string]interface{}) { | ||||
| 				updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt) | ||||
| 			} | ||||
| 		case []interface{}: | ||||
| 			for _, v := range m.([]interface{}) { | ||||
| 				switch v.(type) { | ||||
| 				// flatten out a list of maps - keys are processed | ||||
| 				case map[string]interface{}: | ||||
| 					for _, vv := range v.(map[string]interface{}) { | ||||
| 						updateValuesForKeyPath(key, value, vv, keys[1:], subkeys, cnt) | ||||
| 					} | ||||
| 				default: | ||||
| 					updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	default: // key - must be map[string]interface{} | ||||
| 		switch m.(type) { | ||||
| 		case map[string]interface{}: | ||||
| 			if v, ok := m.(map[string]interface{})[keys[0]]; ok { | ||||
| 				updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt) | ||||
| 			} | ||||
| 		case []interface{}: // may be buried in list | ||||
| 			for _, v := range m.([]interface{}) { | ||||
| 				switch v.(type) { | ||||
| 				case map[string]interface{}: | ||||
| 					if vv, ok := v.(map[string]interface{})[keys[0]]; ok { | ||||
| 						updateValuesForKeyPath(key, value, vv, keys[1:], subkeys, cnt) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // change value if key and subkeys are present | ||||
| func updateValue(key string, value interface{}, m interface{}, keys0 string, subkeys map[string]interface{}, cnt *int) { | ||||
| 	// there are two possible options for the value of 'keys0': map[string]interface, []interface{} | ||||
| 	// and 'key' is a key in the map or is a key in a map in a list. | ||||
| 	switch m.(type) { | ||||
| 	case map[string]interface{}: // gotta have the last key | ||||
| 		if keys0 == "*" { | ||||
| 			for k := range m.(map[string]interface{}) { | ||||
| 				updateValue(key, value, m, k, subkeys, cnt) | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 		endVal, _ := m.(map[string]interface{})[keys0] | ||||
| 
 | ||||
| 		// if newV key is the end of path, replace the value for path-end | ||||
| 		// may be []interface{} - means replace just an entry w/ subkeys | ||||
| 		// otherwise replace the keys0 value if subkeys are there | ||||
| 		// NOTE: this will replace the subkeys, also | ||||
| 		if key == keys0 { | ||||
| 			switch endVal.(type) { | ||||
| 			case map[string]interface{}: | ||||
| 				if hasSubKeys(m, subkeys) { | ||||
| 					(m.(map[string]interface{}))[keys0] = value | ||||
| 					(*cnt)++ | ||||
| 				} | ||||
| 			case []interface{}: | ||||
| 				// without subkeys can't select list member to modify | ||||
| 				// so key:value spec is it ... | ||||
| 				if hasSubKeys(m, subkeys) { | ||||
| 					(m.(map[string]interface{}))[keys0] = value | ||||
| 					(*cnt)++ | ||||
| 					break | ||||
| 				} | ||||
| 				nv := make([]interface{}, 0) | ||||
| 				var valmodified bool | ||||
| 				for _, v := range endVal.([]interface{}) { | ||||
| 					// check entry subkeys | ||||
| 					if hasSubKeys(v, subkeys) { | ||||
| 						// replace v with value | ||||
| 						nv = append(nv, value) | ||||
| 						valmodified = true | ||||
| 						(*cnt)++ | ||||
| 						continue | ||||
| 					} | ||||
| 					nv = append(nv, v) | ||||
| 				} | ||||
| 				if valmodified { | ||||
| 					(m.(map[string]interface{}))[keys0] = interface{}(nv) | ||||
| 				} | ||||
| 			default: // anything else is a strict replacement | ||||
| 				if hasSubKeys(m, subkeys) { | ||||
| 					(m.(map[string]interface{}))[keys0] = value | ||||
| 					(*cnt)++ | ||||
| 				} | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// so value is for an element of endVal | ||||
| 		// if endVal is a map then 'key' must be there w/ subkeys | ||||
| 		// if endVal is a list then 'key' must be in a list member w/ subkeys | ||||
| 		switch endVal.(type) { | ||||
| 		case map[string]interface{}: | ||||
| 			if !hasSubKeys(endVal, subkeys) { | ||||
| 				return | ||||
| 			} | ||||
| 			if _, ok := (endVal.(map[string]interface{}))[key]; ok { | ||||
| 				(endVal.(map[string]interface{}))[key] = value | ||||
| 				(*cnt)++ | ||||
| 			} | ||||
| 		case []interface{}: // keys0 points to a list, check subkeys | ||||
| 			for _, v := range endVal.([]interface{}) { | ||||
| 				// got to be a map so we can replace value for 'key' | ||||
| 				vv, vok := v.(map[string]interface{}) | ||||
| 				if !vok { | ||||
| 					continue | ||||
| 				} | ||||
| 				if _, ok := vv[key]; !ok { | ||||
| 					continue | ||||
| 				} | ||||
| 				if !hasSubKeys(vv, subkeys) { | ||||
| 					continue | ||||
| 				} | ||||
| 				vv[key] = value | ||||
| 				(*cnt)++ | ||||
| 			} | ||||
| 		} | ||||
| 	case []interface{}: // key may be in a list member | ||||
| 		// don't need to handle keys0 == "*"; we're looking at everything, anyway. | ||||
| 		for _, v := range m.([]interface{}) { | ||||
| 			// only map values - we're looking for 'key' | ||||
| 			mm, ok := v.(map[string]interface{}) | ||||
| 			if !ok { | ||||
| 				continue | ||||
| 			} | ||||
| 			if _, ok := mm[key]; !ok { | ||||
| 				continue | ||||
| 			} | ||||
| 			if !hasSubKeys(mm, subkeys) { | ||||
| 				continue | ||||
| 			} | ||||
| 			mm[key] = value | ||||
| 			(*cnt)++ | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// return | ||||
| } | ||||
							
								
								
									
										1139
									
								
								vendor/github.com/clbanning/mxj/xml.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1139
									
								
								vendor/github.com/clbanning/mxj/xml.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										828
									
								
								vendor/github.com/clbanning/mxj/xmlseq.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										828
									
								
								vendor/github.com/clbanning/mxj/xmlseq.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,828 +0,0 @@ | |||
| // Copyright 2012-2016 Charles Banning. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file | ||||
| 
 | ||||
| // xmlseq.go - version of xml.go with sequence # injection on Decoding and sorting on Encoding. | ||||
| // Also, handles comments, directives and process instructions. | ||||
| 
 | ||||
| package mxj | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/xml" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| var NoRoot = errors.New("no root key") | ||||
| var NO_ROOT = NoRoot // maintain backwards compatibility | ||||
| 
 | ||||
| // ------------------- NewMapXmlSeq & NewMapXmlSeqReader ... ------------------------- | ||||
| 
 | ||||
| // This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure. | ||||
| // The xml.Decoder.RawToken method is used to parse the XML, so there is no checking for appropriate xml.EndElement values; | ||||
| // thus, it is assumed that the XML is valid. | ||||
| // | ||||
| // NewMapXmlSeq - convert a XML doc into a Map with elements id'd with decoding sequence int - #seq. | ||||
| // If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible. | ||||
| // NOTE: "#seq" key/value pairs are removed on encoding with mv.XmlSeq() / mv.XmlSeqIndent(). | ||||
| //	• attributes are a map - map["#attr"]map["attr_key"]map[string]interface{}{"#text":<aval>, "#seq":<num>} | ||||
| //	• all simple elements are decoded as map["#text"]interface{} with a "#seq" k:v pair, as well. | ||||
| //	• lists always decode as map["list_tag"][]map[string]interface{} where the array elements are maps that | ||||
| //	  include a "#seq" k:v pair based on sequence they are decoded.  Thus, XML like: | ||||
| //	      <doc> | ||||
| //	         <ltag>value 1</ltag> | ||||
| //	         <newtag>value 2</newtag> | ||||
| //	         <ltag>value 3</ltag> | ||||
| //	      </doc> | ||||
| //	  is decoded as: | ||||
| //	    doc : | ||||
| //	      ltag :[[]interface{}] | ||||
| //	        [item: 0] | ||||
| //	          #seq :[int] 0 | ||||
| //	          #text :[string] value 1 | ||||
| //	        [item: 1] | ||||
| //	          #seq :[int] 2 | ||||
| //	          #text :[string] value 3 | ||||
| //	      newtag : | ||||
| //	        #seq :[int] 1 | ||||
| //	        #text :[string] value 2 | ||||
| //	  It will encode in proper sequence even though the Map representation merges all "ltag" elements in an array. | ||||
| //	• comments - "<!--comment-->" -  are decoded as map["#comment"]map["#text"]"cmnt_text" with a "#seq" k:v pair. | ||||
| //	• directives - "<!text>" - are decoded as map["#directive"]map[#text"]"directive_text" with a "#seq" k:v pair. | ||||
| //	• process instructions  - "<?instr?>" - are decoded as map["#procinst"]interface{} where the #procinst value | ||||
| //	  is of map[string]interface{} type with the following keys: #target, #inst, and #seq. | ||||
| //	• comments, directives, and procinsts that are NOT part of a document with a root key will be returned as | ||||
| //	  map[string]interface{} and the error value 'NoRoot'. | ||||
| //	• note: "<![CDATA[" syntax is lost in xml.Decode parser - and is not handled here, either. | ||||
| //	   and: "\r\n" is converted to "\n" | ||||
| // | ||||
| //	NOTES: | ||||
| //	   1. The 'xmlVal' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other | ||||
| //	      extraneous xml.CharData will be ignored unless io.EOF is reached first. | ||||
| //	   2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to | ||||
| //	      re-encode the message in its original structure. | ||||
| //	   3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case. | ||||
| // | ||||
| //	NAME SPACES: | ||||
| //	   1. Keys in the Map value that are parsed from a <name space prefix>:<local name> tag preserve the | ||||
| //	      "<prefix>:" notation rather than stripping it as with NewMapXml(). | ||||
| //	   2. Attribute keys for name space prefix declarations preserve "xmlns:<prefix>" notation. | ||||
| func NewMapXmlSeq(xmlVal []byte, cast ...bool) (Map, error) { | ||||
| 	var r bool | ||||
| 	if len(cast) == 1 { | ||||
| 		r = cast[0] | ||||
| 	} | ||||
| 	return xmlSeqToMap(xmlVal, r) | ||||
| } | ||||
| 
 | ||||
| // This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure. | ||||
| // | ||||
| // Get next XML doc from an io.Reader as a Map value.  Returns Map value. | ||||
| //	NOTES: | ||||
| //	   1. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other | ||||
| //	      extraneous xml.CharData will be ignored unless io.EOF is reached first. | ||||
| //	   2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to | ||||
| //	      re-encode the message in its original structure. | ||||
| //	   3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case. | ||||
| func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (Map, error) { | ||||
| 	var r bool | ||||
| 	if len(cast) == 1 { | ||||
| 		r = cast[0] | ||||
| 	} | ||||
| 
 | ||||
| 	// We need to put an *os.File reader in a ByteReader or the xml.NewDecoder | ||||
| 	// will wrap it in a bufio.Reader and seek on the file beyond where the | ||||
| 	// xml.Decoder parses! | ||||
| 	if _, ok := xmlReader.(io.ByteReader); !ok { | ||||
| 		xmlReader = myByteReader(xmlReader) // see code at EOF | ||||
| 	} | ||||
| 
 | ||||
| 	// build the map | ||||
| 	return xmlSeqReaderToMap(xmlReader, r) | ||||
| } | ||||
| 
 | ||||
| // This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure. | ||||
| // | ||||
| // Get next XML doc from an io.Reader as a Map value.  Returns Map value and slice with the raw XML. | ||||
| //	NOTES: | ||||
| //	   1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte | ||||
| //	      using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact. | ||||
| //	      See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large | ||||
| //	      data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body | ||||
| //	      you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call. | ||||
| //	    2. The 'raw' return value may be larger than the XML text value. | ||||
| //	    3. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other | ||||
| //	       extraneous xml.CharData will be ignored unless io.EOF is reached first. | ||||
| //	    4. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to | ||||
| //	       re-encode the message in its original structure. | ||||
| //	    5. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case. | ||||
| func NewMapXmlSeqReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) { | ||||
| 	var r bool | ||||
| 	if len(cast) == 1 { | ||||
| 		r = cast[0] | ||||
| 	} | ||||
| 	// create TeeReader so we can retrieve raw XML | ||||
| 	buf := make([]byte, 0) | ||||
| 	wb := bytes.NewBuffer(buf) | ||||
| 	trdr := myTeeReader(xmlReader, wb) | ||||
| 
 | ||||
| 	m, err := xmlSeqReaderToMap(trdr, r) | ||||
| 
 | ||||
| 	// retrieve the raw XML that was decoded | ||||
| 	b := wb.Bytes() | ||||
| 
 | ||||
| 	// err may be NoRoot | ||||
| 	return m, b, err | ||||
| } | ||||
| 
 | ||||
| // xmlSeqReaderToMap() - parse a XML io.Reader to a map[string]interface{} value | ||||
| func xmlSeqReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) { | ||||
| 	// parse the Reader | ||||
| 	p := xml.NewDecoder(rdr) | ||||
| 	if CustomDecoder != nil { | ||||
| 		useCustomDecoder(p) | ||||
| 	} else { | ||||
| 		p.CharsetReader = XmlCharsetReader | ||||
| 	} | ||||
| 	return xmlSeqToMapParser("", nil, p, r) | ||||
| } | ||||
| 
 | ||||
| // xmlSeqToMap - convert a XML doc into map[string]interface{} value | ||||
| func xmlSeqToMap(doc []byte, r bool) (map[string]interface{}, error) { | ||||
| 	b := bytes.NewReader(doc) | ||||
| 	p := xml.NewDecoder(b) | ||||
| 	if CustomDecoder != nil { | ||||
| 		useCustomDecoder(p) | ||||
| 	} else { | ||||
| 		p.CharsetReader = XmlCharsetReader | ||||
| 	} | ||||
| 	return xmlSeqToMapParser("", nil, p, r) | ||||
| } | ||||
| 
 | ||||
| // ===================================== where the work happens ============================= | ||||
| 
 | ||||
| // xmlSeqToMapParser - load a 'clean' XML doc into a map[string]interface{} directly. | ||||
| // Add #seq tag value for each element decoded - to be used for Encoding later. | ||||
| func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) { | ||||
| 	if snakeCaseKeys { | ||||
| 		skey = strings.Replace(skey, "-", "_", -1) | ||||
| 	} | ||||
| 
 | ||||
| 	// NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' in 'n'. | ||||
| 	var n, na map[string]interface{} | ||||
| 	var seq int // for including seq num when decoding | ||||
| 
 | ||||
| 	// Allocate maps and load attributes, if any. | ||||
| 	// NOTE: on entry from NewMapXml(), etc., skey=="", and we fall through | ||||
| 	//       to get StartElement then recurse with skey==xml.StartElement.Name.Local | ||||
| 	//       where we begin allocating map[string]interface{} values 'n' and 'na'. | ||||
| 	if skey != "" { | ||||
| 		// 'n' only needs one slot - save call to runtime•hashGrow() | ||||
| 		// 'na' we don't know | ||||
| 		n = make(map[string]interface{}, 1) | ||||
| 		na = make(map[string]interface{}) | ||||
| 		if len(a) > 0 { | ||||
| 			// xml.Attr is decoded into: map["#attr"]map[<attr_label>]interface{} | ||||
| 			// where interface{} is map[string]interface{}{"#text":<attr_val>, "#seq":<attr_seq>} | ||||
| 			aa := make(map[string]interface{}, len(a)) | ||||
| 			for i, v := range a { | ||||
| 				if snakeCaseKeys { | ||||
| 					v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1) | ||||
| 				} | ||||
| 				if len(v.Name.Space) > 0 { | ||||
| 					aa[v.Name.Space+`:`+v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r), "#seq": i} | ||||
| 				} else { | ||||
| 					aa[v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r), "#seq": i} | ||||
| 				} | ||||
| 			} | ||||
| 			na["#attr"] = aa | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Return XMPP <stream:stream> message. | ||||
| 	if handleXMPPStreamTag && skey == "stream:stream" { | ||||
| 		n[skey] = na | ||||
| 		return n, nil | ||||
| 	} | ||||
| 
 | ||||
| 	for { | ||||
| 		t, err := p.RawToken() | ||||
| 		if err != nil { | ||||
| 			if err != io.EOF { | ||||
| 				return nil, errors.New("xml.Decoder.Token() - " + err.Error()) | ||||
| 			} | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		switch t.(type) { | ||||
| 		case xml.StartElement: | ||||
| 			tt := t.(xml.StartElement) | ||||
| 
 | ||||
| 			// First call to xmlSeqToMapParser() doesn't pass xml.StartElement - the map key. | ||||
| 			// So when the loop is first entered, the first token is the root tag along | ||||
| 			// with any attributes, which we process here. | ||||
| 			// | ||||
| 			// Subsequent calls to xmlSeqToMapParser() will pass in tag+attributes for | ||||
| 			// processing before getting the next token which is the element value, | ||||
| 			// which is done above. | ||||
| 			if skey == "" { | ||||
| 				if len(tt.Name.Space) > 0 { | ||||
| 					return xmlSeqToMapParser(tt.Name.Space+`:`+tt.Name.Local, tt.Attr, p, r) | ||||
| 				} else { | ||||
| 					return xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r) | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// If not initializing the map, parse the element. | ||||
| 			// len(nn) == 1, necessarily - it is just an 'n'. | ||||
| 			var nn map[string]interface{} | ||||
| 			if len(tt.Name.Space) > 0 { | ||||
| 				nn, err = xmlSeqToMapParser(tt.Name.Space+`:`+tt.Name.Local, tt.Attr, p, r) | ||||
| 			} else { | ||||
| 				nn, err = xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r) | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 
 | ||||
| 			// The nn map[string]interface{} value is a na[nn_key] value. | ||||
| 			// We need to see if nn_key already exists - means we're parsing a list. | ||||
| 			// This may require converting na[nn_key] value into []interface{} type. | ||||
| 			// First, extract the key:val for the map - it's a singleton. | ||||
| 			var key string | ||||
| 			var val interface{} | ||||
| 			for key, val = range nn { | ||||
| 				break | ||||
| 			} | ||||
| 
 | ||||
| 			// add "#seq" k:v pair - | ||||
| 			// Sequence number included even in list elements - this should allow us | ||||
| 			// to properly resequence even something goofy like: | ||||
| 			//     <list>item 1</list> | ||||
| 			//     <subelement>item 2</subelement> | ||||
| 			//     <list>item 3</list> | ||||
| 			// where all the "list" subelements are decoded into an array. | ||||
| 			switch val.(type) { | ||||
| 			case map[string]interface{}: | ||||
| 				val.(map[string]interface{})["#seq"] = seq | ||||
| 				seq++ | ||||
| 			case interface{}: // a non-nil simple element: string, float64, bool | ||||
| 				v := map[string]interface{}{"#text": val, "#seq": seq} | ||||
| 				seq++ | ||||
| 				val = v | ||||
| 			} | ||||
| 
 | ||||
| 			// 'na' holding sub-elements of n. | ||||
| 			// See if 'key' already exists. | ||||
| 			// If 'key' exists, then this is a list, if not just add key:val to na. | ||||
| 			if v, ok := na[key]; ok { | ||||
| 				var a []interface{} | ||||
| 				switch v.(type) { | ||||
| 				case []interface{}: | ||||
| 					a = v.([]interface{}) | ||||
| 				default: // anything else - note: v.(type) != nil | ||||
| 					a = []interface{}{v} | ||||
| 				} | ||||
| 				a = append(a, val) | ||||
| 				na[key] = a | ||||
| 			} else { | ||||
| 				na[key] = val // save it as a singleton | ||||
| 			} | ||||
| 		case xml.EndElement: | ||||
| 			if skey != "" { | ||||
| 				tt := t.(xml.EndElement) | ||||
| 				if snakeCaseKeys { | ||||
| 					tt.Name.Local = strings.Replace(tt.Name.Local, "-", "_", -1) | ||||
| 				} | ||||
| 				var name string | ||||
| 				if len(tt.Name.Space) > 0 { | ||||
| 					name = tt.Name.Space + `:` + tt.Name.Local | ||||
| 				} else { | ||||
| 					name = tt.Name.Local | ||||
| 				} | ||||
| 				if skey != name { | ||||
| 					return nil, fmt.Errorf("element %s not properly terminated, got %s at #%d", | ||||
| 						skey, name, p.InputOffset()) | ||||
| 				} | ||||
| 			} | ||||
| 			// len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case. | ||||
| 			if len(n) == 0 { | ||||
| 				// If len(na)==0 we have an empty element == ""; | ||||
| 				// it has no xml.Attr nor xml.CharData. | ||||
| 				// Empty element content will be  map["etag"]map["#text"]"" | ||||
| 				// after #seq injection - map["etag"]map["#seq"]seq - after return. | ||||
| 				if len(na) > 0 { | ||||
| 					n[skey] = na | ||||
| 				} else { | ||||
| 					n[skey] = "" // empty element | ||||
| 				} | ||||
| 			} | ||||
| 			return n, nil | ||||
| 		case xml.CharData: | ||||
| 			// clean up possible noise | ||||
| 			tt := strings.Trim(string(t.(xml.CharData)), "\t\r\b\n ") | ||||
| 			if skey == "" { | ||||
| 				// per Adrian (http://www.adrianlungu.com/) catch stray text | ||||
| 				// in decoder stream - | ||||
| 				// https://github.com/clbanning/mxj/pull/14#issuecomment-182816374 | ||||
| 				// NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get | ||||
| 				// a p.Token() decoding error when the BOM is UTF-16 or UTF-32. | ||||
| 				continue | ||||
| 			} | ||||
| 			if len(tt) > 0 { | ||||
| 				// every simple element is a #text and has #seq associated with it | ||||
| 				na["#text"] = cast(tt, r) | ||||
| 				na["#seq"] = seq | ||||
| 				seq++ | ||||
| 			} | ||||
| 		case xml.Comment: | ||||
| 			if n == nil { // no root 'key' | ||||
| 				n = map[string]interface{}{"#comment": string(t.(xml.Comment))} | ||||
| 				return n, NoRoot | ||||
| 			} | ||||
| 			cm := make(map[string]interface{}, 2) | ||||
| 			cm["#text"] = string(t.(xml.Comment)) | ||||
| 			cm["#seq"] = seq | ||||
| 			seq++ | ||||
| 			na["#comment"] = cm | ||||
| 		case xml.Directive: | ||||
| 			if n == nil { // no root 'key' | ||||
| 				n = map[string]interface{}{"#directive": string(t.(xml.Directive))} | ||||
| 				return n, NoRoot | ||||
| 			} | ||||
| 			dm := make(map[string]interface{}, 2) | ||||
| 			dm["#text"] = string(t.(xml.Directive)) | ||||
| 			dm["#seq"] = seq | ||||
| 			seq++ | ||||
| 			na["#directive"] = dm | ||||
| 		case xml.ProcInst: | ||||
| 			if n == nil { | ||||
| 				na = map[string]interface{}{"#target": t.(xml.ProcInst).Target, "#inst": string(t.(xml.ProcInst).Inst)} | ||||
| 				n = map[string]interface{}{"#procinst": na} | ||||
| 				return n, NoRoot | ||||
| 			} | ||||
| 			pm := make(map[string]interface{}, 3) | ||||
| 			pm["#target"] = t.(xml.ProcInst).Target | ||||
| 			pm["#inst"] = string(t.(xml.ProcInst).Inst) | ||||
| 			pm["#seq"] = seq | ||||
| 			seq++ | ||||
| 			na["#procinst"] = pm | ||||
| 		default: | ||||
| 			// noop - shouldn't ever get here, now, since we handle all token types | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ------------------ END: NewMapXml & NewMapXmlReader ------------------------- | ||||
| 
 | ||||
| // --------------------- mv.XmlSeq & mv.XmlSeqWriter ------------------------- | ||||
| 
 | ||||
| // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co. | ||||
| // | ||||
| // Encode a Map as XML with elements sorted on #seq.  The companion of NewMapXmlSeq(). | ||||
| // The following rules apply. | ||||
| //    - The key label "#text" is treated as the value for a simple element with attributes. | ||||
| //    - The "#seq" key is used to seqence the subelements or attributes but is ignored for writing. | ||||
| //    - The "#attr" map key identifies the map of attribute map[string]interface{} values with "#text" key. | ||||
| //    - The "#comment" map key identifies a comment in the value "#text" map entry - <!--comment-->. | ||||
| //    - The "#directive" map key identifies a directive in the value "#text" map entry - <!directive>. | ||||
| //    - The "#procinst" map key identifies a process instruction in the value "#target" and "#inst" | ||||
| //      map entries - <?target inst?>. | ||||
| //    - Value type encoding: | ||||
| //          > string, bool, float64, int, int32, int64, float32: per "%v" formating | ||||
| //          > []bool, []uint8: by casting to string | ||||
| //          > structures, etc.: handed to xml.Marshal() - if there is an error, the element | ||||
| //            value is "UNKNOWN" | ||||
| //    - Elements with only attribute values or are null are terminated using "/>" unless XmlGoEmptyElemSystax() called. | ||||
| //    - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible. | ||||
| //      Thus, `{ "key":"value" }` encodes as "<key>value</key>". | ||||
| func (mv Map) XmlSeq(rootTag ...string) ([]byte, error) { | ||||
| 	m := map[string]interface{}(mv) | ||||
| 	var err error | ||||
| 	s := new(string) | ||||
| 	p := new(pretty) // just a stub | ||||
| 
 | ||||
| 	if len(m) == 1 && len(rootTag) == 0 { | ||||
| 		for key, value := range m { | ||||
| 			// if it's an array, see if all values are map[string]interface{} | ||||
| 			// we force a new root tag if we'll end up with no key:value in the list | ||||
| 			// so: key:[string_val, bool:true] --> <doc><key>string_val</key><bool>true</bool></doc> | ||||
| 			switch value.(type) { | ||||
| 			case []interface{}: | ||||
| 				for _, v := range value.([]interface{}) { | ||||
| 					switch v.(type) { | ||||
| 					case map[string]interface{}: // noop | ||||
| 					default: // anything else | ||||
| 						err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p) | ||||
| 						goto done | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			err = mapToXmlSeqIndent(false, s, key, value, p) | ||||
| 		} | ||||
| 	} else if len(rootTag) == 1 { | ||||
| 		err = mapToXmlSeqIndent(false, s, rootTag[0], m, p) | ||||
| 	} else { | ||||
| 		err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p) | ||||
| 	} | ||||
| done: | ||||
| 	return []byte(*s), err | ||||
| } | ||||
| 
 | ||||
| // The following implementation is provided only for symmetry with NewMapXmlReader[Raw] | ||||
| // The names will also provide a key for the number of return arguments. | ||||
| 
 | ||||
| // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co. | ||||
| // | ||||
| // Writes the Map as  XML on the Writer. | ||||
| // See XmlSeq() for encoding rules. | ||||
| func (mv Map) XmlSeqWriter(xmlWriter io.Writer, rootTag ...string) error { | ||||
| 	x, err := mv.XmlSeq(rootTag...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = xmlWriter.Write(x) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co. | ||||
| // | ||||
| // Writes the Map as  XML on the Writer. []byte is the raw XML that was written. | ||||
| // See XmlSeq() for encoding rules. | ||||
| func (mv Map) XmlSeqWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) { | ||||
| 	x, err := mv.XmlSeq(rootTag...) | ||||
| 	if err != nil { | ||||
| 		return x, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = xmlWriter.Write(x) | ||||
| 	return x, err | ||||
| } | ||||
| 
 | ||||
| // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co. | ||||
| // | ||||
| // Writes the Map as pretty XML on the Writer. | ||||
| // See Xml() for encoding rules. | ||||
| func (mv Map) XmlSeqIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error { | ||||
| 	x, err := mv.XmlSeqIndent(prefix, indent, rootTag...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = xmlWriter.Write(x) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co. | ||||
| // | ||||
| // Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written. | ||||
| // See XmlSeq() for encoding rules. | ||||
| func (mv Map) XmlSeqIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) { | ||||
| 	x, err := mv.XmlSeqIndent(prefix, indent, rootTag...) | ||||
| 	if err != nil { | ||||
| 		return x, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = xmlWriter.Write(x) | ||||
| 	return x, err | ||||
| } | ||||
| 
 | ||||
| // -------------------- END: mv.Xml & mv.XmlWriter ------------------------------- | ||||
| 
 | ||||
| // ---------------------- XmlSeqIndent ---------------------------- | ||||
| 
 | ||||
| // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co. | ||||
| // | ||||
| // Encode a map[string]interface{} as a pretty XML string. | ||||
| // See mv.XmlSeq() for encoding rules. | ||||
| func (mv Map) XmlSeqIndent(prefix, indent string, rootTag ...string) ([]byte, error) { | ||||
| 	m := map[string]interface{}(mv) | ||||
| 
 | ||||
| 	var err error | ||||
| 	s := new(string) | ||||
| 	p := new(pretty) | ||||
| 	p.indent = indent | ||||
| 	p.padding = prefix | ||||
| 
 | ||||
| 	if len(m) == 1 && len(rootTag) == 0 { | ||||
| 		// this can extract the key for the single map element | ||||
| 		// use it if it isn't a key for a list | ||||
| 		for key, value := range m { | ||||
| 			if _, ok := value.([]interface{}); ok { | ||||
| 				err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p) | ||||
| 			} else { | ||||
| 				err = mapToXmlSeqIndent(true, s, key, value, p) | ||||
| 			} | ||||
| 		} | ||||
| 	} else if len(rootTag) == 1 { | ||||
| 		err = mapToXmlSeqIndent(true, s, rootTag[0], m, p) | ||||
| 	} else { | ||||
| 		err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p) | ||||
| 	} | ||||
| 	return []byte(*s), err | ||||
| } | ||||
| 
 | ||||
| // where the work actually happens | ||||
| // returns an error if an attribute is not atomic | ||||
| func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{}, pp *pretty) error { | ||||
| 	var endTag bool | ||||
| 	var isSimple bool | ||||
| 	var noEndTag bool | ||||
| 	var elen int | ||||
| 	var ss string | ||||
| 	p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start} | ||||
| 
 | ||||
| 	switch value.(type) { | ||||
| 	case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32: | ||||
| 		if doIndent { | ||||
| 			*s += p.padding | ||||
| 		} | ||||
| 		if key != "#comment" && key != "#directive" && key != "#procinst" { | ||||
| 			*s += `<` + key | ||||
| 		} | ||||
| 	} | ||||
| 	switch value.(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		val := value.(map[string]interface{}) | ||||
| 
 | ||||
| 		if key == "#comment" { | ||||
| 			*s += `<!--` + val["#text"].(string) + `-->` | ||||
| 			noEndTag = true | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		if key == "#directive" { | ||||
| 			*s += `<!` + val["#text"].(string) + `>` | ||||
| 			noEndTag = true | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		if key == "#procinst" { | ||||
| 			*s += `<?` + val["#target"].(string) + ` ` + val["#inst"].(string) + `?>` | ||||
| 			noEndTag = true | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		haveAttrs := false | ||||
| 		// process attributes first | ||||
| 		if v, ok := val["#attr"].(map[string]interface{}); ok { | ||||
| 			// First, unroll the map[string]interface{} into a []keyval array. | ||||
| 			// Then sequence it. | ||||
| 			kv := make([]keyval, len(v)) | ||||
| 			n := 0 | ||||
| 			for ak, av := range v { | ||||
| 				kv[n] = keyval{ak, av} | ||||
| 				n++ | ||||
| 			} | ||||
| 			sort.Sort(elemListSeq(kv)) | ||||
| 			// Now encode the attributes in original decoding sequence, using keyval array. | ||||
| 			for _, a := range kv { | ||||
| 				vv := a.v.(map[string]interface{}) | ||||
| 				switch vv["#text"].(type) { | ||||
| 				case string: | ||||
| 					if xmlEscapeChars { | ||||
| 						ss = escapeChars(vv["#text"].(string)) | ||||
| 					} else { | ||||
| 						ss = vv["#text"].(string) | ||||
| 					} | ||||
| 					*s += ` ` + a.k + `="` + ss + `"` | ||||
| 				case float64, bool, int, int32, int64, float32: | ||||
| 					*s += ` ` + a.k + `="` + fmt.Sprintf("%v", vv["#text"]) + `"` | ||||
| 				case []byte: | ||||
| 					if xmlEscapeChars { | ||||
| 						ss = escapeChars(string(vv["#text"].([]byte))) | ||||
| 					} else { | ||||
| 						ss = string(vv["#text"].([]byte)) | ||||
| 					} | ||||
| 					*s += ` ` + a.k + `="` + ss + `"` | ||||
| 				default: | ||||
| 					return fmt.Errorf("invalid attribute value for: %s", a.k) | ||||
| 				} | ||||
| 			} | ||||
| 			haveAttrs = true | ||||
| 		} | ||||
| 
 | ||||
| 		// simple element? | ||||
| 		// every map value has, at least, "#seq" and, perhaps, "#text" and/or "#attr" | ||||
| 		_, seqOK := val["#seq"] // have key | ||||
| 		if v, ok := val["#text"]; ok && ((len(val) == 3 && haveAttrs) || (len(val) == 2 && !haveAttrs)) && seqOK { | ||||
| 			if stmp, ok := v.(string); ok && stmp != "" { | ||||
| 				if xmlEscapeChars { | ||||
| 					stmp = escapeChars(stmp) | ||||
| 				} | ||||
| 				*s += ">" + stmp | ||||
| 				endTag = true | ||||
| 				elen = 1 | ||||
| 			} | ||||
| 			isSimple = true | ||||
| 			break | ||||
| 		} else if !ok && ((len(val) == 2 && haveAttrs) || (len(val) == 1 && !haveAttrs)) && seqOK { | ||||
| 			// here no #text but have #seq or #seq+#attr | ||||
| 			endTag = false | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		// we now need to sequence everything except attributes | ||||
| 		// 'kv' will hold everything that needs to be written | ||||
| 		kv := make([]keyval, 0) | ||||
| 		for k, v := range val { | ||||
| 			if k == "#attr" { // already processed | ||||
| 				continue | ||||
| 			} | ||||
| 			if k == "#seq" { // ignore - just for sorting | ||||
| 				continue | ||||
| 			} | ||||
| 			switch v.(type) { | ||||
| 			case []interface{}: | ||||
| 				// unwind the array as separate entries | ||||
| 				for _, vv := range v.([]interface{}) { | ||||
| 					kv = append(kv, keyval{k, vv}) | ||||
| 				} | ||||
| 			default: | ||||
| 				kv = append(kv, keyval{k, v}) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// close tag with possible attributes | ||||
| 		*s += ">" | ||||
| 		if doIndent { | ||||
| 			*s += "\n" | ||||
| 		} | ||||
| 		// something more complex | ||||
| 		p.mapDepth++ | ||||
| 		sort.Sort(elemListSeq(kv)) | ||||
| 		i := 0 | ||||
| 		for _, v := range kv { | ||||
| 			switch v.v.(type) { | ||||
| 			case []interface{}: | ||||
| 			default: | ||||
| 				if i == 0 && doIndent { | ||||
| 					p.Indent() | ||||
| 				} | ||||
| 			} | ||||
| 			i++ | ||||
| 			if err := mapToXmlSeqIndent(doIndent, s, v.k, v.v, p); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			switch v.v.(type) { | ||||
| 			case []interface{}: // handled in []interface{} case | ||||
| 			default: | ||||
| 				if doIndent { | ||||
| 					p.Outdent() | ||||
| 				} | ||||
| 			} | ||||
| 			i-- | ||||
| 		} | ||||
| 		p.mapDepth-- | ||||
| 		endTag = true | ||||
| 		elen = 1 // we do have some content other than attrs | ||||
| 	case []interface{}: | ||||
| 		for _, v := range value.([]interface{}) { | ||||
| 			if doIndent { | ||||
| 				p.Indent() | ||||
| 			} | ||||
| 			if err := mapToXmlSeqIndent(doIndent, s, key, v, p); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if doIndent { | ||||
| 				p.Outdent() | ||||
| 			} | ||||
| 		} | ||||
| 		return nil | ||||
| 	case nil: | ||||
| 		// terminate the tag | ||||
| 		if doIndent { | ||||
| 			*s += p.padding | ||||
| 		} | ||||
| 		*s += "<" + key | ||||
| 		endTag, isSimple = true, true | ||||
| 		break | ||||
| 	default: // handle anything - even goofy stuff | ||||
| 		elen = 0 | ||||
| 		switch value.(type) { | ||||
| 		case string: | ||||
| 			if xmlEscapeChars { | ||||
| 				ss = escapeChars(value.(string)) | ||||
| 			} else { | ||||
| 				ss = value.(string) | ||||
| 			} | ||||
| 			elen = len(ss) | ||||
| 			if elen > 0 { | ||||
| 				*s += ">" + ss | ||||
| 			} | ||||
| 		case float64, bool, int, int32, int64, float32: | ||||
| 			v := fmt.Sprintf("%v", value) | ||||
| 			elen = len(v) | ||||
| 			if elen > 0 { | ||||
| 				*s += ">" + v | ||||
| 			} | ||||
| 		case []byte: // NOTE: byte is just an alias for uint8 | ||||
| 			// similar to how xml.Marshal handles []byte structure members | ||||
| 			if xmlEscapeChars { | ||||
| 				ss = escapeChars(string(value.([]byte))) | ||||
| 			} else { | ||||
| 				ss = string(value.([]byte)) | ||||
| 			} | ||||
| 			elen = len(ss) | ||||
| 			if elen > 0 { | ||||
| 				*s += ">" + ss | ||||
| 			} | ||||
| 		default: | ||||
| 			var v []byte | ||||
| 			var err error | ||||
| 			if doIndent { | ||||
| 				v, err = xml.MarshalIndent(value, p.padding, p.indent) | ||||
| 			} else { | ||||
| 				v, err = xml.Marshal(value) | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				*s += ">UNKNOWN" | ||||
| 			} else { | ||||
| 				elen = len(v) | ||||
| 				if elen > 0 { | ||||
| 					*s += string(v) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		isSimple = true | ||||
| 		endTag = true | ||||
| 	} | ||||
| 	if endTag && !noEndTag { | ||||
| 		if doIndent { | ||||
| 			if !isSimple { | ||||
| 				*s += p.padding | ||||
| 			} | ||||
| 		} | ||||
| 		switch value.(type) { | ||||
| 		case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32: | ||||
| 			if elen > 0 || useGoXmlEmptyElemSyntax { | ||||
| 				if elen == 0 { | ||||
| 					*s += ">" | ||||
| 				} | ||||
| 				*s += `</` + key + ">" | ||||
| 			} else { | ||||
| 				*s += `/>` | ||||
| 			} | ||||
| 		} | ||||
| 	} else if !noEndTag { | ||||
| 		if useGoXmlEmptyElemSyntax { | ||||
| 			*s += `</` + key + ">" | ||||
| 			// *s += "></" + key + ">" | ||||
| 		} else { | ||||
| 			*s += "/>" | ||||
| 		} | ||||
| 	} | ||||
| 	if doIndent { | ||||
| 		if p.cnt > p.start { | ||||
| 			*s += "\n" | ||||
| 		} | ||||
| 		p.Outdent() | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // the element sort implementation | ||||
| 
 | ||||
| type keyval struct { | ||||
| 	k string | ||||
| 	v interface{} | ||||
| } | ||||
| type elemListSeq []keyval | ||||
| 
 | ||||
| func (e elemListSeq) Len() int { | ||||
| 	return len(e) | ||||
| } | ||||
| 
 | ||||
| func (e elemListSeq) Swap(i, j int) { | ||||
| 	e[i], e[j] = e[j], e[i] | ||||
| } | ||||
| 
 | ||||
| func (e elemListSeq) Less(i, j int) bool { | ||||
| 	var iseq, jseq int | ||||
| 	var ok bool | ||||
| 	if iseq, ok = e[i].v.(map[string]interface{})["#seq"].(int); !ok { | ||||
| 		iseq = 9999999 | ||||
| 	} | ||||
| 
 | ||||
| 	if jseq, ok = e[j].v.(map[string]interface{})["#seq"].(int); !ok { | ||||
| 		jseq = 9999999 | ||||
| 	} | ||||
| 
 | ||||
| 	return iseq <= jseq | ||||
| } | ||||
| 
 | ||||
| // =============== https://groups.google.com/forum/#!topic/golang-nuts/lHPOHD-8qio | ||||
| 
 | ||||
| // BeautifyXml (re)formats an XML doc similar to Map.XmlIndent(). | ||||
| func BeautifyXml(b []byte, prefix, indent string) ([]byte, error) { | ||||
| 	x, err := NewMapXmlSeq(b) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return x.XmlSeqIndent(prefix, indent) | ||||
| } | ||||
							
								
								
									
										21
									
								
								vendor/github.com/dustin/go-humanize/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/dustin/go-humanize/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,21 +0,0 @@ | |||
| sudo: false | ||||
| language: go | ||||
| go: | ||||
|   - 1.3.x | ||||
|   - 1.5.x | ||||
|   - 1.6.x | ||||
|   - 1.7.x | ||||
|   - 1.8.x | ||||
|   - 1.9.x | ||||
|   - master | ||||
| matrix: | ||||
|   allow_failures: | ||||
|     - go: master | ||||
|   fast_finish: true | ||||
| install: | ||||
|   - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). | ||||
| script: | ||||
|   - go get -t -v ./... | ||||
|   - diff -u <(echo -n) <(gofmt -d -s .) | ||||
|   - go tool vet . | ||||
|   - go test -v -race ./... | ||||
							
								
								
									
										21
									
								
								vendor/github.com/dustin/go-humanize/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/dustin/go-humanize/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,21 +0,0 @@ | |||
| Copyright (c) 2005-2008  Dustin Sallings <dustin@spy.net> | ||||
| 
 | ||||
| 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. | ||||
| 
 | ||||
| <http://www.opensource.org/licenses/mit-license.php> | ||||
							
								
								
									
										124
									
								
								vendor/github.com/dustin/go-humanize/README.markdown
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										124
									
								
								vendor/github.com/dustin/go-humanize/README.markdown
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,124 +0,0 @@ | |||
| # Humane Units [](https://travis-ci.org/dustin/go-humanize) [](https://godoc.org/github.com/dustin/go-humanize) | ||||
| 
 | ||||
| Just a few functions for helping humanize times and sizes. | ||||
| 
 | ||||
| `go get` it as `github.com/dustin/go-humanize`, import it as | ||||
| `"github.com/dustin/go-humanize"`, use it as `humanize`. | ||||
| 
 | ||||
| See [godoc](https://godoc.org/github.com/dustin/go-humanize) for | ||||
| complete documentation. | ||||
| 
 | ||||
| ## Sizes | ||||
| 
 | ||||
| This lets you take numbers like `82854982` and convert them to useful | ||||
| strings like, `83 MB` or `79 MiB` (whichever you prefer). | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| ```go | ||||
| fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. | ||||
| ``` | ||||
| 
 | ||||
| ## Times | ||||
| 
 | ||||
| This lets you take a `time.Time` and spit it out in relative terms. | ||||
| For example, `12 seconds ago` or `3 days from now`. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| ```go | ||||
| fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. | ||||
| ``` | ||||
| 
 | ||||
| Thanks to Kyle Lemons for the time implementation from an IRC | ||||
| conversation one day. It's pretty neat. | ||||
| 
 | ||||
| ## Ordinals | ||||
| 
 | ||||
| From a [mailing list discussion][odisc] where a user wanted to be able | ||||
| to label ordinals. | ||||
| 
 | ||||
|     0 -> 0th | ||||
|     1 -> 1st | ||||
|     2 -> 2nd | ||||
|     3 -> 3rd | ||||
|     4 -> 4th | ||||
|     [...] | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| ```go | ||||
| fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. | ||||
| ``` | ||||
| 
 | ||||
| ## Commas | ||||
| 
 | ||||
| Want to shove commas into numbers? Be my guest. | ||||
| 
 | ||||
|     0 -> 0 | ||||
|     100 -> 100 | ||||
|     1000 -> 1,000 | ||||
|     1000000000 -> 1,000,000,000 | ||||
|     -100000 -> -100,000 | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| ```go | ||||
| fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. | ||||
| ``` | ||||
| 
 | ||||
| ## Ftoa | ||||
| 
 | ||||
| Nicer float64 formatter that removes trailing zeros. | ||||
| 
 | ||||
| ```go | ||||
| fmt.Printf("%f", 2.24)                // 2.240000 | ||||
| fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 | ||||
| fmt.Printf("%f", 2.0)                 // 2.000000 | ||||
| fmt.Printf("%s", humanize.Ftoa(2.0))  // 2 | ||||
| ``` | ||||
| 
 | ||||
| ## SI notation | ||||
| 
 | ||||
| Format numbers with [SI notation][sinotation]. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| ```go | ||||
| humanize.SI(0.00000000223, "M") // 2.23 nM | ||||
| ``` | ||||
| 
 | ||||
| ## English-specific functions | ||||
| 
 | ||||
| The following functions are in the `humanize/english` subpackage. | ||||
| 
 | ||||
| ### Plurals | ||||
| 
 | ||||
| Simple English pluralization | ||||
| 
 | ||||
| ```go | ||||
| english.PluralWord(1, "object", "") // object | ||||
| english.PluralWord(42, "object", "") // objects | ||||
| english.PluralWord(2, "bus", "") // buses | ||||
| english.PluralWord(99, "locus", "loci") // loci | ||||
| 
 | ||||
| english.Plural(1, "object", "") // 1 object | ||||
| english.Plural(42, "object", "") // 42 objects | ||||
| english.Plural(2, "bus", "") // 2 buses | ||||
| english.Plural(99, "locus", "loci") // 99 loci | ||||
| ``` | ||||
| 
 | ||||
| ### Word series | ||||
| 
 | ||||
| Format comma-separated words lists with conjuctions: | ||||
| 
 | ||||
| ```go | ||||
| english.WordSeries([]string{"foo"}, "and") // foo | ||||
| english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar | ||||
| english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz | ||||
| 
 | ||||
| english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz | ||||
| ``` | ||||
| 
 | ||||
| [odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion | ||||
| [sinotation]: http://en.wikipedia.org/wiki/Metric_prefix | ||||
							
								
								
									
										31
									
								
								vendor/github.com/dustin/go-humanize/big.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/dustin/go-humanize/big.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,31 +0,0 @@ | |||
| package humanize | ||||
| 
 | ||||
| import ( | ||||
| 	"math/big" | ||||
| ) | ||||
| 
 | ||||
| // order of magnitude (to a max order) | ||||
| func oomm(n, b *big.Int, maxmag int) (float64, int) { | ||||
| 	mag := 0 | ||||
| 	m := &big.Int{} | ||||
| 	for n.Cmp(b) >= 0 { | ||||
| 		n.DivMod(n, b, m) | ||||
| 		mag++ | ||||
| 		if mag == maxmag && maxmag >= 0 { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag | ||||
| } | ||||
| 
 | ||||
| // total order of magnitude | ||||
| // (same as above, but with no upper limit) | ||||
| func oom(n, b *big.Int) (float64, int) { | ||||
| 	mag := 0 | ||||
| 	m := &big.Int{} | ||||
| 	for n.Cmp(b) >= 0 { | ||||
| 		n.DivMod(n, b, m) | ||||
| 		mag++ | ||||
| 	} | ||||
| 	return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag | ||||
| } | ||||
							
								
								
									
										173
									
								
								vendor/github.com/dustin/go-humanize/bigbytes.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										173
									
								
								vendor/github.com/dustin/go-humanize/bigbytes.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,173 +0,0 @@ | |||
| package humanize | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"math/big" | ||||
| 	"strings" | ||||
| 	"unicode" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	bigIECExp = big.NewInt(1024) | ||||
| 
 | ||||
| 	// BigByte is one byte in bit.Ints | ||||
| 	BigByte = big.NewInt(1) | ||||
| 	// BigKiByte is 1,024 bytes in bit.Ints | ||||
| 	BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) | ||||
| 	// BigMiByte is 1,024 k bytes in bit.Ints | ||||
| 	BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) | ||||
| 	// BigGiByte is 1,024 m bytes in bit.Ints | ||||
| 	BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) | ||||
| 	// BigTiByte is 1,024 g bytes in bit.Ints | ||||
| 	BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) | ||||
| 	// BigPiByte is 1,024 t bytes in bit.Ints | ||||
| 	BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) | ||||
| 	// BigEiByte is 1,024 p bytes in bit.Ints | ||||
| 	BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) | ||||
| 	// BigZiByte is 1,024 e bytes in bit.Ints | ||||
| 	BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) | ||||
| 	// BigYiByte is 1,024 z bytes in bit.Ints | ||||
| 	BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	bigSIExp = big.NewInt(1000) | ||||
| 
 | ||||
| 	// BigSIByte is one SI byte in big.Ints | ||||
| 	BigSIByte = big.NewInt(1) | ||||
| 	// BigKByte is 1,000 SI bytes in big.Ints | ||||
| 	BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) | ||||
| 	// BigMByte is 1,000 SI k bytes in big.Ints | ||||
| 	BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) | ||||
| 	// BigGByte is 1,000 SI m bytes in big.Ints | ||||
| 	BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) | ||||
| 	// BigTByte is 1,000 SI g bytes in big.Ints | ||||
| 	BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) | ||||
| 	// BigPByte is 1,000 SI t bytes in big.Ints | ||||
| 	BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) | ||||
| 	// BigEByte is 1,000 SI p bytes in big.Ints | ||||
| 	BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) | ||||
| 	// BigZByte is 1,000 SI e bytes in big.Ints | ||||
| 	BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) | ||||
| 	// BigYByte is 1,000 SI z bytes in big.Ints | ||||
| 	BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) | ||||
| ) | ||||
| 
 | ||||
| var bigBytesSizeTable = map[string]*big.Int{ | ||||
| 	"b":   BigByte, | ||||
| 	"kib": BigKiByte, | ||||
| 	"kb":  BigKByte, | ||||
| 	"mib": BigMiByte, | ||||
| 	"mb":  BigMByte, | ||||
| 	"gib": BigGiByte, | ||||
| 	"gb":  BigGByte, | ||||
| 	"tib": BigTiByte, | ||||
| 	"tb":  BigTByte, | ||||
| 	"pib": BigPiByte, | ||||
| 	"pb":  BigPByte, | ||||
| 	"eib": BigEiByte, | ||||
| 	"eb":  BigEByte, | ||||
| 	"zib": BigZiByte, | ||||
| 	"zb":  BigZByte, | ||||
| 	"yib": BigYiByte, | ||||
| 	"yb":  BigYByte, | ||||
| 	// Without suffix | ||||
| 	"":   BigByte, | ||||
| 	"ki": BigKiByte, | ||||
| 	"k":  BigKByte, | ||||
| 	"mi": BigMiByte, | ||||
| 	"m":  BigMByte, | ||||
| 	"gi": BigGiByte, | ||||
| 	"g":  BigGByte, | ||||
| 	"ti": BigTiByte, | ||||
| 	"t":  BigTByte, | ||||
| 	"pi": BigPiByte, | ||||
| 	"p":  BigPByte, | ||||
| 	"ei": BigEiByte, | ||||
| 	"e":  BigEByte, | ||||
| 	"z":  BigZByte, | ||||
| 	"zi": BigZiByte, | ||||
| 	"y":  BigYByte, | ||||
| 	"yi": BigYiByte, | ||||
| } | ||||
| 
 | ||||
| var ten = big.NewInt(10) | ||||
| 
 | ||||
| func humanateBigBytes(s, base *big.Int, sizes []string) string { | ||||
| 	if s.Cmp(ten) < 0 { | ||||
| 		return fmt.Sprintf("%d B", s) | ||||
| 	} | ||||
| 	c := (&big.Int{}).Set(s) | ||||
| 	val, mag := oomm(c, base, len(sizes)-1) | ||||
| 	suffix := sizes[mag] | ||||
| 	f := "%.0f %s" | ||||
| 	if val < 10 { | ||||
| 		f = "%.1f %s" | ||||
| 	} | ||||
| 
 | ||||
| 	return fmt.Sprintf(f, val, suffix) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // BigBytes produces a human readable representation of an SI size. | ||||
| // | ||||
| // See also: ParseBigBytes. | ||||
| // | ||||
| // BigBytes(82854982) -> 83 MB | ||||
| func BigBytes(s *big.Int) string { | ||||
| 	sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} | ||||
| 	return humanateBigBytes(s, bigSIExp, sizes) | ||||
| } | ||||
| 
 | ||||
| // BigIBytes produces a human readable representation of an IEC size. | ||||
| // | ||||
| // See also: ParseBigBytes. | ||||
| // | ||||
| // BigIBytes(82854982) -> 79 MiB | ||||
| func BigIBytes(s *big.Int) string { | ||||
| 	sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} | ||||
| 	return humanateBigBytes(s, bigIECExp, sizes) | ||||
| } | ||||
| 
 | ||||
| // ParseBigBytes parses a string representation of bytes into the number | ||||
| // of bytes it represents. | ||||
| // | ||||
| // See also: BigBytes, BigIBytes. | ||||
| // | ||||
| // ParseBigBytes("42 MB") -> 42000000, nil | ||||
| // ParseBigBytes("42 mib") -> 44040192, nil | ||||
| func ParseBigBytes(s string) (*big.Int, error) { | ||||
| 	lastDigit := 0 | ||||
| 	hasComma := false | ||||
| 	for _, r := range s { | ||||
| 		if !(unicode.IsDigit(r) || r == '.' || r == ',') { | ||||
| 			break | ||||
| 		} | ||||
| 		if r == ',' { | ||||
| 			hasComma = true | ||||
| 		} | ||||
| 		lastDigit++ | ||||
| 	} | ||||
| 
 | ||||
| 	num := s[:lastDigit] | ||||
| 	if hasComma { | ||||
| 		num = strings.Replace(num, ",", "", -1) | ||||
| 	} | ||||
| 
 | ||||
| 	val := &big.Rat{} | ||||
| 	_, err := fmt.Sscanf(num, "%f", val) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) | ||||
| 	if m, ok := bigBytesSizeTable[extra]; ok { | ||||
| 		mv := (&big.Rat{}).SetInt(m) | ||||
| 		val.Mul(val, mv) | ||||
| 		rv := &big.Int{} | ||||
| 		rv.Div(val.Num(), val.Denom()) | ||||
| 		return rv, nil | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, fmt.Errorf("unhandled size name: %v", extra) | ||||
| } | ||||
							
								
								
									
										143
									
								
								vendor/github.com/dustin/go-humanize/bytes.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										143
									
								
								vendor/github.com/dustin/go-humanize/bytes.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,143 +0,0 @@ | |||
| package humanize | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"unicode" | ||||
| ) | ||||
| 
 | ||||
| // IEC Sizes. | ||||
| // kibis of bits | ||||
| const ( | ||||
| 	Byte = 1 << (iota * 10) | ||||
| 	KiByte | ||||
| 	MiByte | ||||
| 	GiByte | ||||
| 	TiByte | ||||
| 	PiByte | ||||
| 	EiByte | ||||
| ) | ||||
| 
 | ||||
| // SI Sizes. | ||||
| const ( | ||||
| 	IByte = 1 | ||||
| 	KByte = IByte * 1000 | ||||
| 	MByte = KByte * 1000 | ||||
| 	GByte = MByte * 1000 | ||||
| 	TByte = GByte * 1000 | ||||
| 	PByte = TByte * 1000 | ||||
| 	EByte = PByte * 1000 | ||||
| ) | ||||
| 
 | ||||
| var bytesSizeTable = map[string]uint64{ | ||||
| 	"b":   Byte, | ||||
| 	"kib": KiByte, | ||||
| 	"kb":  KByte, | ||||
| 	"mib": MiByte, | ||||
| 	"mb":  MByte, | ||||
| 	"gib": GiByte, | ||||
| 	"gb":  GByte, | ||||
| 	"tib": TiByte, | ||||
| 	"tb":  TByte, | ||||
| 	"pib": PiByte, | ||||
| 	"pb":  PByte, | ||||
| 	"eib": EiByte, | ||||
| 	"eb":  EByte, | ||||
| 	// Without suffix | ||||
| 	"":   Byte, | ||||
| 	"ki": KiByte, | ||||
| 	"k":  KByte, | ||||
| 	"mi": MiByte, | ||||
| 	"m":  MByte, | ||||
| 	"gi": GiByte, | ||||
| 	"g":  GByte, | ||||
| 	"ti": TiByte, | ||||
| 	"t":  TByte, | ||||
| 	"pi": PiByte, | ||||
| 	"p":  PByte, | ||||
| 	"ei": EiByte, | ||||
| 	"e":  EByte, | ||||
| } | ||||
| 
 | ||||
| func logn(n, b float64) float64 { | ||||
| 	return math.Log(n) / math.Log(b) | ||||
| } | ||||
| 
 | ||||
| func humanateBytes(s uint64, base float64, sizes []string) string { | ||||
| 	if s < 10 { | ||||
| 		return fmt.Sprintf("%d B", s) | ||||
| 	} | ||||
| 	e := math.Floor(logn(float64(s), base)) | ||||
| 	suffix := sizes[int(e)] | ||||
| 	val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 | ||||
| 	f := "%.0f %s" | ||||
| 	if val < 10 { | ||||
| 		f = "%.1f %s" | ||||
| 	} | ||||
| 
 | ||||
| 	return fmt.Sprintf(f, val, suffix) | ||||
| } | ||||
| 
 | ||||
| // Bytes produces a human readable representation of an SI size. | ||||
| // | ||||
| // See also: ParseBytes. | ||||
| // | ||||
| // Bytes(82854982) -> 83 MB | ||||
| func Bytes(s uint64) string { | ||||
| 	sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} | ||||
| 	return humanateBytes(s, 1000, sizes) | ||||
| } | ||||
| 
 | ||||
| // IBytes produces a human readable representation of an IEC size. | ||||
| // | ||||
| // See also: ParseBytes. | ||||
| // | ||||
| // IBytes(82854982) -> 79 MiB | ||||
| func IBytes(s uint64) string { | ||||
| 	sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} | ||||
| 	return humanateBytes(s, 1024, sizes) | ||||
| } | ||||
| 
 | ||||
| // ParseBytes parses a string representation of bytes into the number | ||||
| // of bytes it represents. | ||||
| // | ||||
| // See Also: Bytes, IBytes. | ||||
| // | ||||
| // ParseBytes("42 MB") -> 42000000, nil | ||||
| // ParseBytes("42 mib") -> 44040192, nil | ||||
| func ParseBytes(s string) (uint64, error) { | ||||
| 	lastDigit := 0 | ||||
| 	hasComma := false | ||||
| 	for _, r := range s { | ||||
| 		if !(unicode.IsDigit(r) || r == '.' || r == ',') { | ||||
| 			break | ||||
| 		} | ||||
| 		if r == ',' { | ||||
| 			hasComma = true | ||||
| 		} | ||||
| 		lastDigit++ | ||||
| 	} | ||||
| 
 | ||||
| 	num := s[:lastDigit] | ||||
| 	if hasComma { | ||||
| 		num = strings.Replace(num, ",", "", -1) | ||||
| 	} | ||||
| 
 | ||||
| 	f, err := strconv.ParseFloat(num, 64) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 
 | ||||
| 	extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) | ||||
| 	if m, ok := bytesSizeTable[extra]; ok { | ||||
| 		f *= float64(m) | ||||
| 		if f >= math.MaxUint64 { | ||||
| 			return 0, fmt.Errorf("too large: %v", s) | ||||
| 		} | ||||
| 		return uint64(f), nil | ||||
| 	} | ||||
| 
 | ||||
| 	return 0, fmt.Errorf("unhandled size name: %v", extra) | ||||
| } | ||||
							
								
								
									
										116
									
								
								vendor/github.com/dustin/go-humanize/comma.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/dustin/go-humanize/comma.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,116 +0,0 @@ | |||
| package humanize | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"math" | ||||
| 	"math/big" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Comma produces a string form of the given number in base 10 with | ||||
| // commas after every three orders of magnitude. | ||||
| // | ||||
| // e.g. Comma(834142) -> 834,142 | ||||
| func Comma(v int64) string { | ||||
| 	sign := "" | ||||
| 
 | ||||
| 	// Min int64 can't be negated to a usable value, so it has to be special cased. | ||||
| 	if v == math.MinInt64 { | ||||
| 		return "-9,223,372,036,854,775,808" | ||||
| 	} | ||||
| 
 | ||||
| 	if v < 0 { | ||||
| 		sign = "-" | ||||
| 		v = 0 - v | ||||
| 	} | ||||
| 
 | ||||
| 	parts := []string{"", "", "", "", "", "", ""} | ||||
| 	j := len(parts) - 1 | ||||
| 
 | ||||
| 	for v > 999 { | ||||
| 		parts[j] = strconv.FormatInt(v%1000, 10) | ||||
| 		switch len(parts[j]) { | ||||
| 		case 2: | ||||
| 			parts[j] = "0" + parts[j] | ||||
| 		case 1: | ||||
| 			parts[j] = "00" + parts[j] | ||||
| 		} | ||||
| 		v = v / 1000 | ||||
| 		j-- | ||||
| 	} | ||||
| 	parts[j] = strconv.Itoa(int(v)) | ||||
| 	return sign + strings.Join(parts[j:], ",") | ||||
| } | ||||
| 
 | ||||
| // Commaf produces a string form of the given number in base 10 with | ||||
| // commas after every three orders of magnitude. | ||||
| // | ||||
| // e.g. Commaf(834142.32) -> 834,142.32 | ||||
| func Commaf(v float64) string { | ||||
| 	buf := &bytes.Buffer{} | ||||
| 	if v < 0 { | ||||
| 		buf.Write([]byte{'-'}) | ||||
| 		v = 0 - v | ||||
| 	} | ||||
| 
 | ||||
| 	comma := []byte{','} | ||||
| 
 | ||||
| 	parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") | ||||
| 	pos := 0 | ||||
| 	if len(parts[0])%3 != 0 { | ||||
| 		pos += len(parts[0]) % 3 | ||||
| 		buf.WriteString(parts[0][:pos]) | ||||
| 		buf.Write(comma) | ||||
| 	} | ||||
| 	for ; pos < len(parts[0]); pos += 3 { | ||||
| 		buf.WriteString(parts[0][pos : pos+3]) | ||||
| 		buf.Write(comma) | ||||
| 	} | ||||
| 	buf.Truncate(buf.Len() - 1) | ||||
| 
 | ||||
| 	if len(parts) > 1 { | ||||
| 		buf.Write([]byte{'.'}) | ||||
| 		buf.WriteString(parts[1]) | ||||
| 	} | ||||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| // CommafWithDigits works like the Commaf but limits the resulting | ||||
| // string to the given number of decimal places. | ||||
| // | ||||
| // e.g. CommafWithDigits(834142.32, 1) -> 834,142.3 | ||||
| func CommafWithDigits(f float64, decimals int) string { | ||||
| 	return stripTrailingDigits(Commaf(f), decimals) | ||||
| } | ||||
| 
 | ||||
| // BigComma produces a string form of the given big.Int in base 10 | ||||
| // with commas after every three orders of magnitude. | ||||
| func BigComma(b *big.Int) string { | ||||
| 	sign := "" | ||||
| 	if b.Sign() < 0 { | ||||
| 		sign = "-" | ||||
| 		b.Abs(b) | ||||
| 	} | ||||
| 
 | ||||
| 	athousand := big.NewInt(1000) | ||||
| 	c := (&big.Int{}).Set(b) | ||||
| 	_, m := oom(c, athousand) | ||||
| 	parts := make([]string, m+1) | ||||
| 	j := len(parts) - 1 | ||||
| 
 | ||||
| 	mod := &big.Int{} | ||||
| 	for b.Cmp(athousand) >= 0 { | ||||
| 		b.DivMod(b, athousand, mod) | ||||
| 		parts[j] = strconv.FormatInt(mod.Int64(), 10) | ||||
| 		switch len(parts[j]) { | ||||
| 		case 2: | ||||
| 			parts[j] = "0" + parts[j] | ||||
| 		case 1: | ||||
| 			parts[j] = "00" + parts[j] | ||||
| 		} | ||||
| 		j-- | ||||
| 	} | ||||
| 	parts[j] = strconv.Itoa(int(b.Int64())) | ||||
| 	return sign + strings.Join(parts[j:], ",") | ||||
| } | ||||
							
								
								
									
										40
									
								
								vendor/github.com/dustin/go-humanize/commaf.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/dustin/go-humanize/commaf.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,40 +0,0 @@ | |||
| // +build go1.6 | ||||
| 
 | ||||
| package humanize | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"math/big" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // BigCommaf produces a string form of the given big.Float in base 10 | ||||
| // with commas after every three orders of magnitude. | ||||
| func BigCommaf(v *big.Float) string { | ||||
| 	buf := &bytes.Buffer{} | ||||
| 	if v.Sign() < 0 { | ||||
| 		buf.Write([]byte{'-'}) | ||||
| 		v.Abs(v) | ||||
| 	} | ||||
| 
 | ||||
| 	comma := []byte{','} | ||||
| 
 | ||||
| 	parts := strings.Split(v.Text('f', -1), ".") | ||||
| 	pos := 0 | ||||
| 	if len(parts[0])%3 != 0 { | ||||
| 		pos += len(parts[0]) % 3 | ||||
| 		buf.WriteString(parts[0][:pos]) | ||||
| 		buf.Write(comma) | ||||
| 	} | ||||
| 	for ; pos < len(parts[0]); pos += 3 { | ||||
| 		buf.WriteString(parts[0][pos : pos+3]) | ||||
| 		buf.Write(comma) | ||||
| 	} | ||||
| 	buf.Truncate(buf.Len() - 1) | ||||
| 
 | ||||
| 	if len(parts) > 1 { | ||||
| 		buf.Write([]byte{'.'}) | ||||
| 		buf.WriteString(parts[1]) | ||||
| 	} | ||||
| 	return buf.String() | ||||
| } | ||||
							
								
								
									
										46
									
								
								vendor/github.com/dustin/go-humanize/ftoa.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/dustin/go-humanize/ftoa.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,46 +0,0 @@ | |||
| package humanize | ||||
| 
 | ||||
| import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| func stripTrailingZeros(s string) string { | ||||
| 	offset := len(s) - 1 | ||||
| 	for offset > 0 { | ||||
| 		if s[offset] == '.' { | ||||
| 			offset-- | ||||
| 			break | ||||
| 		} | ||||
| 		if s[offset] != '0' { | ||||
| 			break | ||||
| 		} | ||||
| 		offset-- | ||||
| 	} | ||||
| 	return s[:offset+1] | ||||
| } | ||||
| 
 | ||||
| func stripTrailingDigits(s string, digits int) string { | ||||
| 	if i := strings.Index(s, "."); i >= 0 { | ||||
| 		if digits <= 0 { | ||||
| 			return s[:i] | ||||
| 		} | ||||
| 		i++ | ||||
| 		if i+digits >= len(s) { | ||||
| 			return s | ||||
| 		} | ||||
| 		return s[:i+digits] | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| // Ftoa converts a float to a string with no trailing zeros. | ||||
| func Ftoa(num float64) string { | ||||
| 	return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) | ||||
| } | ||||
| 
 | ||||
| // FtoaWithDigits converts a float to a string but limits the resulting string | ||||
| // to the given number of decimal places, and no trailing zeros. | ||||
| func FtoaWithDigits(num float64, digits int) string { | ||||
| 	return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits)) | ||||
| } | ||||
							
								
								
									
										8
									
								
								vendor/github.com/dustin/go-humanize/humanize.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/dustin/go-humanize/humanize.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,8 +0,0 @@ | |||
| /* | ||||
| Package humanize converts boring ugly numbers to human-friendly strings and back. | ||||
| 
 | ||||
| Durations can be turned into strings such as "3 days ago", numbers | ||||
| representing sizes like 82854982 into useful strings like, "83 MB" or | ||||
| "79 MiB" (whichever you prefer). | ||||
| */ | ||||
| package humanize | ||||
							
								
								
									
										192
									
								
								vendor/github.com/dustin/go-humanize/number.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										192
									
								
								vendor/github.com/dustin/go-humanize/number.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,192 +0,0 @@ | |||
| package humanize | ||||
| 
 | ||||
| /* | ||||
| Slightly adapted from the source to fit go-humanize. | ||||
| 
 | ||||
| Author: https://github.com/gorhill | ||||
| Source: https://gist.github.com/gorhill/5285193 | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| import ( | ||||
| 	"math" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	renderFloatPrecisionMultipliers = [...]float64{ | ||||
| 		1, | ||||
| 		10, | ||||
| 		100, | ||||
| 		1000, | ||||
| 		10000, | ||||
| 		100000, | ||||
| 		1000000, | ||||
| 		10000000, | ||||
| 		100000000, | ||||
| 		1000000000, | ||||
| 	} | ||||
| 
 | ||||
| 	renderFloatPrecisionRounders = [...]float64{ | ||||
| 		0.5, | ||||
| 		0.05, | ||||
| 		0.005, | ||||
| 		0.0005, | ||||
| 		0.00005, | ||||
| 		0.000005, | ||||
| 		0.0000005, | ||||
| 		0.00000005, | ||||
| 		0.000000005, | ||||
| 		0.0000000005, | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| // FormatFloat produces a formatted number as string based on the following user-specified criteria: | ||||
| // * thousands separator | ||||
| // * decimal separator | ||||
| // * decimal precision | ||||
| // | ||||
| // Usage: s := RenderFloat(format, n) | ||||
| // The format parameter tells how to render the number n. | ||||
| // | ||||
| // See examples: http://play.golang.org/p/LXc1Ddm1lJ | ||||
| // | ||||
| // Examples of format strings, given n = 12345.6789: | ||||
| // "#,###.##" => "12,345.67" | ||||
| // "#,###." => "12,345" | ||||
| // "#,###" => "12345,678" | ||||
| // "#\u202F###,##" => "12 345,68" | ||||
| // "#.###,###### => 12.345,678900 | ||||
| // "" (aka default format) => 12,345.67 | ||||
| // | ||||
| // The highest precision allowed is 9 digits after the decimal symbol. | ||||
| // There is also a version for integer number, FormatInteger(), | ||||
| // which is convenient for calls within template. | ||||
| func FormatFloat(format string, n float64) string { | ||||
| 	// Special cases: | ||||
| 	//   NaN = "NaN" | ||||
| 	//   +Inf = "+Infinity" | ||||
| 	//   -Inf = "-Infinity" | ||||
| 	if math.IsNaN(n) { | ||||
| 		return "NaN" | ||||
| 	} | ||||
| 	if n > math.MaxFloat64 { | ||||
| 		return "Infinity" | ||||
| 	} | ||||
| 	if n < -math.MaxFloat64 { | ||||
| 		return "-Infinity" | ||||
| 	} | ||||
| 
 | ||||
| 	// default format | ||||
| 	precision := 2 | ||||
| 	decimalStr := "." | ||||
| 	thousandStr := "," | ||||
| 	positiveStr := "" | ||||
| 	negativeStr := "-" | ||||
| 
 | ||||
| 	if len(format) > 0 { | ||||
| 		format := []rune(format) | ||||
| 
 | ||||
| 		// If there is an explicit format directive, | ||||
| 		// then default values are these: | ||||
| 		precision = 9 | ||||
| 		thousandStr = "" | ||||
| 
 | ||||
| 		// collect indices of meaningful formatting directives | ||||
| 		formatIndx := []int{} | ||||
| 		for i, char := range format { | ||||
| 			if char != '#' && char != '0' { | ||||
| 				formatIndx = append(formatIndx, i) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if len(formatIndx) > 0 { | ||||
| 			// Directive at index 0: | ||||
| 			//   Must be a '+' | ||||
| 			//   Raise an error if not the case | ||||
| 			// index: 0123456789 | ||||
| 			//        +0.000,000 | ||||
| 			//        +000,000.0 | ||||
| 			//        +0000.00 | ||||
| 			//        +0000 | ||||
| 			if formatIndx[0] == 0 { | ||||
| 				if format[formatIndx[0]] != '+' { | ||||
| 					panic("RenderFloat(): invalid positive sign directive") | ||||
| 				} | ||||
| 				positiveStr = "+" | ||||
| 				formatIndx = formatIndx[1:] | ||||
| 			} | ||||
| 
 | ||||
| 			// Two directives: | ||||
| 			//   First is thousands separator | ||||
| 			//   Raise an error if not followed by 3-digit | ||||
| 			// 0123456789 | ||||
| 			// 0.000,000 | ||||
| 			// 000,000.00 | ||||
| 			if len(formatIndx) == 2 { | ||||
| 				if (formatIndx[1] - formatIndx[0]) != 4 { | ||||
| 					panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") | ||||
| 				} | ||||
| 				thousandStr = string(format[formatIndx[0]]) | ||||
| 				formatIndx = formatIndx[1:] | ||||
| 			} | ||||
| 
 | ||||
| 			// One directive: | ||||
| 			//   Directive is decimal separator | ||||
| 			//   The number of digit-specifier following the separator indicates wanted precision | ||||
| 			// 0123456789 | ||||
| 			// 0.00 | ||||
| 			// 000,0000 | ||||
| 			if len(formatIndx) == 1 { | ||||
| 				decimalStr = string(format[formatIndx[0]]) | ||||
| 				precision = len(format) - formatIndx[0] - 1 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// generate sign part | ||||
| 	var signStr string | ||||
| 	if n >= 0.000000001 { | ||||
| 		signStr = positiveStr | ||||
| 	} else if n <= -0.000000001 { | ||||
| 		signStr = negativeStr | ||||
| 		n = -n | ||||
| 	} else { | ||||
| 		signStr = "" | ||||
| 		n = 0.0 | ||||
| 	} | ||||
| 
 | ||||
| 	// split number into integer and fractional parts | ||||
| 	intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) | ||||
| 
 | ||||
| 	// generate integer part string | ||||
| 	intStr := strconv.FormatInt(int64(intf), 10) | ||||
| 
 | ||||
| 	// add thousand separator if required | ||||
| 	if len(thousandStr) > 0 { | ||||
| 		for i := len(intStr); i > 3; { | ||||
| 			i -= 3 | ||||
| 			intStr = intStr[:i] + thousandStr + intStr[i:] | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// no fractional part, we can leave now | ||||
| 	if precision == 0 { | ||||
| 		return signStr + intStr | ||||
| 	} | ||||
| 
 | ||||
| 	// generate fractional part | ||||
| 	fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) | ||||
| 	// may need padding | ||||
| 	if len(fracStr) < precision { | ||||
| 		fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr | ||||
| 	} | ||||
| 
 | ||||
| 	return signStr + intStr + decimalStr + fracStr | ||||
| } | ||||
| 
 | ||||
| // FormatInteger produces a formatted number as string. | ||||
| // See FormatFloat. | ||||
| func FormatInteger(format string, n int) string { | ||||
| 	return FormatFloat(format, float64(n)) | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/github.com/dustin/go-humanize/ordinals.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/dustin/go-humanize/ordinals.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,25 +0,0 @@ | |||
| package humanize | ||||
| 
 | ||||
| import "strconv" | ||||
| 
 | ||||
| // Ordinal gives you the input number in a rank/ordinal format. | ||||
| // | ||||
| // Ordinal(3) -> 3rd | ||||
| func Ordinal(x int) string { | ||||
| 	suffix := "th" | ||||
| 	switch x % 10 { | ||||
| 	case 1: | ||||
| 		if x%100 != 11 { | ||||
| 			suffix = "st" | ||||
| 		} | ||||
| 	case 2: | ||||
| 		if x%100 != 12 { | ||||
| 			suffix = "nd" | ||||
| 		} | ||||
| 	case 3: | ||||
| 		if x%100 != 13 { | ||||
| 			suffix = "rd" | ||||
| 		} | ||||
| 	} | ||||
| 	return strconv.Itoa(x) + suffix | ||||
| } | ||||
							
								
								
									
										123
									
								
								vendor/github.com/dustin/go-humanize/si.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/dustin/go-humanize/si.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,123 +0,0 @@ | |||
| package humanize | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"math" | ||||
| 	"regexp" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| var siPrefixTable = map[float64]string{ | ||||
| 	-24: "y", // yocto | ||||
| 	-21: "z", // zepto | ||||
| 	-18: "a", // atto | ||||
| 	-15: "f", // femto | ||||
| 	-12: "p", // pico | ||||
| 	-9:  "n", // nano | ||||
| 	-6:  "µ", // micro | ||||
| 	-3:  "m", // milli | ||||
| 	0:   "", | ||||
| 	3:   "k", // kilo | ||||
| 	6:   "M", // mega | ||||
| 	9:   "G", // giga | ||||
| 	12:  "T", // tera | ||||
| 	15:  "P", // peta | ||||
| 	18:  "E", // exa | ||||
| 	21:  "Z", // zetta | ||||
| 	24:  "Y", // yotta | ||||
| } | ||||
| 
 | ||||
| var revSIPrefixTable = revfmap(siPrefixTable) | ||||
| 
 | ||||
| // revfmap reverses the map and precomputes the power multiplier | ||||
| func revfmap(in map[float64]string) map[string]float64 { | ||||
| 	rv := map[string]float64{} | ||||
| 	for k, v := range in { | ||||
| 		rv[v] = math.Pow(10, k) | ||||
| 	} | ||||
| 	return rv | ||||
| } | ||||
| 
 | ||||
| var riParseRegex *regexp.Regexp | ||||
| 
 | ||||
| func init() { | ||||
| 	ri := `^([\-0-9.]+)\s?([` | ||||
| 	for _, v := range siPrefixTable { | ||||
| 		ri += v | ||||
| 	} | ||||
| 	ri += `]?)(.*)` | ||||
| 
 | ||||
| 	riParseRegex = regexp.MustCompile(ri) | ||||
| } | ||||
| 
 | ||||
| // ComputeSI finds the most appropriate SI prefix for the given number | ||||
| // and returns the prefix along with the value adjusted to be within | ||||
| // that prefix. | ||||
| // | ||||
| // See also: SI, ParseSI. | ||||
| // | ||||
| // e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") | ||||
| func ComputeSI(input float64) (float64, string) { | ||||
| 	if input == 0 { | ||||
| 		return 0, "" | ||||
| 	} | ||||
| 	mag := math.Abs(input) | ||||
| 	exponent := math.Floor(logn(mag, 10)) | ||||
| 	exponent = math.Floor(exponent/3) * 3 | ||||
| 
 | ||||
| 	value := mag / math.Pow(10, exponent) | ||||
| 
 | ||||
| 	// Handle special case where value is exactly 1000.0 | ||||
| 	// Should return 1 M instead of 1000 k | ||||
| 	if value == 1000.0 { | ||||
| 		exponent += 3 | ||||
| 		value = mag / math.Pow(10, exponent) | ||||
| 	} | ||||
| 
 | ||||
| 	value = math.Copysign(value, input) | ||||
| 
 | ||||
| 	prefix := siPrefixTable[exponent] | ||||
| 	return value, prefix | ||||
| } | ||||
| 
 | ||||
| // SI returns a string with default formatting. | ||||
| // | ||||
| // SI uses Ftoa to format float value, removing trailing zeros. | ||||
| // | ||||
| // See also: ComputeSI, ParseSI. | ||||
| // | ||||
| // e.g. SI(1000000, "B") -> 1 MB | ||||
| // e.g. SI(2.2345e-12, "F") -> 2.2345 pF | ||||
| func SI(input float64, unit string) string { | ||||
| 	value, prefix := ComputeSI(input) | ||||
| 	return Ftoa(value) + " " + prefix + unit | ||||
| } | ||||
| 
 | ||||
| // SIWithDigits works like SI but limits the resulting string to the | ||||
| // given number of decimal places. | ||||
| // | ||||
| // e.g. SIWithDigits(1000000, 0, "B") -> 1 MB | ||||
| // e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF | ||||
| func SIWithDigits(input float64, decimals int, unit string) string { | ||||
| 	value, prefix := ComputeSI(input) | ||||
| 	return FtoaWithDigits(value, decimals) + " " + prefix + unit | ||||
| } | ||||
| 
 | ||||
| var errInvalid = errors.New("invalid input") | ||||
| 
 | ||||
| // ParseSI parses an SI string back into the number and unit. | ||||
| // | ||||
| // See also: SI, ComputeSI. | ||||
| // | ||||
| // e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) | ||||
| func ParseSI(input string) (float64, string, error) { | ||||
| 	found := riParseRegex.FindStringSubmatch(input) | ||||
| 	if len(found) != 4 { | ||||
| 		return 0, "", errInvalid | ||||
| 	} | ||||
| 	mag := revSIPrefixTable[found[2]] | ||||
| 	unit := found[3] | ||||
| 
 | ||||
| 	base, err := strconv.ParseFloat(found[1], 64) | ||||
| 	return base * mag, unit, err | ||||
| } | ||||
							
								
								
									
										117
									
								
								vendor/github.com/dustin/go-humanize/times.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										117
									
								
								vendor/github.com/dustin/go-humanize/times.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,117 +0,0 @@ | |||
| package humanize | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"sort" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Seconds-based time units | ||||
| const ( | ||||
| 	Day      = 24 * time.Hour | ||||
| 	Week     = 7 * Day | ||||
| 	Month    = 30 * Day | ||||
| 	Year     = 12 * Month | ||||
| 	LongTime = 37 * Year | ||||
| ) | ||||
| 
 | ||||
| // Time formats a time into a relative string. | ||||
| // | ||||
| // Time(someT) -> "3 weeks ago" | ||||
| func Time(then time.Time) string { | ||||
| 	return RelTime(then, time.Now(), "ago", "from now") | ||||
| } | ||||
| 
 | ||||
| // A RelTimeMagnitude struct contains a relative time point at which | ||||
| // the relative format of time will switch to a new format string.  A | ||||
| // slice of these in ascending order by their "D" field is passed to | ||||
| // CustomRelTime to format durations. | ||||
| // | ||||
| // The Format field is a string that may contain a "%s" which will be | ||||
| // replaced with the appropriate signed label (e.g. "ago" or "from | ||||
| // now") and a "%d" that will be replaced by the quantity. | ||||
| // | ||||
| // The DivBy field is the amount of time the time difference must be | ||||
| // divided by in order to display correctly. | ||||
| // | ||||
| // e.g. if D is 2*time.Minute and you want to display "%d minutes %s" | ||||
| // DivBy should be time.Minute so whatever the duration is will be | ||||
| // expressed in minutes. | ||||
| type RelTimeMagnitude struct { | ||||
| 	D      time.Duration | ||||
| 	Format string | ||||
| 	DivBy  time.Duration | ||||
| } | ||||
| 
 | ||||
| var defaultMagnitudes = []RelTimeMagnitude{ | ||||
| 	{time.Second, "now", time.Second}, | ||||
| 	{2 * time.Second, "1 second %s", 1}, | ||||
| 	{time.Minute, "%d seconds %s", time.Second}, | ||||
| 	{2 * time.Minute, "1 minute %s", 1}, | ||||
| 	{time.Hour, "%d minutes %s", time.Minute}, | ||||
| 	{2 * time.Hour, "1 hour %s", 1}, | ||||
| 	{Day, "%d hours %s", time.Hour}, | ||||
| 	{2 * Day, "1 day %s", 1}, | ||||
| 	{Week, "%d days %s", Day}, | ||||
| 	{2 * Week, "1 week %s", 1}, | ||||
| 	{Month, "%d weeks %s", Week}, | ||||
| 	{2 * Month, "1 month %s", 1}, | ||||
| 	{Year, "%d months %s", Month}, | ||||
| 	{18 * Month, "1 year %s", 1}, | ||||
| 	{2 * Year, "2 years %s", 1}, | ||||
| 	{LongTime, "%d years %s", Year}, | ||||
| 	{math.MaxInt64, "a long while %s", 1}, | ||||
| } | ||||
| 
 | ||||
| // RelTime formats a time into a relative string. | ||||
| // | ||||
| // It takes two times and two labels.  In addition to the generic time | ||||
| // delta string (e.g. 5 minutes), the labels are used applied so that | ||||
| // the label corresponding to the smaller time is applied. | ||||
| // | ||||
| // RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" | ||||
| func RelTime(a, b time.Time, albl, blbl string) string { | ||||
| 	return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) | ||||
| } | ||||
| 
 | ||||
| // CustomRelTime formats a time into a relative string. | ||||
| // | ||||
| // It takes two times two labels and a table of relative time formats. | ||||
| // In addition to the generic time delta string (e.g. 5 minutes), the | ||||
| // labels are used applied so that the label corresponding to the | ||||
| // smaller time is applied. | ||||
| func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { | ||||
| 	lbl := albl | ||||
| 	diff := b.Sub(a) | ||||
| 
 | ||||
| 	if a.After(b) { | ||||
| 		lbl = blbl | ||||
| 		diff = a.Sub(b) | ||||
| 	} | ||||
| 
 | ||||
| 	n := sort.Search(len(magnitudes), func(i int) bool { | ||||
| 		return magnitudes[i].D > diff | ||||
| 	}) | ||||
| 
 | ||||
| 	if n >= len(magnitudes) { | ||||
| 		n = len(magnitudes) - 1 | ||||
| 	} | ||||
| 	mag := magnitudes[n] | ||||
| 	args := []interface{}{} | ||||
| 	escaped := false | ||||
| 	for _, ch := range mag.Format { | ||||
| 		if escaped { | ||||
| 			switch ch { | ||||
| 			case 's': | ||||
| 				args = append(args, lbl) | ||||
| 			case 'd': | ||||
| 				args = append(args, diff/mag.DivBy) | ||||
| 			} | ||||
| 			escaped = false | ||||
| 		} else { | ||||
| 			escaped = ch == '%' | ||||
| 		} | ||||
| 	} | ||||
| 	return fmt.Sprintf(mag.Format, args...) | ||||
| } | ||||
							
								
								
									
										20
									
								
								vendor/github.com/ghodss/yaml/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/ghodss/yaml/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,20 +0,0 @@ | |||
| # OSX leaves these everywhere on SMB shares | ||||
| ._* | ||||
| 
 | ||||
| # Eclipse files | ||||
| .classpath | ||||
| .project | ||||
| .settings/** | ||||
| 
 | ||||
| # Emacs save files | ||||
| *~ | ||||
| 
 | ||||
| # Vim-related files | ||||
| [._]*.s[a-w][a-z] | ||||
| [._]s[a-w][a-z] | ||||
| *.un~ | ||||
| Session.vim | ||||
| .netrwhist | ||||
| 
 | ||||
| # Go test binaries | ||||
| *.test | ||||
							
								
								
									
										7
									
								
								vendor/github.com/ghodss/yaml/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/ghodss/yaml/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,7 +0,0 @@ | |||
| language: go | ||||
| go: | ||||
|   - 1.3 | ||||
|   - 1.4 | ||||
| script: | ||||
|   - go test | ||||
|   - go build | ||||
							
								
								
									
										50
									
								
								vendor/github.com/ghodss/yaml/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/ghodss/yaml/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,50 +0,0 @@ | |||
| The MIT License (MIT) | ||||
| 
 | ||||
| Copyright (c) 2014 Sam Ghods | ||||
| 
 | ||||
| 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. | ||||
| 
 | ||||
| 
 | ||||
| Copyright (c) 2012 The Go Authors. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
| 
 | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										121
									
								
								vendor/github.com/ghodss/yaml/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										121
									
								
								vendor/github.com/ghodss/yaml/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,121 +0,0 @@ | |||
| # YAML marshaling and unmarshaling support for Go | ||||
| 
 | ||||
| [](https://travis-ci.org/ghodss/yaml) | ||||
| 
 | ||||
| ## Introduction | ||||
| 
 | ||||
| A wrapper around [go-yaml](https://github.com/go-yaml/yaml) designed to enable a better way of handling YAML when marshaling to and from structs. | ||||
| 
 | ||||
| In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/). | ||||
| 
 | ||||
| ## Compatibility | ||||
| 
 | ||||
| This package uses [go-yaml](https://github.com/go-yaml/yaml) and therefore supports [everything go-yaml supports](https://github.com/go-yaml/yaml#compatibility). | ||||
| 
 | ||||
| ## Caveats | ||||
| 
 | ||||
| **Caveat #1:** When using `yaml.Marshal` and `yaml.Unmarshal`, binary data should NOT be preceded with the `!!binary` YAML tag. If you do, go-yaml will convert the binary data from base64 to native binary data, which is not compatible with JSON. You can still use binary in your YAML files though - just store them without the `!!binary` tag and decode the base64 in your code (e.g. in the custom JSON methods `MarshalJSON` and `UnmarshalJSON`). This also has the benefit that your YAML and your JSON binary data will be decoded exactly the same way. As an example: | ||||
| 
 | ||||
| ``` | ||||
| BAD: | ||||
| 	exampleKey: !!binary gIGC | ||||
| 
 | ||||
| GOOD: | ||||
| 	exampleKey: gIGC | ||||
| ... and decode the base64 data in your code. | ||||
| ``` | ||||
| 
 | ||||
| **Caveat #2:** When using `YAMLToJSON` directly, maps with keys that are maps will result in an error since this is not supported by JSON. This error will occur in `Unmarshal` as well since you can't unmarshal map keys anyways since struct fields can't be keys. | ||||
| 
 | ||||
| ## Installation and usage | ||||
| 
 | ||||
| To install, run: | ||||
| 
 | ||||
| ``` | ||||
| $ go get github.com/ghodss/yaml | ||||
| ``` | ||||
| 
 | ||||
| And import using: | ||||
| 
 | ||||
| ``` | ||||
| import "github.com/ghodss/yaml" | ||||
| ``` | ||||
| 
 | ||||
| Usage is very similar to the JSON library: | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/ghodss/yaml" | ||||
| ) | ||||
| 
 | ||||
| type Person struct { | ||||
| 	Name string `json:"name"` // Affects YAML field names too. | ||||
| 	Age  int    `json:"age"` | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	// Marshal a Person struct to YAML. | ||||
| 	p := Person{"John", 30} | ||||
| 	y, err := yaml.Marshal(p) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("err: %v\n", err) | ||||
| 		return | ||||
| 	} | ||||
| 	fmt.Println(string(y)) | ||||
| 	/* Output: | ||||
| 	age: 30 | ||||
| 	name: John | ||||
| 	*/ | ||||
| 
 | ||||
| 	// Unmarshal the YAML back into a Person struct. | ||||
| 	var p2 Person | ||||
| 	err = yaml.Unmarshal(y, &p2) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("err: %v\n", err) | ||||
| 		return | ||||
| 	} | ||||
| 	fmt.Println(p2) | ||||
| 	/* Output: | ||||
| 	{John 30} | ||||
| 	*/ | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| `yaml.YAMLToJSON` and `yaml.JSONToYAML` methods are also available: | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/ghodss/yaml" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	j := []byte(`{"name": "John", "age": 30}`) | ||||
| 	y, err := yaml.JSONToYAML(j) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("err: %v\n", err) | ||||
| 		return | ||||
| 	} | ||||
| 	fmt.Println(string(y)) | ||||
| 	/* Output: | ||||
| 	name: John | ||||
| 	age: 30 | ||||
| 	*/ | ||||
| 	j2, err := yaml.YAMLToJSON(y) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("err: %v\n", err) | ||||
| 		return | ||||
| 	} | ||||
| 	fmt.Println(string(j2)) | ||||
| 	/* Output: | ||||
| 	{"age":30,"name":"John"} | ||||
| 	*/ | ||||
| } | ||||
| ``` | ||||
							
								
								
									
										501
									
								
								vendor/github.com/ghodss/yaml/fields.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										501
									
								
								vendor/github.com/ghodss/yaml/fields.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,501 +0,0 @@ | |||
| // Copyright 2013 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding" | ||||
| 	"encoding/json" | ||||
| 	"reflect" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"unicode" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
| 
 | ||||
| // indirect walks down v allocating pointers as needed, | ||||
| // until it gets to a non-pointer. | ||||
| // if it encounters an Unmarshaler, indirect stops and returns that. | ||||
| // if decodingNull is true, indirect stops at the last pointer so it can be set to nil. | ||||
| func indirect(v reflect.Value, decodingNull bool) (json.Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { | ||||
| 	// If v is a named type and is addressable, | ||||
| 	// start with its address, so that if the type has pointer methods, | ||||
| 	// we find them. | ||||
| 	if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { | ||||
| 		v = v.Addr() | ||||
| 	} | ||||
| 	for { | ||||
| 		// Load value from interface, but only if the result will be | ||||
| 		// usefully addressable. | ||||
| 		if v.Kind() == reflect.Interface && !v.IsNil() { | ||||
| 			e := v.Elem() | ||||
| 			if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { | ||||
| 				v = e | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if v.Kind() != reflect.Ptr { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { | ||||
| 			break | ||||
| 		} | ||||
| 		if v.IsNil() { | ||||
| 			if v.CanSet() { | ||||
| 				v.Set(reflect.New(v.Type().Elem())) | ||||
| 			} else { | ||||
| 				v = reflect.New(v.Type().Elem()) | ||||
| 			} | ||||
| 		} | ||||
| 		if v.Type().NumMethod() > 0 { | ||||
| 			if u, ok := v.Interface().(json.Unmarshaler); ok { | ||||
| 				return u, nil, reflect.Value{} | ||||
| 			} | ||||
| 			if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { | ||||
| 				return nil, u, reflect.Value{} | ||||
| 			} | ||||
| 		} | ||||
| 		v = v.Elem() | ||||
| 	} | ||||
| 	return nil, nil, v | ||||
| } | ||||
| 
 | ||||
| // A field represents a single field found in a struct. | ||||
| type field struct { | ||||
| 	name      string | ||||
| 	nameBytes []byte                 // []byte(name) | ||||
| 	equalFold func(s, t []byte) bool // bytes.EqualFold or equivalent | ||||
| 
 | ||||
| 	tag       bool | ||||
| 	index     []int | ||||
| 	typ       reflect.Type | ||||
| 	omitEmpty bool | ||||
| 	quoted    bool | ||||
| } | ||||
| 
 | ||||
| func fillField(f field) field { | ||||
| 	f.nameBytes = []byte(f.name) | ||||
| 	f.equalFold = foldFunc(f.nameBytes) | ||||
| 	return f | ||||
| } | ||||
| 
 | ||||
| // byName sorts field by name, breaking ties with depth, | ||||
| // then breaking ties with "name came from json tag", then | ||||
| // breaking ties with index sequence. | ||||
| type byName []field | ||||
| 
 | ||||
| func (x byName) Len() int { return len(x) } | ||||
| 
 | ||||
| func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } | ||||
| 
 | ||||
| func (x byName) Less(i, j int) bool { | ||||
| 	if x[i].name != x[j].name { | ||||
| 		return x[i].name < x[j].name | ||||
| 	} | ||||
| 	if len(x[i].index) != len(x[j].index) { | ||||
| 		return len(x[i].index) < len(x[j].index) | ||||
| 	} | ||||
| 	if x[i].tag != x[j].tag { | ||||
| 		return x[i].tag | ||||
| 	} | ||||
| 	return byIndex(x).Less(i, j) | ||||
| } | ||||
| 
 | ||||
| // byIndex sorts field by index sequence. | ||||
| type byIndex []field | ||||
| 
 | ||||
| func (x byIndex) Len() int { return len(x) } | ||||
| 
 | ||||
| func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } | ||||
| 
 | ||||
| func (x byIndex) Less(i, j int) bool { | ||||
| 	for k, xik := range x[i].index { | ||||
| 		if k >= len(x[j].index) { | ||||
| 			return false | ||||
| 		} | ||||
| 		if xik != x[j].index[k] { | ||||
| 			return xik < x[j].index[k] | ||||
| 		} | ||||
| 	} | ||||
| 	return len(x[i].index) < len(x[j].index) | ||||
| } | ||||
| 
 | ||||
| // typeFields returns a list of fields that JSON should recognize for the given type. | ||||
| // The algorithm is breadth-first search over the set of structs to include - the top struct | ||||
| // and then any reachable anonymous structs. | ||||
| func typeFields(t reflect.Type) []field { | ||||
| 	// Anonymous fields to explore at the current level and the next. | ||||
| 	current := []field{} | ||||
| 	next := []field{{typ: t}} | ||||
| 
 | ||||
| 	// Count of queued names for current level and the next. | ||||
| 	count := map[reflect.Type]int{} | ||||
| 	nextCount := map[reflect.Type]int{} | ||||
| 
 | ||||
| 	// Types already visited at an earlier level. | ||||
| 	visited := map[reflect.Type]bool{} | ||||
| 
 | ||||
| 	// Fields found. | ||||
| 	var fields []field | ||||
| 
 | ||||
| 	for len(next) > 0 { | ||||
| 		current, next = next, current[:0] | ||||
| 		count, nextCount = nextCount, map[reflect.Type]int{} | ||||
| 
 | ||||
| 		for _, f := range current { | ||||
| 			if visited[f.typ] { | ||||
| 				continue | ||||
| 			} | ||||
| 			visited[f.typ] = true | ||||
| 
 | ||||
| 			// Scan f.typ for fields to include. | ||||
| 			for i := 0; i < f.typ.NumField(); i++ { | ||||
| 				sf := f.typ.Field(i) | ||||
| 				if sf.PkgPath != "" { // unexported | ||||
| 					continue | ||||
| 				} | ||||
| 				tag := sf.Tag.Get("json") | ||||
| 				if tag == "-" { | ||||
| 					continue | ||||
| 				} | ||||
| 				name, opts := parseTag(tag) | ||||
| 				if !isValidTag(name) { | ||||
| 					name = "" | ||||
| 				} | ||||
| 				index := make([]int, len(f.index)+1) | ||||
| 				copy(index, f.index) | ||||
| 				index[len(f.index)] = i | ||||
| 
 | ||||
| 				ft := sf.Type | ||||
| 				if ft.Name() == "" && ft.Kind() == reflect.Ptr { | ||||
| 					// Follow pointer. | ||||
| 					ft = ft.Elem() | ||||
| 				} | ||||
| 
 | ||||
| 				// Record found field and index sequence. | ||||
| 				if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { | ||||
| 					tagged := name != "" | ||||
| 					if name == "" { | ||||
| 						name = sf.Name | ||||
| 					} | ||||
| 					fields = append(fields, fillField(field{ | ||||
| 						name:      name, | ||||
| 						tag:       tagged, | ||||
| 						index:     index, | ||||
| 						typ:       ft, | ||||
| 						omitEmpty: opts.Contains("omitempty"), | ||||
| 						quoted:    opts.Contains("string"), | ||||
| 					})) | ||||
| 					if count[f.typ] > 1 { | ||||
| 						// If there were multiple instances, add a second, | ||||
| 						// so that the annihilation code will see a duplicate. | ||||
| 						// It only cares about the distinction between 1 or 2, | ||||
| 						// so don't bother generating any more copies. | ||||
| 						fields = append(fields, fields[len(fields)-1]) | ||||
| 					} | ||||
| 					continue | ||||
| 				} | ||||
| 
 | ||||
| 				// Record new anonymous struct to explore in next round. | ||||
| 				nextCount[ft]++ | ||||
| 				if nextCount[ft] == 1 { | ||||
| 					next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft})) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	sort.Sort(byName(fields)) | ||||
| 
 | ||||
| 	// Delete all fields that are hidden by the Go rules for embedded fields, | ||||
| 	// except that fields with JSON tags are promoted. | ||||
| 
 | ||||
| 	// The fields are sorted in primary order of name, secondary order | ||||
| 	// of field index length. Loop over names; for each name, delete | ||||
| 	// hidden fields by choosing the one dominant field that survives. | ||||
| 	out := fields[:0] | ||||
| 	for advance, i := 0, 0; i < len(fields); i += advance { | ||||
| 		// One iteration per name. | ||||
| 		// Find the sequence of fields with the name of this first field. | ||||
| 		fi := fields[i] | ||||
| 		name := fi.name | ||||
| 		for advance = 1; i+advance < len(fields); advance++ { | ||||
| 			fj := fields[i+advance] | ||||
| 			if fj.name != name { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if advance == 1 { // Only one field with this name | ||||
| 			out = append(out, fi) | ||||
| 			continue | ||||
| 		} | ||||
| 		dominant, ok := dominantField(fields[i : i+advance]) | ||||
| 		if ok { | ||||
| 			out = append(out, dominant) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fields = out | ||||
| 	sort.Sort(byIndex(fields)) | ||||
| 
 | ||||
| 	return fields | ||||
| } | ||||
| 
 | ||||
| // dominantField looks through the fields, all of which are known to | ||||
| // have the same name, to find the single field that dominates the | ||||
| // others using Go's embedding rules, modified by the presence of | ||||
| // JSON tags. If there are multiple top-level fields, the boolean | ||||
| // will be false: This condition is an error in Go and we skip all | ||||
| // the fields. | ||||
| func dominantField(fields []field) (field, bool) { | ||||
| 	// The fields are sorted in increasing index-length order. The winner | ||||
| 	// must therefore be one with the shortest index length. Drop all | ||||
| 	// longer entries, which is easy: just truncate the slice. | ||||
| 	length := len(fields[0].index) | ||||
| 	tagged := -1 // Index of first tagged field. | ||||
| 	for i, f := range fields { | ||||
| 		if len(f.index) > length { | ||||
| 			fields = fields[:i] | ||||
| 			break | ||||
| 		} | ||||
| 		if f.tag { | ||||
| 			if tagged >= 0 { | ||||
| 				// Multiple tagged fields at the same level: conflict. | ||||
| 				// Return no field. | ||||
| 				return field{}, false | ||||
| 			} | ||||
| 			tagged = i | ||||
| 		} | ||||
| 	} | ||||
| 	if tagged >= 0 { | ||||
| 		return fields[tagged], true | ||||
| 	} | ||||
| 	// All remaining fields have the same length. If there's more than one, | ||||
| 	// we have a conflict (two fields named "X" at the same level) and we | ||||
| 	// return no field. | ||||
| 	if len(fields) > 1 { | ||||
| 		return field{}, false | ||||
| 	} | ||||
| 	return fields[0], true | ||||
| } | ||||
| 
 | ||||
| var fieldCache struct { | ||||
| 	sync.RWMutex | ||||
| 	m map[reflect.Type][]field | ||||
| } | ||||
| 
 | ||||
| // cachedTypeFields is like typeFields but uses a cache to avoid repeated work. | ||||
| func cachedTypeFields(t reflect.Type) []field { | ||||
| 	fieldCache.RLock() | ||||
| 	f := fieldCache.m[t] | ||||
| 	fieldCache.RUnlock() | ||||
| 	if f != nil { | ||||
| 		return f | ||||
| 	} | ||||
| 
 | ||||
| 	// Compute fields without lock. | ||||
| 	// Might duplicate effort but won't hold other computations back. | ||||
| 	f = typeFields(t) | ||||
| 	if f == nil { | ||||
| 		f = []field{} | ||||
| 	} | ||||
| 
 | ||||
| 	fieldCache.Lock() | ||||
| 	if fieldCache.m == nil { | ||||
| 		fieldCache.m = map[reflect.Type][]field{} | ||||
| 	} | ||||
| 	fieldCache.m[t] = f | ||||
| 	fieldCache.Unlock() | ||||
| 	return f | ||||
| } | ||||
| 
 | ||||
| func isValidTag(s string) bool { | ||||
| 	if s == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	for _, c := range s { | ||||
| 		switch { | ||||
| 		case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): | ||||
| 			// Backslash and quote chars are reserved, but | ||||
| 			// otherwise any punctuation chars are allowed | ||||
| 			// in a tag name. | ||||
| 		default: | ||||
| 			if !unicode.IsLetter(c) && !unicode.IsDigit(c) { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	caseMask     = ^byte(0x20) // Mask to ignore case in ASCII. | ||||
| 	kelvin       = '\u212a' | ||||
| 	smallLongEss = '\u017f' | ||||
| ) | ||||
| 
 | ||||
| // foldFunc returns one of four different case folding equivalence | ||||
| // functions, from most general (and slow) to fastest: | ||||
| // | ||||
| // 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8 | ||||
| // 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S') | ||||
| // 3) asciiEqualFold, no special, but includes non-letters (including _) | ||||
| // 4) simpleLetterEqualFold, no specials, no non-letters. | ||||
| // | ||||
| // The letters S and K are special because they map to 3 runes, not just 2: | ||||
| //  * S maps to s and to U+017F 'ſ' Latin small letter long s | ||||
| //  * k maps to K and to U+212A 'K' Kelvin sign | ||||
| // See http://play.golang.org/p/tTxjOc0OGo | ||||
| // | ||||
| // The returned function is specialized for matching against s and | ||||
| // should only be given s. It's not curried for performance reasons. | ||||
| func foldFunc(s []byte) func(s, t []byte) bool { | ||||
| 	nonLetter := false | ||||
| 	special := false // special letter | ||||
| 	for _, b := range s { | ||||
| 		if b >= utf8.RuneSelf { | ||||
| 			return bytes.EqualFold | ||||
| 		} | ||||
| 		upper := b & caseMask | ||||
| 		if upper < 'A' || upper > 'Z' { | ||||
| 			nonLetter = true | ||||
| 		} else if upper == 'K' || upper == 'S' { | ||||
| 			// See above for why these letters are special. | ||||
| 			special = true | ||||
| 		} | ||||
| 	} | ||||
| 	if special { | ||||
| 		return equalFoldRight | ||||
| 	} | ||||
| 	if nonLetter { | ||||
| 		return asciiEqualFold | ||||
| 	} | ||||
| 	return simpleLetterEqualFold | ||||
| } | ||||
| 
 | ||||
| // equalFoldRight is a specialization of bytes.EqualFold when s is | ||||
| // known to be all ASCII (including punctuation), but contains an 's', | ||||
| // 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t. | ||||
| // See comments on foldFunc. | ||||
| func equalFoldRight(s, t []byte) bool { | ||||
| 	for _, sb := range s { | ||||
| 		if len(t) == 0 { | ||||
| 			return false | ||||
| 		} | ||||
| 		tb := t[0] | ||||
| 		if tb < utf8.RuneSelf { | ||||
| 			if sb != tb { | ||||
| 				sbUpper := sb & caseMask | ||||
| 				if 'A' <= sbUpper && sbUpper <= 'Z' { | ||||
| 					if sbUpper != tb&caseMask { | ||||
| 						return false | ||||
| 					} | ||||
| 				} else { | ||||
| 					return false | ||||
| 				} | ||||
| 			} | ||||
| 			t = t[1:] | ||||
| 			continue | ||||
| 		} | ||||
| 		// sb is ASCII and t is not. t must be either kelvin | ||||
| 		// sign or long s; sb must be s, S, k, or K. | ||||
| 		tr, size := utf8.DecodeRune(t) | ||||
| 		switch sb { | ||||
| 		case 's', 'S': | ||||
| 			if tr != smallLongEss { | ||||
| 				return false | ||||
| 			} | ||||
| 		case 'k', 'K': | ||||
| 			if tr != kelvin { | ||||
| 				return false | ||||
| 			} | ||||
| 		default: | ||||
| 			return false | ||||
| 		} | ||||
| 		t = t[size:] | ||||
| 
 | ||||
| 	} | ||||
| 	if len(t) > 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // asciiEqualFold is a specialization of bytes.EqualFold for use when | ||||
| // s is all ASCII (but may contain non-letters) and contains no | ||||
| // special-folding letters. | ||||
| // See comments on foldFunc. | ||||
| func asciiEqualFold(s, t []byte) bool { | ||||
| 	if len(s) != len(t) { | ||||
| 		return false | ||||
| 	} | ||||
| 	for i, sb := range s { | ||||
| 		tb := t[i] | ||||
| 		if sb == tb { | ||||
| 			continue | ||||
| 		} | ||||
| 		if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') { | ||||
| 			if sb&caseMask != tb&caseMask { | ||||
| 				return false | ||||
| 			} | ||||
| 		} else { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // simpleLetterEqualFold is a specialization of bytes.EqualFold for | ||||
| // use when s is all ASCII letters (no underscores, etc) and also | ||||
| // doesn't contain 'k', 'K', 's', or 'S'. | ||||
| // See comments on foldFunc. | ||||
| func simpleLetterEqualFold(s, t []byte) bool { | ||||
| 	if len(s) != len(t) { | ||||
| 		return false | ||||
| 	} | ||||
| 	for i, b := range s { | ||||
| 		if b&caseMask != t[i]&caseMask { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // tagOptions is the string following a comma in a struct field's "json" | ||||
| // tag, or the empty string. It does not include the leading comma. | ||||
| type tagOptions string | ||||
| 
 | ||||
| // parseTag splits a struct field's json tag into its name and | ||||
| // comma-separated options. | ||||
| func parseTag(tag string) (string, tagOptions) { | ||||
| 	if idx := strings.Index(tag, ","); idx != -1 { | ||||
| 		return tag[:idx], tagOptions(tag[idx+1:]) | ||||
| 	} | ||||
| 	return tag, tagOptions("") | ||||
| } | ||||
| 
 | ||||
| // Contains reports whether a comma-separated list of options | ||||
| // contains a particular substr flag. substr must be surrounded by a | ||||
| // string boundary or commas. | ||||
| func (o tagOptions) Contains(optionName string) bool { | ||||
| 	if len(o) == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	s := string(o) | ||||
| 	for s != "" { | ||||
| 		var next string | ||||
| 		i := strings.Index(s, ",") | ||||
| 		if i >= 0 { | ||||
| 			s, next = s[:i], s[i+1:] | ||||
| 		} | ||||
| 		if s == optionName { | ||||
| 			return true | ||||
| 		} | ||||
| 		s = next | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										277
									
								
								vendor/github.com/ghodss/yaml/yaml.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										277
									
								
								vendor/github.com/ghodss/yaml/yaml.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,277 +0,0 @@ | |||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"gopkg.in/yaml.v2" | ||||
| ) | ||||
| 
 | ||||
| // Marshals the object into JSON then converts JSON to YAML and returns the | ||||
| // YAML. | ||||
| func Marshal(o interface{}) ([]byte, error) { | ||||
| 	j, err := json.Marshal(o) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error marshaling into JSON: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	y, err := JSONToYAML(j) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error converting JSON to YAML: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return y, nil | ||||
| } | ||||
| 
 | ||||
| // Converts YAML to JSON then uses JSON to unmarshal into an object. | ||||
| func Unmarshal(y []byte, o interface{}) error { | ||||
| 	vo := reflect.ValueOf(o) | ||||
| 	j, err := yamlToJSON(y, &vo) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error converting YAML to JSON: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = json.Unmarshal(j, o) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error unmarshaling JSON: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Convert JSON to YAML. | ||||
| func JSONToYAML(j []byte) ([]byte, error) { | ||||
| 	// Convert the JSON to an object. | ||||
| 	var jsonObj interface{} | ||||
| 	// We are using yaml.Unmarshal here (instead of json.Unmarshal) because the | ||||
| 	// Go JSON library doesn't try to pick the right number type (int, float, | ||||
| 	// etc.) when unmarshalling to interface{}, it just picks float64 | ||||
| 	// universally. go-yaml does go through the effort of picking the right | ||||
| 	// number type, so we can preserve number type throughout this process. | ||||
| 	err := yaml.Unmarshal(j, &jsonObj) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Marshal this object into YAML. | ||||
| 	return yaml.Marshal(jsonObj) | ||||
| } | ||||
| 
 | ||||
| // Convert YAML to JSON. Since JSON is a subset of YAML, passing JSON through | ||||
| // this method should be a no-op. | ||||
| // | ||||
| // Things YAML can do that are not supported by JSON: | ||||
| // * In YAML you can have binary and null keys in your maps. These are invalid | ||||
| //   in JSON. (int and float keys are converted to strings.) | ||||
| // * Binary data in YAML with the !!binary tag is not supported. If you want to | ||||
| //   use binary data with this library, encode the data as base64 as usual but do | ||||
| //   not use the !!binary tag in your YAML. This will ensure the original base64 | ||||
| //   encoded data makes it all the way through to the JSON. | ||||
| func YAMLToJSON(y []byte) ([]byte, error) { | ||||
| 	return yamlToJSON(y, nil) | ||||
| } | ||||
| 
 | ||||
| func yamlToJSON(y []byte, jsonTarget *reflect.Value) ([]byte, error) { | ||||
| 	// Convert the YAML to an object. | ||||
| 	var yamlObj interface{} | ||||
| 	err := yaml.Unmarshal(y, &yamlObj) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// YAML objects are not completely compatible with JSON objects (e.g. you | ||||
| 	// can have non-string keys in YAML). So, convert the YAML-compatible object | ||||
| 	// to a JSON-compatible object, failing with an error if irrecoverable | ||||
| 	// incompatibilties happen along the way. | ||||
| 	jsonObj, err := convertToJSONableObject(yamlObj, jsonTarget) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Convert this object to JSON and return the data. | ||||
| 	return json.Marshal(jsonObj) | ||||
| } | ||||
| 
 | ||||
| func convertToJSONableObject(yamlObj interface{}, jsonTarget *reflect.Value) (interface{}, error) { | ||||
| 	var err error | ||||
| 
 | ||||
| 	// Resolve jsonTarget to a concrete value (i.e. not a pointer or an | ||||
| 	// interface). We pass decodingNull as false because we're not actually | ||||
| 	// decoding into the value, we're just checking if the ultimate target is a | ||||
| 	// string. | ||||
| 	if jsonTarget != nil { | ||||
| 		ju, tu, pv := indirect(*jsonTarget, false) | ||||
| 		// We have a JSON or Text Umarshaler at this level, so we can't be trying | ||||
| 		// to decode into a string. | ||||
| 		if ju != nil || tu != nil { | ||||
| 			jsonTarget = nil | ||||
| 		} else { | ||||
| 			jsonTarget = &pv | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// If yamlObj is a number or a boolean, check if jsonTarget is a string - | ||||
| 	// if so, coerce.  Else return normal. | ||||
| 	// If yamlObj is a map or array, find the field that each key is | ||||
| 	// unmarshaling to, and when you recurse pass the reflect.Value for that | ||||
| 	// field back into this function. | ||||
| 	switch typedYAMLObj := yamlObj.(type) { | ||||
| 	case map[interface{}]interface{}: | ||||
| 		// JSON does not support arbitrary keys in a map, so we must convert | ||||
| 		// these keys to strings. | ||||
| 		// | ||||
| 		// From my reading of go-yaml v2 (specifically the resolve function), | ||||
| 		// keys can only have the types string, int, int64, float64, binary | ||||
| 		// (unsupported), or null (unsupported). | ||||
| 		strMap := make(map[string]interface{}) | ||||
| 		for k, v := range typedYAMLObj { | ||||
| 			// Resolve the key to a string first. | ||||
| 			var keyString string | ||||
| 			switch typedKey := k.(type) { | ||||
| 			case string: | ||||
| 				keyString = typedKey | ||||
| 			case int: | ||||
| 				keyString = strconv.Itoa(typedKey) | ||||
| 			case int64: | ||||
| 				// go-yaml will only return an int64 as a key if the system | ||||
| 				// architecture is 32-bit and the key's value is between 32-bit | ||||
| 				// and 64-bit. Otherwise the key type will simply be int. | ||||
| 				keyString = strconv.FormatInt(typedKey, 10) | ||||
| 			case float64: | ||||
| 				// Stolen from go-yaml to use the same conversion to string as | ||||
| 				// the go-yaml library uses to convert float to string when | ||||
| 				// Marshaling. | ||||
| 				s := strconv.FormatFloat(typedKey, 'g', -1, 32) | ||||
| 				switch s { | ||||
| 				case "+Inf": | ||||
| 					s = ".inf" | ||||
| 				case "-Inf": | ||||
| 					s = "-.inf" | ||||
| 				case "NaN": | ||||
| 					s = ".nan" | ||||
| 				} | ||||
| 				keyString = s | ||||
| 			case bool: | ||||
| 				if typedKey { | ||||
| 					keyString = "true" | ||||
| 				} else { | ||||
| 					keyString = "false" | ||||
| 				} | ||||
| 			default: | ||||
| 				return nil, fmt.Errorf("Unsupported map key of type: %s, key: %+#v, value: %+#v", | ||||
| 					reflect.TypeOf(k), k, v) | ||||
| 			} | ||||
| 
 | ||||
| 			// jsonTarget should be a struct or a map. If it's a struct, find | ||||
| 			// the field it's going to map to and pass its reflect.Value. If | ||||
| 			// it's a map, find the element type of the map and pass the | ||||
| 			// reflect.Value created from that type. If it's neither, just pass | ||||
| 			// nil - JSON conversion will error for us if it's a real issue. | ||||
| 			if jsonTarget != nil { | ||||
| 				t := *jsonTarget | ||||
| 				if t.Kind() == reflect.Struct { | ||||
| 					keyBytes := []byte(keyString) | ||||
| 					// Find the field that the JSON library would use. | ||||
| 					var f *field | ||||
| 					fields := cachedTypeFields(t.Type()) | ||||
| 					for i := range fields { | ||||
| 						ff := &fields[i] | ||||
| 						if bytes.Equal(ff.nameBytes, keyBytes) { | ||||
| 							f = ff | ||||
| 							break | ||||
| 						} | ||||
| 						// Do case-insensitive comparison. | ||||
| 						if f == nil && ff.equalFold(ff.nameBytes, keyBytes) { | ||||
| 							f = ff | ||||
| 						} | ||||
| 					} | ||||
| 					if f != nil { | ||||
| 						// Find the reflect.Value of the most preferential | ||||
| 						// struct field. | ||||
| 						jtf := t.Field(f.index[0]) | ||||
| 						strMap[keyString], err = convertToJSONableObject(v, &jtf) | ||||
| 						if err != nil { | ||||
| 							return nil, err | ||||
| 						} | ||||
| 						continue | ||||
| 					} | ||||
| 				} else if t.Kind() == reflect.Map { | ||||
| 					// Create a zero value of the map's element type to use as | ||||
| 					// the JSON target. | ||||
| 					jtv := reflect.Zero(t.Type().Elem()) | ||||
| 					strMap[keyString], err = convertToJSONableObject(v, &jtv) | ||||
| 					if err != nil { | ||||
| 						return nil, err | ||||
| 					} | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
| 			strMap[keyString], err = convertToJSONableObject(v, nil) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 		return strMap, nil | ||||
| 	case []interface{}: | ||||
| 		// We need to recurse into arrays in case there are any | ||||
| 		// map[interface{}]interface{}'s inside and to convert any | ||||
| 		// numbers to strings. | ||||
| 
 | ||||
| 		// If jsonTarget is a slice (which it really should be), find the | ||||
| 		// thing it's going to map to. If it's not a slice, just pass nil | ||||
| 		// - JSON conversion will error for us if it's a real issue. | ||||
| 		var jsonSliceElemValue *reflect.Value | ||||
| 		if jsonTarget != nil { | ||||
| 			t := *jsonTarget | ||||
| 			if t.Kind() == reflect.Slice { | ||||
| 				// By default slices point to nil, but we need a reflect.Value | ||||
| 				// pointing to a value of the slice type, so we create one here. | ||||
| 				ev := reflect.Indirect(reflect.New(t.Type().Elem())) | ||||
| 				jsonSliceElemValue = &ev | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Make and use a new array. | ||||
| 		arr := make([]interface{}, len(typedYAMLObj)) | ||||
| 		for i, v := range typedYAMLObj { | ||||
| 			arr[i], err = convertToJSONableObject(v, jsonSliceElemValue) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 		return arr, nil | ||||
| 	default: | ||||
| 		// If the target type is a string and the YAML type is a number, | ||||
| 		// convert the YAML type to a string. | ||||
| 		if jsonTarget != nil && (*jsonTarget).Kind() == reflect.String { | ||||
| 			// Based on my reading of go-yaml, it may return int, int64, | ||||
| 			// float64, or uint64. | ||||
| 			var s string | ||||
| 			switch typedVal := typedYAMLObj.(type) { | ||||
| 			case int: | ||||
| 				s = strconv.FormatInt(int64(typedVal), 10) | ||||
| 			case int64: | ||||
| 				s = strconv.FormatInt(typedVal, 10) | ||||
| 			case float64: | ||||
| 				s = strconv.FormatFloat(typedVal, 'g', -1, 32) | ||||
| 			case uint64: | ||||
| 				s = strconv.FormatUint(typedVal, 10) | ||||
| 			case bool: | ||||
| 				if typedVal { | ||||
| 					s = "true" | ||||
| 				} else { | ||||
| 					s = "false" | ||||
| 				} | ||||
| 			} | ||||
| 			if len(s) > 0 { | ||||
| 				yamlObj = interface{}(s) | ||||
| 			} | ||||
| 		} | ||||
| 		return yamlObj, nil | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, nil | ||||
| } | ||||
							
								
								
									
										3
									
								
								vendor/github.com/go-chi/chi/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/go-chi/chi/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,3 +0,0 @@ | |||
| .idea | ||||
| *.sw? | ||||
| .vscode | ||||
							
								
								
									
										18
									
								
								vendor/github.com/go-chi/chi/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/go-chi/chi/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,18 +0,0 @@ | |||
| language: go | ||||
| 
 | ||||
| go: | ||||
|   - 1.10.x | ||||
|   - 1.11.x | ||||
|   - 1.12.x | ||||
| 
 | ||||
| script: | ||||
|   - go get -d -t ./... | ||||
|   - go vet ./... | ||||
|   - go test ./... | ||||
|   - > | ||||
|     go_version=$(go version); | ||||
|     if [ ${go_version:13:4} = "1.12" ]; then | ||||
|       go get -u golang.org/x/tools/cmd/goimports; | ||||
|       goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :; | ||||
|     fi | ||||
| 
 | ||||
							
								
								
									
										139
									
								
								vendor/github.com/go-chi/chi/CHANGELOG.md
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										139
									
								
								vendor/github.com/go-chi/chi/CHANGELOG.md
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,139 +0,0 @@ | |||
| # Changelog | ||||
| 
 | ||||
| ## v4.0.0 (2019-01-10) | ||||
| 
 | ||||
| - chi v4 requires Go 1.10.3+ (or Go 1.9.7+) - we have deprecated support for Go 1.7 and 1.8 | ||||
| - router: respond with 404 on router with no routes (#362) | ||||
| - router: additional check to ensure wildcard is at the end of a url pattern (#333) | ||||
| - middleware: deprecate use of http.CloseNotifier (#347) | ||||
| - middleware: fix RedirectSlashes to include query params on redirect (#334) | ||||
| - History of changes: see https://github.com/go-chi/chi/compare/v3.3.4...v4.0.0 | ||||
| 
 | ||||
| 
 | ||||
| ## v3.3.4 (2019-01-07) | ||||
| 
 | ||||
| - Minor middleware improvements. No changes to core library/router. Moving v3 into its | ||||
| - own branch as a version of chi for Go 1.7, 1.8, 1.9, 1.10, 1.11 | ||||
| - History of changes: see https://github.com/go-chi/chi/compare/v3.3.3...v3.3.4 | ||||
| 
 | ||||
| 
 | ||||
| ## v3.3.3 (2018-08-27) | ||||
| 
 | ||||
| - Minor release | ||||
| - See https://github.com/go-chi/chi/compare/v3.3.2...v3.3.3 | ||||
| 
 | ||||
| 
 | ||||
| ## v3.3.2 (2017-12-22) | ||||
| 
 | ||||
| - Support to route trailing slashes on mounted sub-routers (#281) | ||||
| - middleware: new `ContentCharset` to check matching charsets. Thank you | ||||
|   @csucu for your community contribution! | ||||
| 
 | ||||
| 
 | ||||
| ## v3.3.1 (2017-11-20) | ||||
| 
 | ||||
| - middleware: new `AllowContentType` handler for explicit whitelist of accepted request Content-Types | ||||
| - middleware: new `SetHeader` handler for short-hand middleware to set a response header key/value | ||||
| - Minor bug fixes | ||||
| 
 | ||||
| 
 | ||||
| ## v3.3.0 (2017-10-10) | ||||
| 
 | ||||
| - New chi.RegisterMethod(method) to add support for custom HTTP methods, see _examples/custom-method for usage | ||||
| - Deprecated LINK and UNLINK methods from the default list, please use `chi.RegisterMethod("LINK")` and `chi.RegisterMethod("UNLINK")` in an `init()` function | ||||
| 
 | ||||
| 
 | ||||
| ## v3.2.1 (2017-08-31) | ||||
| 
 | ||||
| - Add new `Match(rctx *Context, method, path string) bool` method to `Routes` interface | ||||
|   and `Mux`. Match searches the mux's routing tree for a handler that matches the method/path | ||||
| - Add new `RouteMethod` to `*Context` | ||||
| - Add new `Routes` pointer to `*Context` | ||||
| - Add new `middleware.GetHead` to route missing HEAD requests to GET handler | ||||
| - Updated benchmarks (see README) | ||||
| 
 | ||||
| 
 | ||||
| ## v3.1.5 (2017-08-02) | ||||
| 
 | ||||
| - Setup golint and go vet for the project | ||||
| - As per golint, we've redefined `func ServerBaseContext(h http.Handler, baseCtx context.Context) http.Handler` | ||||
|   to `func ServerBaseContext(baseCtx context.Context, h http.Handler) http.Handler` | ||||
| 
 | ||||
| 
 | ||||
| ## v3.1.0 (2017-07-10) | ||||
| 
 | ||||
| - Fix a few minor issues after v3 release | ||||
| - Move `docgen` sub-pkg to https://github.com/go-chi/docgen | ||||
| - Move `render` sub-pkg to https://github.com/go-chi/render | ||||
| - Add new `URLFormat` handler to chi/middleware sub-pkg to make working with url mime  | ||||
|   suffixes easier, ie. parsing `/articles/1.json` and `/articles/1.xml`. See comments in | ||||
|   https://github.com/go-chi/chi/blob/master/middleware/url_format.go for example usage. | ||||
| 
 | ||||
| 
 | ||||
| ## v3.0.0 (2017-06-21) | ||||
| 
 | ||||
| - Major update to chi library with many exciting updates, but also some *breaking changes* | ||||
| - URL parameter syntax changed from `/:id` to `/{id}` for even more flexible routing, such as | ||||
|   `/articles/{month}-{day}-{year}-{slug}`, `/articles/{id}`, and `/articles/{id}.{ext}` on the | ||||
|   same router | ||||
| - Support for regexp for routing patterns, in the form of `/{paramKey:regExp}` for example: | ||||
|   `r.Get("/articles/{name:[a-z]+}", h)` and `chi.URLParam(r, "name")` | ||||
| - Add `Method` and `MethodFunc` to `chi.Router` to allow routing definitions such as | ||||
|   `r.Method("GET", "/", h)` which provides a cleaner interface for custom handlers like | ||||
|   in `_examples/custom-handler` | ||||
| - Deprecating `mux#FileServer` helper function. Instead, we encourage users to create their | ||||
|   own using file handler with the stdlib, see `_examples/fileserver` for an example | ||||
| - Add support for LINK/UNLINK http methods via `r.Method()` and `r.MethodFunc()` | ||||
| - Moved the chi project to its own organization, to allow chi-related community packages to | ||||
|   be easily discovered and supported, at: https://github.com/go-chi | ||||
| - *NOTE:* please update your import paths to `"github.com/go-chi/chi"` | ||||
| - *NOTE:* chi v2 is still available at https://github.com/go-chi/chi/tree/v2 | ||||
| 
 | ||||
| 
 | ||||
| ## v2.1.0 (2017-03-30) | ||||
| 
 | ||||
| - Minor improvements and update to the chi core library | ||||
| - Introduced a brand new `chi/render` sub-package to complete the story of building | ||||
|   APIs to offer a pattern for managing well-defined request / response payloads. Please | ||||
|   check out the updated `_examples/rest` example for how it works. | ||||
| - Added `MethodNotAllowed(h http.HandlerFunc)` to chi.Router interface | ||||
| 
 | ||||
| 
 | ||||
| ## v2.0.0 (2017-01-06) | ||||
| 
 | ||||
| - After many months of v2 being in an RC state with many companies and users running it in | ||||
|   production, the inclusion of some improvements to the middlewares, we are very pleased to | ||||
|   announce v2.0.0 of chi. | ||||
| 
 | ||||
| 
 | ||||
| ## v2.0.0-rc1 (2016-07-26) | ||||
| 
 | ||||
| - Huge update! chi v2 is a large refactor targetting Go 1.7+. As of Go 1.7, the popular | ||||
|   community `"net/context"` package has been included in the standard library as `"context"` and | ||||
|   utilized by `"net/http"` and `http.Request` to managing deadlines, cancelation signals and other | ||||
|   request-scoped values. We're very excited about the new context addition and are proud to | ||||
|   introduce chi v2, a minimal and powerful routing package for building large HTTP services, | ||||
|   with zero external dependencies. Chi focuses on idiomatic design and encourages the use of  | ||||
|   stdlib HTTP handlers and middlwares. | ||||
| - chi v2 deprecates its `chi.Handler` interface and requires `http.Handler` or `http.HandlerFunc` | ||||
| - chi v2 stores URL routing parameters and patterns in the standard request context: `r.Context()` | ||||
| - chi v2 lower-level routing context is accessible by `chi.RouteContext(r.Context()) *chi.Context`, | ||||
|   which provides direct access to URL routing parameters, the routing path and the matching | ||||
|   routing patterns. | ||||
| - Users upgrading from chi v1 to v2, need to: | ||||
|   1. Update the old chi.Handler signature, `func(ctx context.Context, w http.ResponseWriter, r *http.Request)` to | ||||
|      the standard http.Handler: `func(w http.ResponseWriter, r *http.Request)` | ||||
|   2. Use `chi.URLParam(r *http.Request, paramKey string) string` | ||||
|      or `URLParamFromCtx(ctx context.Context, paramKey string) string` to access a url parameter value | ||||
| 
 | ||||
| 
 | ||||
| ## v1.0.0 (2016-07-01) | ||||
| 
 | ||||
| - Released chi v1 stable https://github.com/go-chi/chi/tree/v1.0.0 for Go 1.6 and older. | ||||
| 
 | ||||
| 
 | ||||
| ## v0.9.0 (2016-03-31) | ||||
| 
 | ||||
| - Reuse context objects via sync.Pool for zero-allocation routing [#33](https://github.com/go-chi/chi/pull/33) | ||||
| - BREAKING NOTE: due to subtle API changes, previously `chi.URLParams(ctx)["id"]` used to access url parameters | ||||
|   has changed to: `chi.URLParam(ctx, "id")` | ||||
							
								
								
									
										31
									
								
								vendor/github.com/go-chi/chi/CONTRIBUTING.md
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/go-chi/chi/CONTRIBUTING.md
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,31 +0,0 @@ | |||
| # Contributing | ||||
| 
 | ||||
| ## Prerequisites | ||||
| 
 | ||||
| 1. [Install Go][go-install]. | ||||
| 2. Download the sources and switch the working directory: | ||||
| 
 | ||||
|     ```bash | ||||
|     go get -u -d github.com/go-chi/chi | ||||
|     cd $GOPATH/src/github.com/go-chi/chi | ||||
|     ``` | ||||
| 
 | ||||
| ## Submitting a Pull Request | ||||
| 
 | ||||
| A typical workflow is: | ||||
| 
 | ||||
| 1. [Fork the repository.][fork] [This tip maybe also helpful.][go-fork-tip] | ||||
| 2. [Create a topic branch.][branch] | ||||
| 3. Add tests for your change. | ||||
| 4. Run `go test`. If your tests pass, return to the step 3. | ||||
| 5. Implement the change and ensure the steps from the previous step pass. | ||||
| 6. Run `goimports -w .`, to ensure the new code conforms to Go formatting guideline. | ||||
| 7. [Add, commit and push your changes.][git-help] | ||||
| 8. [Submit a pull request.][pull-req] | ||||
| 
 | ||||
| [go-install]: https://golang.org/doc/install | ||||
| [go-fork-tip]: http://blog.campoy.cat/2014/03/github-and-go-forking-pull-requests-and.html | ||||
| [fork]: https://help.github.com/articles/fork-a-repo | ||||
| [branch]: http://learn.github.com/p/branching.html | ||||
| [git-help]: https://guides.github.com | ||||
| [pull-req]: https://help.github.com/articles/using-pull-requests | ||||
							
								
								
									
										20
									
								
								vendor/github.com/go-chi/chi/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/go-chi/chi/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,20 +0,0 @@ | |||
| Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka), Google Inc. | ||||
| 
 | ||||
| MIT License | ||||
| 
 | ||||
| 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. | ||||
							
								
								
									
										438
									
								
								vendor/github.com/go-chi/chi/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										438
									
								
								vendor/github.com/go-chi/chi/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,438 +0,0 @@ | |||
| # <img alt="chi" src="https://cdn.rawgit.com/go-chi/chi/master/_examples/chi.svg" width="220" /> | ||||
| 
 | ||||
| 
 | ||||
| [![GoDoc Widget]][GoDoc] [![Travis Widget]][Travis] | ||||
| 
 | ||||
| `chi` is a lightweight, idiomatic and composable router for building Go HTTP services. It's | ||||
| especially good at helping you write large REST API services that are kept maintainable as your | ||||
| project grows and changes. `chi` is built on the new `context` package introduced in Go 1.7 to | ||||
| handle signaling, cancelation and request-scoped values across a handler chain. | ||||
| 
 | ||||
| The focus of the project has been to seek out an elegant and comfortable design for writing | ||||
| REST API servers, written during the development of the Pressly API service that powers our | ||||
| public API service, which in turn powers all of our client-side applications. | ||||
| 
 | ||||
| The key considerations of chi's design are: project structure, maintainability, standard http | ||||
| handlers (stdlib-only), developer productivity, and deconstructing a large system into many small | ||||
| parts. The core router `github.com/go-chi/chi` is quite small (less than 1000 LOC), but we've also | ||||
| included some useful/optional subpackages: [middleware](/middleware), [render](https://github.com/go-chi/render) and [docgen](https://github.com/go-chi/docgen). We hope you enjoy it too! | ||||
| 
 | ||||
| ## Install | ||||
| 
 | ||||
| `go get -u github.com/go-chi/chi` | ||||
| 
 | ||||
| 
 | ||||
| ## Features | ||||
| 
 | ||||
| * **Lightweight** - cloc'd in ~1000 LOC for the chi router | ||||
| * **Fast** - yes, see [benchmarks](#benchmarks) | ||||
| * **100% compatible with net/http** - use any http or middleware pkg in the ecosystem that is also compatible with `net/http` | ||||
| * **Designed for modular/composable APIs** - middlewares, inline middlewares, route groups and subrouter mounting | ||||
| * **Context control** - built on new `context` package, providing value chaining, cancelations and timeouts | ||||
| * **Robust** - in production at Pressly, CloudFlare, Heroku, 99Designs, and many others (see [discussion](https://github.com/go-chi/chi/issues/91)) | ||||
| * **Doc generation** - `docgen` auto-generates routing documentation from your source to JSON or Markdown | ||||
| * **No external dependencies** - plain ol' Go stdlib + net/http | ||||
| 
 | ||||
| 
 | ||||
| ## Examples | ||||
| 
 | ||||
| See [_examples/](https://github.com/go-chi/chi/blob/master/_examples/) for a variety of examples. | ||||
| 
 | ||||
| 
 | ||||
| **As easy as:** | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"github.com/go-chi/chi" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	r := chi.NewRouter() | ||||
| 	r.Get("/", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Write([]byte("welcome")) | ||||
| 	}) | ||||
| 	http.ListenAndServe(":3000", r) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| **REST Preview:** | ||||
| 
 | ||||
| Here is a little preview of how routing looks like with chi. Also take a look at the generated routing docs | ||||
| in JSON ([routes.json](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.json)) and in | ||||
| Markdown ([routes.md](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.md)). | ||||
| 
 | ||||
| I highly recommend reading the source of the [examples](https://github.com/go-chi/chi/blob/master/_examples/) listed | ||||
| above, they will show you all the features of chi and serve as a good form of documentation. | ||||
| 
 | ||||
| ```go | ||||
| import ( | ||||
|   //... | ||||
|   "context" | ||||
|   "github.com/go-chi/chi" | ||||
|   "github.com/go-chi/chi/middleware" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
|   r := chi.NewRouter() | ||||
| 
 | ||||
|   // A good base middleware stack | ||||
|   r.Use(middleware.RequestID) | ||||
|   r.Use(middleware.RealIP) | ||||
|   r.Use(middleware.Logger) | ||||
|   r.Use(middleware.Recoverer) | ||||
| 
 | ||||
|   // Set a timeout value on the request context (ctx), that will signal | ||||
|   // through ctx.Done() that the request has timed out and further | ||||
|   // processing should be stopped. | ||||
|   r.Use(middleware.Timeout(60 * time.Second)) | ||||
| 
 | ||||
|   r.Get("/", func(w http.ResponseWriter, r *http.Request) { | ||||
|     w.Write([]byte("hi")) | ||||
|   }) | ||||
| 
 | ||||
|   // RESTy routes for "articles" resource | ||||
|   r.Route("/articles", func(r chi.Router) { | ||||
|     r.With(paginate).Get("/", listArticles)                           // GET /articles | ||||
|     r.With(paginate).Get("/{month}-{day}-{year}", listArticlesByDate) // GET /articles/01-16-2017 | ||||
| 
 | ||||
|     r.Post("/", createArticle)                                        // POST /articles | ||||
|     r.Get("/search", searchArticles)                                  // GET /articles/search | ||||
| 
 | ||||
|     // Regexp url parameters: | ||||
|     r.Get("/{articleSlug:[a-z-]+}", getArticleBySlug)                // GET /articles/home-is-toronto | ||||
| 
 | ||||
|     // Subrouters: | ||||
|     r.Route("/{articleID}", func(r chi.Router) { | ||||
|       r.Use(ArticleCtx) | ||||
|       r.Get("/", getArticle)                                          // GET /articles/123 | ||||
|       r.Put("/", updateArticle)                                       // PUT /articles/123 | ||||
|       r.Delete("/", deleteArticle)                                    // DELETE /articles/123 | ||||
|     }) | ||||
|   }) | ||||
| 
 | ||||
|   // Mount the admin sub-router | ||||
|   r.Mount("/admin", adminRouter()) | ||||
| 
 | ||||
|   http.ListenAndServe(":3333", r) | ||||
| } | ||||
| 
 | ||||
| func ArticleCtx(next http.Handler) http.Handler { | ||||
|   return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
|     articleID := chi.URLParam(r, "articleID") | ||||
|     article, err := dbGetArticle(articleID) | ||||
|     if err != nil { | ||||
|       http.Error(w, http.StatusText(404), 404) | ||||
|       return | ||||
|     } | ||||
|     ctx := context.WithValue(r.Context(), "article", article) | ||||
|     next.ServeHTTP(w, r.WithContext(ctx)) | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| func getArticle(w http.ResponseWriter, r *http.Request) { | ||||
|   ctx := r.Context() | ||||
|   article, ok := ctx.Value("article").(*Article) | ||||
|   if !ok { | ||||
|     http.Error(w, http.StatusText(422), 422) | ||||
|     return | ||||
|   } | ||||
|   w.Write([]byte(fmt.Sprintf("title:%s", article.Title))) | ||||
| } | ||||
| 
 | ||||
| // A completely separate router for administrator routes | ||||
| func adminRouter() http.Handler { | ||||
|   r := chi.NewRouter() | ||||
|   r.Use(AdminOnly) | ||||
|   r.Get("/", adminIndex) | ||||
|   r.Get("/accounts", adminListAccounts) | ||||
|   return r | ||||
| } | ||||
| 
 | ||||
| func AdminOnly(next http.Handler) http.Handler { | ||||
|   return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
|     ctx := r.Context() | ||||
|     perm, ok := ctx.Value("acl.permission").(YourPermissionType) | ||||
|     if !ok || !perm.IsAdmin() { | ||||
|       http.Error(w, http.StatusText(403), 403) | ||||
|       return | ||||
|     } | ||||
|     next.ServeHTTP(w, r) | ||||
|   }) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Router design | ||||
| 
 | ||||
| chi's router is based on a kind of [Patricia Radix trie](https://en.wikipedia.org/wiki/Radix_tree). | ||||
| The router is fully compatible with `net/http`. | ||||
| 
 | ||||
| Built on top of the tree is the `Router` interface: | ||||
| 
 | ||||
| ```go | ||||
| // Router consisting of the core routing methods used by chi's Mux, | ||||
| // using only the standard net/http. | ||||
| type Router interface { | ||||
| 	http.Handler | ||||
| 	Routes | ||||
| 
 | ||||
| 	// Use appends one of more middlewares onto the Router stack. | ||||
| 	Use(middlewares ...func(http.Handler) http.Handler) | ||||
| 
 | ||||
| 	// With adds inline middlewares for an endpoint handler. | ||||
| 	With(middlewares ...func(http.Handler) http.Handler) Router | ||||
| 
 | ||||
| 	// Group adds a new inline-Router along the current routing | ||||
| 	// path, with a fresh middleware stack for the inline-Router. | ||||
| 	Group(fn func(r Router)) Router | ||||
| 
 | ||||
| 	// Route mounts a sub-Router along a `pattern`` string. | ||||
| 	Route(pattern string, fn func(r Router)) Router | ||||
| 
 | ||||
| 	// Mount attaches another http.Handler along ./pattern/* | ||||
| 	Mount(pattern string, h http.Handler) | ||||
| 
 | ||||
| 	// Handle and HandleFunc adds routes for `pattern` that matches | ||||
| 	// all HTTP methods. | ||||
| 	Handle(pattern string, h http.Handler) | ||||
| 	HandleFunc(pattern string, h http.HandlerFunc) | ||||
| 
 | ||||
| 	// Method and MethodFunc adds routes for `pattern` that matches | ||||
| 	// the `method` HTTP method. | ||||
| 	Method(method, pattern string, h http.Handler) | ||||
| 	MethodFunc(method, pattern string, h http.HandlerFunc) | ||||
| 
 | ||||
| 	// HTTP-method routing along `pattern` | ||||
| 	Connect(pattern string, h http.HandlerFunc) | ||||
| 	Delete(pattern string, h http.HandlerFunc) | ||||
| 	Get(pattern string, h http.HandlerFunc) | ||||
| 	Head(pattern string, h http.HandlerFunc) | ||||
| 	Options(pattern string, h http.HandlerFunc) | ||||
| 	Patch(pattern string, h http.HandlerFunc) | ||||
| 	Post(pattern string, h http.HandlerFunc) | ||||
| 	Put(pattern string, h http.HandlerFunc) | ||||
| 	Trace(pattern string, h http.HandlerFunc) | ||||
| 
 | ||||
| 	// NotFound defines a handler to respond whenever a route could | ||||
| 	// not be found. | ||||
| 	NotFound(h http.HandlerFunc) | ||||
| 
 | ||||
| 	// MethodNotAllowed defines a handler to respond whenever a method is | ||||
| 	// not allowed. | ||||
| 	MethodNotAllowed(h http.HandlerFunc) | ||||
| } | ||||
| 
 | ||||
| // Routes interface adds two methods for router traversal, which is also | ||||
| // used by the github.com/go-chi/docgen package to generate documentation for Routers. | ||||
| type Routes interface { | ||||
| 	// Routes returns the routing tree in an easily traversable structure. | ||||
| 	Routes() []Route | ||||
| 
 | ||||
| 	// Middlewares returns the list of middlewares in use by the router. | ||||
| 	Middlewares() Middlewares | ||||
| 
 | ||||
| 	// Match searches the routing tree for a handler that matches | ||||
| 	// the method/path - similar to routing a http request, but without | ||||
| 	// executing the handler thereafter. | ||||
| 	Match(rctx *Context, method, path string) bool | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Each routing method accepts a URL `pattern` and chain of `handlers`. The URL pattern | ||||
| supports named params (ie. `/users/{userID}`) and wildcards (ie. `/admin/*`). URL parameters | ||||
| can be fetched at runtime by calling `chi.URLParam(r, "userID")` for named parameters | ||||
| and `chi.URLParam(r, "*")` for a wildcard parameter. | ||||
| 
 | ||||
| 
 | ||||
| ### Middleware handlers | ||||
| 
 | ||||
| chi's middlewares are just stdlib net/http middleware handlers. There is nothing special | ||||
| about them, which means the router and all the tooling is designed to be compatible and | ||||
| friendly with any middleware in the community. This offers much better extensibility and reuse | ||||
| of packages and is at the heart of chi's purpose. | ||||
| 
 | ||||
| Here is an example of a standard net/http middleware handler using the new request context | ||||
| available in Go. This middleware sets a hypothetical user identifier on the request | ||||
| context and calls the next handler in the chain. | ||||
| 
 | ||||
| ```go | ||||
| // HTTP middleware setting a value on the request context | ||||
| func MyMiddleware(next http.Handler) http.Handler { | ||||
|   return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
|     ctx := context.WithValue(r.Context(), "user", "123") | ||||
|     next.ServeHTTP(w, r.WithContext(ctx)) | ||||
|   }) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ### Request handlers | ||||
| 
 | ||||
| chi uses standard net/http request handlers. This little snippet is an example of a http.Handler | ||||
| func that reads a user identifier from the request context - hypothetically, identifying | ||||
| the user sending an authenticated request, validated+set by a previous middleware handler. | ||||
| 
 | ||||
| ```go | ||||
| // HTTP handler accessing data from the request context. | ||||
| func MyRequestHandler(w http.ResponseWriter, r *http.Request) { | ||||
|   user := r.Context().Value("user").(string) | ||||
|   w.Write([]byte(fmt.Sprintf("hi %s", user))) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ### URL parameters | ||||
| 
 | ||||
| chi's router parses and stores URL parameters right onto the request context. Here is | ||||
| an example of how to access URL params in your net/http handlers. And of course, middlewares | ||||
| are able to access the same information. | ||||
| 
 | ||||
| ```go | ||||
| // HTTP handler accessing the url routing parameters. | ||||
| func MyRequestHandler(w http.ResponseWriter, r *http.Request) { | ||||
|   userID := chi.URLParam(r, "userID") // from a route like /users/{userID} | ||||
| 
 | ||||
|   ctx := r.Context() | ||||
|   key := ctx.Value("key").(string) | ||||
| 
 | ||||
|   w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key))) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Middlewares | ||||
| 
 | ||||
| chi comes equipped with an optional `middleware` package, providing a suite of standard | ||||
| `net/http` middlewares. Please note, any middleware in the ecosystem that is also compatible | ||||
| with `net/http` can be used with chi's mux. | ||||
| 
 | ||||
| ### Core middlewares | ||||
| 
 | ||||
| ----------------------------------------------------------------------------------------------------------- | ||||
| | chi/middleware Handler | description                                                                     | | ||||
| |:----------------------|:--------------------------------------------------------------------------------- | ||||
| | AllowContentType      | Explicit whitelist of accepted request Content-Types                            | | ||||
| | Compress              | Gzip compression for clients that accept compressed responses                   | | ||||
| | GetHead               | Automatically route undefined HEAD requests to GET handlers                     | | ||||
| | Heartbeat             | Monitoring endpoint to check the servers pulse                                  | | ||||
| | Logger                | Logs the start and end of each request with the elapsed processing time         | | ||||
| | NoCache               | Sets response headers to prevent clients from caching                           | | ||||
| | Profiler              | Easily attach net/http/pprof to your routers                                    | | ||||
| | RealIP                | Sets a http.Request's RemoteAddr to either X-Forwarded-For or X-Real-IP         | | ||||
| | Recoverer             | Gracefully absorb panics and prints the stack trace                             | | ||||
| | RequestID             | Injects a request ID into the context of each request                           | | ||||
| | RedirectSlashes       | Redirect slashes on routing paths                                               | | ||||
| | SetHeader             | Short-hand middleware to set a response header key/value                        | | ||||
| | StripSlashes          | Strip slashes on routing paths                                                  | | ||||
| | Throttle              | Puts a ceiling on the number of concurrent requests                             | | ||||
| | Timeout               | Signals to the request context when the timeout deadline is reached             | | ||||
| | URLFormat             | Parse extension from url and put it on request context                          | | ||||
| | WithValue             | Short-hand middleware to set a key/value on the request context                 | | ||||
| ----------------------------------------------------------------------------------------------------------- | ||||
| 
 | ||||
| ### Auxiliary middlewares & packages | ||||
| 
 | ||||
| Please see https://github.com/go-chi for additional packages. | ||||
| 
 | ||||
| -------------------------------------------------------------------------------------------------------------------- | ||||
| | package                                            | description                                                 | | ||||
| |:---------------------------------------------------|:------------------------------------------------------------- | ||||
| | [cors](https://github.com/go-chi/cors)             | Cross-origin resource sharing (CORS)                        | | ||||
| | [docgen](https://github.com/go-chi/docgen)         | Print chi.Router routes at runtime                          | | ||||
| | [jwtauth](https://github.com/go-chi/jwtauth)       | JWT authentication                                          | | ||||
| | [hostrouter](https://github.com/go-chi/hostrouter) | Domain/host based request routing                           | | ||||
| | [httpcoala](https://github.com/go-chi/httpcoala)   | HTTP request coalescer                                      | | ||||
| | [chi-authz](https://github.com/casbin/chi-authz)   | Request ACL via https://github.com/hsluoyz/casbin           | | ||||
| | [phi](https://github.com/fate-lovely/phi)          | Port chi to [fasthttp](https://github.com/valyala/fasthttp) | | ||||
| -------------------------------------------------------------------------------------------------------------------- | ||||
| 
 | ||||
| please [submit a PR](./CONTRIBUTING.md) if you'd like to include a link to a chi-compatible middleware | ||||
| 
 | ||||
| 
 | ||||
| ## context? | ||||
| 
 | ||||
| `context` is a tiny pkg that provides simple interface to signal context across call stacks | ||||
| and goroutines. It was originally written by [Sameer Ajmani](https://github.com/Sajmani) | ||||
| and is available in stdlib since go1.7. | ||||
| 
 | ||||
| Learn more at https://blog.golang.org/context | ||||
| 
 | ||||
| and.. | ||||
| * Docs: https://golang.org/pkg/context | ||||
| * Source: https://github.com/golang/go/tree/master/src/context | ||||
| 
 | ||||
| 
 | ||||
| ## Benchmarks | ||||
| 
 | ||||
| The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark | ||||
| 
 | ||||
| Results as of Jan 9, 2019 with Go 1.11.4 on Linux X1 Carbon laptop | ||||
| 
 | ||||
| ```shell | ||||
| BenchmarkChi_Param            3000000         475 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_Param5           2000000         696 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_Param20          1000000        1275 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_ParamWrite       3000000         505 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_GithubStatic     3000000         508 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_GithubParam      2000000         669 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_GithubAll          10000      134627 ns/op     87699 B/op    609 allocs/op | ||||
| BenchmarkChi_GPlusStatic      3000000         402 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_GPlusParam       3000000         500 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_GPlus2Params     3000000         586 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_GPlusAll          200000        7237 ns/op      5616 B/op     39 allocs/op | ||||
| BenchmarkChi_ParseStatic      3000000         408 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_ParseParam       3000000         488 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_Parse2Params     3000000         551 ns/op       432 B/op      3 allocs/op | ||||
| BenchmarkChi_ParseAll          100000       13508 ns/op     11232 B/op     78 allocs/op | ||||
| BenchmarkChi_StaticAll          20000       81933 ns/op     67826 B/op    471 allocs/op | ||||
| ``` | ||||
| 
 | ||||
| Comparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc | ||||
| 
 | ||||
| NOTE: the allocs in the benchmark above are from the calls to http.Request's | ||||
| `WithContext(context.Context)` method that clones the http.Request, sets the `Context()` | ||||
| on the duplicated (alloc'd) request and returns it the new request object. This is just | ||||
| how setting context on a request in Go works. | ||||
| 
 | ||||
| 
 | ||||
| ## Credits | ||||
| 
 | ||||
| * Carl Jackson for https://github.com/zenazn/goji | ||||
|   * Parts of chi's thinking comes from goji, and chi's middleware package | ||||
|     sources from goji. | ||||
| * Armon Dadgar for https://github.com/armon/go-radix | ||||
| * Contributions: [@VojtechVitek](https://github.com/VojtechVitek) | ||||
| 
 | ||||
| We'll be more than happy to see [your contributions](./CONTRIBUTING.md)! | ||||
| 
 | ||||
| 
 | ||||
| ## Beyond REST | ||||
| 
 | ||||
| chi is just a http router that lets you decompose request handling into many smaller layers. | ||||
| Many companies including Pressly.com (of course) use chi to write REST services for their public | ||||
| APIs. But, REST is just a convention for managing state via HTTP, and there's a lot of other pieces | ||||
| required to write a complete client-server system or network of microservices. | ||||
| 
 | ||||
| Looking ahead beyond REST, I also recommend some newer works in the field coming from | ||||
| [gRPC](https://github.com/grpc/grpc-go), [NATS](https://nats.io), [go-kit](https://github.com/go-kit/kit) | ||||
| and even [graphql](https://github.com/graphql-go/graphql). They're all pretty cool with their | ||||
| own unique approaches and benefits. Specifically, I'd look at gRPC since it makes client-server | ||||
| communication feel like a single program on a single computer, no need to hand-write a client library | ||||
| and the request/response payloads are typed contracts. NATS is pretty amazing too as a super | ||||
| fast and lightweight pub-sub transport that can speak protobufs, with nice service discovery - | ||||
| an excellent combination with gRPC. | ||||
| 
 | ||||
| 
 | ||||
| ## License | ||||
| 
 | ||||
| Copyright (c) 2015-present [Peter Kieltyka](https://github.com/pkieltyka) | ||||
| 
 | ||||
| Licensed under [MIT License](./LICENSE) | ||||
| 
 | ||||
| [GoDoc]: https://godoc.org/github.com/go-chi/chi | ||||
| [GoDoc Widget]: https://godoc.org/github.com/go-chi/chi?status.svg | ||||
| [Travis]: https://travis-ci.org/go-chi/chi | ||||
| [Travis Widget]: https://travis-ci.org/go-chi/chi.svg?branch=master | ||||
							
								
								
									
										49
									
								
								vendor/github.com/go-chi/chi/chain.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/go-chi/chi/chain.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,49 +0,0 @@ | |||
| package chi | ||||
| 
 | ||||
| import "net/http" | ||||
| 
 | ||||
| // Chain returns a Middlewares type from a slice of middleware handlers. | ||||
| func Chain(middlewares ...func(http.Handler) http.Handler) Middlewares { | ||||
| 	return Middlewares(middlewares) | ||||
| } | ||||
| 
 | ||||
| // Handler builds and returns a http.Handler from the chain of middlewares, | ||||
| // with `h http.Handler` as the final handler. | ||||
| func (mws Middlewares) Handler(h http.Handler) http.Handler { | ||||
| 	return &ChainHandler{mws, h, chain(mws, h)} | ||||
| } | ||||
| 
 | ||||
| // HandlerFunc builds and returns a http.Handler from the chain of middlewares, | ||||
| // with `h http.Handler` as the final handler. | ||||
| func (mws Middlewares) HandlerFunc(h http.HandlerFunc) http.Handler { | ||||
| 	return &ChainHandler{mws, h, chain(mws, h)} | ||||
| } | ||||
| 
 | ||||
| // ChainHandler is a http.Handler with support for handler composition and | ||||
| // execution. | ||||
| type ChainHandler struct { | ||||
| 	Middlewares Middlewares | ||||
| 	Endpoint    http.Handler | ||||
| 	chain       http.Handler | ||||
| } | ||||
| 
 | ||||
| func (c *ChainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	c.chain.ServeHTTP(w, r) | ||||
| } | ||||
| 
 | ||||
| // chain builds a http.Handler composed of an inline middleware stack and endpoint | ||||
| // handler in the order they are passed. | ||||
| func chain(middlewares []func(http.Handler) http.Handler, endpoint http.Handler) http.Handler { | ||||
| 	// Return ahead of time if there aren't any middlewares for the chain | ||||
| 	if len(middlewares) == 0 { | ||||
| 		return endpoint | ||||
| 	} | ||||
| 
 | ||||
| 	// Wrap the end handler with the middleware chain | ||||
| 	h := middlewares[len(middlewares)-1](endpoint) | ||||
| 	for i := len(middlewares) - 2; i >= 0; i-- { | ||||
| 		h = middlewares[i](h) | ||||
| 	} | ||||
| 
 | ||||
| 	return h | ||||
| } | ||||
							
								
								
									
										134
									
								
								vendor/github.com/go-chi/chi/chi.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										134
									
								
								vendor/github.com/go-chi/chi/chi.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,134 +0,0 @@ | |||
| // | ||||
| // Package chi is a small, idiomatic and composable router for building HTTP services. | ||||
| // | ||||
| // chi requires Go 1.7 or newer. | ||||
| // | ||||
| // Example: | ||||
| //  package main | ||||
| // | ||||
| //  import ( | ||||
| //  	"net/http" | ||||
| // | ||||
| //  	"github.com/go-chi/chi" | ||||
| //  	"github.com/go-chi/chi/middleware" | ||||
| //  ) | ||||
| // | ||||
| //  func main() { | ||||
| //  	r := chi.NewRouter() | ||||
| //  	r.Use(middleware.Logger) | ||||
| //  	r.Use(middleware.Recoverer) | ||||
| // | ||||
| //  	r.Get("/", func(w http.ResponseWriter, r *http.Request) { | ||||
| //  		w.Write([]byte("root.")) | ||||
| //  	}) | ||||
| // | ||||
| //  	http.ListenAndServe(":3333", r) | ||||
| //  } | ||||
| // | ||||
| // See github.com/go-chi/chi/_examples/ for more in-depth examples. | ||||
| // | ||||
| // URL patterns allow for easy matching of path components in HTTP | ||||
| // requests. The matching components can then be accessed using | ||||
| // chi.URLParam(). All patterns must begin with a slash. | ||||
| // | ||||
| // A simple named placeholder {name} matches any sequence of characters | ||||
| // up to the next / or the end of the URL. Trailing slashes on paths must | ||||
| // be handled explicitly. | ||||
| // | ||||
| // A placeholder with a name followed by a colon allows a regular | ||||
| // expression match, for example {number:\\d+}. The regular expression | ||||
| // syntax is Go's normal regexp RE2 syntax, except that regular expressions | ||||
| // including { or } are not supported, and / will never be | ||||
| // matched. An anonymous regexp pattern is allowed, using an empty string | ||||
| // before the colon in the placeholder, such as {:\\d+} | ||||
| // | ||||
| // The special placeholder of asterisk matches the rest of the requested | ||||
| // URL. Any trailing characters in the pattern are ignored. This is the only | ||||
| // placeholder which will match / characters. | ||||
| // | ||||
| // Examples: | ||||
| //  "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/" | ||||
| //  "/user/{name}/info" matches "/user/jsmith/info" | ||||
| //  "/page/*" matches "/page/intro/latest" | ||||
| //  "/page/*/index" also matches "/page/intro/latest" | ||||
| //  "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01" | ||||
| // | ||||
| package chi | ||||
| 
 | ||||
| import "net/http" | ||||
| 
 | ||||
| // NewRouter returns a new Mux object that implements the Router interface. | ||||
| func NewRouter() *Mux { | ||||
| 	return NewMux() | ||||
| } | ||||
| 
 | ||||
| // Router consisting of the core routing methods used by chi's Mux, | ||||
| // using only the standard net/http. | ||||
| type Router interface { | ||||
| 	http.Handler | ||||
| 	Routes | ||||
| 
 | ||||
| 	// Use appends one of more middlewares onto the Router stack. | ||||
| 	Use(middlewares ...func(http.Handler) http.Handler) | ||||
| 
 | ||||
| 	// With adds inline middlewares for an endpoint handler. | ||||
| 	With(middlewares ...func(http.Handler) http.Handler) Router | ||||
| 
 | ||||
| 	// Group adds a new inline-Router along the current routing | ||||
| 	// path, with a fresh middleware stack for the inline-Router. | ||||
| 	Group(fn func(r Router)) Router | ||||
| 
 | ||||
| 	// Route mounts a sub-Router along a `pattern`` string. | ||||
| 	Route(pattern string, fn func(r Router)) Router | ||||
| 
 | ||||
| 	// Mount attaches another http.Handler along ./pattern/* | ||||
| 	Mount(pattern string, h http.Handler) | ||||
| 
 | ||||
| 	// Handle and HandleFunc adds routes for `pattern` that matches | ||||
| 	// all HTTP methods. | ||||
| 	Handle(pattern string, h http.Handler) | ||||
| 	HandleFunc(pattern string, h http.HandlerFunc) | ||||
| 
 | ||||
| 	// Method and MethodFunc adds routes for `pattern` that matches | ||||
| 	// the `method` HTTP method. | ||||
| 	Method(method, pattern string, h http.Handler) | ||||
| 	MethodFunc(method, pattern string, h http.HandlerFunc) | ||||
| 
 | ||||
| 	// HTTP-method routing along `pattern` | ||||
| 	Connect(pattern string, h http.HandlerFunc) | ||||
| 	Delete(pattern string, h http.HandlerFunc) | ||||
| 	Get(pattern string, h http.HandlerFunc) | ||||
| 	Head(pattern string, h http.HandlerFunc) | ||||
| 	Options(pattern string, h http.HandlerFunc) | ||||
| 	Patch(pattern string, h http.HandlerFunc) | ||||
| 	Post(pattern string, h http.HandlerFunc) | ||||
| 	Put(pattern string, h http.HandlerFunc) | ||||
| 	Trace(pattern string, h http.HandlerFunc) | ||||
| 
 | ||||
| 	// NotFound defines a handler to respond whenever a route could | ||||
| 	// not be found. | ||||
| 	NotFound(h http.HandlerFunc) | ||||
| 
 | ||||
| 	// MethodNotAllowed defines a handler to respond whenever a method is | ||||
| 	// not allowed. | ||||
| 	MethodNotAllowed(h http.HandlerFunc) | ||||
| } | ||||
| 
 | ||||
| // Routes interface adds two methods for router traversal, which is also | ||||
| // used by the `docgen` subpackage to generation documentation for Routers. | ||||
| type Routes interface { | ||||
| 	// Routes returns the routing tree in an easily traversable structure. | ||||
| 	Routes() []Route | ||||
| 
 | ||||
| 	// Middlewares returns the list of middlewares in use by the router. | ||||
| 	Middlewares() Middlewares | ||||
| 
 | ||||
| 	// Match searches the routing tree for a handler that matches | ||||
| 	// the method/path - similar to routing a http request, but without | ||||
| 	// executing the handler thereafter. | ||||
| 	Match(rctx *Context, method, path string) bool | ||||
| } | ||||
| 
 | ||||
| // Middlewares type is a slice of standard middleware handlers with methods | ||||
| // to compose middleware chains and http.Handler's. | ||||
| type Middlewares []func(http.Handler) http.Handler | ||||
							
								
								
									
										161
									
								
								vendor/github.com/go-chi/chi/context.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										161
									
								
								vendor/github.com/go-chi/chi/context.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,161 +0,0 @@ | |||
| package chi | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// RouteCtxKey is the context.Context key to store the request context. | ||||
| 	RouteCtxKey = &contextKey{"RouteContext"} | ||||
| ) | ||||
| 
 | ||||
| // Context is the default routing context set on the root node of a | ||||
| // request context to track route patterns, URL parameters and | ||||
| // an optional routing path. | ||||
| type Context struct { | ||||
| 	Routes Routes | ||||
| 
 | ||||
| 	// Routing path/method override used during the route search. | ||||
| 	// See Mux#routeHTTP method. | ||||
| 	RoutePath   string | ||||
| 	RouteMethod string | ||||
| 
 | ||||
| 	// Routing pattern stack throughout the lifecycle of the request, | ||||
| 	// across all connected routers. It is a record of all matching | ||||
| 	// patterns across a stack of sub-routers. | ||||
| 	RoutePatterns []string | ||||
| 
 | ||||
| 	// URLParams are the stack of routeParams captured during the | ||||
| 	// routing lifecycle across a stack of sub-routers. | ||||
| 	URLParams RouteParams | ||||
| 
 | ||||
| 	// The endpoint routing pattern that matched the request URI path | ||||
| 	// or `RoutePath` of the current sub-router. This value will update | ||||
| 	// during the lifecycle of a request passing through a stack of | ||||
| 	// sub-routers. | ||||
| 	routePattern string | ||||
| 
 | ||||
| 	// Route parameters matched for the current sub-router. It is | ||||
| 	// intentionally unexported so it cant be tampered. | ||||
| 	routeParams RouteParams | ||||
| 
 | ||||
| 	// methodNotAllowed hint | ||||
| 	methodNotAllowed bool | ||||
| } | ||||
| 
 | ||||
| // NewRouteContext returns a new routing Context object. | ||||
| func NewRouteContext() *Context { | ||||
| 	return &Context{} | ||||
| } | ||||
| 
 | ||||
| // Reset a routing context to its initial state. | ||||
| func (x *Context) Reset() { | ||||
| 	x.Routes = nil | ||||
| 	x.RoutePath = "" | ||||
| 	x.RouteMethod = "" | ||||
| 	x.RoutePatterns = x.RoutePatterns[:0] | ||||
| 	x.URLParams.Keys = x.URLParams.Keys[:0] | ||||
| 	x.URLParams.Values = x.URLParams.Values[:0] | ||||
| 
 | ||||
| 	x.routePattern = "" | ||||
| 	x.routeParams.Keys = x.routeParams.Keys[:0] | ||||
| 	x.routeParams.Values = x.routeParams.Values[:0] | ||||
| 	x.methodNotAllowed = false | ||||
| } | ||||
| 
 | ||||
| // URLParam returns the corresponding URL parameter value from the request | ||||
| // routing context. | ||||
| func (x *Context) URLParam(key string) string { | ||||
| 	for k := len(x.URLParams.Keys) - 1; k >= 0; k-- { | ||||
| 		if x.URLParams.Keys[k] == key { | ||||
| 			return x.URLParams.Values[k] | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| // RoutePattern builds the routing pattern string for the particular | ||||
| // request, at the particular point during routing. This means, the value | ||||
| // will change throughout the execution of a request in a router. That is | ||||
| // why its advised to only use this value after calling the next handler. | ||||
| // | ||||
| // For example, | ||||
| // | ||||
| //   func Instrument(next http.Handler) http.Handler { | ||||
| //     return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| //       next.ServeHTTP(w, r) | ||||
| //       routePattern := chi.RouteContext(r.Context()).RoutePattern() | ||||
| //       measure(w, r, routePattern) | ||||
| //   	 }) | ||||
| //   } | ||||
| func (x *Context) RoutePattern() string { | ||||
| 	routePattern := strings.Join(x.RoutePatterns, "") | ||||
| 	return strings.Replace(routePattern, "/*/", "/", -1) | ||||
| } | ||||
| 
 | ||||
| // RouteContext returns chi's routing Context object from a | ||||
| // http.Request Context. | ||||
| func RouteContext(ctx context.Context) *Context { | ||||
| 	return ctx.Value(RouteCtxKey).(*Context) | ||||
| } | ||||
| 
 | ||||
| // URLParam returns the url parameter from a http.Request object. | ||||
| func URLParam(r *http.Request, key string) string { | ||||
| 	if rctx := RouteContext(r.Context()); rctx != nil { | ||||
| 		return rctx.URLParam(key) | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| // URLParamFromCtx returns the url parameter from a http.Request Context. | ||||
| func URLParamFromCtx(ctx context.Context, key string) string { | ||||
| 	if rctx := RouteContext(ctx); rctx != nil { | ||||
| 		return rctx.URLParam(key) | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| // RouteParams is a structure to track URL routing parameters efficiently. | ||||
| type RouteParams struct { | ||||
| 	Keys, Values []string | ||||
| } | ||||
| 
 | ||||
| // Add will append a URL parameter to the end of the route param | ||||
| func (s *RouteParams) Add(key, value string) { | ||||
| 	(*s).Keys = append((*s).Keys, key) | ||||
| 	(*s).Values = append((*s).Values, value) | ||||
| } | ||||
| 
 | ||||
| // ServerBaseContext wraps an http.Handler to set the request context to the | ||||
| // `baseCtx`. | ||||
| func ServerBaseContext(baseCtx context.Context, h http.Handler) http.Handler { | ||||
| 	fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		ctx := r.Context() | ||||
| 		baseCtx := baseCtx | ||||
| 
 | ||||
| 		// Copy over default net/http server context keys | ||||
| 		if v, ok := ctx.Value(http.ServerContextKey).(*http.Server); ok { | ||||
| 			baseCtx = context.WithValue(baseCtx, http.ServerContextKey, v) | ||||
| 		} | ||||
| 		if v, ok := ctx.Value(http.LocalAddrContextKey).(net.Addr); ok { | ||||
| 			baseCtx = context.WithValue(baseCtx, http.LocalAddrContextKey, v) | ||||
| 		} | ||||
| 
 | ||||
| 		h.ServeHTTP(w, r.WithContext(baseCtx)) | ||||
| 	}) | ||||
| 	return fn | ||||
| } | ||||
| 
 | ||||
| // contextKey is a value for use with context.WithValue. It's used as | ||||
| // a pointer so it fits in an interface{} without allocation. This technique | ||||
| // for defining context keys was copied from Go 1.7's new use of context in net/http. | ||||
| type contextKey struct { | ||||
| 	name string | ||||
| } | ||||
| 
 | ||||
| func (k *contextKey) String() string { | ||||
| 	return "chi context value " + k.name | ||||
| } | ||||
							
								
								
									
										275
									
								
								vendor/github.com/go-chi/chi/middleware/compress.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										275
									
								
								vendor/github.com/go-chi/chi/middleware/compress.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,275 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"compress/flate" | ||||
| 	"compress/gzip" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| var encoders = map[string]EncoderFunc{} | ||||
| 
 | ||||
| var encodingPrecedence = []string{"br", "gzip", "deflate"} | ||||
| 
 | ||||
| func init() { | ||||
| 	// TODO: | ||||
| 	// lzma: Opera. | ||||
| 	// sdch: Chrome, Android. Gzip output + dictionary header. | ||||
| 	// br:   Brotli, see https://github.com/go-chi/chi/pull/326 | ||||
| 
 | ||||
| 	// TODO: Exception for old MSIE browsers that can't handle non-HTML? | ||||
| 	// https://zoompf.com/blog/2012/02/lose-the-wait-http-compression | ||||
| 	SetEncoder("gzip", encoderGzip) | ||||
| 
 | ||||
| 	// HTTP 1.1 "deflate" (RFC 2616) stands for DEFLATE data (RFC 1951) | ||||
| 	// wrapped with zlib (RFC 1950). The zlib wrapper uses Adler-32 | ||||
| 	// checksum compared to CRC-32 used in "gzip" and thus is faster. | ||||
| 	// | ||||
| 	// But.. some old browsers (MSIE, Safari 5.1) incorrectly expect | ||||
| 	// raw DEFLATE data only, without the mentioned zlib wrapper. | ||||
| 	// Because of this major confusion, most modern browsers try it | ||||
| 	// both ways, first looking for zlib headers. | ||||
| 	// Quote by Mark Adler: http://stackoverflow.com/a/9186091/385548 | ||||
| 	// | ||||
| 	// The list of browsers having problems is quite big, see: | ||||
| 	// http://zoompf.com/blog/2012/02/lose-the-wait-http-compression | ||||
| 	// https://web.archive.org/web/20120321182910/http://www.vervestudios.co/projects/compression-tests/results | ||||
| 	// | ||||
| 	// That's why we prefer gzip over deflate. It's just more reliable | ||||
| 	// and not significantly slower than gzip. | ||||
| 	SetEncoder("deflate", encoderDeflate) | ||||
| 
 | ||||
| 	// NOTE: Not implemented, intentionally: | ||||
| 	// case "compress": // LZW. Deprecated. | ||||
| 	// case "bzip2":    // Too slow on-the-fly. | ||||
| 	// case "zopfli":   // Too slow on-the-fly. | ||||
| 	// case "xz":       // Too slow on-the-fly. | ||||
| } | ||||
| 
 | ||||
| // An EncoderFunc is a function that wraps the provided ResponseWriter with a | ||||
| // streaming compression algorithm and returns it. | ||||
| // | ||||
| // In case of failure, the function should return nil. | ||||
| type EncoderFunc func(w http.ResponseWriter, level int) io.Writer | ||||
| 
 | ||||
| // SetEncoder can be used to set the implementation of a compression algorithm. | ||||
| // | ||||
| // The encoding should be a standardised identifier. See: | ||||
| // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding | ||||
| // | ||||
| // For example, add the Brotli algortithm: | ||||
| // | ||||
| //  import brotli_enc "gopkg.in/kothar/brotli-go.v0/enc" | ||||
| // | ||||
| //  middleware.SetEncoder("br", func(w http.ResponseWriter, level int) io.Writer { | ||||
| //    params := brotli_enc.NewBrotliParams() | ||||
| //    params.SetQuality(level) | ||||
| //    return brotli_enc.NewBrotliWriter(params, w) | ||||
| //  }) | ||||
| func SetEncoder(encoding string, fn EncoderFunc) { | ||||
| 	encoding = strings.ToLower(encoding) | ||||
| 	if encoding == "" { | ||||
| 		panic("the encoding can not be empty") | ||||
| 	} | ||||
| 	if fn == nil { | ||||
| 		panic("attempted to set a nil encoder function") | ||||
| 	} | ||||
| 	encoders[encoding] = fn | ||||
| 
 | ||||
| 	var e string | ||||
| 	for _, v := range encodingPrecedence { | ||||
| 		if v == encoding { | ||||
| 			e = v | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if e == "" { | ||||
| 		encodingPrecedence = append([]string{e}, encodingPrecedence...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var defaultContentTypes = map[string]struct{}{ | ||||
| 	"text/html":                {}, | ||||
| 	"text/css":                 {}, | ||||
| 	"text/plain":               {}, | ||||
| 	"text/javascript":          {}, | ||||
| 	"application/javascript":   {}, | ||||
| 	"application/x-javascript": {}, | ||||
| 	"application/json":         {}, | ||||
| 	"application/atom+xml":     {}, | ||||
| 	"application/rss+xml":      {}, | ||||
| 	"image/svg+xml":            {}, | ||||
| } | ||||
| 
 | ||||
| // DefaultCompress is a middleware that compresses response | ||||
| // body of predefined content types to a data format based | ||||
| // on Accept-Encoding request header. It uses a default | ||||
| // compression level. | ||||
| func DefaultCompress(next http.Handler) http.Handler { | ||||
| 	return Compress(flate.DefaultCompression)(next) | ||||
| } | ||||
| 
 | ||||
| // Compress is a middleware that compresses response | ||||
| // body of a given content types to a data format based | ||||
| // on Accept-Encoding request header. It uses a given | ||||
| // compression level. | ||||
| // | ||||
| // NOTE: make sure to set the Content-Type header on your response | ||||
| // otherwise this middleware will not compress the response body. For ex, in | ||||
| // your handler you should set w.Header().Set("Content-Type", http.DetectContentType(yourBody)) | ||||
| // or set it manually. | ||||
| func Compress(level int, types ...string) func(next http.Handler) http.Handler { | ||||
| 	contentTypes := defaultContentTypes | ||||
| 	if len(types) > 0 { | ||||
| 		contentTypes = make(map[string]struct{}, len(types)) | ||||
| 		for _, t := range types { | ||||
| 			contentTypes[t] = struct{}{} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| 		fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 			encoder, encoding := selectEncoder(r.Header) | ||||
| 
 | ||||
| 			cw := &compressResponseWriter{ | ||||
| 				ResponseWriter: w, | ||||
| 				w:              w, | ||||
| 				contentTypes:   contentTypes, | ||||
| 				encoder:        encoder, | ||||
| 				encoding:       encoding, | ||||
| 				level:          level, | ||||
| 			} | ||||
| 			defer cw.Close() | ||||
| 
 | ||||
| 			next.ServeHTTP(cw, r) | ||||
| 		} | ||||
| 
 | ||||
| 		return http.HandlerFunc(fn) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func selectEncoder(h http.Header) (EncoderFunc, string) { | ||||
| 	header := h.Get("Accept-Encoding") | ||||
| 
 | ||||
| 	// Parse the names of all accepted algorithms from the header. | ||||
| 	accepted := strings.Split(strings.ToLower(header), ",") | ||||
| 
 | ||||
| 	// Find supported encoder by accepted list by precedence | ||||
| 	for _, name := range encodingPrecedence { | ||||
| 		if fn, ok := encoders[name]; ok && matchAcceptEncoding(accepted, name) { | ||||
| 			return fn, name | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// No encoder found to match the accepted encoding | ||||
| 	return nil, "" | ||||
| } | ||||
| 
 | ||||
| func matchAcceptEncoding(accepted []string, encoding string) bool { | ||||
| 	for _, v := range accepted { | ||||
| 		if strings.Index(v, encoding) >= 0 { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| type compressResponseWriter struct { | ||||
| 	http.ResponseWriter | ||||
| 	w            io.Writer | ||||
| 	encoder      EncoderFunc | ||||
| 	encoding     string | ||||
| 	contentTypes map[string]struct{} | ||||
| 	level        int | ||||
| 	wroteHeader  bool | ||||
| } | ||||
| 
 | ||||
| func (cw *compressResponseWriter) WriteHeader(code int) { | ||||
| 	if cw.wroteHeader { | ||||
| 		return | ||||
| 	} | ||||
| 	cw.wroteHeader = true | ||||
| 	defer cw.ResponseWriter.WriteHeader(code) | ||||
| 
 | ||||
| 	// Already compressed data? | ||||
| 	if cw.Header().Get("Content-Encoding") != "" { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Parse the first part of the Content-Type response header. | ||||
| 	contentType := "" | ||||
| 	parts := strings.Split(cw.Header().Get("Content-Type"), ";") | ||||
| 	if len(parts) > 0 { | ||||
| 		contentType = parts[0] | ||||
| 	} | ||||
| 
 | ||||
| 	// Is the content type compressable? | ||||
| 	if _, ok := cw.contentTypes[contentType]; !ok { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if cw.encoder != nil && cw.encoding != "" { | ||||
| 		if wr := cw.encoder(cw.ResponseWriter, cw.level); wr != nil { | ||||
| 			cw.w = wr | ||||
| 			cw.Header().Set("Content-Encoding", cw.encoding) | ||||
| 
 | ||||
| 			// The content-length after compression is unknown | ||||
| 			cw.Header().Del("Content-Length") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (cw *compressResponseWriter) Write(p []byte) (int, error) { | ||||
| 	if !cw.wroteHeader { | ||||
| 		cw.WriteHeader(http.StatusOK) | ||||
| 	} | ||||
| 
 | ||||
| 	return cw.w.Write(p) | ||||
| } | ||||
| 
 | ||||
| func (cw *compressResponseWriter) Flush() { | ||||
| 	if f, ok := cw.w.(http.Flusher); ok { | ||||
| 		f.Flush() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (cw *compressResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { | ||||
| 	if hj, ok := cw.w.(http.Hijacker); ok { | ||||
| 		return hj.Hijack() | ||||
| 	} | ||||
| 	return nil, nil, errors.New("chi/middleware: http.Hijacker is unavailable on the writer") | ||||
| } | ||||
| 
 | ||||
| func (cw *compressResponseWriter) Push(target string, opts *http.PushOptions) error { | ||||
| 	if ps, ok := cw.w.(http.Pusher); ok { | ||||
| 		return ps.Push(target, opts) | ||||
| 	} | ||||
| 	return errors.New("chi/middleware: http.Pusher is unavailable on the writer") | ||||
| } | ||||
| 
 | ||||
| func (cw *compressResponseWriter) Close() error { | ||||
| 	if c, ok := cw.w.(io.WriteCloser); ok { | ||||
| 		return c.Close() | ||||
| 	} | ||||
| 	return errors.New("chi/middleware: io.WriteCloser is unavailable on the writer") | ||||
| } | ||||
| 
 | ||||
| func encoderGzip(w http.ResponseWriter, level int) io.Writer { | ||||
| 	gw, err := gzip.NewWriterLevel(w, level) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return gw | ||||
| } | ||||
| 
 | ||||
| func encoderDeflate(w http.ResponseWriter, level int) io.Writer { | ||||
| 	dw, err := flate.NewWriter(w, level) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return dw | ||||
| } | ||||
							
								
								
									
										51
									
								
								vendor/github.com/go-chi/chi/middleware/content_charset.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								vendor/github.com/go-chi/chi/middleware/content_charset.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,51 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // ContentCharset generates a handler that writes a 415 Unsupported Media Type response if none of the charsets match. | ||||
| // An empty charset will allow requests with no Content-Type header or no specified charset. | ||||
| func ContentCharset(charsets ...string) func(next http.Handler) http.Handler { | ||||
| 	for i, c := range charsets { | ||||
| 		charsets[i] = strings.ToLower(c) | ||||
| 	} | ||||
| 
 | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 			if !contentEncoding(r.Header.Get("Content-Type"), charsets...) { | ||||
| 				w.WriteHeader(http.StatusUnsupportedMediaType) | ||||
| 				return | ||||
| 			} | ||||
| 
 | ||||
| 			next.ServeHTTP(w, r) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Check the content encoding against a list of acceptable values. | ||||
| func contentEncoding(ce string, charsets ...string) bool { | ||||
| 	_, ce = split(strings.ToLower(ce), ";") | ||||
| 	_, ce = split(ce, "charset=") | ||||
| 	ce, _ = split(ce, ";") | ||||
| 	for _, c := range charsets { | ||||
| 		if ce == c { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // Split a string in two parts, cleaning any whitespace. | ||||
| func split(str, sep string) (string, string) { | ||||
| 	var a, b string | ||||
| 	var parts = strings.SplitN(str, sep, 2) | ||||
| 	a = strings.TrimSpace(parts[0]) | ||||
| 	if len(parts) == 2 { | ||||
| 		b = strings.TrimSpace(parts[1]) | ||||
| 	} | ||||
| 
 | ||||
| 	return a, b | ||||
| } | ||||
							
								
								
									
										51
									
								
								vendor/github.com/go-chi/chi/middleware/content_type.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								vendor/github.com/go-chi/chi/middleware/content_type.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,51 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // SetHeader is a convenience handler to set a response header key/value | ||||
| func SetHeader(key, value string) func(next http.Handler) http.Handler { | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| 		fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 			w.Header().Set(key, value) | ||||
| 			next.ServeHTTP(w, r) | ||||
| 		} | ||||
| 		return http.HandlerFunc(fn) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // AllowContentType enforces a whitelist of request Content-Types otherwise responds | ||||
| // with a 415 Unsupported Media Type status. | ||||
| func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handler { | ||||
| 	cT := []string{} | ||||
| 	for _, t := range contentTypes { | ||||
| 		cT = append(cT, strings.ToLower(t)) | ||||
| 	} | ||||
| 
 | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| 		fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 			if r.ContentLength == 0 { | ||||
| 				// skip check for empty content body | ||||
| 				next.ServeHTTP(w, r) | ||||
| 				return | ||||
| 			} | ||||
| 
 | ||||
| 			s := strings.ToLower(strings.TrimSpace(r.Header.Get("Content-Type"))) | ||||
| 			if i := strings.Index(s, ";"); i > -1 { | ||||
| 				s = s[0:i] | ||||
| 			} | ||||
| 
 | ||||
| 			for _, t := range cT { | ||||
| 				if t == s { | ||||
| 					next.ServeHTTP(w, r) | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			w.WriteHeader(http.StatusUnsupportedMediaType) | ||||
| 		} | ||||
| 		return http.HandlerFunc(fn) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										39
									
								
								vendor/github.com/go-chi/chi/middleware/get_head.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/go-chi/chi/middleware/get_head.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,39 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"github.com/go-chi/chi" | ||||
| ) | ||||
| 
 | ||||
| // GetHead automatically route undefined HEAD requests to GET handlers. | ||||
| func GetHead(next http.Handler) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		if r.Method == "HEAD" { | ||||
| 			rctx := chi.RouteContext(r.Context()) | ||||
| 			routePath := rctx.RoutePath | ||||
| 			if routePath == "" { | ||||
| 				if r.URL.RawPath != "" { | ||||
| 					routePath = r.URL.RawPath | ||||
| 				} else { | ||||
| 					routePath = r.URL.Path | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// Temporary routing context to look-ahead before routing the request | ||||
| 			tctx := chi.NewRouteContext() | ||||
| 
 | ||||
| 			// Attempt to find a HEAD handler for the routing path, if not found, traverse | ||||
| 			// the router as through its a GET route, but proceed with the request | ||||
| 			// with the HEAD method. | ||||
| 			if !rctx.Routes.Match(tctx, "HEAD", routePath) { | ||||
| 				rctx.RouteMethod = "GET" | ||||
| 				rctx.RoutePath = routePath | ||||
| 				next.ServeHTTP(w, r) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		next.ServeHTTP(w, r) | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										26
									
								
								vendor/github.com/go-chi/chi/middleware/heartbeat.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/go-chi/chi/middleware/heartbeat.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,26 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Heartbeat endpoint middleware useful to setting up a path like | ||||
| // `/ping` that load balancers or uptime testing external services | ||||
| // can make a request before hitting any routes. It's also convenient | ||||
| // to place this above ACL middlewares as well. | ||||
| func Heartbeat(endpoint string) func(http.Handler) http.Handler { | ||||
| 	f := func(h http.Handler) http.Handler { | ||||
| 		fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 			if r.Method == "GET" && strings.EqualFold(r.URL.Path, endpoint) { | ||||
| 				w.Header().Set("Content-Type", "text/plain") | ||||
| 				w.WriteHeader(http.StatusOK) | ||||
| 				w.Write([]byte(".")) | ||||
| 				return | ||||
| 			} | ||||
| 			h.ServeHTTP(w, r) | ||||
| 		} | ||||
| 		return http.HandlerFunc(fn) | ||||
| 	} | ||||
| 	return f | ||||
| } | ||||
							
								
								
									
										158
									
								
								vendor/github.com/go-chi/chi/middleware/logger.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										158
									
								
								vendor/github.com/go-chi/chi/middleware/logger.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,158 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// LogEntryCtxKey is the context.Context key to store the request log entry. | ||||
| 	LogEntryCtxKey = &contextKey{"LogEntry"} | ||||
| 
 | ||||
| 	// DefaultLogger is called by the Logger middleware handler to log each request. | ||||
| 	// Its made a package-level variable so that it can be reconfigured for custom | ||||
| 	// logging configurations. | ||||
| 	DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: false}) | ||||
| ) | ||||
| 
 | ||||
| // Logger is a middleware that logs the start and end of each request, along | ||||
| // with some useful data about what was requested, what the response status was, | ||||
| // and how long it took to return. When standard output is a TTY, Logger will | ||||
| // print in color, otherwise it will print in black and white. Logger prints a | ||||
| // request ID if one is provided. | ||||
| // | ||||
| // Alternatively, look at https://github.com/pressly/lg and the `lg.RequestLogger` | ||||
| // middleware pkg. | ||||
| func Logger(next http.Handler) http.Handler { | ||||
| 	return DefaultLogger(next) | ||||
| } | ||||
| 
 | ||||
| // RequestLogger returns a logger handler using a custom LogFormatter. | ||||
| func RequestLogger(f LogFormatter) func(next http.Handler) http.Handler { | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| 		fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 			entry := f.NewLogEntry(r) | ||||
| 			ww := NewWrapResponseWriter(w, r.ProtoMajor) | ||||
| 
 | ||||
| 			t1 := time.Now() | ||||
| 			defer func() { | ||||
| 				entry.Write(ww.Status(), ww.BytesWritten(), time.Since(t1)) | ||||
| 			}() | ||||
| 
 | ||||
| 			next.ServeHTTP(ww, WithLogEntry(r, entry)) | ||||
| 		} | ||||
| 		return http.HandlerFunc(fn) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // LogFormatter initiates the beginning of a new LogEntry per request. | ||||
| // See DefaultLogFormatter for an example implementation. | ||||
| type LogFormatter interface { | ||||
| 	NewLogEntry(r *http.Request) LogEntry | ||||
| } | ||||
| 
 | ||||
| // LogEntry records the final log when a request completes. | ||||
| // See defaultLogEntry for an example implementation. | ||||
| type LogEntry interface { | ||||
| 	Write(status, bytes int, elapsed time.Duration) | ||||
| 	Panic(v interface{}, stack []byte) | ||||
| } | ||||
| 
 | ||||
| // GetLogEntry returns the in-context LogEntry for a request. | ||||
| func GetLogEntry(r *http.Request) LogEntry { | ||||
| 	entry, _ := r.Context().Value(LogEntryCtxKey).(LogEntry) | ||||
| 	return entry | ||||
| } | ||||
| 
 | ||||
| // WithLogEntry sets the in-context LogEntry for a request. | ||||
| func WithLogEntry(r *http.Request, entry LogEntry) *http.Request { | ||||
| 	r = r.WithContext(context.WithValue(r.Context(), LogEntryCtxKey, entry)) | ||||
| 	return r | ||||
| } | ||||
| 
 | ||||
| // LoggerInterface accepts printing to stdlib logger or compatible logger. | ||||
| type LoggerInterface interface { | ||||
| 	Print(v ...interface{}) | ||||
| } | ||||
| 
 | ||||
| // DefaultLogFormatter is a simple logger that implements a LogFormatter. | ||||
| type DefaultLogFormatter struct { | ||||
| 	Logger  LoggerInterface | ||||
| 	NoColor bool | ||||
| } | ||||
| 
 | ||||
| // NewLogEntry creates a new LogEntry for the request. | ||||
| func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry { | ||||
| 	useColor := !l.NoColor | ||||
| 	entry := &defaultLogEntry{ | ||||
| 		DefaultLogFormatter: l, | ||||
| 		request:             r, | ||||
| 		buf:                 &bytes.Buffer{}, | ||||
| 		useColor:            useColor, | ||||
| 	} | ||||
| 
 | ||||
| 	reqID := GetReqID(r.Context()) | ||||
| 	if reqID != "" { | ||||
| 		cW(entry.buf, useColor, nYellow, "[%s] ", reqID) | ||||
| 	} | ||||
| 	cW(entry.buf, useColor, nCyan, "\"") | ||||
| 	cW(entry.buf, useColor, bMagenta, "%s ", r.Method) | ||||
| 
 | ||||
| 	scheme := "http" | ||||
| 	if r.TLS != nil { | ||||
| 		scheme = "https" | ||||
| 	} | ||||
| 	cW(entry.buf, useColor, nCyan, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto) | ||||
| 
 | ||||
| 	entry.buf.WriteString("from ") | ||||
| 	entry.buf.WriteString(r.RemoteAddr) | ||||
| 	entry.buf.WriteString(" - ") | ||||
| 
 | ||||
| 	return entry | ||||
| } | ||||
| 
 | ||||
| type defaultLogEntry struct { | ||||
| 	*DefaultLogFormatter | ||||
| 	request  *http.Request | ||||
| 	buf      *bytes.Buffer | ||||
| 	useColor bool | ||||
| } | ||||
| 
 | ||||
| func (l *defaultLogEntry) Write(status, bytes int, elapsed time.Duration) { | ||||
| 	switch { | ||||
| 	case status < 200: | ||||
| 		cW(l.buf, l.useColor, bBlue, "%03d", status) | ||||
| 	case status < 300: | ||||
| 		cW(l.buf, l.useColor, bGreen, "%03d", status) | ||||
| 	case status < 400: | ||||
| 		cW(l.buf, l.useColor, bCyan, "%03d", status) | ||||
| 	case status < 500: | ||||
| 		cW(l.buf, l.useColor, bYellow, "%03d", status) | ||||
| 	default: | ||||
| 		cW(l.buf, l.useColor, bRed, "%03d", status) | ||||
| 	} | ||||
| 
 | ||||
| 	cW(l.buf, l.useColor, bBlue, " %dB", bytes) | ||||
| 
 | ||||
| 	l.buf.WriteString(" in ") | ||||
| 	if elapsed < 500*time.Millisecond { | ||||
| 		cW(l.buf, l.useColor, nGreen, "%s", elapsed) | ||||
| 	} else if elapsed < 5*time.Second { | ||||
| 		cW(l.buf, l.useColor, nYellow, "%s", elapsed) | ||||
| 	} else { | ||||
| 		cW(l.buf, l.useColor, nRed, "%s", elapsed) | ||||
| 	} | ||||
| 
 | ||||
| 	l.Logger.Print(l.buf.String()) | ||||
| } | ||||
| 
 | ||||
| func (l *defaultLogEntry) Panic(v interface{}, stack []byte) { | ||||
| 	panicEntry := l.NewLogEntry(l.request).(*defaultLogEntry) | ||||
| 	cW(panicEntry.buf, l.useColor, bRed, "panic: %+v", v) | ||||
| 	l.Logger.Print(panicEntry.buf.String()) | ||||
| 	l.Logger.Print(string(stack)) | ||||
| } | ||||
							
								
								
									
										12
									
								
								vendor/github.com/go-chi/chi/middleware/middleware.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/go-chi/chi/middleware/middleware.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,12 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| // contextKey is a value for use with context.WithValue. It's used as | ||||
| // a pointer so it fits in an interface{} without allocation. This technique | ||||
| // for defining context keys was copied from Go 1.7's new use of context in net/http. | ||||
| type contextKey struct { | ||||
| 	name string | ||||
| } | ||||
| 
 | ||||
| func (k *contextKey) String() string { | ||||
| 	return "chi/middleware context value " + k.name | ||||
| } | ||||
							
								
								
									
										58
									
								
								vendor/github.com/go-chi/chi/middleware/nocache.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										58
									
								
								vendor/github.com/go-chi/chi/middleware/nocache.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,58 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| // Ported from Goji's middleware, source: | ||||
| // https://github.com/zenazn/goji/tree/master/web/middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Unix epoch time | ||||
| var epoch = time.Unix(0, 0).Format(time.RFC1123) | ||||
| 
 | ||||
| // Taken from https://github.com/mytrile/nocache | ||||
| var noCacheHeaders = map[string]string{ | ||||
| 	"Expires":         epoch, | ||||
| 	"Cache-Control":   "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", | ||||
| 	"Pragma":          "no-cache", | ||||
| 	"X-Accel-Expires": "0", | ||||
| } | ||||
| 
 | ||||
| var etagHeaders = []string{ | ||||
| 	"ETag", | ||||
| 	"If-Modified-Since", | ||||
| 	"If-Match", | ||||
| 	"If-None-Match", | ||||
| 	"If-Range", | ||||
| 	"If-Unmodified-Since", | ||||
| } | ||||
| 
 | ||||
| // NoCache is a simple piece of middleware that sets a number of HTTP headers to prevent | ||||
| // a router (or subrouter) from being cached by an upstream proxy and/or client. | ||||
| // | ||||
| // As per http://wiki.nginx.org/HttpProxyModule - NoCache sets: | ||||
| //      Expires: Thu, 01 Jan 1970 00:00:00 UTC | ||||
| //      Cache-Control: no-cache, private, max-age=0 | ||||
| //      X-Accel-Expires: 0 | ||||
| //      Pragma: no-cache (for HTTP/1.0 proxies/clients) | ||||
| func NoCache(h http.Handler) http.Handler { | ||||
| 	fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 
 | ||||
| 		// Delete any ETag headers that may have been set | ||||
| 		for _, v := range etagHeaders { | ||||
| 			if r.Header.Get(v) != "" { | ||||
| 				r.Header.Del(v) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Set our NoCache headers | ||||
| 		for k, v := range noCacheHeaders { | ||||
| 			w.Header().Set(k, v) | ||||
| 		} | ||||
| 
 | ||||
| 		h.ServeHTTP(w, r) | ||||
| 	} | ||||
| 
 | ||||
| 	return http.HandlerFunc(fn) | ||||
| } | ||||
							
								
								
									
										55
									
								
								vendor/github.com/go-chi/chi/middleware/profiler.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/go-chi/chi/middleware/profiler.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,55 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"expvar" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/http/pprof" | ||||
| 
 | ||||
| 	"github.com/go-chi/chi" | ||||
| ) | ||||
| 
 | ||||
| // Profiler is a convenient subrouter used for mounting net/http/pprof. ie. | ||||
| // | ||||
| //  func MyService() http.Handler { | ||||
| //    r := chi.NewRouter() | ||||
| //    // ..middlewares | ||||
| //    r.Mount("/debug", middleware.Profiler()) | ||||
| //    // ..routes | ||||
| //    return r | ||||
| //  } | ||||
| func Profiler() http.Handler { | ||||
| 	r := chi.NewRouter() | ||||
| 	r.Use(NoCache) | ||||
| 
 | ||||
| 	r.Get("/", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		http.Redirect(w, r, r.RequestURI+"/pprof/", 301) | ||||
| 	}) | ||||
| 	r.HandleFunc("/pprof", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		http.Redirect(w, r, r.RequestURI+"/", 301) | ||||
| 	}) | ||||
| 
 | ||||
| 	r.HandleFunc("/pprof/*", pprof.Index) | ||||
| 	r.HandleFunc("/pprof/cmdline", pprof.Cmdline) | ||||
| 	r.HandleFunc("/pprof/profile", pprof.Profile) | ||||
| 	r.HandleFunc("/pprof/symbol", pprof.Symbol) | ||||
| 	r.HandleFunc("/pprof/trace", pprof.Trace) | ||||
| 	r.HandleFunc("/vars", expVars) | ||||
| 
 | ||||
| 	return r | ||||
| } | ||||
| 
 | ||||
| // Replicated from expvar.go as not public. | ||||
| func expVars(w http.ResponseWriter, r *http.Request) { | ||||
| 	first := true | ||||
| 	w.Header().Set("Content-Type", "application/json") | ||||
| 	fmt.Fprintf(w, "{\n") | ||||
| 	expvar.Do(func(kv expvar.KeyValue) { | ||||
| 		if !first { | ||||
| 			fmt.Fprintf(w, ",\n") | ||||
| 		} | ||||
| 		first = false | ||||
| 		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) | ||||
| 	}) | ||||
| 	fmt.Fprintf(w, "\n}\n") | ||||
| } | ||||
							
								
								
									
										54
									
								
								vendor/github.com/go-chi/chi/middleware/realip.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/go-chi/chi/middleware/realip.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,54 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| // Ported from Goji's middleware, source: | ||||
| // https://github.com/zenazn/goji/tree/master/web/middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") | ||||
| var xRealIP = http.CanonicalHeaderKey("X-Real-IP") | ||||
| 
 | ||||
| // RealIP is a middleware that sets a http.Request's RemoteAddr to the results | ||||
| // of parsing either the X-Forwarded-For header or the X-Real-IP header (in that | ||||
| // order). | ||||
| // | ||||
| // This middleware should be inserted fairly early in the middleware stack to | ||||
| // ensure that subsequent layers (e.g., request loggers) which examine the | ||||
| // RemoteAddr will see the intended value. | ||||
| // | ||||
| // You should only use this middleware if you can trust the headers passed to | ||||
| // you (in particular, the two headers this middleware uses), for example | ||||
| // because you have placed a reverse proxy like HAProxy or nginx in front of | ||||
| // chi. If your reverse proxies are configured to pass along arbitrary header | ||||
| // values from the client, or if you use this middleware without a reverse | ||||
| // proxy, malicious clients will be able to make you very sad (or, depending on | ||||
| // how you're using RemoteAddr, vulnerable to an attack of some sort). | ||||
| func RealIP(h http.Handler) http.Handler { | ||||
| 	fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 		if rip := realIP(r); rip != "" { | ||||
| 			r.RemoteAddr = rip | ||||
| 		} | ||||
| 		h.ServeHTTP(w, r) | ||||
| 	} | ||||
| 
 | ||||
| 	return http.HandlerFunc(fn) | ||||
| } | ||||
| 
 | ||||
| func realIP(r *http.Request) string { | ||||
| 	var ip string | ||||
| 
 | ||||
| 	if xff := r.Header.Get(xForwardedFor); xff != "" { | ||||
| 		i := strings.Index(xff, ", ") | ||||
| 		if i == -1 { | ||||
| 			i = len(xff) | ||||
| 		} | ||||
| 		ip = xff[:i] | ||||
| 	} else if xrip := r.Header.Get(xRealIP); xrip != "" { | ||||
| 		ip = xrip | ||||
| 	} | ||||
| 
 | ||||
| 	return ip | ||||
| } | ||||
							
								
								
									
										39
									
								
								vendor/github.com/go-chi/chi/middleware/recoverer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/go-chi/chi/middleware/recoverer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,39 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| // The original work was derived from Goji's middleware, source: | ||||
| // https://github.com/zenazn/goji/tree/master/web/middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"runtime/debug" | ||||
| ) | ||||
| 
 | ||||
| // Recoverer is a middleware that recovers from panics, logs the panic (and a | ||||
| // backtrace), and returns a HTTP 500 (Internal Server Error) status if | ||||
| // possible. Recoverer prints a request ID if one is provided. | ||||
| // | ||||
| // Alternatively, look at https://github.com/pressly/lg middleware pkgs. | ||||
| func Recoverer(next http.Handler) http.Handler { | ||||
| 	fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 		defer func() { | ||||
| 			if rvr := recover(); rvr != nil { | ||||
| 
 | ||||
| 				logEntry := GetLogEntry(r) | ||||
| 				if logEntry != nil { | ||||
| 					logEntry.Panic(rvr, debug.Stack()) | ||||
| 				} else { | ||||
| 					fmt.Fprintf(os.Stderr, "Panic: %+v\n", rvr) | ||||
| 					debug.PrintStack() | ||||
| 				} | ||||
| 
 | ||||
| 				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) | ||||
| 			} | ||||
| 		}() | ||||
| 
 | ||||
| 		next.ServeHTTP(w, r) | ||||
| 	} | ||||
| 
 | ||||
| 	return http.HandlerFunc(fn) | ||||
| } | ||||
							
								
								
									
										92
									
								
								vendor/github.com/go-chi/chi/middleware/request_id.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										92
									
								
								vendor/github.com/go-chi/chi/middleware/request_id.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,92 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| // Ported from Goji's middleware, source: | ||||
| // https://github.com/zenazn/goji/tree/master/web/middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/rand" | ||||
| 	"encoding/base64" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"sync/atomic" | ||||
| ) | ||||
| 
 | ||||
| // Key to use when setting the request ID. | ||||
| type ctxKeyRequestID int | ||||
| 
 | ||||
| // RequestIDKey is the key that holds the unique request ID in a request context. | ||||
| const RequestIDKey ctxKeyRequestID = 0 | ||||
| 
 | ||||
| var prefix string | ||||
| var reqid uint64 | ||||
| 
 | ||||
| // A quick note on the statistics here: we're trying to calculate the chance that | ||||
| // two randomly generated base62 prefixes will collide. We use the formula from | ||||
| // http://en.wikipedia.org/wiki/Birthday_problem | ||||
| // | ||||
| // P[m, n] \approx 1 - e^{-m^2/2n} | ||||
| // | ||||
| // We ballpark an upper bound for $m$ by imagining (for whatever reason) a server | ||||
| // that restarts every second over 10 years, for $m = 86400 * 365 * 10 = 315360000$ | ||||
| // | ||||
| // For a $k$ character base-62 identifier, we have $n(k) = 62^k$ | ||||
| // | ||||
| // Plugging this in, we find $P[m, n(10)] \approx 5.75%$, which is good enough for | ||||
| // our purposes, and is surely more than anyone would ever need in practice -- a | ||||
| // process that is rebooted a handful of times a day for a hundred years has less | ||||
| // than a millionth of a percent chance of generating two colliding IDs. | ||||
| 
 | ||||
| func init() { | ||||
| 	hostname, err := os.Hostname() | ||||
| 	if hostname == "" || err != nil { | ||||
| 		hostname = "localhost" | ||||
| 	} | ||||
| 	var buf [12]byte | ||||
| 	var b64 string | ||||
| 	for len(b64) < 10 { | ||||
| 		rand.Read(buf[:]) | ||||
| 		b64 = base64.StdEncoding.EncodeToString(buf[:]) | ||||
| 		b64 = strings.NewReplacer("+", "", "/", "").Replace(b64) | ||||
| 	} | ||||
| 
 | ||||
| 	prefix = fmt.Sprintf("%s/%s", hostname, b64[0:10]) | ||||
| } | ||||
| 
 | ||||
| // RequestID is a middleware that injects a request ID into the context of each | ||||
| // request. A request ID is a string of the form "host.example.com/random-0001", | ||||
| // where "random" is a base62 random string that uniquely identifies this go | ||||
| // process, and where the last number is an atomically incremented request | ||||
| // counter. | ||||
| func RequestID(next http.Handler) http.Handler { | ||||
| 	fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 		ctx := r.Context() | ||||
| 		requestID := r.Header.Get("X-Request-Id") | ||||
| 		if requestID == "" { | ||||
| 			myid := atomic.AddUint64(&reqid, 1) | ||||
| 			requestID = fmt.Sprintf("%s-%06d", prefix, myid) | ||||
| 		} | ||||
| 		ctx = context.WithValue(ctx, RequestIDKey, requestID) | ||||
| 		next.ServeHTTP(w, r.WithContext(ctx)) | ||||
| 	} | ||||
| 	return http.HandlerFunc(fn) | ||||
| } | ||||
| 
 | ||||
| // GetReqID returns a request ID from the given context if one is present. | ||||
| // Returns the empty string if a request ID cannot be found. | ||||
| func GetReqID(ctx context.Context) string { | ||||
| 	if ctx == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	if reqID, ok := ctx.Value(RequestIDKey).(string); ok { | ||||
| 		return reqID | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| // NextRequestID generates the next request ID in the sequence. | ||||
| func NextRequestID() uint64 { | ||||
| 	return atomic.AddUint64(&reqid, 1) | ||||
| } | ||||
							
								
								
									
										56
									
								
								vendor/github.com/go-chi/chi/middleware/strip.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										56
									
								
								vendor/github.com/go-chi/chi/middleware/strip.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,56 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"github.com/go-chi/chi" | ||||
| ) | ||||
| 
 | ||||
| // StripSlashes is a middleware that will match request paths with a trailing | ||||
| // slash, strip it from the path and continue routing through the mux, if a route | ||||
| // matches, then it will serve the handler. | ||||
| func StripSlashes(next http.Handler) http.Handler { | ||||
| 	fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 		var path string | ||||
| 		rctx := chi.RouteContext(r.Context()) | ||||
| 		if rctx.RoutePath != "" { | ||||
| 			path = rctx.RoutePath | ||||
| 		} else { | ||||
| 			path = r.URL.Path | ||||
| 		} | ||||
| 		if len(path) > 1 && path[len(path)-1] == '/' { | ||||
| 			rctx.RoutePath = path[:len(path)-1] | ||||
| 		} | ||||
| 		next.ServeHTTP(w, r) | ||||
| 	} | ||||
| 	return http.HandlerFunc(fn) | ||||
| } | ||||
| 
 | ||||
| // RedirectSlashes is a middleware that will match request paths with a trailing | ||||
| // slash and redirect to the same path, less the trailing slash. | ||||
| // | ||||
| // NOTE: RedirectSlashes middleware is *incompatible* with http.FileServer, | ||||
| // see https://github.com/go-chi/chi/issues/343 | ||||
| func RedirectSlashes(next http.Handler) http.Handler { | ||||
| 	fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 		var path string | ||||
| 		rctx := chi.RouteContext(r.Context()) | ||||
| 		if rctx.RoutePath != "" { | ||||
| 			path = rctx.RoutePath | ||||
| 		} else { | ||||
| 			path = r.URL.Path | ||||
| 		} | ||||
| 		if len(path) > 1 && path[len(path)-1] == '/' { | ||||
| 			if r.URL.RawQuery != "" { | ||||
| 				path = fmt.Sprintf("%s?%s", path[:len(path)-1], r.URL.RawQuery) | ||||
| 			} else { | ||||
| 				path = path[:len(path)-1] | ||||
| 			} | ||||
| 			http.Redirect(w, r, path, 301) | ||||
| 			return | ||||
| 		} | ||||
| 		next.ServeHTTP(w, r) | ||||
| 	} | ||||
| 	return http.HandlerFunc(fn) | ||||
| } | ||||
							
								
								
									
										63
									
								
								vendor/github.com/go-chi/chi/middleware/terminal.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								vendor/github.com/go-chi/chi/middleware/terminal.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,63 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| // Ported from Goji's middleware, source: | ||||
| // https://github.com/zenazn/goji/tree/master/web/middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// Normal colors | ||||
| 	nBlack   = []byte{'\033', '[', '3', '0', 'm'} | ||||
| 	nRed     = []byte{'\033', '[', '3', '1', 'm'} | ||||
| 	nGreen   = []byte{'\033', '[', '3', '2', 'm'} | ||||
| 	nYellow  = []byte{'\033', '[', '3', '3', 'm'} | ||||
| 	nBlue    = []byte{'\033', '[', '3', '4', 'm'} | ||||
| 	nMagenta = []byte{'\033', '[', '3', '5', 'm'} | ||||
| 	nCyan    = []byte{'\033', '[', '3', '6', 'm'} | ||||
| 	nWhite   = []byte{'\033', '[', '3', '7', 'm'} | ||||
| 	// Bright colors | ||||
| 	bBlack   = []byte{'\033', '[', '3', '0', ';', '1', 'm'} | ||||
| 	bRed     = []byte{'\033', '[', '3', '1', ';', '1', 'm'} | ||||
| 	bGreen   = []byte{'\033', '[', '3', '2', ';', '1', 'm'} | ||||
| 	bYellow  = []byte{'\033', '[', '3', '3', ';', '1', 'm'} | ||||
| 	bBlue    = []byte{'\033', '[', '3', '4', ';', '1', 'm'} | ||||
| 	bMagenta = []byte{'\033', '[', '3', '5', ';', '1', 'm'} | ||||
| 	bCyan    = []byte{'\033', '[', '3', '6', ';', '1', 'm'} | ||||
| 	bWhite   = []byte{'\033', '[', '3', '7', ';', '1', 'm'} | ||||
| 
 | ||||
| 	reset = []byte{'\033', '[', '0', 'm'} | ||||
| ) | ||||
| 
 | ||||
| var isTTY bool | ||||
| 
 | ||||
| func init() { | ||||
| 	// This is sort of cheating: if stdout is a character device, we assume | ||||
| 	// that means it's a TTY. Unfortunately, there are many non-TTY | ||||
| 	// character devices, but fortunately stdout is rarely set to any of | ||||
| 	// them. | ||||
| 	// | ||||
| 	// We could solve this properly by pulling in a dependency on | ||||
| 	// code.google.com/p/go.crypto/ssh/terminal, for instance, but as a | ||||
| 	// heuristic for whether to print in color or in black-and-white, I'd | ||||
| 	// really rather not. | ||||
| 	fi, err := os.Stdout.Stat() | ||||
| 	if err == nil { | ||||
| 		m := os.ModeDevice | os.ModeCharDevice | ||||
| 		isTTY = fi.Mode()&m == m | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // colorWrite | ||||
| func cW(w io.Writer, useColor bool, color []byte, s string, args ...interface{}) { | ||||
| 	if isTTY && useColor { | ||||
| 		w.Write(color) | ||||
| 	} | ||||
| 	fmt.Fprintf(w, s, args...) | ||||
| 	if isTTY && useColor { | ||||
| 		w.Write(reset) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										101
									
								
								vendor/github.com/go-chi/chi/middleware/throttle.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										101
									
								
								vendor/github.com/go-chi/chi/middleware/throttle.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,101 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	errCapacityExceeded = "Server capacity exceeded." | ||||
| 	errTimedOut         = "Timed out while waiting for a pending request to complete." | ||||
| 	errContextCanceled  = "Context was canceled." | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	defaultBacklogTimeout = time.Second * 60 | ||||
| ) | ||||
| 
 | ||||
| // Throttle is a middleware that limits number of currently processed requests | ||||
| // at a time. | ||||
| func Throttle(limit int) func(http.Handler) http.Handler { | ||||
| 	return ThrottleBacklog(limit, 0, defaultBacklogTimeout) | ||||
| } | ||||
| 
 | ||||
| // ThrottleBacklog is a middleware that limits number of currently processed | ||||
| // requests at a time and provides a backlog for holding a finite number of | ||||
| // pending requests. | ||||
| func ThrottleBacklog(limit int, backlogLimit int, backlogTimeout time.Duration) func(http.Handler) http.Handler { | ||||
| 	if limit < 1 { | ||||
| 		panic("chi/middleware: Throttle expects limit > 0") | ||||
| 	} | ||||
| 
 | ||||
| 	if backlogLimit < 0 { | ||||
| 		panic("chi/middleware: Throttle expects backlogLimit to be positive") | ||||
| 	} | ||||
| 
 | ||||
| 	t := throttler{ | ||||
| 		tokens:         make(chan token, limit), | ||||
| 		backlogTokens:  make(chan token, limit+backlogLimit), | ||||
| 		backlogTimeout: backlogTimeout, | ||||
| 	} | ||||
| 
 | ||||
| 	// Filling tokens. | ||||
| 	for i := 0; i < limit+backlogLimit; i++ { | ||||
| 		if i < limit { | ||||
| 			t.tokens <- token{} | ||||
| 		} | ||||
| 		t.backlogTokens <- token{} | ||||
| 	} | ||||
| 
 | ||||
| 	fn := func(h http.Handler) http.Handler { | ||||
| 		t.h = h | ||||
| 		return &t | ||||
| 	} | ||||
| 
 | ||||
| 	return fn | ||||
| } | ||||
| 
 | ||||
| // token represents a request that is being processed. | ||||
| type token struct{} | ||||
| 
 | ||||
| // throttler limits number of currently processed requests at a time. | ||||
| type throttler struct { | ||||
| 	h              http.Handler | ||||
| 	tokens         chan token | ||||
| 	backlogTokens  chan token | ||||
| 	backlogTimeout time.Duration | ||||
| } | ||||
| 
 | ||||
| // ServeHTTP is the primary throttler request handler | ||||
| func (t *throttler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	ctx := r.Context() | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		http.Error(w, errContextCanceled, http.StatusServiceUnavailable) | ||||
| 		return | ||||
| 	case btok := <-t.backlogTokens: | ||||
| 		timer := time.NewTimer(t.backlogTimeout) | ||||
| 
 | ||||
| 		defer func() { | ||||
| 			t.backlogTokens <- btok | ||||
| 		}() | ||||
| 
 | ||||
| 		select { | ||||
| 		case <-timer.C: | ||||
| 			http.Error(w, errTimedOut, http.StatusServiceUnavailable) | ||||
| 			return | ||||
| 		case <-ctx.Done(): | ||||
| 			http.Error(w, errContextCanceled, http.StatusServiceUnavailable) | ||||
| 			return | ||||
| 		case tok := <-t.tokens: | ||||
| 			defer func() { | ||||
| 				t.tokens <- tok | ||||
| 			}() | ||||
| 			t.h.ServeHTTP(w, r) | ||||
| 		} | ||||
| 		return | ||||
| 	default: | ||||
| 		http.Error(w, errCapacityExceeded, http.StatusServiceUnavailable) | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										49
									
								
								vendor/github.com/go-chi/chi/middleware/timeout.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/go-chi/chi/middleware/timeout.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,49 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Timeout is a middleware that cancels ctx after a given timeout and return | ||||
| // a 504 Gateway Timeout error to the client. | ||||
| // | ||||
| // It's required that you select the ctx.Done() channel to check for the signal | ||||
| // if the context has reached its deadline and return, otherwise the timeout | ||||
| // signal will be just ignored. | ||||
| // | ||||
| // ie. a route/handler may look like: | ||||
| // | ||||
| //  r.Get("/long", func(w http.ResponseWriter, r *http.Request) { | ||||
| // 	 ctx := r.Context() | ||||
| // 	 processTime := time.Duration(rand.Intn(4)+1) * time.Second | ||||
| // | ||||
| // 	 select { | ||||
| // 	 case <-ctx.Done(): | ||||
| // 	 	return | ||||
| // | ||||
| // 	 case <-time.After(processTime): | ||||
| // 	 	 // The above channel simulates some hard work. | ||||
| // 	 } | ||||
| // | ||||
| // 	 w.Write([]byte("done")) | ||||
| //  }) | ||||
| // | ||||
| func Timeout(timeout time.Duration) func(next http.Handler) http.Handler { | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| 		fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 			ctx, cancel := context.WithTimeout(r.Context(), timeout) | ||||
| 			defer func() { | ||||
| 				cancel() | ||||
| 				if ctx.Err() == context.DeadlineExceeded { | ||||
| 					w.WriteHeader(http.StatusGatewayTimeout) | ||||
| 				} | ||||
| 			}() | ||||
| 
 | ||||
| 			r = r.WithContext(ctx) | ||||
| 			next.ServeHTTP(w, r) | ||||
| 		} | ||||
| 		return http.HandlerFunc(fn) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										72
									
								
								vendor/github.com/go-chi/chi/middleware/url_format.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										72
									
								
								vendor/github.com/go-chi/chi/middleware/url_format.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,72 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/go-chi/chi" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// URLFormatCtxKey is the context.Context key to store the URL format data | ||||
| 	// for a request. | ||||
| 	URLFormatCtxKey = &contextKey{"URLFormat"} | ||||
| ) | ||||
| 
 | ||||
| // URLFormat is a middleware that parses the url extension from a request path and stores it | ||||
| // on the context as a string under the key `middleware.URLFormatCtxKey`. The middleware will | ||||
| // trim the suffix from the routing path and continue routing. | ||||
| // | ||||
| // Routers should not include a url parameter for the suffix when using this middleware. | ||||
| // | ||||
| // Sample usage.. for url paths: `/articles/1`, `/articles/1.json` and `/articles/1.xml` | ||||
| // | ||||
| //  func routes() http.Handler { | ||||
| //    r := chi.NewRouter() | ||||
| //    r.Use(middleware.URLFormat) | ||||
| // | ||||
| //    r.Get("/articles/{id}", ListArticles) | ||||
| // | ||||
| //    return r | ||||
| //  } | ||||
| // | ||||
| //  func ListArticles(w http.ResponseWriter, r *http.Request) { | ||||
| // 	  urlFormat, _ := r.Context().Value(middleware.URLFormatCtxKey).(string) | ||||
| // | ||||
| // 	  switch urlFormat { | ||||
| // 	  case "json": | ||||
| // 	  	render.JSON(w, r, articles) | ||||
| // 	  case "xml:" | ||||
| // 	  	render.XML(w, r, articles) | ||||
| // 	  default: | ||||
| // 	  	render.JSON(w, r, articles) | ||||
| // 	  } | ||||
| // } | ||||
| // | ||||
| func URLFormat(next http.Handler) http.Handler { | ||||
| 	fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 		ctx := r.Context() | ||||
| 
 | ||||
| 		var format string | ||||
| 		path := r.URL.Path | ||||
| 
 | ||||
| 		if strings.Index(path, ".") > 0 { | ||||
| 			base := strings.LastIndex(path, "/") | ||||
| 			idx := strings.Index(path[base:], ".") | ||||
| 
 | ||||
| 			if idx > 0 { | ||||
| 				idx += base | ||||
| 				format = path[idx+1:] | ||||
| 
 | ||||
| 				rctx := chi.RouteContext(r.Context()) | ||||
| 				rctx.RoutePath = path[:idx] | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		r = r.WithContext(context.WithValue(ctx, URLFormatCtxKey, format)) | ||||
| 
 | ||||
| 		next.ServeHTTP(w, r) | ||||
| 	} | ||||
| 	return http.HandlerFunc(fn) | ||||
| } | ||||
							
								
								
									
										17
									
								
								vendor/github.com/go-chi/chi/middleware/value.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/go-chi/chi/middleware/value.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,17 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| // WithValue is a middleware that sets a given key/value in a context chain. | ||||
| func WithValue(key interface{}, val interface{}) func(next http.Handler) http.Handler { | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| 		fn := func(w http.ResponseWriter, r *http.Request) { | ||||
| 			r = r.WithContext(context.WithValue(r.Context(), key, val)) | ||||
| 			next.ServeHTTP(w, r) | ||||
| 		} | ||||
| 		return http.HandlerFunc(fn) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										183
									
								
								vendor/github.com/go-chi/chi/middleware/wrap_writer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										183
									
								
								vendor/github.com/go-chi/chi/middleware/wrap_writer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,183 +0,0 @@ | |||
| package middleware | ||||
| 
 | ||||
| // The original work was derived from Goji's middleware, source: | ||||
| // https://github.com/zenazn/goji/tree/master/web/middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| // NewWrapResponseWriter wraps an http.ResponseWriter, returning a proxy that allows you to | ||||
| // hook into various parts of the response process. | ||||
| func NewWrapResponseWriter(w http.ResponseWriter, protoMajor int) WrapResponseWriter { | ||||
| 	_, fl := w.(http.Flusher) | ||||
| 
 | ||||
| 	bw := basicWriter{ResponseWriter: w} | ||||
| 
 | ||||
| 	if protoMajor == 2 { | ||||
| 		_, ps := w.(http.Pusher) | ||||
| 		if fl && ps { | ||||
| 			return &http2FancyWriter{bw} | ||||
| 		} | ||||
| 	} else { | ||||
| 		_, hj := w.(http.Hijacker) | ||||
| 		_, rf := w.(io.ReaderFrom) | ||||
| 		if fl && hj && rf { | ||||
| 			return &httpFancyWriter{bw} | ||||
| 		} | ||||
| 	} | ||||
| 	if fl { | ||||
| 		return &flushWriter{bw} | ||||
| 	} | ||||
| 
 | ||||
| 	return &bw | ||||
| } | ||||
| 
 | ||||
| // WrapResponseWriter is a proxy around an http.ResponseWriter that allows you to hook | ||||
| // into various parts of the response process. | ||||
| type WrapResponseWriter interface { | ||||
| 	http.ResponseWriter | ||||
| 	// Status returns the HTTP status of the request, or 0 if one has not | ||||
| 	// yet been sent. | ||||
| 	Status() int | ||||
| 	// BytesWritten returns the total number of bytes sent to the client. | ||||
| 	BytesWritten() int | ||||
| 	// Tee causes the response body to be written to the given io.Writer in | ||||
| 	// addition to proxying the writes through. Only one io.Writer can be | ||||
| 	// tee'd to at once: setting a second one will overwrite the first. | ||||
| 	// Writes will be sent to the proxy before being written to this | ||||
| 	// io.Writer. It is illegal for the tee'd writer to be modified | ||||
| 	// concurrently with writes. | ||||
| 	Tee(io.Writer) | ||||
| 	// Unwrap returns the original proxied target. | ||||
| 	Unwrap() http.ResponseWriter | ||||
| } | ||||
| 
 | ||||
| // basicWriter wraps a http.ResponseWriter that implements the minimal | ||||
| // http.ResponseWriter interface. | ||||
| type basicWriter struct { | ||||
| 	http.ResponseWriter | ||||
| 	wroteHeader bool | ||||
| 	code        int | ||||
| 	bytes       int | ||||
| 	tee         io.Writer | ||||
| } | ||||
| 
 | ||||
| func (b *basicWriter) WriteHeader(code int) { | ||||
| 	if !b.wroteHeader { | ||||
| 		b.code = code | ||||
| 		b.wroteHeader = true | ||||
| 		b.ResponseWriter.WriteHeader(code) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (b *basicWriter) Write(buf []byte) (int, error) { | ||||
| 	b.WriteHeader(http.StatusOK) | ||||
| 	n, err := b.ResponseWriter.Write(buf) | ||||
| 	if b.tee != nil { | ||||
| 		_, err2 := b.tee.Write(buf[:n]) | ||||
| 		// Prefer errors generated by the proxied writer. | ||||
| 		if err == nil { | ||||
| 			err = err2 | ||||
| 		} | ||||
| 	} | ||||
| 	b.bytes += n | ||||
| 	return n, err | ||||
| } | ||||
| 
 | ||||
| func (b *basicWriter) maybeWriteHeader() { | ||||
| 	if !b.wroteHeader { | ||||
| 		b.WriteHeader(http.StatusOK) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (b *basicWriter) Status() int { | ||||
| 	return b.code | ||||
| } | ||||
| 
 | ||||
| func (b *basicWriter) BytesWritten() int { | ||||
| 	return b.bytes | ||||
| } | ||||
| 
 | ||||
| func (b *basicWriter) Tee(w io.Writer) { | ||||
| 	b.tee = w | ||||
| } | ||||
| 
 | ||||
| func (b *basicWriter) Unwrap() http.ResponseWriter { | ||||
| 	return b.ResponseWriter | ||||
| } | ||||
| 
 | ||||
| type flushWriter struct { | ||||
| 	basicWriter | ||||
| } | ||||
| 
 | ||||
| func (f *flushWriter) Flush() { | ||||
| 	f.wroteHeader = true | ||||
| 
 | ||||
| 	fl := f.basicWriter.ResponseWriter.(http.Flusher) | ||||
| 	fl.Flush() | ||||
| } | ||||
| 
 | ||||
| var _ http.Flusher = &flushWriter{} | ||||
| 
 | ||||
| // httpFancyWriter is a HTTP writer that additionally satisfies | ||||
| // http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case | ||||
| // of wrapping the http.ResponseWriter that package http gives you, in order to | ||||
| // make the proxied object support the full method set of the proxied object. | ||||
| type httpFancyWriter struct { | ||||
| 	basicWriter | ||||
| } | ||||
| 
 | ||||
| func (f *httpFancyWriter) Flush() { | ||||
| 	f.wroteHeader = true | ||||
| 
 | ||||
| 	fl := f.basicWriter.ResponseWriter.(http.Flusher) | ||||
| 	fl.Flush() | ||||
| } | ||||
| 
 | ||||
| func (f *httpFancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { | ||||
| 	hj := f.basicWriter.ResponseWriter.(http.Hijacker) | ||||
| 	return hj.Hijack() | ||||
| } | ||||
| 
 | ||||
| func (f *http2FancyWriter) Push(target string, opts *http.PushOptions) error { | ||||
| 	return f.basicWriter.ResponseWriter.(http.Pusher).Push(target, opts) | ||||
| } | ||||
| 
 | ||||
| func (f *httpFancyWriter) ReadFrom(r io.Reader) (int64, error) { | ||||
| 	if f.basicWriter.tee != nil { | ||||
| 		n, err := io.Copy(&f.basicWriter, r) | ||||
| 		f.basicWriter.bytes += int(n) | ||||
| 		return n, err | ||||
| 	} | ||||
| 	rf := f.basicWriter.ResponseWriter.(io.ReaderFrom) | ||||
| 	f.basicWriter.maybeWriteHeader() | ||||
| 	n, err := rf.ReadFrom(r) | ||||
| 	f.basicWriter.bytes += int(n) | ||||
| 	return n, err | ||||
| } | ||||
| 
 | ||||
| var _ http.Flusher = &httpFancyWriter{} | ||||
| var _ http.Hijacker = &httpFancyWriter{} | ||||
| var _ http.Pusher = &http2FancyWriter{} | ||||
| var _ io.ReaderFrom = &httpFancyWriter{} | ||||
| 
 | ||||
| // http2FancyWriter is a HTTP2 writer that additionally satisfies | ||||
| // http.Flusher, and io.ReaderFrom. It exists for the common case | ||||
| // of wrapping the http.ResponseWriter that package http gives you, in order to | ||||
| // make the proxied object support the full method set of the proxied object. | ||||
| type http2FancyWriter struct { | ||||
| 	basicWriter | ||||
| } | ||||
| 
 | ||||
| func (f *http2FancyWriter) Flush() { | ||||
| 	f.wroteHeader = true | ||||
| 
 | ||||
| 	fl := f.basicWriter.ResponseWriter.(http.Flusher) | ||||
| 	fl.Flush() | ||||
| } | ||||
| 
 | ||||
| var _ http.Flusher = &http2FancyWriter{} | ||||
							
								
								
									
										460
									
								
								vendor/github.com/go-chi/chi/mux.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										460
									
								
								vendor/github.com/go-chi/chi/mux.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,460 +0,0 @@ | |||
| package chi | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| var _ Router = &Mux{} | ||||
| 
 | ||||
| // Mux is a simple HTTP route multiplexer that parses a request path, | ||||
| // records any URL params, and executes an end handler. It implements | ||||
| // the http.Handler interface and is friendly with the standard library. | ||||
| // | ||||
| // Mux is designed to be fast, minimal and offer a powerful API for building | ||||
| // modular and composable HTTP services with a large set of handlers. It's | ||||
| // particularly useful for writing large REST API services that break a handler | ||||
| // into many smaller parts composed of middlewares and end handlers. | ||||
| type Mux struct { | ||||
| 	// The radix trie router | ||||
| 	tree *node | ||||
| 
 | ||||
| 	// The middleware stack | ||||
| 	middlewares []func(http.Handler) http.Handler | ||||
| 
 | ||||
| 	// Controls the behaviour of middleware chain generation when a mux | ||||
| 	// is registered as an inline group inside another mux. | ||||
| 	inline bool | ||||
| 	parent *Mux | ||||
| 
 | ||||
| 	// The computed mux handler made of the chained middleware stack and | ||||
| 	// the tree router | ||||
| 	handler http.Handler | ||||
| 
 | ||||
| 	// Routing context pool | ||||
| 	pool *sync.Pool | ||||
| 
 | ||||
| 	// Custom route not found handler | ||||
| 	notFoundHandler http.HandlerFunc | ||||
| 
 | ||||
| 	// Custom method not allowed handler | ||||
| 	methodNotAllowedHandler http.HandlerFunc | ||||
| } | ||||
| 
 | ||||
| // NewMux returns a newly initialized Mux object that implements the Router | ||||
| // interface. | ||||
| func NewMux() *Mux { | ||||
| 	mux := &Mux{tree: &node{}, pool: &sync.Pool{}} | ||||
| 	mux.pool.New = func() interface{} { | ||||
| 		return NewRouteContext() | ||||
| 	} | ||||
| 	return mux | ||||
| } | ||||
| 
 | ||||
| // ServeHTTP is the single method of the http.Handler interface that makes | ||||
| // Mux interoperable with the standard library. It uses a sync.Pool to get and | ||||
| // reuse routing contexts for each request. | ||||
| func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Ensure the mux has some routes defined on the mux | ||||
| 	if mx.handler == nil { | ||||
| 		mx.NotFoundHandler().ServeHTTP(w, r) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if a routing context already exists from a parent router. | ||||
| 	rctx, _ := r.Context().Value(RouteCtxKey).(*Context) | ||||
| 	if rctx != nil { | ||||
| 		mx.handler.ServeHTTP(w, r) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Fetch a RouteContext object from the sync pool, and call the computed | ||||
| 	// mx.handler that is comprised of mx.middlewares + mx.routeHTTP. | ||||
| 	// Once the request is finished, reset the routing context and put it back | ||||
| 	// into the pool for reuse from another request. | ||||
| 	rctx = mx.pool.Get().(*Context) | ||||
| 	rctx.Reset() | ||||
| 	rctx.Routes = mx | ||||
| 	r = r.WithContext(context.WithValue(r.Context(), RouteCtxKey, rctx)) | ||||
| 	mx.handler.ServeHTTP(w, r) | ||||
| 	mx.pool.Put(rctx) | ||||
| } | ||||
| 
 | ||||
| // Use appends a middleware handler to the Mux middleware stack. | ||||
| // | ||||
| // The middleware stack for any Mux will execute before searching for a matching | ||||
| // route to a specific handler, which provides opportunity to respond early, | ||||
| // change the course of the request execution, or set request-scoped values for | ||||
| // the next http.Handler. | ||||
| func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler) { | ||||
| 	if mx.handler != nil { | ||||
| 		panic("chi: all middlewares must be defined before routes on a mux") | ||||
| 	} | ||||
| 	mx.middlewares = append(mx.middlewares, middlewares...) | ||||
| } | ||||
| 
 | ||||
| // Handle adds the route `pattern` that matches any http method to | ||||
| // execute the `handler` http.Handler. | ||||
| func (mx *Mux) Handle(pattern string, handler http.Handler) { | ||||
| 	mx.handle(mALL, pattern, handler) | ||||
| } | ||||
| 
 | ||||
| // HandleFunc adds the route `pattern` that matches any http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) HandleFunc(pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.handle(mALL, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // Method adds the route `pattern` that matches `method` http method to | ||||
| // execute the `handler` http.Handler. | ||||
| func (mx *Mux) Method(method, pattern string, handler http.Handler) { | ||||
| 	m, ok := methodMap[strings.ToUpper(method)] | ||||
| 	if !ok { | ||||
| 		panic(fmt.Sprintf("chi: '%s' http method is not supported.", method)) | ||||
| 	} | ||||
| 	mx.handle(m, pattern, handler) | ||||
| } | ||||
| 
 | ||||
| // MethodFunc adds the route `pattern` that matches `method` http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) MethodFunc(method, pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.Method(method, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // Connect adds the route `pattern` that matches a CONNECT http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) Connect(pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.handle(mCONNECT, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // Delete adds the route `pattern` that matches a DELETE http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) Delete(pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.handle(mDELETE, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // Get adds the route `pattern` that matches a GET http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) Get(pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.handle(mGET, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // Head adds the route `pattern` that matches a HEAD http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) Head(pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.handle(mHEAD, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // Options adds the route `pattern` that matches a OPTIONS http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) Options(pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.handle(mOPTIONS, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // Patch adds the route `pattern` that matches a PATCH http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) Patch(pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.handle(mPATCH, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // Post adds the route `pattern` that matches a POST http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) Post(pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.handle(mPOST, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // Put adds the route `pattern` that matches a PUT http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) Put(pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.handle(mPUT, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // Trace adds the route `pattern` that matches a TRACE http method to | ||||
| // execute the `handlerFn` http.HandlerFunc. | ||||
| func (mx *Mux) Trace(pattern string, handlerFn http.HandlerFunc) { | ||||
| 	mx.handle(mTRACE, pattern, handlerFn) | ||||
| } | ||||
| 
 | ||||
| // NotFound sets a custom http.HandlerFunc for routing paths that could | ||||
| // not be found. The default 404 handler is `http.NotFound`. | ||||
| func (mx *Mux) NotFound(handlerFn http.HandlerFunc) { | ||||
| 	// Build NotFound handler chain | ||||
| 	m := mx | ||||
| 	hFn := handlerFn | ||||
| 	if mx.inline && mx.parent != nil { | ||||
| 		m = mx.parent | ||||
| 		hFn = Chain(mx.middlewares...).HandlerFunc(hFn).ServeHTTP | ||||
| 	} | ||||
| 
 | ||||
| 	// Update the notFoundHandler from this point forward | ||||
| 	m.notFoundHandler = hFn | ||||
| 	m.updateSubRoutes(func(subMux *Mux) { | ||||
| 		if subMux.notFoundHandler == nil { | ||||
| 			subMux.NotFound(hFn) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // MethodNotAllowed sets a custom http.HandlerFunc for routing paths where the | ||||
| // method is unresolved. The default handler returns a 405 with an empty body. | ||||
| func (mx *Mux) MethodNotAllowed(handlerFn http.HandlerFunc) { | ||||
| 	// Build MethodNotAllowed handler chain | ||||
| 	m := mx | ||||
| 	hFn := handlerFn | ||||
| 	if mx.inline && mx.parent != nil { | ||||
| 		m = mx.parent | ||||
| 		hFn = Chain(mx.middlewares...).HandlerFunc(hFn).ServeHTTP | ||||
| 	} | ||||
| 
 | ||||
| 	// Update the methodNotAllowedHandler from this point forward | ||||
| 	m.methodNotAllowedHandler = hFn | ||||
| 	m.updateSubRoutes(func(subMux *Mux) { | ||||
| 		if subMux.methodNotAllowedHandler == nil { | ||||
| 			subMux.MethodNotAllowed(hFn) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // With adds inline middlewares for an endpoint handler. | ||||
| func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router { | ||||
| 	// Similarly as in handle(), we must build the mux handler once further | ||||
| 	// middleware registration isn't allowed for this stack, like now. | ||||
| 	if !mx.inline && mx.handler == nil { | ||||
| 		mx.buildRouteHandler() | ||||
| 	} | ||||
| 
 | ||||
| 	// Copy middlewares from parent inline muxs | ||||
| 	var mws Middlewares | ||||
| 	if mx.inline { | ||||
| 		mws = make(Middlewares, len(mx.middlewares)) | ||||
| 		copy(mws, mx.middlewares) | ||||
| 	} | ||||
| 	mws = append(mws, middlewares...) | ||||
| 
 | ||||
| 	im := &Mux{pool: mx.pool, inline: true, parent: mx, tree: mx.tree, middlewares: mws} | ||||
| 
 | ||||
| 	return im | ||||
| } | ||||
| 
 | ||||
| // Group creates a new inline-Mux with a fresh middleware stack. It's useful | ||||
| // for a group of handlers along the same routing path that use an additional | ||||
| // set of middlewares. See _examples/. | ||||
| func (mx *Mux) Group(fn func(r Router)) Router { | ||||
| 	im := mx.With().(*Mux) | ||||
| 	if fn != nil { | ||||
| 		fn(im) | ||||
| 	} | ||||
| 	return im | ||||
| } | ||||
| 
 | ||||
| // Route creates a new Mux with a fresh middleware stack and mounts it | ||||
| // along the `pattern` as a subrouter. Effectively, this is a short-hand | ||||
| // call to Mount. See _examples/. | ||||
| func (mx *Mux) Route(pattern string, fn func(r Router)) Router { | ||||
| 	subRouter := NewRouter() | ||||
| 	if fn != nil { | ||||
| 		fn(subRouter) | ||||
| 	} | ||||
| 	mx.Mount(pattern, subRouter) | ||||
| 	return subRouter | ||||
| } | ||||
| 
 | ||||
| // Mount attaches another http.Handler or chi Router as a subrouter along a routing | ||||
| // path. It's very useful to split up a large API as many independent routers and | ||||
| // compose them as a single service using Mount. See _examples/. | ||||
| // | ||||
| // Note that Mount() simply sets a wildcard along the `pattern` that will continue | ||||
| // routing at the `handler`, which in most cases is another chi.Router. As a result, | ||||
| // if you define two Mount() routes on the exact same pattern the mount will panic. | ||||
| func (mx *Mux) Mount(pattern string, handler http.Handler) { | ||||
| 	// Provide runtime safety for ensuring a pattern isn't mounted on an existing | ||||
| 	// routing pattern. | ||||
| 	if mx.tree.findPattern(pattern+"*") || mx.tree.findPattern(pattern+"/*") { | ||||
| 		panic(fmt.Sprintf("chi: attempting to Mount() a handler on an existing path, '%s'", pattern)) | ||||
| 	} | ||||
| 
 | ||||
| 	// Assign sub-Router's with the parent not found & method not allowed handler if not specified. | ||||
| 	subr, ok := handler.(*Mux) | ||||
| 	if ok && subr.notFoundHandler == nil && mx.notFoundHandler != nil { | ||||
| 		subr.NotFound(mx.notFoundHandler) | ||||
| 	} | ||||
| 	if ok && subr.methodNotAllowedHandler == nil && mx.methodNotAllowedHandler != nil { | ||||
| 		subr.MethodNotAllowed(mx.methodNotAllowedHandler) | ||||
| 	} | ||||
| 
 | ||||
| 	// Wrap the sub-router in a handlerFunc to scope the request path for routing. | ||||
| 	mountHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		rctx := RouteContext(r.Context()) | ||||
| 		rctx.RoutePath = mx.nextRoutePath(rctx) | ||||
| 		handler.ServeHTTP(w, r) | ||||
| 	}) | ||||
| 
 | ||||
| 	if pattern == "" || pattern[len(pattern)-1] != '/' { | ||||
| 		mx.handle(mALL|mSTUB, pattern, mountHandler) | ||||
| 		mx.handle(mALL|mSTUB, pattern+"/", mountHandler) | ||||
| 		pattern += "/" | ||||
| 	} | ||||
| 
 | ||||
| 	method := mALL | ||||
| 	subroutes, _ := handler.(Routes) | ||||
| 	if subroutes != nil { | ||||
| 		method |= mSTUB | ||||
| 	} | ||||
| 	n := mx.handle(method, pattern+"*", mountHandler) | ||||
| 
 | ||||
| 	if subroutes != nil { | ||||
| 		n.subroutes = subroutes | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Routes returns a slice of routing information from the tree, | ||||
| // useful for traversing available routes of a router. | ||||
| func (mx *Mux) Routes() []Route { | ||||
| 	return mx.tree.routes() | ||||
| } | ||||
| 
 | ||||
| // Middlewares returns a slice of middleware handler functions. | ||||
| func (mx *Mux) Middlewares() Middlewares { | ||||
| 	return mx.middlewares | ||||
| } | ||||
| 
 | ||||
| // Match searches the routing tree for a handler that matches the method/path. | ||||
| // It's similar to routing a http request, but without executing the handler | ||||
| // thereafter. | ||||
| // | ||||
| // Note: the *Context state is updated during execution, so manage | ||||
| // the state carefully or make a NewRouteContext(). | ||||
| func (mx *Mux) Match(rctx *Context, method, path string) bool { | ||||
| 	m, ok := methodMap[method] | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	node, _, h := mx.tree.FindRoute(rctx, m, path) | ||||
| 
 | ||||
| 	if node != nil && node.subroutes != nil { | ||||
| 		rctx.RoutePath = mx.nextRoutePath(rctx) | ||||
| 		return node.subroutes.Match(rctx, method, rctx.RoutePath) | ||||
| 	} | ||||
| 
 | ||||
| 	return h != nil | ||||
| } | ||||
| 
 | ||||
| // NotFoundHandler returns the default Mux 404 responder whenever a route | ||||
| // cannot be found. | ||||
| func (mx *Mux) NotFoundHandler() http.HandlerFunc { | ||||
| 	if mx.notFoundHandler != nil { | ||||
| 		return mx.notFoundHandler | ||||
| 	} | ||||
| 	return http.NotFound | ||||
| } | ||||
| 
 | ||||
| // MethodNotAllowedHandler returns the default Mux 405 responder whenever | ||||
| // a method cannot be resolved for a route. | ||||
| func (mx *Mux) MethodNotAllowedHandler() http.HandlerFunc { | ||||
| 	if mx.methodNotAllowedHandler != nil { | ||||
| 		return mx.methodNotAllowedHandler | ||||
| 	} | ||||
| 	return methodNotAllowedHandler | ||||
| } | ||||
| 
 | ||||
| // buildRouteHandler builds the single mux handler that is a chain of the middleware | ||||
| // stack, as defined by calls to Use(), and the tree router (Mux) itself. After this | ||||
| // point, no other middlewares can be registered on this Mux's stack. But you can still | ||||
| // compose additional middlewares via Group()'s or using a chained middleware handler. | ||||
| func (mx *Mux) buildRouteHandler() { | ||||
| 	mx.handler = chain(mx.middlewares, http.HandlerFunc(mx.routeHTTP)) | ||||
| } | ||||
| 
 | ||||
| // handle registers a http.Handler in the routing tree for a particular http method | ||||
| // and routing pattern. | ||||
| func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *node { | ||||
| 	if len(pattern) == 0 || pattern[0] != '/' { | ||||
| 		panic(fmt.Sprintf("chi: routing pattern must begin with '/' in '%s'", pattern)) | ||||
| 	} | ||||
| 
 | ||||
| 	// Build the final routing handler for this Mux. | ||||
| 	if !mx.inline && mx.handler == nil { | ||||
| 		mx.buildRouteHandler() | ||||
| 	} | ||||
| 
 | ||||
| 	// Build endpoint handler with inline middlewares for the route | ||||
| 	var h http.Handler | ||||
| 	if mx.inline { | ||||
| 		mx.handler = http.HandlerFunc(mx.routeHTTP) | ||||
| 		h = Chain(mx.middlewares...).Handler(handler) | ||||
| 	} else { | ||||
| 		h = handler | ||||
| 	} | ||||
| 
 | ||||
| 	// Add the endpoint to the tree and return the node | ||||
| 	return mx.tree.InsertRoute(method, pattern, h) | ||||
| } | ||||
| 
 | ||||
| // routeHTTP routes a http.Request through the Mux routing tree to serve | ||||
| // the matching handler for a particular http method. | ||||
| func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Grab the route context object | ||||
| 	rctx := r.Context().Value(RouteCtxKey).(*Context) | ||||
| 
 | ||||
| 	// The request routing path | ||||
| 	routePath := rctx.RoutePath | ||||
| 	if routePath == "" { | ||||
| 		if r.URL.RawPath != "" { | ||||
| 			routePath = r.URL.RawPath | ||||
| 		} else { | ||||
| 			routePath = r.URL.Path | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if method is supported by chi | ||||
| 	if rctx.RouteMethod == "" { | ||||
| 		rctx.RouteMethod = r.Method | ||||
| 	} | ||||
| 	method, ok := methodMap[rctx.RouteMethod] | ||||
| 	if !ok { | ||||
| 		mx.MethodNotAllowedHandler().ServeHTTP(w, r) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Find the route | ||||
| 	if _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil { | ||||
| 		h.ServeHTTP(w, r) | ||||
| 		return | ||||
| 	} | ||||
| 	if rctx.methodNotAllowed { | ||||
| 		mx.MethodNotAllowedHandler().ServeHTTP(w, r) | ||||
| 	} else { | ||||
| 		mx.NotFoundHandler().ServeHTTP(w, r) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (mx *Mux) nextRoutePath(rctx *Context) string { | ||||
| 	routePath := "/" | ||||
| 	nx := len(rctx.routeParams.Keys) - 1 // index of last param in list | ||||
| 	if nx >= 0 && rctx.routeParams.Keys[nx] == "*" && len(rctx.routeParams.Values) > nx { | ||||
| 		routePath += rctx.routeParams.Values[nx] | ||||
| 	} | ||||
| 	return routePath | ||||
| } | ||||
| 
 | ||||
| // Recursively update data on child routers. | ||||
| func (mx *Mux) updateSubRoutes(fn func(subMux *Mux)) { | ||||
| 	for _, r := range mx.tree.routes() { | ||||
| 		subMux, ok := r.SubRoutes.(*Mux) | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		fn(subMux) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // methodNotAllowedHandler is a helper function to respond with a 405, | ||||
| // method not allowed. | ||||
| func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) { | ||||
| 	w.WriteHeader(405) | ||||
| 	w.Write(nil) | ||||
| } | ||||
							
								
								
									
										846
									
								
								vendor/github.com/go-chi/chi/tree.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										846
									
								
								vendor/github.com/go-chi/chi/tree.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,846 +0,0 @@ | |||
| package chi | ||||
| 
 | ||||
| // Radix tree implementation below is a based on the original work by | ||||
| // Armon Dadgar in https://github.com/armon/go-radix/blob/master/radix.go | ||||
| // (MIT licensed). It's been heavily modified for use as a HTTP routing tree. | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"net/http" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| type methodTyp int | ||||
| 
 | ||||
| const ( | ||||
| 	mSTUB methodTyp = 1 << iota | ||||
| 	mCONNECT | ||||
| 	mDELETE | ||||
| 	mGET | ||||
| 	mHEAD | ||||
| 	mOPTIONS | ||||
| 	mPATCH | ||||
| 	mPOST | ||||
| 	mPUT | ||||
| 	mTRACE | ||||
| ) | ||||
| 
 | ||||
| var mALL = mCONNECT | mDELETE | mGET | mHEAD | | ||||
| 	mOPTIONS | mPATCH | mPOST | mPUT | mTRACE | ||||
| 
 | ||||
| var methodMap = map[string]methodTyp{ | ||||
| 	http.MethodConnect: mCONNECT, | ||||
| 	http.MethodDelete:  mDELETE, | ||||
| 	http.MethodGet:     mGET, | ||||
| 	http.MethodHead:    mHEAD, | ||||
| 	http.MethodOptions: mOPTIONS, | ||||
| 	http.MethodPatch:   mPATCH, | ||||
| 	http.MethodPost:    mPOST, | ||||
| 	http.MethodPut:     mPUT, | ||||
| 	http.MethodTrace:   mTRACE, | ||||
| } | ||||
| 
 | ||||
| // RegisterMethod adds support for custom HTTP method handlers, available | ||||
| // via Router#Method and Router#MethodFunc | ||||
| func RegisterMethod(method string) { | ||||
| 	if method == "" { | ||||
| 		return | ||||
| 	} | ||||
| 	method = strings.ToUpper(method) | ||||
| 	if _, ok := methodMap[method]; ok { | ||||
| 		return | ||||
| 	} | ||||
| 	n := len(methodMap) | ||||
| 	if n > strconv.IntSize { | ||||
| 		panic(fmt.Sprintf("chi: max number of methods reached (%d)", strconv.IntSize)) | ||||
| 	} | ||||
| 	mt := methodTyp(math.Exp2(float64(n))) | ||||
| 	methodMap[method] = mt | ||||
| 	mALL |= mt | ||||
| } | ||||
| 
 | ||||
| type nodeTyp uint8 | ||||
| 
 | ||||
| const ( | ||||
| 	ntStatic   nodeTyp = iota // /home | ||||
| 	ntRegexp                  // /{id:[0-9]+} | ||||
| 	ntParam                   // /{user} | ||||
| 	ntCatchAll                // /api/v1/* | ||||
| ) | ||||
| 
 | ||||
| type node struct { | ||||
| 	// node type: static, regexp, param, catchAll | ||||
| 	typ nodeTyp | ||||
| 
 | ||||
| 	// first byte of the prefix | ||||
| 	label byte | ||||
| 
 | ||||
| 	// first byte of the child prefix | ||||
| 	tail byte | ||||
| 
 | ||||
| 	// prefix is the common prefix we ignore | ||||
| 	prefix string | ||||
| 
 | ||||
| 	// regexp matcher for regexp nodes | ||||
| 	rex *regexp.Regexp | ||||
| 
 | ||||
| 	// HTTP handler endpoints on the leaf node | ||||
| 	endpoints endpoints | ||||
| 
 | ||||
| 	// subroutes on the leaf node | ||||
| 	subroutes Routes | ||||
| 
 | ||||
| 	// child nodes should be stored in-order for iteration, | ||||
| 	// in groups of the node type. | ||||
| 	children [ntCatchAll + 1]nodes | ||||
| } | ||||
| 
 | ||||
| // endpoints is a mapping of http method constants to handlers | ||||
| // for a given route. | ||||
| type endpoints map[methodTyp]*endpoint | ||||
| 
 | ||||
| type endpoint struct { | ||||
| 	// endpoint handler | ||||
| 	handler http.Handler | ||||
| 
 | ||||
| 	// pattern is the routing pattern for handler nodes | ||||
| 	pattern string | ||||
| 
 | ||||
| 	// parameter keys recorded on handler nodes | ||||
| 	paramKeys []string | ||||
| } | ||||
| 
 | ||||
| func (s endpoints) Value(method methodTyp) *endpoint { | ||||
| 	mh, ok := s[method] | ||||
| 	if !ok { | ||||
| 		mh = &endpoint{} | ||||
| 		s[method] = mh | ||||
| 	} | ||||
| 	return mh | ||||
| } | ||||
| 
 | ||||
| func (n *node) InsertRoute(method methodTyp, pattern string, handler http.Handler) *node { | ||||
| 	var parent *node | ||||
| 	search := pattern | ||||
| 
 | ||||
| 	for { | ||||
| 		// Handle key exhaustion | ||||
| 		if len(search) == 0 { | ||||
| 			// Insert or update the node's leaf handler | ||||
| 			n.setEndpoint(method, handler, pattern) | ||||
| 			return n | ||||
| 		} | ||||
| 
 | ||||
| 		// We're going to be searching for a wild node next, | ||||
| 		// in this case, we need to get the tail | ||||
| 		var label = search[0] | ||||
| 		var segTail byte | ||||
| 		var segEndIdx int | ||||
| 		var segTyp nodeTyp | ||||
| 		var segRexpat string | ||||
| 		if label == '{' || label == '*' { | ||||
| 			segTyp, _, segRexpat, segTail, _, segEndIdx = patNextSegment(search) | ||||
| 		} | ||||
| 
 | ||||
| 		var prefix string | ||||
| 		if segTyp == ntRegexp { | ||||
| 			prefix = segRexpat | ||||
| 		} | ||||
| 
 | ||||
| 		// Look for the edge to attach to | ||||
| 		parent = n | ||||
| 		n = n.getEdge(segTyp, label, segTail, prefix) | ||||
| 
 | ||||
| 		// No edge, create one | ||||
| 		if n == nil { | ||||
| 			child := &node{label: label, tail: segTail, prefix: search} | ||||
| 			hn := parent.addChild(child, search) | ||||
| 			hn.setEndpoint(method, handler, pattern) | ||||
| 
 | ||||
| 			return hn | ||||
| 		} | ||||
| 
 | ||||
| 		// Found an edge to match the pattern | ||||
| 
 | ||||
| 		if n.typ > ntStatic { | ||||
| 			// We found a param node, trim the param from the search path and continue. | ||||
| 			// This param/wild pattern segment would already be on the tree from a previous | ||||
| 			// call to addChild when creating a new node. | ||||
| 			search = search[segEndIdx:] | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// Static nodes fall below here. | ||||
| 		// Determine longest prefix of the search key on match. | ||||
| 		commonPrefix := longestPrefix(search, n.prefix) | ||||
| 		if commonPrefix == len(n.prefix) { | ||||
| 			// the common prefix is as long as the current node's prefix we're attempting to insert. | ||||
| 			// keep the search going. | ||||
| 			search = search[commonPrefix:] | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// Split the node | ||||
| 		child := &node{ | ||||
| 			typ:    ntStatic, | ||||
| 			prefix: search[:commonPrefix], | ||||
| 		} | ||||
| 		parent.replaceChild(search[0], segTail, child) | ||||
| 
 | ||||
| 		// Restore the existing node | ||||
| 		n.label = n.prefix[commonPrefix] | ||||
| 		n.prefix = n.prefix[commonPrefix:] | ||||
| 		child.addChild(n, n.prefix) | ||||
| 
 | ||||
| 		// If the new key is a subset, set the method/handler on this node and finish. | ||||
| 		search = search[commonPrefix:] | ||||
| 		if len(search) == 0 { | ||||
| 			child.setEndpoint(method, handler, pattern) | ||||
| 			return child | ||||
| 		} | ||||
| 
 | ||||
| 		// Create a new edge for the node | ||||
| 		subchild := &node{ | ||||
| 			typ:    ntStatic, | ||||
| 			label:  search[0], | ||||
| 			prefix: search, | ||||
| 		} | ||||
| 		hn := child.addChild(subchild, search) | ||||
| 		hn.setEndpoint(method, handler, pattern) | ||||
| 		return hn | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // addChild appends the new `child` node to the tree using the `pattern` as the trie key. | ||||
| // For a URL router like chi's, we split the static, param, regexp and wildcard segments | ||||
| // into different nodes. In addition, addChild will recursively call itself until every | ||||
| // pattern segment is added to the url pattern tree as individual nodes, depending on type. | ||||
| func (n *node) addChild(child *node, prefix string) *node { | ||||
| 	search := prefix | ||||
| 
 | ||||
| 	// handler leaf node added to the tree is the child. | ||||
| 	// this may be overridden later down the flow | ||||
| 	hn := child | ||||
| 
 | ||||
| 	// Parse next segment | ||||
| 	segTyp, _, segRexpat, segTail, segStartIdx, segEndIdx := patNextSegment(search) | ||||
| 
 | ||||
| 	// Add child depending on next up segment | ||||
| 	switch segTyp { | ||||
| 
 | ||||
| 	case ntStatic: | ||||
| 		// Search prefix is all static (that is, has no params in path) | ||||
| 		// noop | ||||
| 
 | ||||
| 	default: | ||||
| 		// Search prefix contains a param, regexp or wildcard | ||||
| 
 | ||||
| 		if segTyp == ntRegexp { | ||||
| 			rex, err := regexp.Compile(segRexpat) | ||||
| 			if err != nil { | ||||
| 				panic(fmt.Sprintf("chi: invalid regexp pattern '%s' in route param", segRexpat)) | ||||
| 			} | ||||
| 			child.prefix = segRexpat | ||||
| 			child.rex = rex | ||||
| 		} | ||||
| 
 | ||||
| 		if segStartIdx == 0 { | ||||
| 			// Route starts with a param | ||||
| 			child.typ = segTyp | ||||
| 
 | ||||
| 			if segTyp == ntCatchAll { | ||||
| 				segStartIdx = -1 | ||||
| 			} else { | ||||
| 				segStartIdx = segEndIdx | ||||
| 			} | ||||
| 			if segStartIdx < 0 { | ||||
| 				segStartIdx = len(search) | ||||
| 			} | ||||
| 			child.tail = segTail // for params, we set the tail | ||||
| 
 | ||||
| 			if segStartIdx != len(search) { | ||||
| 				// add static edge for the remaining part, split the end. | ||||
| 				// its not possible to have adjacent param nodes, so its certainly | ||||
| 				// going to be a static node next. | ||||
| 
 | ||||
| 				search = search[segStartIdx:] // advance search position | ||||
| 
 | ||||
| 				nn := &node{ | ||||
| 					typ:    ntStatic, | ||||
| 					label:  search[0], | ||||
| 					prefix: search, | ||||
| 				} | ||||
| 				hn = child.addChild(nn, search) | ||||
| 			} | ||||
| 
 | ||||
| 		} else if segStartIdx > 0 { | ||||
| 			// Route has some param | ||||
| 
 | ||||
| 			// starts with a static segment | ||||
| 			child.typ = ntStatic | ||||
| 			child.prefix = search[:segStartIdx] | ||||
| 			child.rex = nil | ||||
| 
 | ||||
| 			// add the param edge node | ||||
| 			search = search[segStartIdx:] | ||||
| 
 | ||||
| 			nn := &node{ | ||||
| 				typ:   segTyp, | ||||
| 				label: search[0], | ||||
| 				tail:  segTail, | ||||
| 			} | ||||
| 			hn = child.addChild(nn, search) | ||||
| 
 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	n.children[child.typ] = append(n.children[child.typ], child) | ||||
| 	n.children[child.typ].Sort() | ||||
| 	return hn | ||||
| } | ||||
| 
 | ||||
| func (n *node) replaceChild(label, tail byte, child *node) { | ||||
| 	for i := 0; i < len(n.children[child.typ]); i++ { | ||||
| 		if n.children[child.typ][i].label == label && n.children[child.typ][i].tail == tail { | ||||
| 			n.children[child.typ][i] = child | ||||
| 			n.children[child.typ][i].label = label | ||||
| 			n.children[child.typ][i].tail = tail | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	panic("chi: replacing missing child") | ||||
| } | ||||
| 
 | ||||
| func (n *node) getEdge(ntyp nodeTyp, label, tail byte, prefix string) *node { | ||||
| 	nds := n.children[ntyp] | ||||
| 	for i := 0; i < len(nds); i++ { | ||||
| 		if nds[i].label == label && nds[i].tail == tail { | ||||
| 			if ntyp == ntRegexp && nds[i].prefix != prefix { | ||||
| 				continue | ||||
| 			} | ||||
| 			return nds[i] | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (n *node) setEndpoint(method methodTyp, handler http.Handler, pattern string) { | ||||
| 	// Set the handler for the method type on the node | ||||
| 	if n.endpoints == nil { | ||||
| 		n.endpoints = make(endpoints, 0) | ||||
| 	} | ||||
| 
 | ||||
| 	paramKeys := patParamKeys(pattern) | ||||
| 
 | ||||
| 	if method&mSTUB == mSTUB { | ||||
| 		n.endpoints.Value(mSTUB).handler = handler | ||||
| 	} | ||||
| 	if method&mALL == mALL { | ||||
| 		h := n.endpoints.Value(mALL) | ||||
| 		h.handler = handler | ||||
| 		h.pattern = pattern | ||||
| 		h.paramKeys = paramKeys | ||||
| 		for _, m := range methodMap { | ||||
| 			h := n.endpoints.Value(m) | ||||
| 			h.handler = handler | ||||
| 			h.pattern = pattern | ||||
| 			h.paramKeys = paramKeys | ||||
| 		} | ||||
| 	} else { | ||||
| 		h := n.endpoints.Value(method) | ||||
| 		h.handler = handler | ||||
| 		h.pattern = pattern | ||||
| 		h.paramKeys = paramKeys | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (n *node) FindRoute(rctx *Context, method methodTyp, path string) (*node, endpoints, http.Handler) { | ||||
| 	// Reset the context routing pattern and params | ||||
| 	rctx.routePattern = "" | ||||
| 	rctx.routeParams.Keys = rctx.routeParams.Keys[:0] | ||||
| 	rctx.routeParams.Values = rctx.routeParams.Values[:0] | ||||
| 
 | ||||
| 	// Find the routing handlers for the path | ||||
| 	rn := n.findRoute(rctx, method, path) | ||||
| 	if rn == nil { | ||||
| 		return nil, nil, nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Record the routing params in the request lifecycle | ||||
| 	rctx.URLParams.Keys = append(rctx.URLParams.Keys, rctx.routeParams.Keys...) | ||||
| 	rctx.URLParams.Values = append(rctx.URLParams.Values, rctx.routeParams.Values...) | ||||
| 
 | ||||
| 	// Record the routing pattern in the request lifecycle | ||||
| 	if rn.endpoints[method].pattern != "" { | ||||
| 		rctx.routePattern = rn.endpoints[method].pattern | ||||
| 		rctx.RoutePatterns = append(rctx.RoutePatterns, rctx.routePattern) | ||||
| 	} | ||||
| 
 | ||||
| 	return rn, rn.endpoints, rn.endpoints[method].handler | ||||
| } | ||||
| 
 | ||||
| // Recursive edge traversal by checking all nodeTyp groups along the way. | ||||
| // It's like searching through a multi-dimensional radix trie. | ||||
| func (n *node) findRoute(rctx *Context, method methodTyp, path string) *node { | ||||
| 	nn := n | ||||
| 	search := path | ||||
| 
 | ||||
| 	for t, nds := range nn.children { | ||||
| 		ntyp := nodeTyp(t) | ||||
| 		if len(nds) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		var xn *node | ||||
| 		xsearch := search | ||||
| 
 | ||||
| 		var label byte | ||||
| 		if search != "" { | ||||
| 			label = search[0] | ||||
| 		} | ||||
| 
 | ||||
| 		switch ntyp { | ||||
| 		case ntStatic: | ||||
| 			xn = nds.findEdge(label) | ||||
| 			if xn == nil || !strings.HasPrefix(xsearch, xn.prefix) { | ||||
| 				continue | ||||
| 			} | ||||
| 			xsearch = xsearch[len(xn.prefix):] | ||||
| 
 | ||||
| 		case ntParam, ntRegexp: | ||||
| 			// short-circuit and return no matching route for empty param values | ||||
| 			if xsearch == "" { | ||||
| 				continue | ||||
| 			} | ||||
| 
 | ||||
| 			// serially loop through each node grouped by the tail delimiter | ||||
| 			for idx := 0; idx < len(nds); idx++ { | ||||
| 				xn = nds[idx] | ||||
| 
 | ||||
| 				// label for param nodes is the delimiter byte | ||||
| 				p := strings.IndexByte(xsearch, xn.tail) | ||||
| 
 | ||||
| 				if p < 0 { | ||||
| 					if xn.tail == '/' { | ||||
| 						p = len(xsearch) | ||||
| 					} else { | ||||
| 						continue | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				if ntyp == ntRegexp && xn.rex != nil { | ||||
| 					if xn.rex.Match([]byte(xsearch[:p])) == false { | ||||
| 						continue | ||||
| 					} | ||||
| 				} else if strings.IndexByte(xsearch[:p], '/') != -1 { | ||||
| 					// avoid a match across path segments | ||||
| 					continue | ||||
| 				} | ||||
| 
 | ||||
| 				rctx.routeParams.Values = append(rctx.routeParams.Values, xsearch[:p]) | ||||
| 				xsearch = xsearch[p:] | ||||
| 				break | ||||
| 			} | ||||
| 
 | ||||
| 		default: | ||||
| 			// catch-all nodes | ||||
| 			rctx.routeParams.Values = append(rctx.routeParams.Values, search) | ||||
| 			xn = nds[0] | ||||
| 			xsearch = "" | ||||
| 		} | ||||
| 
 | ||||
| 		if xn == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// did we find it yet? | ||||
| 		if len(xsearch) == 0 { | ||||
| 			if xn.isLeaf() { | ||||
| 				h, _ := xn.endpoints[method] | ||||
| 				if h != nil && h.handler != nil { | ||||
| 					rctx.routeParams.Keys = append(rctx.routeParams.Keys, h.paramKeys...) | ||||
| 					return xn | ||||
| 				} | ||||
| 
 | ||||
| 				// flag that the routing context found a route, but not a corresponding | ||||
| 				// supported method | ||||
| 				rctx.methodNotAllowed = true | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// recursively find the next node.. | ||||
| 		fin := xn.findRoute(rctx, method, xsearch) | ||||
| 		if fin != nil { | ||||
| 			return fin | ||||
| 		} | ||||
| 
 | ||||
| 		// Did not find final handler, let's remove the param here if it was set | ||||
| 		if xn.typ > ntStatic { | ||||
| 			if len(rctx.routeParams.Values) > 0 { | ||||
| 				rctx.routeParams.Values = rctx.routeParams.Values[:len(rctx.routeParams.Values)-1] | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (n *node) findEdge(ntyp nodeTyp, label byte) *node { | ||||
| 	nds := n.children[ntyp] | ||||
| 	num := len(nds) | ||||
| 	idx := 0 | ||||
| 
 | ||||
| 	switch ntyp { | ||||
| 	case ntStatic, ntParam, ntRegexp: | ||||
| 		i, j := 0, num-1 | ||||
| 		for i <= j { | ||||
| 			idx = i + (j-i)/2 | ||||
| 			if label > nds[idx].label { | ||||
| 				i = idx + 1 | ||||
| 			} else if label < nds[idx].label { | ||||
| 				j = idx - 1 | ||||
| 			} else { | ||||
| 				i = num // breaks cond | ||||
| 			} | ||||
| 		} | ||||
| 		if nds[idx].label != label { | ||||
| 			return nil | ||||
| 		} | ||||
| 		return nds[idx] | ||||
| 
 | ||||
| 	default: // catch all | ||||
| 		return nds[idx] | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (n *node) isEmpty() bool { | ||||
| 	for _, nds := range n.children { | ||||
| 		if len(nds) > 0 { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (n *node) isLeaf() bool { | ||||
| 	return n.endpoints != nil | ||||
| } | ||||
| 
 | ||||
| func (n *node) findPattern(pattern string) bool { | ||||
| 	nn := n | ||||
| 	for _, nds := range nn.children { | ||||
| 		if len(nds) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		n = nn.findEdge(nds[0].typ, pattern[0]) | ||||
| 		if n == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		var idx int | ||||
| 		var xpattern string | ||||
| 
 | ||||
| 		switch n.typ { | ||||
| 		case ntStatic: | ||||
| 			idx = longestPrefix(pattern, n.prefix) | ||||
| 			if idx < len(n.prefix) { | ||||
| 				continue | ||||
| 			} | ||||
| 
 | ||||
| 		case ntParam, ntRegexp: | ||||
| 			idx = strings.IndexByte(pattern, '}') + 1 | ||||
| 
 | ||||
| 		case ntCatchAll: | ||||
| 			idx = longestPrefix(pattern, "*") | ||||
| 
 | ||||
| 		default: | ||||
| 			panic("chi: unknown node type") | ||||
| 		} | ||||
| 
 | ||||
| 		xpattern = pattern[idx:] | ||||
| 		if len(xpattern) == 0 { | ||||
| 			return true | ||||
| 		} | ||||
| 
 | ||||
| 		return n.findPattern(xpattern) | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (n *node) routes() []Route { | ||||
| 	rts := []Route{} | ||||
| 
 | ||||
| 	n.walk(func(eps endpoints, subroutes Routes) bool { | ||||
| 		if eps[mSTUB] != nil && eps[mSTUB].handler != nil && subroutes == nil { | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		// Group methodHandlers by unique patterns | ||||
| 		pats := make(map[string]endpoints, 0) | ||||
| 
 | ||||
| 		for mt, h := range eps { | ||||
| 			if h.pattern == "" { | ||||
| 				continue | ||||
| 			} | ||||
| 			p, ok := pats[h.pattern] | ||||
| 			if !ok { | ||||
| 				p = endpoints{} | ||||
| 				pats[h.pattern] = p | ||||
| 			} | ||||
| 			p[mt] = h | ||||
| 		} | ||||
| 
 | ||||
| 		for p, mh := range pats { | ||||
| 			hs := make(map[string]http.Handler, 0) | ||||
| 			if mh[mALL] != nil && mh[mALL].handler != nil { | ||||
| 				hs["*"] = mh[mALL].handler | ||||
| 			} | ||||
| 
 | ||||
| 			for mt, h := range mh { | ||||
| 				if h.handler == nil { | ||||
| 					continue | ||||
| 				} | ||||
| 				m := methodTypString(mt) | ||||
| 				if m == "" { | ||||
| 					continue | ||||
| 				} | ||||
| 				hs[m] = h.handler | ||||
| 			} | ||||
| 
 | ||||
| 			rt := Route{p, hs, subroutes} | ||||
| 			rts = append(rts, rt) | ||||
| 		} | ||||
| 
 | ||||
| 		return false | ||||
| 	}) | ||||
| 
 | ||||
| 	return rts | ||||
| } | ||||
| 
 | ||||
| func (n *node) walk(fn func(eps endpoints, subroutes Routes) bool) bool { | ||||
| 	// Visit the leaf values if any | ||||
| 	if (n.endpoints != nil || n.subroutes != nil) && fn(n.endpoints, n.subroutes) { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	// Recurse on the children | ||||
| 	for _, ns := range n.children { | ||||
| 		for _, cn := range ns { | ||||
| 			if cn.walk(fn) { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // patNextSegment returns the next segment details from a pattern: | ||||
| // node type, param key, regexp string, param tail byte, param starting index, param ending index | ||||
| func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) { | ||||
| 	ps := strings.Index(pattern, "{") | ||||
| 	ws := strings.Index(pattern, "*") | ||||
| 
 | ||||
| 	if ps < 0 && ws < 0 { | ||||
| 		return ntStatic, "", "", 0, 0, len(pattern) // we return the entire thing | ||||
| 	} | ||||
| 
 | ||||
| 	// Sanity check | ||||
| 	if ps >= 0 && ws >= 0 && ws < ps { | ||||
| 		panic("chi: wildcard '*' must be the last pattern in a route, otherwise use a '{param}'") | ||||
| 	} | ||||
| 
 | ||||
| 	var tail byte = '/' // Default endpoint tail to / byte | ||||
| 
 | ||||
| 	if ps >= 0 { | ||||
| 		// Param/Regexp pattern is next | ||||
| 		nt := ntParam | ||||
| 
 | ||||
| 		// Read to closing } taking into account opens and closes in curl count (cc) | ||||
| 		cc := 0 | ||||
| 		pe := ps | ||||
| 		for i, c := range pattern[ps:] { | ||||
| 			if c == '{' { | ||||
| 				cc++ | ||||
| 			} else if c == '}' { | ||||
| 				cc-- | ||||
| 				if cc == 0 { | ||||
| 					pe = ps + i | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if pe == ps { | ||||
| 			panic("chi: route param closing delimiter '}' is missing") | ||||
| 		} | ||||
| 
 | ||||
| 		key := pattern[ps+1 : pe] | ||||
| 		pe++ // set end to next position | ||||
| 
 | ||||
| 		if pe < len(pattern) { | ||||
| 			tail = pattern[pe] | ||||
| 		} | ||||
| 
 | ||||
| 		var rexpat string | ||||
| 		if idx := strings.Index(key, ":"); idx >= 0 { | ||||
| 			nt = ntRegexp | ||||
| 			rexpat = key[idx+1:] | ||||
| 			key = key[:idx] | ||||
| 		} | ||||
| 
 | ||||
| 		if len(rexpat) > 0 { | ||||
| 			if rexpat[0] != '^' { | ||||
| 				rexpat = "^" + rexpat | ||||
| 			} | ||||
| 			if rexpat[len(rexpat)-1] != '$' { | ||||
| 				rexpat = rexpat + "$" | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return nt, key, rexpat, tail, ps, pe | ||||
| 	} | ||||
| 
 | ||||
| 	// Wildcard pattern as finale | ||||
| 	if ws < len(pattern)-1 { | ||||
| 		panic("chi: wildcard '*' must be the last value in a route. trim trailing text or use a '{param}' instead") | ||||
| 	} | ||||
| 	return ntCatchAll, "*", "", 0, ws, len(pattern) | ||||
| } | ||||
| 
 | ||||
| func patParamKeys(pattern string) []string { | ||||
| 	pat := pattern | ||||
| 	paramKeys := []string{} | ||||
| 	for { | ||||
| 		ptyp, paramKey, _, _, _, e := patNextSegment(pat) | ||||
| 		if ptyp == ntStatic { | ||||
| 			return paramKeys | ||||
| 		} | ||||
| 		for i := 0; i < len(paramKeys); i++ { | ||||
| 			if paramKeys[i] == paramKey { | ||||
| 				panic(fmt.Sprintf("chi: routing pattern '%s' contains duplicate param key, '%s'", pattern, paramKey)) | ||||
| 			} | ||||
| 		} | ||||
| 		paramKeys = append(paramKeys, paramKey) | ||||
| 		pat = pat[e:] | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // longestPrefix finds the length of the shared prefix | ||||
| // of two strings | ||||
| func longestPrefix(k1, k2 string) int { | ||||
| 	max := len(k1) | ||||
| 	if l := len(k2); l < max { | ||||
| 		max = l | ||||
| 	} | ||||
| 	var i int | ||||
| 	for i = 0; i < max; i++ { | ||||
| 		if k1[i] != k2[i] { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return i | ||||
| } | ||||
| 
 | ||||
| func methodTypString(method methodTyp) string { | ||||
| 	for s, t := range methodMap { | ||||
| 		if method == t { | ||||
| 			return s | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| type nodes []*node | ||||
| 
 | ||||
| // Sort the list of nodes by label | ||||
| func (ns nodes) Sort()              { sort.Sort(ns); ns.tailSort() } | ||||
| func (ns nodes) Len() int           { return len(ns) } | ||||
| func (ns nodes) Swap(i, j int)      { ns[i], ns[j] = ns[j], ns[i] } | ||||
| func (ns nodes) Less(i, j int) bool { return ns[i].label < ns[j].label } | ||||
| 
 | ||||
| // tailSort pushes nodes with '/' as the tail to the end of the list for param nodes. | ||||
| // The list order determines the traversal order. | ||||
| func (ns nodes) tailSort() { | ||||
| 	for i := len(ns) - 1; i >= 0; i-- { | ||||
| 		if ns[i].typ > ntStatic && ns[i].tail == '/' { | ||||
| 			ns.Swap(i, len(ns)-1) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (ns nodes) findEdge(label byte) *node { | ||||
| 	num := len(ns) | ||||
| 	idx := 0 | ||||
| 	i, j := 0, num-1 | ||||
| 	for i <= j { | ||||
| 		idx = i + (j-i)/2 | ||||
| 		if label > ns[idx].label { | ||||
| 			i = idx + 1 | ||||
| 		} else if label < ns[idx].label { | ||||
| 			j = idx - 1 | ||||
| 		} else { | ||||
| 			i = num // breaks cond | ||||
| 		} | ||||
| 	} | ||||
| 	if ns[idx].label != label { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return ns[idx] | ||||
| } | ||||
| 
 | ||||
| // Route describes the details of a routing handler. | ||||
| type Route struct { | ||||
| 	Pattern   string | ||||
| 	Handlers  map[string]http.Handler | ||||
| 	SubRoutes Routes | ||||
| } | ||||
| 
 | ||||
| // WalkFunc is the type of the function called for each method and route visited by Walk. | ||||
| type WalkFunc func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error | ||||
| 
 | ||||
| // Walk walks any router tree that implements Routes interface. | ||||
| func Walk(r Routes, walkFn WalkFunc) error { | ||||
| 	return walk(r, walkFn, "") | ||||
| } | ||||
| 
 | ||||
| func walk(r Routes, walkFn WalkFunc, parentRoute string, parentMw ...func(http.Handler) http.Handler) error { | ||||
| 	for _, route := range r.Routes() { | ||||
| 		mws := make([]func(http.Handler) http.Handler, len(parentMw)) | ||||
| 		copy(mws, parentMw) | ||||
| 		mws = append(mws, r.Middlewares()...) | ||||
| 
 | ||||
| 		if route.SubRoutes != nil { | ||||
| 			if err := walk(route.SubRoutes, walkFn, parentRoute+route.Pattern, mws...); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		for method, handler := range route.Handlers { | ||||
| 			if method == "*" { | ||||
| 				// Ignore a "catchAll" method, since we pass down all the specific methods for each route. | ||||
| 				continue | ||||
| 			} | ||||
| 
 | ||||
| 			fullRoute := parentRoute + route.Pattern | ||||
| 
 | ||||
| 			if chain, ok := handler.(*ChainHandler); ok { | ||||
| 				if err := walkFn(method, fullRoute, chain.Endpoint, append(mws, chain.Middlewares...)...); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} else { | ||||
| 				if err := walkFn(method, fullRoute, handler, mws...); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										15
									
								
								vendor/github.com/gofrs/uuid/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/gofrs/uuid/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,15 +0,0 @@ | |||
| # Binaries for programs and plugins | ||||
| *.exe | ||||
| *.exe~ | ||||
| *.dll | ||||
| *.so | ||||
| *.dylib | ||||
| 
 | ||||
| # Test binary, build with `go test -c` | ||||
| *.test | ||||
| 
 | ||||
| # Output of the go coverage tool, specifically when used with LiteIDE | ||||
| *.out | ||||
| 
 | ||||
| # binary bundle generated by go-fuzz | ||||
| uuid-fuzz.zip | ||||
							
								
								
									
										23
									
								
								vendor/github.com/gofrs/uuid/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/gofrs/uuid/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,23 +0,0 @@ | |||
| language: go | ||||
| sudo: false | ||||
| go: | ||||
|   - 1.7.x | ||||
|   - 1.8.x | ||||
|   - 1.9.x | ||||
|   - 1.10.x | ||||
|   - 1.11.x | ||||
|   - tip | ||||
| matrix: | ||||
|   allow_failures: | ||||
|     - go: tip | ||||
|   fast_finish: true | ||||
| env: | ||||
|   - GO111MODULE=on | ||||
| before_install: | ||||
|   - go get golang.org/x/tools/cmd/cover | ||||
| script: | ||||
|   - go test ./... -race -coverprofile=coverage.txt -covermode=atomic | ||||
| after_success: | ||||
|   - bash <(curl -s https://codecov.io/bash) | ||||
| notifications: | ||||
|   email: false | ||||
							
								
								
									
										20
									
								
								vendor/github.com/gofrs/uuid/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/gofrs/uuid/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,20 +0,0 @@ | |||
| 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. | ||||
							
								
								
									
										109
									
								
								vendor/github.com/gofrs/uuid/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										109
									
								
								vendor/github.com/gofrs/uuid/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,109 +0,0 @@ | |||
| # UUID | ||||
| 
 | ||||
| [](https://github.com/gofrs/uuid/blob/master/LICENSE) | ||||
| [](https://travis-ci.org/gofrs/uuid) | ||||
| [](http://godoc.org/github.com/gofrs/uuid) | ||||
| [](https://codecov.io/gh/gofrs/uuid/) | ||||
| [](https://goreportcard.com/report/github.com/gofrs/uuid) | ||||
| 
 | ||||
| Package uuid provides a pure Go implementation of Universally Unique Identifiers | ||||
| (UUID) variant as defined in RFC-4122. This package supports both the creation | ||||
| and parsing of UUIDs in different formats. | ||||
| 
 | ||||
| This package supports the following UUID versions: | ||||
| * Version 1, based on timestamp and MAC address (RFC-4122) | ||||
| * Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) | ||||
| * Version 3, based on MD5 hashing of a named value (RFC-4122) | ||||
| * Version 4, based on random numbers (RFC-4122) | ||||
| * Version 5, based on SHA-1 hashing of a named value (RFC-4122) | ||||
| 
 | ||||
| ## Project History | ||||
| 
 | ||||
| This project was originally forked from the | ||||
| [github.com/satori/go.uuid](https://github.com/satori/go.uuid) repository after | ||||
| it appeared to be no longer maintained, while exhibiting [critical | ||||
| flaws](https://github.com/satori/go.uuid/issues/73). We have decided to take | ||||
| over this project to ensure it receives regular maintenance for the benefit of | ||||
| the larger Go community. | ||||
| 
 | ||||
| We'd like to thank Maxim Bublis for his hard work on the original iteration of | ||||
| the package. | ||||
| 
 | ||||
| ## License | ||||
| 
 | ||||
| This source code of this package is released under the MIT License. Please see | ||||
| the [LICENSE](https://github.com/gofrs/uuid/blob/master/LICENSE) for the full | ||||
| content of the license. | ||||
| 
 | ||||
| ## Recommended Package Version | ||||
| 
 | ||||
| We recommend using v2.0.0+ of this package, as versions prior to 2.0.0 were | ||||
| created before our fork of the original package and have some known | ||||
| deficiencies. | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| It is recommended to use a package manager like `dep` that understands tagged | ||||
| releases of a package, as well as semantic versioning. | ||||
| 
 | ||||
| If you are unable to make use of a dependency manager with your project, you can | ||||
| use the `go get` command to download it directly: | ||||
| 
 | ||||
| ```Shell | ||||
| $ go get github.com/gofrs/uuid | ||||
| ``` | ||||
| 
 | ||||
| ## Requirements | ||||
| 
 | ||||
| Due to subtests not being supported in older versions of Go, this package is | ||||
| only regularly tested against Go 1.7+. This package may work perfectly fine with | ||||
| Go 1.2+, but support for these older versions is not actively maintained. | ||||
| 
 | ||||
| ## Go 1.11 Modules | ||||
| 
 | ||||
| As of v3.2.0, this repository no longer adopts Go modules, and v3.2.0 no longer has a `go.mod` file.  As a result, v3.2.0 also drops support for the `github.com/gofrs/uuid/v3` import path. Only module-based consumers are impacted.  With the v3.2.0 release, _all_ gofrs/uuid consumers should use the `github.com/gofrs/uuid` import path. | ||||
| 
 | ||||
| An existing module-based consumer will continue to be able to build using the `github.com/gofrs/uuid/v3` import path using any valid consumer `go.mod` that worked prior to the publishing of v3.2.0, but any module-based consumer should start using the `github.com/gofrs/uuid` import path when possible and _must_ use the `github.com/gofrs/uuid` import path prior to upgrading to v3.2.0. | ||||
| 
 | ||||
| Please refer to [Issue #61](https://github.com/gofrs/uuid/issues/61) and [Issue #66](https://github.com/gofrs/uuid/issues/66) for more details. | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| Here is a quick overview of how to use this package. For more detailed | ||||
| documentation, please see the [GoDoc Page](http://godoc.org/github.com/gofrs/uuid). | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"log" | ||||
| 
 | ||||
| 	"github.com/gofrs/uuid" | ||||
| ) | ||||
| 
 | ||||
| // Create a Version 4 UUID, panicking on error. | ||||
| // Use this form to initialize package-level variables. | ||||
| var u1 = uuid.Must(uuid.NewV4()) | ||||
| 
 | ||||
| func main() { | ||||
| 	// Create a Version 4 UUID. | ||||
| 	u2, err := uuid.NewV4() | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("failed to generate UUID: %v", err) | ||||
| 	} | ||||
| 	log.Printf("generated Version 4 UUID %v", u2) | ||||
| 
 | ||||
| 	// Parse a UUID from a string. | ||||
| 	s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" | ||||
| 	u3, err := uuid.FromString(s) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("failed to parse UUID %q: %v", s, err) | ||||
| 	} | ||||
| 	log.Printf("successfully parsed UUID %v", u3) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## References | ||||
| 
 | ||||
| * [RFC-4122](https://tools.ietf.org/html/rfc4122) | ||||
| * [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) | ||||
							
								
								
									
										212
									
								
								vendor/github.com/gofrs/uuid/codec.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										212
									
								
								vendor/github.com/gofrs/uuid/codec.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,212 +0,0 @@ | |||
| // 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 a UUID generated from the raw byte slice input. | ||||
| // It will return an error if the slice isn't 16 bytes long. | ||||
| func FromBytes(input []byte) (UUID, error) { | ||||
| 	u := UUID{} | ||||
| 	err := u.UnmarshalBinary(input) | ||||
| 	return u, err | ||||
| } | ||||
| 
 | ||||
| // FromBytesOrNil returns a UUID generated from the raw byte slice input. | ||||
| // Same behavior as FromBytes(), but returns uuid.Nil instead of an error. | ||||
| func FromBytesOrNil(input []byte) UUID { | ||||
| 	uuid, err := FromBytes(input) | ||||
| 	if err != nil { | ||||
| 		return Nil | ||||
| 	} | ||||
| 	return uuid | ||||
| } | ||||
| 
 | ||||
| // FromString returns a UUID parsed from the input string. | ||||
| // Input is expected in a form accepted by UnmarshalText. | ||||
| func FromString(input string) (UUID, error) { | ||||
| 	u := UUID{} | ||||
| 	err := u.UnmarshalText([]byte(input)) | ||||
| 	return u, err | ||||
| } | ||||
| 
 | ||||
| // FromStringOrNil returns a UUID parsed from the input string. | ||||
| // Same behavior as FromString(), but returns uuid.Nil instead of an 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 the String() method. | ||||
| func (u UUID) MarshalText() ([]byte, error) { | ||||
| 	return []byte(u.String()), nil | ||||
| } | ||||
| 
 | ||||
| // 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" | ||||
| //   "{6ba7b8109dad11d180b400c04fd430c8}", | ||||
| //   "urn:uuid:6ba7b8109dad11d180b400c04fd430c8" | ||||
| // | ||||
| // ABNF for supported UUID text representation follows: | ||||
| // | ||||
| //   URN := 'urn' | ||||
| //   UUID-NID := 'uuid' | ||||
| // | ||||
| //   hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | | ||||
| //             'a' | 'b' | 'c' | 'd' | 'e' | 'f' | | ||||
| //             'A' | 'B' | 'C' | 'D' | 'E' | 'F' | ||||
| // | ||||
| //   hexoct := hexdig hexdig | ||||
| //   2hexoct := hexoct hexoct | ||||
| //   4hexoct := 2hexoct 2hexoct | ||||
| //   6hexoct := 4hexoct 2hexoct | ||||
| //   12hexoct := 6hexoct 6hexoct | ||||
| // | ||||
| //   hashlike := 12hexoct | ||||
| //   canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct | ||||
| // | ||||
| //   plain := canonical | hashlike | ||||
| //   uuid := canonical | hashlike | braced | urn | ||||
| // | ||||
| //   braced := '{' plain '}' | '{' hashlike  '}' | ||||
| //   urn := URN ':' UUID-NID ':' plain | ||||
| // | ||||
| func (u *UUID) UnmarshalText(text []byte) error { | ||||
| 	switch len(text) { | ||||
| 	case 32: | ||||
| 		return u.decodeHashLike(text) | ||||
| 	case 34, 38: | ||||
| 		return u.decodeBraced(text) | ||||
| 	case 36: | ||||
| 		return u.decodeCanonical(text) | ||||
| 	case 41, 45: | ||||
| 		return u.decodeURN(text) | ||||
| 	default: | ||||
| 		return fmt.Errorf("uuid: incorrect UUID length: %s", text) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // decodeCanonical decodes UUID strings that are formatted as defined in RFC-4122 (section 3): | ||||
| // "6ba7b810-9dad-11d1-80b4-00c04fd430c8". | ||||
| func (u *UUID) decodeCanonical(t []byte) 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 err | ||||
| 		} | ||||
| 		src = src[byteGroup:] | ||||
| 		dst = dst[byteGroup/2:] | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // decodeHashLike decodes UUID strings that are using the following format: | ||||
| //  "6ba7b8109dad11d180b400c04fd430c8". | ||||
| func (u *UUID) decodeHashLike(t []byte) error { | ||||
| 	src := t[:] | ||||
| 	dst := u[:] | ||||
| 
 | ||||
| 	_, err := hex.Decode(dst, src) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // decodeBraced decodes UUID strings that are using the following formats: | ||||
| //  "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" | ||||
| //  "{6ba7b8109dad11d180b400c04fd430c8}". | ||||
| func (u *UUID) decodeBraced(t []byte) 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 strings that are using the following formats: | ||||
| //  "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" | ||||
| //  "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". | ||||
| func (u *UUID) decodeURN(t []byte) error { | ||||
| 	total := len(t) | ||||
| 
 | ||||
| 	urnUUIDPrefix := t[:9] | ||||
| 
 | ||||
| 	if !bytes.Equal(urnUUIDPrefix, urnPrefix) { | ||||
| 		return fmt.Errorf("uuid: incorrect UUID format: %s", t) | ||||
| 	} | ||||
| 
 | ||||
| 	return u.decodePlain(t[9:total]) | ||||
| } | ||||
| 
 | ||||
| // decodePlain decodes UUID strings that are using the following formats: | ||||
| //  "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format | ||||
| //  "6ba7b8109dad11d180b400c04fd430c8". | ||||
| func (u *UUID) decodePlain(t []byte) error { | ||||
| 	switch len(t) { | ||||
| 	case 32: | ||||
| 		return u.decodeHashLike(t) | ||||
| 	case 36: | ||||
| 		return u.decodeCanonical(t) | ||||
| 	default: | ||||
| 		return fmt.Errorf("uuid: incorrect UUID length: %s", t) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // MarshalBinary implements the encoding.BinaryMarshaler interface. | ||||
| func (u UUID) MarshalBinary() ([]byte, error) { | ||||
| 	return u.Bytes(), nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. | ||||
| // It will return an error if the slice isn't 16 bytes long. | ||||
| func (u *UUID) UnmarshalBinary(data []byte) error { | ||||
| 	if len(data) != Size { | ||||
| 		return fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) | ||||
| 	} | ||||
| 	copy(u[:], data) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										47
									
								
								vendor/github.com/gofrs/uuid/fuzz.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/gofrs/uuid/fuzz.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,47 +0,0 @@ | |||
| // Copyright (c) 2018 Andrei Tudor Călin <mail@acln.ro> | ||||
| // | ||||
| // 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. | ||||
| 
 | ||||
| // +build gofuzz | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| // Fuzz implements a simple fuzz test for FromString / UnmarshalText. | ||||
| // | ||||
| // To run: | ||||
| // | ||||
| //     $ go get github.com/dvyukov/go-fuzz/... | ||||
| //     $ cd $GOPATH/src/github.com/gofrs/uuid | ||||
| //     $ go-fuzz-build github.com/gofrs/uuid | ||||
| //     $ go-fuzz -bin=uuid-fuzz.zip -workdir=./testdata | ||||
| // | ||||
| // If you make significant changes to FromString / UnmarshalText and add | ||||
| // new cases to fromStringTests (in codec_test.go), please run | ||||
| // | ||||
| //    $ go test -seed_fuzz_corpus | ||||
| // | ||||
| // to seed the corpus with the new interesting inputs, then run the fuzzer. | ||||
| func Fuzz(data []byte) int { | ||||
| 	_, err := FromString(string(data)) | ||||
| 	if err != nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return 1 | ||||
| } | ||||
							
								
								
									
										299
									
								
								vendor/github.com/gofrs/uuid/generator.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										299
									
								
								vendor/github.com/gofrs/uuid/generator.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,299 +0,0 @@ | |||
| // 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" | ||||
| 	"fmt" | ||||
| 	"hash" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Difference in 100-nanosecond intervals between | ||||
| // UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). | ||||
| const epochStart = 122192928000000000 | ||||
| 
 | ||||
| type epochFunc func() time.Time | ||||
| 
 | ||||
| // HWAddrFunc is the function type used to provide hardware (MAC) addresses. | ||||
| type HWAddrFunc func() (net.HardwareAddr, error) | ||||
| 
 | ||||
| // DefaultGenerator is the default UUID Generator used by this package. | ||||
| var DefaultGenerator Generator = NewGen() | ||||
| 
 | ||||
| var ( | ||||
| 	posixUID = uint32(os.Getuid()) | ||||
| 	posixGID = uint32(os.Getgid()) | ||||
| ) | ||||
| 
 | ||||
| // NewV1 returns a UUID based on the current timestamp and MAC address. | ||||
| func NewV1() (UUID, error) { | ||||
| 	return DefaultGenerator.NewV1() | ||||
| } | ||||
| 
 | ||||
| // NewV2 returns a DCE Security UUID based on the POSIX UID/GID. | ||||
| func NewV2(domain byte) (UUID, error) { | ||||
| 	return DefaultGenerator.NewV2(domain) | ||||
| } | ||||
| 
 | ||||
| // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name. | ||||
| func NewV3(ns UUID, name string) UUID { | ||||
| 	return DefaultGenerator.NewV3(ns, name) | ||||
| } | ||||
| 
 | ||||
| // NewV4 returns a randomly generated UUID. | ||||
| func NewV4() (UUID, error) { | ||||
| 	return DefaultGenerator.NewV4() | ||||
| } | ||||
| 
 | ||||
| // NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name. | ||||
| func NewV5(ns UUID, name string) UUID { | ||||
| 	return DefaultGenerator.NewV5(ns, name) | ||||
| } | ||||
| 
 | ||||
| // Generator provides an interface for generating UUIDs. | ||||
| type Generator interface { | ||||
| 	NewV1() (UUID, error) | ||||
| 	NewV2(domain byte) (UUID, error) | ||||
| 	NewV3(ns UUID, name string) UUID | ||||
| 	NewV4() (UUID, error) | ||||
| 	NewV5(ns UUID, name string) UUID | ||||
| } | ||||
| 
 | ||||
| // Gen is a reference UUID generator based on the specifications laid out in | ||||
| // RFC-4122 and DCE 1.1: Authentication and Security Services. This type | ||||
| // satisfies the Generator interface as defined in this package. | ||||
| // | ||||
| // For consumers who are generating V1 UUIDs, but don't want to expose the MAC | ||||
| // address of the node generating the UUIDs, the NewGenWithHWAF() function has been | ||||
| // provided as a convenience. See the function's documentation for more info. | ||||
| // | ||||
| // The authors of this package do not feel that the majority of users will need | ||||
| // to obfuscate their MAC address, and so we recommend using NewGen() to create | ||||
| // a new generator. | ||||
| type Gen struct { | ||||
| 	clockSequenceOnce sync.Once | ||||
| 	hardwareAddrOnce  sync.Once | ||||
| 	storageMutex      sync.Mutex | ||||
| 
 | ||||
| 	rand io.Reader | ||||
| 
 | ||||
| 	epochFunc     epochFunc | ||||
| 	hwAddrFunc    HWAddrFunc | ||||
| 	lastTime      uint64 | ||||
| 	clockSequence uint16 | ||||
| 	hardwareAddr  [6]byte | ||||
| } | ||||
| 
 | ||||
| // interface check -- build will fail if *Gen doesn't satisfy Generator | ||||
| var _ Generator = (*Gen)(nil) | ||||
| 
 | ||||
| // NewGen returns a new instance of Gen with some default values set. Most | ||||
| // people should use this. | ||||
| func NewGen() *Gen { | ||||
| 	return NewGenWithHWAF(defaultHWAddrFunc) | ||||
| } | ||||
| 
 | ||||
| // NewGenWithHWAF builds a new UUID generator with the HWAddrFunc provided. Most | ||||
| // consumers should use NewGen() instead. | ||||
| // | ||||
| // This is used so that consumers can generate their own MAC addresses, for use | ||||
| // in the generated UUIDs, if there is some concern about exposing the physical | ||||
| // address of the machine generating the UUID. | ||||
| // | ||||
| // The Gen generator will only invoke the HWAddrFunc once, and cache that MAC | ||||
| // address for all the future UUIDs generated by it. If you'd like to switch the | ||||
| // MAC address being used, you'll need to create a new generator using this | ||||
| // function. | ||||
| func NewGenWithHWAF(hwaf HWAddrFunc) *Gen { | ||||
| 	return &Gen{ | ||||
| 		epochFunc:  time.Now, | ||||
| 		hwAddrFunc: hwaf, | ||||
| 		rand:       rand.Reader, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // NewV1 returns a UUID based on the current timestamp and MAC address. | ||||
| func (g *Gen) NewV1() (UUID, error) { | ||||
| 	u := UUID{} | ||||
| 
 | ||||
| 	timeNow, clockSeq, err := g.getClockSequence() | ||||
| 	if err != nil { | ||||
| 		return Nil, err | ||||
| 	} | ||||
| 	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) | ||||
| 
 | ||||
| 	hardwareAddr, err := g.getHardwareAddr() | ||||
| 	if err != nil { | ||||
| 		return Nil, err | ||||
| 	} | ||||
| 	copy(u[10:], hardwareAddr) | ||||
| 
 | ||||
| 	u.SetVersion(V1) | ||||
| 	u.SetVariant(VariantRFC4122) | ||||
| 
 | ||||
| 	return u, nil | ||||
| } | ||||
| 
 | ||||
| // NewV2 returns a DCE Security UUID based on the POSIX UID/GID. | ||||
| func (g *Gen) NewV2(domain byte) (UUID, error) { | ||||
| 	u, err := g.NewV1() | ||||
| 	if err != nil { | ||||
| 		return Nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	switch domain { | ||||
| 	case DomainPerson: | ||||
| 		binary.BigEndian.PutUint32(u[:], posixUID) | ||||
| 	case DomainGroup: | ||||
| 		binary.BigEndian.PutUint32(u[:], posixGID) | ||||
| 	} | ||||
| 
 | ||||
| 	u[9] = domain | ||||
| 
 | ||||
| 	u.SetVersion(V2) | ||||
| 	u.SetVariant(VariantRFC4122) | ||||
| 
 | ||||
| 	return u, nil | ||||
| } | ||||
| 
 | ||||
| // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name. | ||||
| func (g *Gen) NewV3(ns UUID, name string) UUID { | ||||
| 	u := newFromHash(md5.New(), ns, name) | ||||
| 	u.SetVersion(V3) | ||||
| 	u.SetVariant(VariantRFC4122) | ||||
| 
 | ||||
| 	return u | ||||
| } | ||||
| 
 | ||||
| // NewV4 returns a randomly generated UUID. | ||||
| func (g *Gen) NewV4() (UUID, error) { | ||||
| 	u := UUID{} | ||||
| 	if _, err := io.ReadFull(g.rand, u[:]); err != nil { | ||||
| 		return Nil, err | ||||
| 	} | ||||
| 	u.SetVersion(V4) | ||||
| 	u.SetVariant(VariantRFC4122) | ||||
| 
 | ||||
| 	return u, nil | ||||
| } | ||||
| 
 | ||||
| // NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name. | ||||
| func (g *Gen) NewV5(ns UUID, name string) UUID { | ||||
| 	u := newFromHash(sha1.New(), ns, name) | ||||
| 	u.SetVersion(V5) | ||||
| 	u.SetVariant(VariantRFC4122) | ||||
| 
 | ||||
| 	return u | ||||
| } | ||||
| 
 | ||||
| // Returns the epoch and clock sequence. | ||||
| func (g *Gen) getClockSequence() (uint64, uint16, error) { | ||||
| 	var err error | ||||
| 	g.clockSequenceOnce.Do(func() { | ||||
| 		buf := make([]byte, 2) | ||||
| 		if _, err = io.ReadFull(g.rand, buf); err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 		g.clockSequence = binary.BigEndian.Uint16(buf) | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return 0, 0, err | ||||
| 	} | ||||
| 
 | ||||
| 	g.storageMutex.Lock() | ||||
| 	defer g.storageMutex.Unlock() | ||||
| 
 | ||||
| 	timeNow := g.getEpoch() | ||||
| 	// Clock didn't change since last UUID generation. | ||||
| 	// Should increase clock sequence. | ||||
| 	if timeNow <= g.lastTime { | ||||
| 		g.clockSequence++ | ||||
| 	} | ||||
| 	g.lastTime = timeNow | ||||
| 
 | ||||
| 	return timeNow, g.clockSequence, nil | ||||
| } | ||||
| 
 | ||||
| // Returns the hardware address. | ||||
| func (g *Gen) getHardwareAddr() ([]byte, error) { | ||||
| 	var err error | ||||
| 	g.hardwareAddrOnce.Do(func() { | ||||
| 		var hwAddr net.HardwareAddr | ||||
| 		if hwAddr, err = g.hwAddrFunc(); err == nil { | ||||
| 			copy(g.hardwareAddr[:], hwAddr) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// Initialize hardwareAddr randomly in case | ||||
| 		// of real network interfaces absence. | ||||
| 		if _, err = io.ReadFull(g.rand, g.hardwareAddr[:]); err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 		// Set multicast bit as recommended by RFC-4122 | ||||
| 		g.hardwareAddr[0] |= 0x01 | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return []byte{}, err | ||||
| 	} | ||||
| 	return g.hardwareAddr[:], nil | ||||
| } | ||||
| 
 | ||||
| // Returns the difference between UUID epoch (October 15, 1582) | ||||
| // and current time in 100-nanosecond intervals. | ||||
| func (g *Gen) getEpoch() uint64 { | ||||
| 	return epochStart + uint64(g.epochFunc().UnixNano()/100) | ||||
| } | ||||
| 
 | ||||
| // Returns the UUID based on the hashing of the 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 | ||||
| } | ||||
| 
 | ||||
| // Returns the hardware address. | ||||
| func defaultHWAddrFunc() (net.HardwareAddr, error) { | ||||
| 	ifaces, err := net.Interfaces() | ||||
| 	if err != nil { | ||||
| 		return []byte{}, err | ||||
| 	} | ||||
| 	for _, iface := range ifaces { | ||||
| 		if len(iface.HardwareAddr) >= 6 { | ||||
| 			return iface.HardwareAddr, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return []byte{}, fmt.Errorf("uuid: no HW address found") | ||||
| } | ||||
							
								
								
									
										109
									
								
								vendor/github.com/gofrs/uuid/sql.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										109
									
								
								vendor/github.com/gofrs/uuid/sql.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,109 +0,0 @@ | |||
| // 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" | ||||
| 	"database/sql/driver" | ||||
| 	"encoding/json" | ||||
| 	"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 will be handled by UnmarshalBinary, while | ||||
| // a longer byte slice or a string will be handled by UnmarshalText. | ||||
| func (u *UUID) Scan(src interface{}) error { | ||||
| 	switch src := src.(type) { | ||||
| 	case UUID: // support gorm convert from UUID to NullUUID | ||||
| 		*u = src | ||||
| 		return nil | ||||
| 
 | ||||
| 	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) | ||||
| } | ||||
| 
 | ||||
| // MarshalJSON marshals the NullUUID as null or the nested UUID | ||||
| func (u NullUUID) MarshalJSON() ([]byte, error) { | ||||
| 	if !u.Valid { | ||||
| 		return json.Marshal(nil) | ||||
| 	} | ||||
| 
 | ||||
| 	return json.Marshal(u.UUID) | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON unmarshals a NullUUID | ||||
| func (u *NullUUID) UnmarshalJSON(b []byte) error { | ||||
| 	if bytes.Equal(b, []byte("null")) { | ||||
| 		u.UUID, u.Valid = Nil, false | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	if err := json.Unmarshal(b, &u.UUID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	u.Valid = true | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										189
									
								
								vendor/github.com/gofrs/uuid/uuid.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										189
									
								
								vendor/github.com/gofrs/uuid/uuid.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,189 +0,0 @@ | |||
| // 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 provides implementations of the Universally Unique Identifier (UUID), as specified in RFC-4122 and DCE 1.1. | ||||
| // | ||||
| // RFC-4122[1] provides the specification for versions 1, 3, 4, and 5. | ||||
| // | ||||
| // DCE 1.1[2] provides the specification for version 2. | ||||
| // | ||||
| // [1] https://tools.ietf.org/html/rfc4122 | ||||
| // [2] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01 | ||||
| package uuid | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"encoding/hex" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Size of a UUID in bytes. | ||||
| const Size = 16 | ||||
| 
 | ||||
| // UUID is an array type to represent the value of a UUID, as defined in RFC-4122. | ||||
| type UUID [Size]byte | ||||
| 
 | ||||
| // UUID versions. | ||||
| const ( | ||||
| 	_  byte = iota | ||||
| 	V1      // Version 1 (date-time and MAC address) | ||||
| 	V2      // Version 2 (date-time and MAC address, DCE security version) | ||||
| 	V3      // Version 3 (namespace name-based) | ||||
| 	V4      // Version 4 (random) | ||||
| 	V5      // Version 5 (namespace name-based) | ||||
| ) | ||||
| 
 | ||||
| // UUID layout variants. | ||||
| const ( | ||||
| 	VariantNCS byte = iota | ||||
| 	VariantRFC4122 | ||||
| 	VariantMicrosoft | ||||
| 	VariantFuture | ||||
| ) | ||||
| 
 | ||||
| // UUID DCE domains. | ||||
| const ( | ||||
| 	DomainPerson = iota | ||||
| 	DomainGroup | ||||
| 	DomainOrg | ||||
| ) | ||||
| 
 | ||||
| // Timestamp is the count of 100-nanosecond intervals since 00:00:00.00, | ||||
| // 15 October 1582 within a V1 UUID. This type has no meaning for V2-V5 | ||||
| // UUIDs since they don't have an embedded timestamp. | ||||
| type Timestamp uint64 | ||||
| 
 | ||||
| const _100nsPerSecond = 10000000 | ||||
| 
 | ||||
| // Time returns the UTC time.Time representation of a Timestamp | ||||
| func (t Timestamp) Time() (time.Time, error) { | ||||
| 	secs := uint64(t) / _100nsPerSecond | ||||
| 	nsecs := 100 * (uint64(t) % _100nsPerSecond) | ||||
| 	return time.Unix(int64(secs)-(epochStart/_100nsPerSecond), int64(nsecs)), nil | ||||
| } | ||||
| 
 | ||||
| // TimestampFromV1 returns the Timestamp embedded within a V1 UUID. | ||||
| // Returns an error if the UUID is any version other than 1. | ||||
| func TimestampFromV1(u UUID) (Timestamp, error) { | ||||
| 	if u.Version() != 1 { | ||||
| 		err := fmt.Errorf("uuid: %s is version %d, not version 1", u, u.Version()) | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	low := binary.BigEndian.Uint32(u[0:4]) | ||||
| 	mid := binary.BigEndian.Uint16(u[4:6]) | ||||
| 	hi := binary.BigEndian.Uint16(u[6:8]) & 0xfff | ||||
| 	return Timestamp(uint64(low) + (uint64(mid) << 32) + (uint64(hi) << 48)), nil | ||||
| } | ||||
| 
 | ||||
| // String parse helpers. | ||||
| var ( | ||||
| 	urnPrefix  = []byte("urn:uuid:") | ||||
| 	byteGroups = []int{8, 4, 4, 4, 12} | ||||
| ) | ||||
| 
 | ||||
| // Nil is the nil UUID, as specified in RFC-4122, that has all 128 bits set to | ||||
| // zero. | ||||
| var Nil = UUID{} | ||||
| 
 | ||||
| // Predefined namespace UUIDs. | ||||
| var ( | ||||
| 	NamespaceDNS  = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) | ||||
| 	NamespaceURL  = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) | ||||
| 	NamespaceOID  = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) | ||||
| 	NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) | ||||
| ) | ||||
| 
 | ||||
| // Version returns the algorithm version used to generate the UUID. | ||||
| func (u UUID) Version() byte { | ||||
| 	return u[6] >> 4 | ||||
| } | ||||
| 
 | ||||
| // Variant returns the UUID layout variant. | ||||
| func (u UUID) Variant() byte { | ||||
| 	switch { | ||||
| 	case (u[8] >> 7) == 0x00: | ||||
| 		return VariantNCS | ||||
| 	case (u[8] >> 6) == 0x02: | ||||
| 		return VariantRFC4122 | ||||
| 	case (u[8] >> 5) == 0x06: | ||||
| 		return VariantMicrosoft | ||||
| 	case (u[8] >> 5) == 0x07: | ||||
| 		fallthrough | ||||
| 	default: | ||||
| 		return VariantFuture | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Bytes returns a byte slice representation of the UUID. | ||||
| func (u UUID) Bytes() []byte { | ||||
| 	return u[:] | ||||
| } | ||||
| 
 | ||||
| // String returns a canonical RFC-4122 string representation of the UUID: | ||||
| // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. | ||||
| func (u UUID) String() string { | ||||
| 	buf := make([]byte, 36) | ||||
| 
 | ||||
| 	hex.Encode(buf[0:8], u[0:4]) | ||||
| 	buf[8] = '-' | ||||
| 	hex.Encode(buf[9:13], u[4:6]) | ||||
| 	buf[13] = '-' | ||||
| 	hex.Encode(buf[14:18], u[6:8]) | ||||
| 	buf[18] = '-' | ||||
| 	hex.Encode(buf[19:23], u[8:10]) | ||||
| 	buf[23] = '-' | ||||
| 	hex.Encode(buf[24:], u[10:]) | ||||
| 
 | ||||
| 	return string(buf) | ||||
| } | ||||
| 
 | ||||
| // SetVersion sets the version bits. | ||||
| func (u *UUID) SetVersion(v byte) { | ||||
| 	u[6] = (u[6] & 0x0f) | (v << 4) | ||||
| } | ||||
| 
 | ||||
| // SetVariant sets the variant bits. | ||||
| func (u *UUID) SetVariant(v byte) { | ||||
| 	switch v { | ||||
| 	case VariantNCS: | ||||
| 		u[8] = (u[8]&(0xff>>1) | (0x00 << 7)) | ||||
| 	case VariantRFC4122: | ||||
| 		u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) | ||||
| 	case VariantMicrosoft: | ||||
| 		u[8] = (u[8]&(0xff>>3) | (0x06 << 5)) | ||||
| 	case VariantFuture: | ||||
| 		fallthrough | ||||
| 	default: | ||||
| 		u[8] = (u[8]&(0xff>>3) | (0x07 << 5)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Must is a helper that wraps a call to a function returning (UUID, error) | ||||
| // and panics if the error is non-nil. It is intended for use in variable | ||||
| // initializations such as | ||||
| //  var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")) | ||||
| func Must(u UUID, err error) UUID { | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return u | ||||
| } | ||||
							
								
								
									
										8
									
								
								vendor/github.com/gorilla/mux/AUTHORS
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/gorilla/mux/AUTHORS
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,8 +0,0 @@ | |||
| # This is the official list of gorilla/mux authors for copyright purposes. | ||||
| # | ||||
| # Please keep the list sorted. | ||||
| 
 | ||||
| Google LLC (https://opensource.google.com/) | ||||
| Kamil Kisielk <kamil@kamilkisiel.net> | ||||
| Matt Silverlock <matt@eatsleeprepeat.net> | ||||
| Rodrigo Moraes (https://github.com/moraes) | ||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue