| @@ -32,6 +32,7 @@ require ( | |||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible | |||
| github.com/dustin/go-humanize v1.0.0 | |||
| github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 | |||
| github.com/elliotchance/orderedmap v1.4.0 | |||
| github.com/emirpasic/gods v1.12.0 | |||
| github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a | |||
| github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect | |||
| @@ -100,7 +101,7 @@ require ( | |||
| github.com/sergi/go-diff v1.1.0 | |||
| github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect | |||
| github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd | |||
| github.com/stretchr/testify v1.4.0 | |||
| github.com/stretchr/testify v1.7.0 | |||
| github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect | |||
| github.com/tinylib/msgp v1.1.2 // indirect | |||
| github.com/tstranex/u2f v1.0.0 | |||
| @@ -180,6 +180,8 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP | |||
| github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 h1:mhPg/0hGebcpiiQLqJD2PWWyoHRLEdZ3sXKaEvT1EQU= | |||
| github.com/editorconfig/editorconfig-core-go/v2 v2.1.1/go.mod h1:/LuhWJiQ9Gvo1DhVpa4ssm5qeg8rrztdtI7j/iCie2k= | |||
| github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= | |||
| github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A= | |||
| github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= | |||
| github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= | |||
| github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= | |||
| github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a h1:M1bRpaZAn4GSsqu3hdK2R8H0AH9O6vqCTCbm2oAFGfE= | |||
| @@ -705,6 +707,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf | |||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | |||
| github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | |||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | |||
| github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | |||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |||
| github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= | |||
| github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= | |||
| github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= | |||
| @@ -1028,6 +1032,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||
| gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||
| gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= | |||
| gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | |||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |||
| grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= | |||
| honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | |||
| honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | |||
| @@ -7,10 +7,11 @@ import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "github.com/elliotchance/orderedmap" | |||
| ) | |||
| type ClientsManager struct { | |||
| Clients map[*Client]bool | |||
| Clients *orderedmap.OrderedMap | |||
| Register chan *Client | |||
| Unregister chan *Client | |||
| } | |||
| @@ -19,10 +20,12 @@ func NewClientsManager() *ClientsManager { | |||
| return &ClientsManager{ | |||
| Register: make(chan *Client), | |||
| Unregister: make(chan *Client), | |||
| Clients: make(map[*Client]bool), | |||
| Clients: orderedmap.NewOrderedMap(), | |||
| } | |||
| } | |||
| const MaxClients = 100 | |||
| var LastActionsQueue = NewSyncQueue(20) | |||
| func (h *ClientsManager) Run() { | |||
| @@ -33,29 +36,33 @@ func (h *ClientsManager) Run() { | |||
| for { | |||
| select { | |||
| case client := <-h.Register: | |||
| h.Clients[client] = true | |||
| h.Clients.Set(client, true) | |||
| if h.Clients.Len() > MaxClients { | |||
| h.Clients.Delete(h.Clients.Front().Key) | |||
| } | |||
| case client := <-h.Unregister: | |||
| if _, ok := h.Clients[client]; ok { | |||
| delete(h.Clients, client) | |||
| if _, ok := h.Clients.Get(client); ok { | |||
| h.Clients.Delete(client) | |||
| close(client.Send) | |||
| } | |||
| case message := <-models.ActionChan: | |||
| LastActionsQueue.Push(message) | |||
| for client := range h.Clients { | |||
| for _, client := range h.Clients.Keys() { | |||
| select { | |||
| case client.Send <- message: | |||
| case client.(*Client).Send <- message: | |||
| default: | |||
| close(client.Send) | |||
| delete(h.Clients, client) | |||
| close(client.(*Client).Send) | |||
| h.Clients.Delete(client) | |||
| } | |||
| } | |||
| case s := <-sig: | |||
| log.Info("received signal", s) | |||
| signalsReceived++ | |||
| if signalsReceived < 2 { | |||
| for client, _ := range h.Clients { | |||
| delete(h.Clients, client) | |||
| client.Close() | |||
| for _, client := range h.Clients.Keys() { | |||
| h.Clients.Delete(client) | |||
| client.(*Client).Close() | |||
| } | |||
| break | |||
| @@ -0,0 +1,12 @@ | |||
| root = true | |||
| [*] | |||
| charset = utf-8 | |||
| end_of_line = lf | |||
| insert_final_newline = true | |||
| trim_trailing_whitespace = true | |||
| [*.go] | |||
| indent_style = tab | |||
| indent_size = 4 | |||
| max_line_length = 80 | |||
| @@ -0,0 +1 @@ | |||
| /.idea | |||
| @@ -0,0 +1,9 @@ | |||
| language: go | |||
| go: | |||
| - 1.11.x | |||
| - 1.12.x | |||
| - master | |||
| script: | |||
| - env GO111MODULE=on go test | |||
| @@ -0,0 +1,21 @@ | |||
| MIT License | |||
| Copyright (c) 2020 Elliot Chance | |||
| 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. | |||
| @@ -0,0 +1,102 @@ | |||
| # 🔃 github.com/elliotchance/orderedmap [](https://godoc.org/github.com/elliotchance/orderedmap) [](https://travis-ci.org/elliotchance/orderedmap) | |||
| ## Installation | |||
| ```bash | |||
| go get -u github.com/elliotchance/orderedmap | |||
| ``` | |||
| ## Basic Usage | |||
| An `*OrderedMap` is a high performance ordered map that maintains amortized O(1) | |||
| for `Set`, `Get`, `Delete` and `Len`: | |||
| ```go | |||
| m := orderedmap.NewOrderedMap() | |||
| m.Set("foo", "bar") | |||
| m.Set("qux", 1.23) | |||
| m.Set(123, true) | |||
| m.Delete("qux") | |||
| ``` | |||
| Internally an `*OrderedMap` uses a combination of a map and linked list. | |||
| ## Iterating | |||
| Be careful using `Keys()` as it will create a copy of all of the keys so it's | |||
| only suitable for a small number of items: | |||
| ```go | |||
| for _, key := range m.Keys() { | |||
| value, _:= m.Get(key) | |||
| fmt.Println(key, value) | |||
| } | |||
| ``` | |||
| For larger maps you should use `Front()` or `Back()` to iterate per element: | |||
| ```go | |||
| // Iterate through all elements from oldest to newest: | |||
| for el := m.Front(); el != nil; el = el.Next() { | |||
| fmt.Println(el.Key, el.Value) | |||
| } | |||
| // You can also use Back and Prev to iterate in reverse: | |||
| for el := m.Back(); el != nil; el = el.Prev() { | |||
| fmt.Println(el.Key, el.Value) | |||
| } | |||
| ``` | |||
| The iterator is safe to use bidirectionally, and will return `nil` once it goes | |||
| beyond the first or last item. | |||
| If the map is changing while the iteration is in-flight it may produce | |||
| unexpected behavior. | |||
| ## Performance | |||
| CPU: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz | |||
| RAM: 8GB | |||
| System: Windows 10 | |||
| ```shell | |||
| $go test -benchmem -run=^$ github.com/elliotchance/orderedmap -bench BenchmarkAll | |||
| ``` | |||
| map[int]bool | |||
| | | map | orderedmap | | |||
| | ------- | ------------------- | ------------------- | | |||
| | set | 198 ns/op, 44 B/op | 722 ns/op, 211 B/op | | |||
| | get | 18 ns/op, 0 B/op | 37.3 ns/op, 0 B/op | | |||
| | delete | 888 ns/op, 211 B/op | 280 ns/op, 44 B/op | | |||
| | Iterate | 206 ns/op, 44 B/op | 693 ns/op, 259 B/op | | |||
| map[string]bool(PS : Use strconv.Itoa()) | |||
| | | map | orderedmap | | |||
| | ----------- | ------------------- | ----------------------- | | |||
| | set | 421 ns/op, 86 B/op | 1048 ns/op, 243 B/op | | |||
| | get | 81.1 ns/op, 2 B/op | 97.8 ns/op, 2 B/op | | |||
| | delete | 737 ns/op, 122 B/op | 1188 ns/op, 251 B/op | | |||
| | Iterate all | 14706 ns/op, 1 B/op | 52671 ns/op, 16391 B/op | | |||
| Big map[int]bool (10000000 keys) | |||
| | | map | orderedmap | | |||
| | ----------- | -------------------------------- | ------------------------------- | | |||
| | set all | 1.834559 s/op, 423.9470291 MB/op | 7.5564667 s/op, 1784.1483 MB/op | | |||
| | get all | 2.6367878 s/op, 423.9698 MB/op | 9.0232475 s/op, 1784.1086 MB/op | | |||
| | Iterate all | 1.9526784 s/op, 423.9042 MB/op | 8.2495265 s/op, 1936.7619 MB/op | | |||
| Big map[string]bool (10000000 keys) | |||
| | | map | orderedmap | | |||
| | ----------- | --------------------------------- | ----------------------------------- | | |||
| | set all | 4.8893923 s/op, 921.33435 MB/op | 10.4405527 s/op, 2089.0144 MB/op | | |||
| | get all | 7.122791 s/op, 997.3802643 MB/op | 13.2613692 s/op, 2165.09521 MB/op | | |||
| | Iterate all | 5.1688922 s/op, 921.4619293 MB/op | 12.6623711 s/op, 2241.5272064 MB/op | | |||
| @@ -0,0 +1,33 @@ | |||
| package orderedmap | |||
| import "container/list" | |||
| type Element struct { | |||
| Key, Value interface{} | |||
| element *list.Element | |||
| } | |||
| func newElement(e *list.Element) *Element { | |||
| if e == nil { | |||
| return nil | |||
| } | |||
| element := e.Value.(*orderedMapElement) | |||
| return &Element{ | |||
| element: e, | |||
| Key: element.key, | |||
| Value: element.value, | |||
| } | |||
| } | |||
| // Next returns the next element, or nil if it finished. | |||
| func (e *Element) Next() *Element { | |||
| return newElement(e.element.Next()) | |||
| } | |||
| // Prev returns the previous element, or nil if it finished. | |||
| func (e *Element) Prev() *Element { | |||
| return newElement(e.element.Prev()) | |||
| } | |||
| @@ -0,0 +1,5 @@ | |||
| module github.com/elliotchance/orderedmap | |||
| go 1.12 | |||
| require github.com/stretchr/testify v1.7.0 | |||
| @@ -0,0 +1,15 @@ | |||
| github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | |||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | |||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | |||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |||
| github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | |||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | |||
| github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | |||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |||
| 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/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | |||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | |||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |||
| @@ -0,0 +1,150 @@ | |||
| package orderedmap | |||
| import "container/list" | |||
| type orderedMapElement struct { | |||
| key, value interface{} | |||
| } | |||
| type OrderedMap struct { | |||
| kv map[interface{}]*list.Element | |||
| ll *list.List | |||
| } | |||
| func NewOrderedMap() *OrderedMap { | |||
| return &OrderedMap{ | |||
| kv: make(map[interface{}]*list.Element), | |||
| ll: list.New(), | |||
| } | |||
| } | |||
| // Get returns the value for a key. If the key does not exist, the second return | |||
| // parameter will be false and the value will be nil. | |||
| func (m *OrderedMap) Get(key interface{}) (interface{}, bool) { | |||
| value, ok := m.kv[key] | |||
| if ok { | |||
| return value.Value.(*orderedMapElement).value, true | |||
| } | |||
| return nil, false | |||
| } | |||
| // Set will set (or replace) a value for a key. If the key was new, then true | |||
| // will be returned. The returned value will be false if the value was replaced | |||
| // (even if the value was the same). | |||
| func (m *OrderedMap) Set(key, value interface{}) bool { | |||
| _, didExist := m.kv[key] | |||
| if !didExist { | |||
| element := m.ll.PushBack(&orderedMapElement{key, value}) | |||
| m.kv[key] = element | |||
| } else { | |||
| m.kv[key].Value.(*orderedMapElement).value = value | |||
| } | |||
| return !didExist | |||
| } | |||
| // GetOrDefault returns the value for a key. If the key does not exist, returns | |||
| // the default value instead. | |||
| func (m *OrderedMap) GetOrDefault(key, defaultValue interface{}) interface{} { | |||
| if value, ok := m.kv[key]; ok { | |||
| return value.Value.(*orderedMapElement).value | |||
| } | |||
| return defaultValue | |||
| } | |||
| // GetElement returns the element for a key. If the key does not exist, the | |||
| // pointer will be nil. | |||
| func (m *OrderedMap) GetElement(key interface{}) *Element { | |||
| value, ok := m.kv[key] | |||
| if ok { | |||
| element := value.Value.(*orderedMapElement) | |||
| return &Element{ | |||
| element: value, | |||
| Key: element.key, | |||
| Value: element.value, | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| // Len returns the number of elements in the map. | |||
| func (m *OrderedMap) Len() int { | |||
| return len(m.kv) | |||
| } | |||
| // Keys returns all of the keys in the order they were inserted. If a key was | |||
| // replaced it will retain the same position. To ensure most recently set keys | |||
| // are always at the end you must always Delete before Set. | |||
| func (m *OrderedMap) Keys() (keys []interface{}) { | |||
| keys = make([]interface{}, m.Len()) | |||
| element := m.ll.Front() | |||
| for i := 0; element != nil; i++ { | |||
| keys[i] = element.Value.(*orderedMapElement).key | |||
| element = element.Next() | |||
| } | |||
| return keys | |||
| } | |||
| // Delete will remove a key from the map. It will return true if the key was | |||
| // removed (the key did exist). | |||
| func (m *OrderedMap) Delete(key interface{}) (didDelete bool) { | |||
| element, ok := m.kv[key] | |||
| if ok { | |||
| m.ll.Remove(element) | |||
| delete(m.kv, key) | |||
| } | |||
| return ok | |||
| } | |||
| // Front will return the element that is the first (oldest Set element). If | |||
| // there are no elements this will return nil. | |||
| func (m *OrderedMap) Front() *Element { | |||
| front := m.ll.Front() | |||
| if front == nil { | |||
| return nil | |||
| } | |||
| element := front.Value.(*orderedMapElement) | |||
| return &Element{ | |||
| element: front, | |||
| Key: element.key, | |||
| Value: element.value, | |||
| } | |||
| } | |||
| // Back will return the element that is the last (most recent Set element). If | |||
| // there are no elements this will return nil. | |||
| func (m *OrderedMap) Back() *Element { | |||
| back := m.ll.Back() | |||
| if back == nil { | |||
| return nil | |||
| } | |||
| element := back.Value.(*orderedMapElement) | |||
| return &Element{ | |||
| element: back, | |||
| Key: element.key, | |||
| Value: element.value, | |||
| } | |||
| } | |||
| // Copy returns a new OrderedMap with the same elements. | |||
| // Using Copy while there are concurrent writes may mangle the result. | |||
| func (m *OrderedMap) Copy() *OrderedMap { | |||
| m2 := NewOrderedMap() | |||
| for el := m.Front(); el != nil; el = el.Next() { | |||
| m2.Set(el.Key, el.Value) | |||
| } | |||
| return m2 | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| MIT License | |||
| Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell | |||
| Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. | |||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| of this software and associated documentation files (the "Software"), to deal | |||
| @@ -0,0 +1,394 @@ | |||
| package assert | |||
| import ( | |||
| "fmt" | |||
| "reflect" | |||
| ) | |||
| type CompareType int | |||
| const ( | |||
| compareLess CompareType = iota - 1 | |||
| compareEqual | |||
| compareGreater | |||
| ) | |||
| var ( | |||
| intType = reflect.TypeOf(int(1)) | |||
| int8Type = reflect.TypeOf(int8(1)) | |||
| int16Type = reflect.TypeOf(int16(1)) | |||
| int32Type = reflect.TypeOf(int32(1)) | |||
| int64Type = reflect.TypeOf(int64(1)) | |||
| uintType = reflect.TypeOf(uint(1)) | |||
| uint8Type = reflect.TypeOf(uint8(1)) | |||
| uint16Type = reflect.TypeOf(uint16(1)) | |||
| uint32Type = reflect.TypeOf(uint32(1)) | |||
| uint64Type = reflect.TypeOf(uint64(1)) | |||
| float32Type = reflect.TypeOf(float32(1)) | |||
| float64Type = reflect.TypeOf(float64(1)) | |||
| stringType = reflect.TypeOf("") | |||
| ) | |||
| func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { | |||
| obj1Value := reflect.ValueOf(obj1) | |||
| obj2Value := reflect.ValueOf(obj2) | |||
| // throughout this switch we try and avoid calling .Convert() if possible, | |||
| // as this has a pretty big performance impact | |||
| switch kind { | |||
| case reflect.Int: | |||
| { | |||
| intobj1, ok := obj1.(int) | |||
| if !ok { | |||
| intobj1 = obj1Value.Convert(intType).Interface().(int) | |||
| } | |||
| intobj2, ok := obj2.(int) | |||
| if !ok { | |||
| intobj2 = obj2Value.Convert(intType).Interface().(int) | |||
| } | |||
| if intobj1 > intobj2 { | |||
| return compareGreater, true | |||
| } | |||
| if intobj1 == intobj2 { | |||
| return compareEqual, true | |||
| } | |||
| if intobj1 < intobj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Int8: | |||
| { | |||
| int8obj1, ok := obj1.(int8) | |||
| if !ok { | |||
| int8obj1 = obj1Value.Convert(int8Type).Interface().(int8) | |||
| } | |||
| int8obj2, ok := obj2.(int8) | |||
| if !ok { | |||
| int8obj2 = obj2Value.Convert(int8Type).Interface().(int8) | |||
| } | |||
| if int8obj1 > int8obj2 { | |||
| return compareGreater, true | |||
| } | |||
| if int8obj1 == int8obj2 { | |||
| return compareEqual, true | |||
| } | |||
| if int8obj1 < int8obj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Int16: | |||
| { | |||
| int16obj1, ok := obj1.(int16) | |||
| if !ok { | |||
| int16obj1 = obj1Value.Convert(int16Type).Interface().(int16) | |||
| } | |||
| int16obj2, ok := obj2.(int16) | |||
| if !ok { | |||
| int16obj2 = obj2Value.Convert(int16Type).Interface().(int16) | |||
| } | |||
| if int16obj1 > int16obj2 { | |||
| return compareGreater, true | |||
| } | |||
| if int16obj1 == int16obj2 { | |||
| return compareEqual, true | |||
| } | |||
| if int16obj1 < int16obj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Int32: | |||
| { | |||
| int32obj1, ok := obj1.(int32) | |||
| if !ok { | |||
| int32obj1 = obj1Value.Convert(int32Type).Interface().(int32) | |||
| } | |||
| int32obj2, ok := obj2.(int32) | |||
| if !ok { | |||
| int32obj2 = obj2Value.Convert(int32Type).Interface().(int32) | |||
| } | |||
| if int32obj1 > int32obj2 { | |||
| return compareGreater, true | |||
| } | |||
| if int32obj1 == int32obj2 { | |||
| return compareEqual, true | |||
| } | |||
| if int32obj1 < int32obj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Int64: | |||
| { | |||
| int64obj1, ok := obj1.(int64) | |||
| if !ok { | |||
| int64obj1 = obj1Value.Convert(int64Type).Interface().(int64) | |||
| } | |||
| int64obj2, ok := obj2.(int64) | |||
| if !ok { | |||
| int64obj2 = obj2Value.Convert(int64Type).Interface().(int64) | |||
| } | |||
| if int64obj1 > int64obj2 { | |||
| return compareGreater, true | |||
| } | |||
| if int64obj1 == int64obj2 { | |||
| return compareEqual, true | |||
| } | |||
| if int64obj1 < int64obj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Uint: | |||
| { | |||
| uintobj1, ok := obj1.(uint) | |||
| if !ok { | |||
| uintobj1 = obj1Value.Convert(uintType).Interface().(uint) | |||
| } | |||
| uintobj2, ok := obj2.(uint) | |||
| if !ok { | |||
| uintobj2 = obj2Value.Convert(uintType).Interface().(uint) | |||
| } | |||
| if uintobj1 > uintobj2 { | |||
| return compareGreater, true | |||
| } | |||
| if uintobj1 == uintobj2 { | |||
| return compareEqual, true | |||
| } | |||
| if uintobj1 < uintobj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Uint8: | |||
| { | |||
| uint8obj1, ok := obj1.(uint8) | |||
| if !ok { | |||
| uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8) | |||
| } | |||
| uint8obj2, ok := obj2.(uint8) | |||
| if !ok { | |||
| uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8) | |||
| } | |||
| if uint8obj1 > uint8obj2 { | |||
| return compareGreater, true | |||
| } | |||
| if uint8obj1 == uint8obj2 { | |||
| return compareEqual, true | |||
| } | |||
| if uint8obj1 < uint8obj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Uint16: | |||
| { | |||
| uint16obj1, ok := obj1.(uint16) | |||
| if !ok { | |||
| uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16) | |||
| } | |||
| uint16obj2, ok := obj2.(uint16) | |||
| if !ok { | |||
| uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16) | |||
| } | |||
| if uint16obj1 > uint16obj2 { | |||
| return compareGreater, true | |||
| } | |||
| if uint16obj1 == uint16obj2 { | |||
| return compareEqual, true | |||
| } | |||
| if uint16obj1 < uint16obj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Uint32: | |||
| { | |||
| uint32obj1, ok := obj1.(uint32) | |||
| if !ok { | |||
| uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32) | |||
| } | |||
| uint32obj2, ok := obj2.(uint32) | |||
| if !ok { | |||
| uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32) | |||
| } | |||
| if uint32obj1 > uint32obj2 { | |||
| return compareGreater, true | |||
| } | |||
| if uint32obj1 == uint32obj2 { | |||
| return compareEqual, true | |||
| } | |||
| if uint32obj1 < uint32obj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Uint64: | |||
| { | |||
| uint64obj1, ok := obj1.(uint64) | |||
| if !ok { | |||
| uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64) | |||
| } | |||
| uint64obj2, ok := obj2.(uint64) | |||
| if !ok { | |||
| uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64) | |||
| } | |||
| if uint64obj1 > uint64obj2 { | |||
| return compareGreater, true | |||
| } | |||
| if uint64obj1 == uint64obj2 { | |||
| return compareEqual, true | |||
| } | |||
| if uint64obj1 < uint64obj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Float32: | |||
| { | |||
| float32obj1, ok := obj1.(float32) | |||
| if !ok { | |||
| float32obj1 = obj1Value.Convert(float32Type).Interface().(float32) | |||
| } | |||
| float32obj2, ok := obj2.(float32) | |||
| if !ok { | |||
| float32obj2 = obj2Value.Convert(float32Type).Interface().(float32) | |||
| } | |||
| if float32obj1 > float32obj2 { | |||
| return compareGreater, true | |||
| } | |||
| if float32obj1 == float32obj2 { | |||
| return compareEqual, true | |||
| } | |||
| if float32obj1 < float32obj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.Float64: | |||
| { | |||
| float64obj1, ok := obj1.(float64) | |||
| if !ok { | |||
| float64obj1 = obj1Value.Convert(float64Type).Interface().(float64) | |||
| } | |||
| float64obj2, ok := obj2.(float64) | |||
| if !ok { | |||
| float64obj2 = obj2Value.Convert(float64Type).Interface().(float64) | |||
| } | |||
| if float64obj1 > float64obj2 { | |||
| return compareGreater, true | |||
| } | |||
| if float64obj1 == float64obj2 { | |||
| return compareEqual, true | |||
| } | |||
| if float64obj1 < float64obj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| case reflect.String: | |||
| { | |||
| stringobj1, ok := obj1.(string) | |||
| if !ok { | |||
| stringobj1 = obj1Value.Convert(stringType).Interface().(string) | |||
| } | |||
| stringobj2, ok := obj2.(string) | |||
| if !ok { | |||
| stringobj2 = obj2Value.Convert(stringType).Interface().(string) | |||
| } | |||
| if stringobj1 > stringobj2 { | |||
| return compareGreater, true | |||
| } | |||
| if stringobj1 == stringobj2 { | |||
| return compareEqual, true | |||
| } | |||
| if stringobj1 < stringobj2 { | |||
| return compareLess, true | |||
| } | |||
| } | |||
| } | |||
| return compareEqual, false | |||
| } | |||
| // Greater asserts that the first element is greater than the second | |||
| // | |||
| // assert.Greater(t, 2, 1) | |||
| // assert.Greater(t, float64(2), float64(1)) | |||
| // assert.Greater(t, "b", "a") | |||
| func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
| return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) | |||
| } | |||
| // GreaterOrEqual asserts that the first element is greater than or equal to the second | |||
| // | |||
| // assert.GreaterOrEqual(t, 2, 1) | |||
| // assert.GreaterOrEqual(t, 2, 2) | |||
| // assert.GreaterOrEqual(t, "b", "a") | |||
| // assert.GreaterOrEqual(t, "b", "b") | |||
| func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
| return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) | |||
| } | |||
| // Less asserts that the first element is less than the second | |||
| // | |||
| // assert.Less(t, 1, 2) | |||
| // assert.Less(t, float64(1), float64(2)) | |||
| // assert.Less(t, "a", "b") | |||
| func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
| return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) | |||
| } | |||
| // LessOrEqual asserts that the first element is less than or equal to the second | |||
| // | |||
| // assert.LessOrEqual(t, 1, 2) | |||
| // assert.LessOrEqual(t, 2, 2) | |||
| // assert.LessOrEqual(t, "a", "b") | |||
| // assert.LessOrEqual(t, "b", "b") | |||
| func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
| return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) | |||
| } | |||
| // Positive asserts that the specified element is positive | |||
| // | |||
| // assert.Positive(t, 1) | |||
| // assert.Positive(t, 1.23) | |||
| func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { | |||
| zero := reflect.Zero(reflect.TypeOf(e)) | |||
| return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs) | |||
| } | |||
| // Negative asserts that the specified element is negative | |||
| // | |||
| // assert.Negative(t, -1) | |||
| // assert.Negative(t, -1.23) | |||
| func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { | |||
| zero := reflect.Zero(reflect.TypeOf(e)) | |||
| return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs) | |||
| } | |||
| func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| e1Kind := reflect.ValueOf(e1).Kind() | |||
| e2Kind := reflect.ValueOf(e2).Kind() | |||
| if e1Kind != e2Kind { | |||
| return Fail(t, "Elements should be the same type", msgAndArgs...) | |||
| } | |||
| compareResult, isComparable := compare(e1, e2, e1Kind) | |||
| if !isComparable { | |||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||
| } | |||
| if !containsValue(allowedComparesResults, compareResult) { | |||
| return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...) | |||
| } | |||
| return true | |||
| } | |||
| func containsValue(values []CompareType, value CompareType) bool { | |||
| for _, v := range values { | |||
| if v == value { | |||
| return true | |||
| } | |||
| } | |||
| return false | |||
| } | |||
| @@ -32,7 +32,8 @@ func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args | |||
| return Contains(t, s, contains, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||
| // DirExistsf checks whether a directory exists in the given path. It also fails | |||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||
| func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -92,7 +93,7 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args | |||
| // EqualValuesf asserts that two objects are equal or convertable to the same types | |||
| // and equal. | |||
| // | |||
| // assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) | |||
| // assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") | |||
| func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -113,6 +114,24 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { | |||
| return Error(t, err, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||
| // This is a wrapper for errors.As. | |||
| func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // Eventuallyf asserts that given condition will be met in waitFor time, | |||
| // periodically checking target function each tick. | |||
| // | |||
| @@ -126,7 +145,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick | |||
| // Exactlyf asserts that two objects are equal in value and type. | |||
| // | |||
| // assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) | |||
| // assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") | |||
| func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -160,7 +179,8 @@ func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { | |||
| return False(t, value, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||
| // FileExistsf checks whether a file exists in the given path. It also fails if | |||
| // the path points to a directory or there is an error when trying to check the file. | |||
| func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -171,7 +191,7 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool | |||
| // Greaterf asserts that the first element is greater than the second | |||
| // | |||
| // assert.Greaterf(t, 2, 1, "error message %s", "formatted") | |||
| // assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) | |||
| // assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") | |||
| // assert.Greaterf(t, "b", "a", "error message %s", "formatted") | |||
| func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -223,7 +243,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u | |||
| // | |||
| // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | |||
| // | |||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -235,7 +255,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, | |||
| // | |||
| // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | |||
| // | |||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -243,6 +263,18 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri | |||
| return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||
| // | |||
| // assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||
| // | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // HTTPSuccessf asserts that a specified handler returns a success status code. | |||
| // | |||
| // assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") | |||
| @@ -257,7 +289,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin | |||
| // Implementsf asserts that an object is implemented by the specified interface. | |||
| // | |||
| // assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||
| // assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||
| func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -267,7 +299,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms | |||
| // InDeltaf asserts that the two numerals are within delta of each other. | |||
| // | |||
| // assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||
| // assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||
| func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -307,6 +339,54 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil | |||
| return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // IsDecreasingf asserts that the collection is decreasing | |||
| // | |||
| // assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") | |||
| // assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||
| // assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||
| func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsDecreasing(t, object, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // IsIncreasingf asserts that the collection is increasing | |||
| // | |||
| // assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") | |||
| // assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||
| // assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||
| func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsIncreasing(t, object, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // IsNonDecreasingf asserts that the collection is not decreasing | |||
| // | |||
| // assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") | |||
| // assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||
| // assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||
| func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // IsNonIncreasingf asserts that the collection is not increasing | |||
| // | |||
| // assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") | |||
| // assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||
| // assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||
| func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // IsTypef asserts that the specified objects are of the same type. | |||
| func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -325,14 +405,6 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int | |||
| return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||
| func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // Lenf asserts that the specified object has specific length. | |||
| // Lenf also fails if the object has a type that len() not accept. | |||
| // | |||
| @@ -347,7 +419,7 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf | |||
| // Lessf asserts that the first element is less than the second | |||
| // | |||
| // assert.Lessf(t, 1, 2, "error message %s", "formatted") | |||
| // assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) | |||
| // assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") | |||
| // assert.Lessf(t, "a", "b", "error message %s", "formatted") | |||
| func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -369,6 +441,28 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . | |||
| return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // Negativef asserts that the specified element is negative | |||
| // | |||
| // assert.Negativef(t, -1, "error message %s", "formatted") | |||
| // assert.Negativef(t, -1.23, "error message %s", "formatted") | |||
| func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Negative(t, e, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||
| // periodically checking the target function each tick. | |||
| // | |||
| // assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||
| func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Never(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // Nilf asserts that the specified object is nil. | |||
| // | |||
| // assert.Nilf(t, err, "error message %s", "formatted") | |||
| @@ -379,6 +473,15 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool | |||
| return Nil(t, object, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // NoDirExistsf checks whether a directory does not exist in the given path. | |||
| // It fails if the path points to an existing _directory_ only. | |||
| func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NoDirExists(t, path, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // NoErrorf asserts that a function returned no error (i.e. `nil`). | |||
| // | |||
| // actualObj, err := SomeFunction() | |||
| @@ -392,6 +495,15 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { | |||
| return NoError(t, err, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // NoFileExistsf checks whether a file does not exist in a given path. It fails | |||
| // if the path points to an existing _file_ only. | |||
| func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NoFileExists(t, path, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the | |||
| // specified substring or element. | |||
| // | |||
| @@ -431,6 +543,25 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, | |||
| return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||
| // | |||
| // assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") | |||
| func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // NotNilf asserts that the specified object is not nil. | |||
| // | |||
| // assert.NotNilf(t, err, "error message %s", "formatted") | |||
| @@ -453,7 +584,7 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo | |||
| // NotRegexpf asserts that a specified regexp does not match a string. | |||
| // | |||
| // assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||
| // assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||
| // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") | |||
| func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -462,6 +593,19 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. | |||
| return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // NotSamef asserts that two pointers do not reference the same object. | |||
| // | |||
| // assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") | |||
| // | |||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||
| // determined based on the equality of both type and value. | |||
| func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // NotSubsetf asserts that the specified list(array, slice...) contains not all | |||
| // elements given in the specified subset(array, slice...). | |||
| // | |||
| @@ -491,6 +635,18 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool | |||
| return Panics(t, f, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||
| // panics, and that the recovered panic value is an error that satisfies the | |||
| // EqualError comparison. | |||
| // | |||
| // assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||
| func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return PanicsWithError(t, errString, f, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that | |||
| // the recovered panic value equals the expected panic value. | |||
| // | |||
| @@ -502,9 +658,20 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str | |||
| return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // Positivef asserts that the specified element is positive | |||
| // | |||
| // assert.Positivef(t, 1, "error message %s", "formatted") | |||
| // assert.Positivef(t, 1.23, "error message %s", "formatted") | |||
| func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Positive(t, e, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // Regexpf asserts that a specified regexp matches a string. | |||
| // | |||
| // assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||
| // assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||
| // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") | |||
| func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -557,6 +724,14 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim | |||
| return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||
| func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) | |||
| } | |||
| // Zerof asserts that i is the zero value for its type. | |||
| func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -53,7 +53,8 @@ func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, | |||
| return Containsf(a.t, s, contains, msg, args...) | |||
| } | |||
| // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||
| // DirExists checks whether a directory exists in the given path. It also fails | |||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||
| func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -61,7 +62,8 @@ func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { | |||
| return DirExists(a.t, path, msgAndArgs...) | |||
| } | |||
| // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||
| // DirExistsf checks whether a directory exists in the given path. It also fails | |||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||
| func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -167,7 +169,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn | |||
| // EqualValuesf asserts that two objects are equal or convertable to the same types | |||
| // and equal. | |||
| // | |||
| // a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) | |||
| // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") | |||
| func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -202,6 +204,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { | |||
| return Error(a.t, err, msgAndArgs...) | |||
| } | |||
| // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||
| // This is a wrapper for errors.As. | |||
| func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return ErrorAs(a.t, err, target, msgAndArgs...) | |||
| } | |||
| // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||
| // This is a wrapper for errors.As. | |||
| func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return ErrorAsf(a.t, err, target, msg, args...) | |||
| } | |||
| // ErrorIs asserts that at least one of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return ErrorIs(a.t, err, target, msgAndArgs...) | |||
| } | |||
| // ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return ErrorIsf(a.t, err, target, msg, args...) | |||
| } | |||
| // Errorf asserts that a function returned an error (i.e. not `nil`). | |||
| // | |||
| // actualObj, err := SomeFunction() | |||
| @@ -249,7 +287,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg | |||
| // Exactlyf asserts that two objects are equal in value and type. | |||
| // | |||
| // a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) | |||
| // a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") | |||
| func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -309,7 +347,8 @@ func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { | |||
| return Falsef(a.t, value, msg, args...) | |||
| } | |||
| // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||
| // FileExists checks whether a file exists in the given path. It also fails if | |||
| // the path points to a directory or there is an error when trying to check the file. | |||
| func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -317,7 +356,8 @@ func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { | |||
| return FileExists(a.t, path, msgAndArgs...) | |||
| } | |||
| // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||
| // FileExistsf checks whether a file exists in the given path. It also fails if | |||
| // the path points to a directory or there is an error when trying to check the file. | |||
| func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -366,7 +406,7 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, | |||
| // Greaterf asserts that the first element is greater than the second | |||
| // | |||
| // a.Greaterf(2, 1, "error message %s", "formatted") | |||
| // a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) | |||
| // a.Greaterf(float64(2), float64(1), "error message %s", "formatted") | |||
| // a.Greaterf("b", "a", "error message %s", "formatted") | |||
| func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -443,7 +483,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri | |||
| // | |||
| // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | |||
| // | |||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -467,7 +507,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s | |||
| // | |||
| // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | |||
| // | |||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -475,6 +515,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url | |||
| return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) | |||
| } | |||
| // HTTPStatusCode asserts that a specified handler returns a specified status code. | |||
| // | |||
| // a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) | |||
| // | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) | |||
| } | |||
| // HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||
| // | |||
| // a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||
| // | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) | |||
| } | |||
| // HTTPSuccess asserts that a specified handler returns a success status code. | |||
| // | |||
| // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | |||
| @@ -511,7 +575,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, | |||
| // Implementsf asserts that an object is implemented by the specified interface. | |||
| // | |||
| // a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||
| // a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||
| func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -521,7 +585,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} | |||
| // InDelta asserts that the two numerals are within delta of each other. | |||
| // | |||
| // a.InDelta(math.Pi, (22 / 7.0), 0.01) | |||
| // a.InDelta(math.Pi, 22/7.0, 0.01) | |||
| func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -563,7 +627,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del | |||
| // InDeltaf asserts that the two numerals are within delta of each other. | |||
| // | |||
| // a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||
| // a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||
| func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -603,6 +667,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo | |||
| return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) | |||
| } | |||
| // IsDecreasing asserts that the collection is decreasing | |||
| // | |||
| // a.IsDecreasing([]int{2, 1, 0}) | |||
| // a.IsDecreasing([]float{2, 1}) | |||
| // a.IsDecreasing([]string{"b", "a"}) | |||
| func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsDecreasing(a.t, object, msgAndArgs...) | |||
| } | |||
| // IsDecreasingf asserts that the collection is decreasing | |||
| // | |||
| // a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") | |||
| // a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") | |||
| // a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||
| func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsDecreasingf(a.t, object, msg, args...) | |||
| } | |||
| // IsIncreasing asserts that the collection is increasing | |||
| // | |||
| // a.IsIncreasing([]int{1, 2, 3}) | |||
| // a.IsIncreasing([]float{1, 2}) | |||
| // a.IsIncreasing([]string{"a", "b"}) | |||
| func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsIncreasing(a.t, object, msgAndArgs...) | |||
| } | |||
| // IsIncreasingf asserts that the collection is increasing | |||
| // | |||
| // a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") | |||
| // a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") | |||
| // a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||
| func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsIncreasingf(a.t, object, msg, args...) | |||
| } | |||
| // IsNonDecreasing asserts that the collection is not decreasing | |||
| // | |||
| // a.IsNonDecreasing([]int{1, 1, 2}) | |||
| // a.IsNonDecreasing([]float{1, 2}) | |||
| // a.IsNonDecreasing([]string{"a", "b"}) | |||
| func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsNonDecreasing(a.t, object, msgAndArgs...) | |||
| } | |||
| // IsNonDecreasingf asserts that the collection is not decreasing | |||
| // | |||
| // a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") | |||
| // a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") | |||
| // a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||
| func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsNonDecreasingf(a.t, object, msg, args...) | |||
| } | |||
| // IsNonIncreasing asserts that the collection is not increasing | |||
| // | |||
| // a.IsNonIncreasing([]int{2, 1, 1}) | |||
| // a.IsNonIncreasing([]float{2, 1}) | |||
| // a.IsNonIncreasing([]string{"b", "a"}) | |||
| func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsNonIncreasing(a.t, object, msgAndArgs...) | |||
| } | |||
| // IsNonIncreasingf asserts that the collection is not increasing | |||
| // | |||
| // a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") | |||
| // a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") | |||
| // a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||
| func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return IsNonIncreasingf(a.t, object, msg, args...) | |||
| } | |||
| // IsType asserts that the specified objects are of the same type. | |||
| func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -639,22 +799,6 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. | |||
| return JSONEqf(a.t, expected, actual, msg, args...) | |||
| } | |||
| // YAMLEq asserts that two YAML strings are equivalent. | |||
| func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return YAMLEq(a.t, expected, actual, msgAndArgs...) | |||
| } | |||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||
| func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return YAMLEqf(a.t, expected, actual, msg, args...) | |||
| } | |||
| // Len asserts that the specified object has specific length. | |||
| // Len also fails if the object has a type that len() not accept. | |||
| // | |||
| @@ -718,7 +862,7 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar | |||
| // Lessf asserts that the first element is less than the second | |||
| // | |||
| // a.Lessf(1, 2, "error message %s", "formatted") | |||
| // a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) | |||
| // a.Lessf(float64(1), float64(2), "error message %s", "formatted") | |||
| // a.Lessf("a", "b", "error message %s", "formatted") | |||
| func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -727,6 +871,50 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i | |||
| return Lessf(a.t, e1, e2, msg, args...) | |||
| } | |||
| // Negative asserts that the specified element is negative | |||
| // | |||
| // a.Negative(-1) | |||
| // a.Negative(-1.23) | |||
| func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Negative(a.t, e, msgAndArgs...) | |||
| } | |||
| // Negativef asserts that the specified element is negative | |||
| // | |||
| // a.Negativef(-1, "error message %s", "formatted") | |||
| // a.Negativef(-1.23, "error message %s", "formatted") | |||
| func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Negativef(a.t, e, msg, args...) | |||
| } | |||
| // Never asserts that the given condition doesn't satisfy in waitFor time, | |||
| // periodically checking the target function each tick. | |||
| // | |||
| // a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) | |||
| func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Never(a.t, condition, waitFor, tick, msgAndArgs...) | |||
| } | |||
| // Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||
| // periodically checking the target function each tick. | |||
| // | |||
| // a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||
| func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Neverf(a.t, condition, waitFor, tick, msg, args...) | |||
| } | |||
| // Nil asserts that the specified object is nil. | |||
| // | |||
| // a.Nil(err) | |||
| @@ -747,6 +935,24 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) b | |||
| return Nilf(a.t, object, msg, args...) | |||
| } | |||
| // NoDirExists checks whether a directory does not exist in the given path. | |||
| // It fails if the path points to an existing _directory_ only. | |||
| func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NoDirExists(a.t, path, msgAndArgs...) | |||
| } | |||
| // NoDirExistsf checks whether a directory does not exist in the given path. | |||
| // It fails if the path points to an existing _directory_ only. | |||
| func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NoDirExistsf(a.t, path, msg, args...) | |||
| } | |||
| // NoError asserts that a function returned no error (i.e. `nil`). | |||
| // | |||
| // actualObj, err := SomeFunction() | |||
| @@ -773,6 +979,24 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { | |||
| return NoErrorf(a.t, err, msg, args...) | |||
| } | |||
| // NoFileExists checks whether a file does not exist in a given path. It fails | |||
| // if the path points to an existing _file_ only. | |||
| func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NoFileExists(a.t, path, msgAndArgs...) | |||
| } | |||
| // NoFileExistsf checks whether a file does not exist in a given path. It fails | |||
| // if the path points to an existing _file_ only. | |||
| func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NoFileExistsf(a.t, path, msg, args...) | |||
| } | |||
| // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | |||
| // specified substring or element. | |||
| // | |||
| @@ -838,6 +1062,26 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr | |||
| return NotEqual(a.t, expected, actual, msgAndArgs...) | |||
| } | |||
| // NotEqualValues asserts that two objects are not equal even when converted to the same type | |||
| // | |||
| // a.NotEqualValues(obj1, obj2) | |||
| func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NotEqualValues(a.t, expected, actual, msgAndArgs...) | |||
| } | |||
| // NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||
| // | |||
| // a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") | |||
| func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NotEqualValuesf(a.t, expected, actual, msg, args...) | |||
| } | |||
| // NotEqualf asserts that the specified values are NOT equal. | |||
| // | |||
| // a.NotEqualf(obj1, obj2, "error message %s", "formatted") | |||
| @@ -851,6 +1095,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str | |||
| return NotEqualf(a.t, expected, actual, msg, args...) | |||
| } | |||
| // NotErrorIs asserts that at none of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NotErrorIs(a.t, err, target, msgAndArgs...) | |||
| } | |||
| // NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NotErrorIsf(a.t, err, target, msg, args...) | |||
| } | |||
| // NotNil asserts that the specified object is not nil. | |||
| // | |||
| // a.NotNil(err) | |||
| @@ -904,7 +1166,7 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in | |||
| // NotRegexpf asserts that a specified regexp does not match a string. | |||
| // | |||
| // a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||
| // a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||
| // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | |||
| func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -913,6 +1175,32 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg | |||
| return NotRegexpf(a.t, rx, str, msg, args...) | |||
| } | |||
| // NotSame asserts that two pointers do not reference the same object. | |||
| // | |||
| // a.NotSame(ptr1, ptr2) | |||
| // | |||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||
| // determined based on the equality of both type and value. | |||
| func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NotSame(a.t, expected, actual, msgAndArgs...) | |||
| } | |||
| // NotSamef asserts that two pointers do not reference the same object. | |||
| // | |||
| // a.NotSamef(ptr1, ptr2, "error message %s", "formatted") | |||
| // | |||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||
| // determined based on the equality of both type and value. | |||
| func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return NotSamef(a.t, expected, actual, msg, args...) | |||
| } | |||
| // NotSubset asserts that the specified list(array, slice...) contains not all | |||
| // elements given in the specified subset(array, slice...). | |||
| // | |||
| @@ -961,6 +1249,30 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||
| return Panics(a.t, f, msgAndArgs...) | |||
| } | |||
| // PanicsWithError asserts that the code inside the specified PanicTestFunc | |||
| // panics, and that the recovered panic value is an error that satisfies the | |||
| // EqualError comparison. | |||
| // | |||
| // a.PanicsWithError("crazy error", func(){ GoCrazy() }) | |||
| func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return PanicsWithError(a.t, errString, f, msgAndArgs...) | |||
| } | |||
| // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||
| // panics, and that the recovered panic value is an error that satisfies the | |||
| // EqualError comparison. | |||
| // | |||
| // a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||
| func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return PanicsWithErrorf(a.t, errString, f, msg, args...) | |||
| } | |||
| // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | |||
| // the recovered panic value equals the expected panic value. | |||
| // | |||
| @@ -993,6 +1305,28 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b | |||
| return Panicsf(a.t, f, msg, args...) | |||
| } | |||
| // Positive asserts that the specified element is positive | |||
| // | |||
| // a.Positive(1) | |||
| // a.Positive(1.23) | |||
| func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Positive(a.t, e, msgAndArgs...) | |||
| } | |||
| // Positivef asserts that the specified element is positive | |||
| // | |||
| // a.Positivef(1, "error message %s", "formatted") | |||
| // a.Positivef(1.23, "error message %s", "formatted") | |||
| func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Positivef(a.t, e, msg, args...) | |||
| } | |||
| // Regexp asserts that a specified regexp matches a string. | |||
| // | |||
| // a.Regexp(regexp.MustCompile("start"), "it's starting") | |||
| @@ -1006,7 +1340,7 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter | |||
| // Regexpf asserts that a specified regexp matches a string. | |||
| // | |||
| // a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||
| // a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||
| // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | |||
| func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -1103,6 +1437,22 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta | |||
| return WithinDurationf(a.t, expected, actual, delta, msg, args...) | |||
| } | |||
| // YAMLEq asserts that two YAML strings are equivalent. | |||
| func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return YAMLEq(a.t, expected, actual, msgAndArgs...) | |||
| } | |||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||
| func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return YAMLEqf(a.t, expected, actual, msg, args...) | |||
| } | |||
| // Zero asserts that i is the zero value for its type. | |||
| func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -5,305 +5,77 @@ import ( | |||
| "reflect" | |||
| ) | |||
| func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { | |||
| switch kind { | |||
| case reflect.Int: | |||
| { | |||
| intobj1 := obj1.(int) | |||
| intobj2 := obj2.(int) | |||
| if intobj1 > intobj2 { | |||
| return -1, true | |||
| } | |||
| if intobj1 == intobj2 { | |||
| return 0, true | |||
| } | |||
| if intobj1 < intobj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Int8: | |||
| { | |||
| int8obj1 := obj1.(int8) | |||
| int8obj2 := obj2.(int8) | |||
| if int8obj1 > int8obj2 { | |||
| return -1, true | |||
| } | |||
| if int8obj1 == int8obj2 { | |||
| return 0, true | |||
| } | |||
| if int8obj1 < int8obj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Int16: | |||
| { | |||
| int16obj1 := obj1.(int16) | |||
| int16obj2 := obj2.(int16) | |||
| if int16obj1 > int16obj2 { | |||
| return -1, true | |||
| } | |||
| if int16obj1 == int16obj2 { | |||
| return 0, true | |||
| } | |||
| if int16obj1 < int16obj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Int32: | |||
| { | |||
| int32obj1 := obj1.(int32) | |||
| int32obj2 := obj2.(int32) | |||
| if int32obj1 > int32obj2 { | |||
| return -1, true | |||
| } | |||
| if int32obj1 == int32obj2 { | |||
| return 0, true | |||
| } | |||
| if int32obj1 < int32obj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Int64: | |||
| { | |||
| int64obj1 := obj1.(int64) | |||
| int64obj2 := obj2.(int64) | |||
| if int64obj1 > int64obj2 { | |||
| return -1, true | |||
| } | |||
| if int64obj1 == int64obj2 { | |||
| return 0, true | |||
| } | |||
| if int64obj1 < int64obj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Uint: | |||
| { | |||
| uintobj1 := obj1.(uint) | |||
| uintobj2 := obj2.(uint) | |||
| if uintobj1 > uintobj2 { | |||
| return -1, true | |||
| } | |||
| if uintobj1 == uintobj2 { | |||
| return 0, true | |||
| } | |||
| if uintobj1 < uintobj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Uint8: | |||
| { | |||
| uint8obj1 := obj1.(uint8) | |||
| uint8obj2 := obj2.(uint8) | |||
| if uint8obj1 > uint8obj2 { | |||
| return -1, true | |||
| } | |||
| if uint8obj1 == uint8obj2 { | |||
| return 0, true | |||
| } | |||
| if uint8obj1 < uint8obj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Uint16: | |||
| { | |||
| uint16obj1 := obj1.(uint16) | |||
| uint16obj2 := obj2.(uint16) | |||
| if uint16obj1 > uint16obj2 { | |||
| return -1, true | |||
| } | |||
| if uint16obj1 == uint16obj2 { | |||
| return 0, true | |||
| } | |||
| if uint16obj1 < uint16obj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Uint32: | |||
| { | |||
| uint32obj1 := obj1.(uint32) | |||
| uint32obj2 := obj2.(uint32) | |||
| if uint32obj1 > uint32obj2 { | |||
| return -1, true | |||
| } | |||
| if uint32obj1 == uint32obj2 { | |||
| return 0, true | |||
| } | |||
| if uint32obj1 < uint32obj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Uint64: | |||
| { | |||
| uint64obj1 := obj1.(uint64) | |||
| uint64obj2 := obj2.(uint64) | |||
| if uint64obj1 > uint64obj2 { | |||
| return -1, true | |||
| } | |||
| if uint64obj1 == uint64obj2 { | |||
| return 0, true | |||
| } | |||
| if uint64obj1 < uint64obj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Float32: | |||
| { | |||
| float32obj1 := obj1.(float32) | |||
| float32obj2 := obj2.(float32) | |||
| if float32obj1 > float32obj2 { | |||
| return -1, true | |||
| } | |||
| if float32obj1 == float32obj2 { | |||
| return 0, true | |||
| } | |||
| if float32obj1 < float32obj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.Float64: | |||
| { | |||
| float64obj1 := obj1.(float64) | |||
| float64obj2 := obj2.(float64) | |||
| if float64obj1 > float64obj2 { | |||
| return -1, true | |||
| } | |||
| if float64obj1 == float64obj2 { | |||
| return 0, true | |||
| } | |||
| if float64obj1 < float64obj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| case reflect.String: | |||
| { | |||
| stringobj1 := obj1.(string) | |||
| stringobj2 := obj2.(string) | |||
| if stringobj1 > stringobj2 { | |||
| return -1, true | |||
| } | |||
| if stringobj1 == stringobj2 { | |||
| return 0, true | |||
| } | |||
| if stringobj1 < stringobj2 { | |||
| return 1, true | |||
| } | |||
| } | |||
| } | |||
| return 0, false | |||
| } | |||
| // Greater asserts that the first element is greater than the second | |||
| // | |||
| // assert.Greater(t, 2, 1) | |||
| // assert.Greater(t, float64(2), float64(1)) | |||
| // assert.Greater(t, "b", "a") | |||
| func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| // isOrdered checks that collection contains orderable elements. | |||
| func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { | |||
| objKind := reflect.TypeOf(object).Kind() | |||
| if objKind != reflect.Slice && objKind != reflect.Array { | |||
| return false | |||
| } | |||
| e1Kind := reflect.ValueOf(e1).Kind() | |||
| e2Kind := reflect.ValueOf(e2).Kind() | |||
| if e1Kind != e2Kind { | |||
| return Fail(t, "Elements should be the same type", msgAndArgs...) | |||
| } | |||
| objValue := reflect.ValueOf(object) | |||
| objLen := objValue.Len() | |||
| res, isComparable := compare(e1, e2, e1Kind) | |||
| if !isComparable { | |||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||
| if objLen <= 1 { | |||
| return true | |||
| } | |||
| if res != -1 { | |||
| return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...) | |||
| } | |||
| value := objValue.Index(0) | |||
| valueInterface := value.Interface() | |||
| firstValueKind := value.Kind() | |||
| return true | |||
| } | |||
| for i := 1; i < objLen; i++ { | |||
| prevValue := value | |||
| prevValueInterface := valueInterface | |||
| // GreaterOrEqual asserts that the first element is greater than or equal to the second | |||
| // | |||
| // assert.GreaterOrEqual(t, 2, 1) | |||
| // assert.GreaterOrEqual(t, 2, 2) | |||
| // assert.GreaterOrEqual(t, "b", "a") | |||
| // assert.GreaterOrEqual(t, "b", "b") | |||
| func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| value = objValue.Index(i) | |||
| valueInterface = value.Interface() | |||
| e1Kind := reflect.ValueOf(e1).Kind() | |||
| e2Kind := reflect.ValueOf(e2).Kind() | |||
| if e1Kind != e2Kind { | |||
| return Fail(t, "Elements should be the same type", msgAndArgs...) | |||
| } | |||
| compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) | |||
| res, isComparable := compare(e1, e2, e1Kind) | |||
| if !isComparable { | |||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||
| } | |||
| if !isComparable { | |||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) | |||
| } | |||
| if res != -1 && res != 0 { | |||
| return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...) | |||
| if !containsValue(allowedComparesResults, compareResult) { | |||
| return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) | |||
| } | |||
| } | |||
| return true | |||
| } | |||
| // Less asserts that the first element is less than the second | |||
| // IsIncreasing asserts that the collection is increasing | |||
| // | |||
| // assert.Less(t, 1, 2) | |||
| // assert.Less(t, float64(1), float64(2)) | |||
| // assert.Less(t, "a", "b") | |||
| func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| e1Kind := reflect.ValueOf(e1).Kind() | |||
| e2Kind := reflect.ValueOf(e2).Kind() | |||
| if e1Kind != e2Kind { | |||
| return Fail(t, "Elements should be the same type", msgAndArgs...) | |||
| } | |||
| res, isComparable := compare(e1, e2, e1Kind) | |||
| if !isComparable { | |||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||
| } | |||
| if res != 1 { | |||
| return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...) | |||
| } | |||
| return true | |||
| // assert.IsIncreasing(t, []int{1, 2, 3}) | |||
| // assert.IsIncreasing(t, []float{1, 2}) | |||
| // assert.IsIncreasing(t, []string{"a", "b"}) | |||
| func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||
| return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) | |||
| } | |||
| // LessOrEqual asserts that the first element is less than or equal to the second | |||
| // IsNonIncreasing asserts that the collection is not increasing | |||
| // | |||
| // assert.LessOrEqual(t, 1, 2) | |||
| // assert.LessOrEqual(t, 2, 2) | |||
| // assert.LessOrEqual(t, "a", "b") | |||
| // assert.LessOrEqual(t, "b", "b") | |||
| func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| e1Kind := reflect.ValueOf(e1).Kind() | |||
| e2Kind := reflect.ValueOf(e2).Kind() | |||
| if e1Kind != e2Kind { | |||
| return Fail(t, "Elements should be the same type", msgAndArgs...) | |||
| } | |||
| res, isComparable := compare(e1, e2, e1Kind) | |||
| if !isComparable { | |||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||
| } | |||
| // assert.IsNonIncreasing(t, []int{2, 1, 1}) | |||
| // assert.IsNonIncreasing(t, []float{2, 1}) | |||
| // assert.IsNonIncreasing(t, []string{"b", "a"}) | |||
| func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||
| return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) | |||
| } | |||
| if res != 1 && res != 0 { | |||
| return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...) | |||
| } | |||
| // IsDecreasing asserts that the collection is decreasing | |||
| // | |||
| // assert.IsDecreasing(t, []int{2, 1, 0}) | |||
| // assert.IsDecreasing(t, []float{2, 1}) | |||
| // assert.IsDecreasing(t, []string{"b", "a"}) | |||
| func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||
| return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) | |||
| } | |||
| return true | |||
| // IsNonDecreasing asserts that the collection is not decreasing | |||
| // | |||
| // assert.IsNonDecreasing(t, []int{1, 1, 2}) | |||
| // assert.IsNonDecreasing(t, []float{1, 2}) | |||
| // assert.IsNonDecreasing(t, []string{"a", "b"}) | |||
| func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||
| return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) | |||
| } | |||
| @@ -11,6 +11,7 @@ import ( | |||
| "reflect" | |||
| "regexp" | |||
| "runtime" | |||
| "runtime/debug" | |||
| "strings" | |||
| "time" | |||
| "unicode" | |||
| @@ -18,10 +19,10 @@ import ( | |||
| "github.com/davecgh/go-spew/spew" | |||
| "github.com/pmezard/go-difflib/difflib" | |||
| yaml "gopkg.in/yaml.v2" | |||
| yaml "gopkg.in/yaml.v3" | |||
| ) | |||
| //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl | |||
| //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" | |||
| // TestingT is an interface wrapper around *testing.T | |||
| type TestingT interface { | |||
| @@ -44,7 +45,7 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool | |||
| // for table driven tests. | |||
| type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool | |||
| // Comparison a custom function that returns true on success and false on failure | |||
| // Comparison is a custom function that returns true on success and false on failure | |||
| type Comparison func() (success bool) | |||
| /* | |||
| @@ -103,11 +104,11 @@ the problem actually occurred in calling code.*/ | |||
| // failed. | |||
| func CallerInfo() []string { | |||
| pc := uintptr(0) | |||
| file := "" | |||
| line := 0 | |||
| ok := false | |||
| name := "" | |||
| var pc uintptr | |||
| var ok bool | |||
| var file string | |||
| var line int | |||
| var name string | |||
| callers := []string{} | |||
| for i := 0; ; i++ { | |||
| @@ -171,8 +172,8 @@ func isTest(name, prefix string) bool { | |||
| if len(name) == len(prefix) { // "Test" is ok | |||
| return true | |||
| } | |||
| rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) | |||
| return !unicode.IsLower(rune) | |||
| r, _ := utf8.DecodeRuneInString(name[len(prefix):]) | |||
| return !unicode.IsLower(r) | |||
| } | |||
| func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { | |||
| @@ -351,6 +352,19 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) | |||
| } | |||
| // validateEqualArgs checks whether provided arguments can be safely used in the | |||
| // Equal/NotEqual functions. | |||
| func validateEqualArgs(expected, actual interface{}) error { | |||
| if expected == nil && actual == nil { | |||
| return nil | |||
| } | |||
| if isFunction(expected) || isFunction(actual) { | |||
| return errors.New("cannot take func type as argument") | |||
| } | |||
| return nil | |||
| } | |||
| // Same asserts that two pointers reference the same object. | |||
| // | |||
| // assert.Same(t, ptr1, ptr2) | |||
| @@ -362,18 +376,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b | |||
| h.Helper() | |||
| } | |||
| expectedPtr, actualPtr := reflect.ValueOf(expected), reflect.ValueOf(actual) | |||
| if expectedPtr.Kind() != reflect.Ptr || actualPtr.Kind() != reflect.Ptr { | |||
| return Fail(t, "Invalid operation: both arguments must be pointers", msgAndArgs...) | |||
| } | |||
| expectedType, actualType := reflect.TypeOf(expected), reflect.TypeOf(actual) | |||
| if expectedType != actualType { | |||
| return Fail(t, fmt.Sprintf("Pointer expected to be of type %v, but was %v", | |||
| expectedType, actualType), msgAndArgs...) | |||
| } | |||
| if expected != actual { | |||
| if !samePointers(expected, actual) { | |||
| return Fail(t, fmt.Sprintf("Not same: \n"+ | |||
| "expected: %p %#v\n"+ | |||
| "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) | |||
| @@ -382,6 +385,42 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b | |||
| return true | |||
| } | |||
| // NotSame asserts that two pointers do not reference the same object. | |||
| // | |||
| // assert.NotSame(t, ptr1, ptr2) | |||
| // | |||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||
| // determined based on the equality of both type and value. | |||
| func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if samePointers(expected, actual) { | |||
| return Fail(t, fmt.Sprintf( | |||
| "Expected and actual point to the same object: %p %#v", | |||
| expected, expected), msgAndArgs...) | |||
| } | |||
| return true | |||
| } | |||
| // samePointers compares two generic interface objects and returns whether | |||
| // they point to the same object | |||
| func samePointers(first, second interface{}) bool { | |||
| firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) | |||
| if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { | |||
| return false | |||
| } | |||
| firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) | |||
| if firstType != secondType { | |||
| return false | |||
| } | |||
| // compare pointer addresses | |||
| return first == second | |||
| } | |||
| // formatUnequalValues takes two values of arbitrary types and returns string | |||
| // representations appropriate to be presented to the user. | |||
| // | |||
| @@ -390,12 +429,27 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b | |||
| // to a type conversion in the Go grammar. | |||
| func formatUnequalValues(expected, actual interface{}) (e string, a string) { | |||
| if reflect.TypeOf(expected) != reflect.TypeOf(actual) { | |||
| return fmt.Sprintf("%T(%#v)", expected, expected), | |||
| fmt.Sprintf("%T(%#v)", actual, actual) | |||
| return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)), | |||
| fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual)) | |||
| } | |||
| switch expected.(type) { | |||
| case time.Duration: | |||
| return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) | |||
| } | |||
| return truncatingFormat(expected), truncatingFormat(actual) | |||
| } | |||
| return fmt.Sprintf("%#v", expected), | |||
| fmt.Sprintf("%#v", actual) | |||
| // truncatingFormat formats the data and truncates it if it's too long. | |||
| // | |||
| // This helps keep formatted error messages lines from exceeding the | |||
| // bufio.MaxScanTokenSize max line length that the go testing framework imposes. | |||
| func truncatingFormat(data interface{}) string { | |||
| value := fmt.Sprintf("%#v", data) | |||
| max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed. | |||
| if len(value) > max { | |||
| value = value[0:max] + "<... truncated>" | |||
| } | |||
| return value | |||
| } | |||
| // EqualValues asserts that two objects are equal or convertable to the same types | |||
| @@ -442,12 +496,12 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} | |||
| // | |||
| // assert.NotNil(t, err) | |||
| func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if !isNil(object) { | |||
| return true | |||
| } | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Fail(t, "Expected value not to be nil.", msgAndArgs...) | |||
| } | |||
| @@ -488,12 +542,12 @@ func isNil(object interface{}) bool { | |||
| // | |||
| // assert.Nil(t, err) | |||
| func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if isNil(object) { | |||
| return true | |||
| } | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) | |||
| } | |||
| @@ -530,12 +584,11 @@ func isEmpty(object interface{}) bool { | |||
| // | |||
| // assert.Empty(t, obj) | |||
| func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| pass := isEmpty(object) | |||
| if !pass { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) | |||
| } | |||
| @@ -550,12 +603,11 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||
| // assert.Equal(t, "two", obj[1]) | |||
| // } | |||
| func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| pass := !isEmpty(object) | |||
| if !pass { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) | |||
| } | |||
| @@ -598,16 +650,10 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) | |||
| // | |||
| // assert.True(t, myBool) | |||
| func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if h, ok := t.(interface { | |||
| Helper() | |||
| }); ok { | |||
| h.Helper() | |||
| } | |||
| if value != true { | |||
| if !value { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Fail(t, "Should be true", msgAndArgs...) | |||
| } | |||
| @@ -619,11 +665,10 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { | |||
| // | |||
| // assert.False(t, myBool) | |||
| func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if value != false { | |||
| if value { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Fail(t, "Should be false", msgAndArgs...) | |||
| } | |||
| @@ -654,6 +699,21 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ | |||
| } | |||
| // NotEqualValues asserts that two objects are not equal even when converted to the same type | |||
| // | |||
| // assert.NotEqualValues(t, obj1, obj2) | |||
| func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if ObjectsAreEqualValues(expected, actual) { | |||
| return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) | |||
| } | |||
| return true | |||
| } | |||
| // containsElement try loop over the list check if the list includes the element. | |||
| // return (false, false) if impossible. | |||
| // return (true, false) if element was not found. | |||
| @@ -706,10 +766,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo | |||
| ok, found := includeElement(s, contains) | |||
| if !ok { | |||
| return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) | |||
| return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) | |||
| } | |||
| if !found { | |||
| return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...) | |||
| return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...) | |||
| } | |||
| return true | |||
| @@ -840,27 +900,39 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface | |||
| return true | |||
| } | |||
| aKind := reflect.TypeOf(listA).Kind() | |||
| bKind := reflect.TypeOf(listB).Kind() | |||
| if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) { | |||
| return false | |||
| } | |||
| extraA, extraB := diffLists(listA, listB) | |||
| if aKind != reflect.Array && aKind != reflect.Slice { | |||
| return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...) | |||
| if len(extraA) == 0 && len(extraB) == 0 { | |||
| return true | |||
| } | |||
| if bKind != reflect.Array && bKind != reflect.Slice { | |||
| return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...) | |||
| return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...) | |||
| } | |||
| // isList checks that the provided value is array or slice. | |||
| func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) { | |||
| kind := reflect.TypeOf(list).Kind() | |||
| if kind != reflect.Array && kind != reflect.Slice { | |||
| return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind), | |||
| msgAndArgs...) | |||
| } | |||
| return true | |||
| } | |||
| // diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B. | |||
| // If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and | |||
| // 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored. | |||
| func diffLists(listA, listB interface{}) (extraA, extraB []interface{}) { | |||
| aValue := reflect.ValueOf(listA) | |||
| bValue := reflect.ValueOf(listB) | |||
| aLen := aValue.Len() | |||
| bLen := bValue.Len() | |||
| if aLen != bLen { | |||
| return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...) | |||
| } | |||
| // Mark indexes in bValue that we already used | |||
| visited := make([]bool, bLen) | |||
| for i := 0; i < aLen; i++ { | |||
| @@ -877,11 +949,38 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface | |||
| } | |||
| } | |||
| if !found { | |||
| return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...) | |||
| extraA = append(extraA, element) | |||
| } | |||
| } | |||
| return true | |||
| for j := 0; j < bLen; j++ { | |||
| if visited[j] { | |||
| continue | |||
| } | |||
| extraB = append(extraB, bValue.Index(j).Interface()) | |||
| } | |||
| return | |||
| } | |||
| func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string { | |||
| var msg bytes.Buffer | |||
| msg.WriteString("elements differ") | |||
| if len(extraA) > 0 { | |||
| msg.WriteString("\n\nextra elements in list A:\n") | |||
| msg.WriteString(spewConfig.Sdump(extraA)) | |||
| } | |||
| if len(extraB) > 0 { | |||
| msg.WriteString("\n\nextra elements in list B:\n") | |||
| msg.WriteString(spewConfig.Sdump(extraB)) | |||
| } | |||
| msg.WriteString("\n\nlistA:\n") | |||
| msg.WriteString(spewConfig.Sdump(listA)) | |||
| msg.WriteString("\n\nlistB:\n") | |||
| msg.WriteString(spewConfig.Sdump(listB)) | |||
| return msg.String() | |||
| } | |||
| // Condition uses a Comparison to assert a complex condition. | |||
| @@ -901,15 +1000,17 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { | |||
| type PanicTestFunc func() | |||
| // didPanic returns true if the function passed to it panics. Otherwise, it returns false. | |||
| func didPanic(f PanicTestFunc) (bool, interface{}) { | |||
| func didPanic(f PanicTestFunc) (bool, interface{}, string) { | |||
| didPanic := false | |||
| var message interface{} | |||
| var stack string | |||
| func() { | |||
| defer func() { | |||
| if message = recover(); message != nil { | |||
| didPanic = true | |||
| stack = string(debug.Stack()) | |||
| } | |||
| }() | |||
| @@ -918,7 +1019,7 @@ func didPanic(f PanicTestFunc) (bool, interface{}) { | |||
| }() | |||
| return didPanic, message | |||
| return didPanic, message, stack | |||
| } | |||
| @@ -930,7 +1031,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||
| h.Helper() | |||
| } | |||
| if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { | |||
| if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic { | |||
| return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | |||
| } | |||
| @@ -946,12 +1047,34 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr | |||
| h.Helper() | |||
| } | |||
| funcDidPanic, panicValue := didPanic(f) | |||
| funcDidPanic, panicValue, panickedStack := didPanic(f) | |||
| if !funcDidPanic { | |||
| return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | |||
| } | |||
| if panicValue != expected { | |||
| return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v", f, expected, panicValue), msgAndArgs...) | |||
| return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, expected, panicValue, panickedStack), msgAndArgs...) | |||
| } | |||
| return true | |||
| } | |||
| // PanicsWithError asserts that the code inside the specified PanicTestFunc | |||
| // panics, and that the recovered panic value is an error that satisfies the | |||
| // EqualError comparison. | |||
| // | |||
| // assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) | |||
| func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| funcDidPanic, panicValue, panickedStack := didPanic(f) | |||
| if !funcDidPanic { | |||
| return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | |||
| } | |||
| panicErr, ok := panicValue.(error) | |||
| if !ok || panicErr.Error() != errString { | |||
| return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...) | |||
| } | |||
| return true | |||
| @@ -965,8 +1088,8 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||
| h.Helper() | |||
| } | |||
| if funcDidPanic, panicValue := didPanic(f); funcDidPanic { | |||
| return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v", f, panicValue), msgAndArgs...) | |||
| if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic { | |||
| return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", f, panicValue, panickedStack), msgAndArgs...) | |||
| } | |||
| return true | |||
| @@ -993,6 +1116,8 @@ func toFloat(x interface{}) (float64, bool) { | |||
| xok := true | |||
| switch xn := x.(type) { | |||
| case uint: | |||
| xf = float64(xn) | |||
| case uint8: | |||
| xf = float64(xn) | |||
| case uint16: | |||
| @@ -1014,7 +1139,7 @@ func toFloat(x interface{}) (float64, bool) { | |||
| case float32: | |||
| xf = float64(xn) | |||
| case float64: | |||
| xf = float64(xn) | |||
| xf = xn | |||
| case time.Duration: | |||
| xf = float64(xn) | |||
| default: | |||
| @@ -1026,7 +1151,7 @@ func toFloat(x interface{}) (float64, bool) { | |||
| // InDelta asserts that the two numerals are within delta of each other. | |||
| // | |||
| // assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) | |||
| // assert.InDelta(t, math.Pi, 22/7.0, 0.01) | |||
| func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -1128,6 +1253,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { | |||
| if !aok { | |||
| return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) | |||
| } | |||
| if math.IsNaN(af) { | |||
| return 0, errors.New("expected value must not be NaN") | |||
| } | |||
| if af == 0 { | |||
| return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") | |||
| } | |||
| @@ -1135,6 +1263,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { | |||
| if !bok { | |||
| return 0, fmt.Errorf("actual value %q cannot be converted to float", actual) | |||
| } | |||
| if math.IsNaN(bf) { | |||
| return 0, errors.New("actual value must not be NaN") | |||
| } | |||
| return math.Abs(af-bf) / math.Abs(af), nil | |||
| } | |||
| @@ -1144,6 +1275,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if math.IsNaN(epsilon) { | |||
| return Fail(t, "epsilon must not be NaN") | |||
| } | |||
| actualEpsilon, err := calcRelativeError(expected, actual) | |||
| if err != nil { | |||
| return Fail(t, err.Error(), msgAndArgs...) | |||
| @@ -1191,10 +1325,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m | |||
| // assert.Equal(t, expectedObj, actualObj) | |||
| // } | |||
| func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if err != nil { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) | |||
| } | |||
| @@ -1208,11 +1342,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { | |||
| // assert.Equal(t, expectedError, err) | |||
| // } | |||
| func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if err == nil { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| return Fail(t, "An error is expected but got nil.", msgAndArgs...) | |||
| } | |||
| @@ -1314,7 +1447,8 @@ func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { | |||
| return true | |||
| } | |||
| // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||
| // FileExists checks whether a file exists in the given path. It also fails if | |||
| // the path points to a directory or there is an error when trying to check the file. | |||
| func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -1332,7 +1466,24 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||
| return true | |||
| } | |||
| // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||
| // NoFileExists checks whether a file does not exist in a given path. It fails | |||
| // if the path points to an existing _file_ only. | |||
| func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| info, err := os.Lstat(path) | |||
| if err != nil { | |||
| return true | |||
| } | |||
| if info.IsDir() { | |||
| return true | |||
| } | |||
| return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...) | |||
| } | |||
| // DirExists checks whether a directory exists in the given path. It also fails | |||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||
| func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -1350,6 +1501,25 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||
| return true | |||
| } | |||
| // NoDirExists checks whether a directory does not exist in the given path. | |||
| // It fails if the path points to an existing _directory_ only. | |||
| func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| info, err := os.Lstat(path) | |||
| if err != nil { | |||
| if os.IsNotExist(err) { | |||
| return true | |||
| } | |||
| return true | |||
| } | |||
| if !info.IsDir() { | |||
| return true | |||
| } | |||
| return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...) | |||
| } | |||
| // JSONEq asserts that two JSON strings are equivalent. | |||
| // | |||
| // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | |||
| @@ -1439,15 +1609,6 @@ func diff(expected interface{}, actual interface{}) string { | |||
| return "\n\nDiff:\n" + diff | |||
| } | |||
| // validateEqualArgs checks whether provided arguments can be safely used in the | |||
| // Equal/NotEqual functions. | |||
| func validateEqualArgs(expected, actual interface{}) error { | |||
| if isFunction(expected) || isFunction(actual) { | |||
| return errors.New("cannot take func type as argument") | |||
| } | |||
| return nil | |||
| } | |||
| func isFunction(arg interface{}) bool { | |||
| if arg == nil { | |||
| return false | |||
| @@ -1460,6 +1621,8 @@ var spewConfig = spew.ConfigState{ | |||
| DisablePointerAddresses: true, | |||
| DisableCapacities: true, | |||
| SortKeys: true, | |||
| DisableMethods: true, | |||
| MaxDepth: 10, | |||
| } | |||
| type tHelper interface { | |||
| @@ -1475,24 +1638,137 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t | |||
| h.Helper() | |||
| } | |||
| ch := make(chan bool, 1) | |||
| timer := time.NewTimer(waitFor) | |||
| ticker := time.NewTicker(tick) | |||
| checkPassed := make(chan bool) | |||
| defer timer.Stop() | |||
| ticker := time.NewTicker(tick) | |||
| defer ticker.Stop() | |||
| defer close(checkPassed) | |||
| for { | |||
| for tick := ticker.C; ; { | |||
| select { | |||
| case <-timer.C: | |||
| return Fail(t, "Condition never satisfied", msgAndArgs...) | |||
| case result := <-checkPassed: | |||
| if result { | |||
| case <-tick: | |||
| tick = nil | |||
| go func() { ch <- condition() }() | |||
| case v := <-ch: | |||
| if v { | |||
| return true | |||
| } | |||
| case <-ticker.C: | |||
| go func() { | |||
| checkPassed <- condition() | |||
| }() | |||
| tick = ticker.C | |||
| } | |||
| } | |||
| } | |||
| // Never asserts that the given condition doesn't satisfy in waitFor time, | |||
| // periodically checking the target function each tick. | |||
| // | |||
| // assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) | |||
| func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| ch := make(chan bool, 1) | |||
| timer := time.NewTimer(waitFor) | |||
| defer timer.Stop() | |||
| ticker := time.NewTicker(tick) | |||
| defer ticker.Stop() | |||
| for tick := ticker.C; ; { | |||
| select { | |||
| case <-timer.C: | |||
| return true | |||
| case <-tick: | |||
| tick = nil | |||
| go func() { ch <- condition() }() | |||
| case v := <-ch: | |||
| if v { | |||
| return Fail(t, "Condition satisfied", msgAndArgs...) | |||
| } | |||
| tick = ticker.C | |||
| } | |||
| } | |||
| } | |||
| // ErrorIs asserts that at least one of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if errors.Is(err, target) { | |||
| return true | |||
| } | |||
| var expectedText string | |||
| if target != nil { | |||
| expectedText = target.Error() | |||
| } | |||
| chain := buildErrorChainString(err) | |||
| return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ | |||
| "expected: %q\n"+ | |||
| "in chain: %s", expectedText, chain, | |||
| ), msgAndArgs...) | |||
| } | |||
| // NotErrorIs asserts that at none of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if !errors.Is(err, target) { | |||
| return true | |||
| } | |||
| var expectedText string | |||
| if target != nil { | |||
| expectedText = target.Error() | |||
| } | |||
| chain := buildErrorChainString(err) | |||
| return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ | |||
| "found: %q\n"+ | |||
| "in chain: %s", expectedText, chain, | |||
| ), msgAndArgs...) | |||
| } | |||
| // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||
| // This is a wrapper for errors.As. | |||
| func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if errors.As(err, target) { | |||
| return true | |||
| } | |||
| chain := buildErrorChainString(err) | |||
| return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ | |||
| "expected: %q\n"+ | |||
| "in chain: %s", target, chain, | |||
| ), msgAndArgs...) | |||
| } | |||
| func buildErrorChainString(err error) string { | |||
| if err == nil { | |||
| return "" | |||
| } | |||
| e := errors.Unwrap(err) | |||
| chain := fmt.Sprintf("%q", err.Error()) | |||
| for e != nil { | |||
| chain += fmt.Sprintf("\n\t%q", e.Error()) | |||
| e = errors.Unwrap(e) | |||
| } | |||
| return chain | |||
| } | |||
| @@ -13,4 +13,4 @@ func New(t TestingT) *Assertions { | |||
| } | |||
| } | |||
| //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs | |||
| //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" | |||
| @@ -33,7 +33,6 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value | |||
| code, err := httpCode(handler, method, url, values) | |||
| if err != nil { | |||
| Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | |||
| return false | |||
| } | |||
| isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent | |||
| @@ -56,7 +55,6 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu | |||
| code, err := httpCode(handler, method, url, values) | |||
| if err != nil { | |||
| Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | |||
| return false | |||
| } | |||
| isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect | |||
| @@ -79,7 +77,6 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values | |||
| code, err := httpCode(handler, method, url, values) | |||
| if err != nil { | |||
| Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | |||
| return false | |||
| } | |||
| isErrorCode := code >= http.StatusBadRequest | |||
| @@ -90,6 +87,28 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values | |||
| return isErrorCode | |||
| } | |||
| // HTTPStatusCode asserts that a specified handler returns a specified status code. | |||
| // | |||
| // assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) | |||
| // | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| code, err := httpCode(handler, method, url, values) | |||
| if err != nil { | |||
| Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | |||
| } | |||
| successful := code == statuscode | |||
| if !successful { | |||
| Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code)) | |||
| } | |||
| return successful | |||
| } | |||
| // HTTPBody is a helper that returns HTTP body of the response. It returns | |||
| // empty string if building a new request fails. | |||
| func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { | |||
| @@ -13,4 +13,4 @@ func New(t TestingT) *Assertions { | |||
| } | |||
| } | |||
| //go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl -include-format-funcs | |||
| //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require_forward.go.tmpl -include-format-funcs" | |||
| @@ -66,7 +66,8 @@ func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args | |||
| t.FailNow() | |||
| } | |||
| // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||
| // DirExists checks whether a directory exists in the given path. It also fails | |||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||
| func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -77,7 +78,8 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||
| t.FailNow() | |||
| } | |||
| // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||
| // DirExistsf checks whether a directory exists in the given path. It also fails | |||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||
| func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -210,7 +212,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg | |||
| // EqualValuesf asserts that two objects are equal or convertable to the same types | |||
| // and equal. | |||
| // | |||
| // assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) | |||
| // assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") | |||
| func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -254,6 +256,54 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) { | |||
| t.FailNow() | |||
| } | |||
| // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||
| // This is a wrapper for errors.As. | |||
| func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.ErrorAs(t, err, target, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||
| // This is a wrapper for errors.As. | |||
| func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.ErrorAsf(t, err, target, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // ErrorIs asserts that at least one of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.ErrorIs(t, err, target, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.ErrorIsf(t, err, target, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // Errorf asserts that a function returned an error (i.e. not `nil`). | |||
| // | |||
| // actualObj, err := SomeFunction() | |||
| @@ -275,12 +325,12 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { | |||
| // | |||
| // assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) | |||
| func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | |||
| if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { | |||
| return | |||
| } | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| @@ -289,12 +339,12 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t | |||
| // | |||
| // assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||
| func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | |||
| if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { | |||
| return | |||
| } | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| @@ -313,7 +363,7 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. | |||
| // Exactlyf asserts that two objects are equal in value and type. | |||
| // | |||
| // assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) | |||
| // assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") | |||
| func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -394,7 +444,8 @@ func Falsef(t TestingT, value bool, msg string, args ...interface{}) { | |||
| t.FailNow() | |||
| } | |||
| // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||
| // FileExists checks whether a file exists in the given path. It also fails if | |||
| // the path points to a directory or there is an error when trying to check the file. | |||
| func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -405,7 +456,8 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||
| t.FailNow() | |||
| } | |||
| // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||
| // FileExistsf checks whether a file exists in the given path. It also fails if | |||
| // the path points to a directory or there is an error when trying to check the file. | |||
| func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -466,7 +518,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg | |||
| // Greaterf asserts that the first element is greater than the second | |||
| // | |||
| // assert.Greaterf(t, 2, 1, "error message %s", "formatted") | |||
| // assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) | |||
| // assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") | |||
| // assert.Greaterf(t, "b", "a", "error message %s", "formatted") | |||
| func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -561,7 +613,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, | |||
| // | |||
| // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | |||
| // | |||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -591,7 +643,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin | |||
| // | |||
| // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | |||
| // | |||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -602,6 +654,36 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri | |||
| t.FailNow() | |||
| } | |||
| // HTTPStatusCode asserts that a specified handler returns a specified status code. | |||
| // | |||
| // assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) | |||
| // | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.HTTPStatusCode(t, handler, method, url, values, statuscode, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||
| // | |||
| // assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||
| // | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.HTTPStatusCodef(t, handler, method, url, values, statuscode, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // HTTPSuccess asserts that a specified handler returns a success status code. | |||
| // | |||
| // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) | |||
| @@ -647,7 +729,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg | |||
| // Implementsf asserts that an object is implemented by the specified interface. | |||
| // | |||
| // assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||
| // assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||
| func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -660,7 +742,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms | |||
| // InDelta asserts that the two numerals are within delta of each other. | |||
| // | |||
| // assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) | |||
| // assert.InDelta(t, math.Pi, 22/7.0, 0.01) | |||
| func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -717,7 +799,7 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f | |||
| // InDeltaf asserts that the two numerals are within delta of each other. | |||
| // | |||
| // assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||
| // assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||
| func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -772,71 +854,169 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl | |||
| t.FailNow() | |||
| } | |||
| // IsType asserts that the specified objects are of the same type. | |||
| func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | |||
| // IsDecreasing asserts that the collection is decreasing | |||
| // | |||
| // assert.IsDecreasing(t, []int{2, 1, 0}) | |||
| // assert.IsDecreasing(t, []float{2, 1}) | |||
| // assert.IsDecreasing(t, []string{"b", "a"}) | |||
| func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.IsType(t, expectedType, object, msgAndArgs...) { | |||
| if assert.IsDecreasing(t, object, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // IsTypef asserts that the specified objects are of the same type. | |||
| func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { | |||
| // IsDecreasingf asserts that the collection is decreasing | |||
| // | |||
| // assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") | |||
| // assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||
| // assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||
| func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.IsTypef(t, expectedType, object, msg, args...) { | |||
| if assert.IsDecreasingf(t, object, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // JSONEq asserts that two JSON strings are equivalent. | |||
| // IsIncreasing asserts that the collection is increasing | |||
| // | |||
| // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | |||
| func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||
| // assert.IsIncreasing(t, []int{1, 2, 3}) | |||
| // assert.IsIncreasing(t, []float{1, 2}) | |||
| // assert.IsIncreasing(t, []string{"a", "b"}) | |||
| func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.JSONEq(t, expected, actual, msgAndArgs...) { | |||
| if assert.IsIncreasing(t, object, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // JSONEqf asserts that two JSON strings are equivalent. | |||
| // IsIncreasingf asserts that the collection is increasing | |||
| // | |||
| // assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") | |||
| func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||
| // assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") | |||
| // assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||
| // assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||
| func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.JSONEqf(t, expected, actual, msg, args...) { | |||
| if assert.IsIncreasingf(t, object, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // YAMLEq asserts that two YAML strings are equivalent. | |||
| func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||
| // IsNonDecreasing asserts that the collection is not decreasing | |||
| // | |||
| // assert.IsNonDecreasing(t, []int{1, 1, 2}) | |||
| // assert.IsNonDecreasing(t, []float{1, 2}) | |||
| // assert.IsNonDecreasing(t, []string{"a", "b"}) | |||
| func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.YAMLEq(t, expected, actual, msgAndArgs...) { | |||
| if assert.IsNonDecreasing(t, object, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||
| func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||
| // IsNonDecreasingf asserts that the collection is not decreasing | |||
| // | |||
| // assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") | |||
| // assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||
| // assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||
| func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.YAMLEqf(t, expected, actual, msg, args...) { | |||
| if assert.IsNonDecreasingf(t, object, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // IsNonIncreasing asserts that the collection is not increasing | |||
| // | |||
| // assert.IsNonIncreasing(t, []int{2, 1, 1}) | |||
| // assert.IsNonIncreasing(t, []float{2, 1}) | |||
| // assert.IsNonIncreasing(t, []string{"b", "a"}) | |||
| func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.IsNonIncreasing(t, object, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // IsNonIncreasingf asserts that the collection is not increasing | |||
| // | |||
| // assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") | |||
| // assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||
| // assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||
| func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.IsNonIncreasingf(t, object, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // IsType asserts that the specified objects are of the same type. | |||
| func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.IsType(t, expectedType, object, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // IsTypef asserts that the specified objects are of the same type. | |||
| func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.IsTypef(t, expectedType, object, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // JSONEq asserts that two JSON strings are equivalent. | |||
| // | |||
| // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | |||
| func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.JSONEq(t, expected, actual, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // JSONEqf asserts that two JSON strings are equivalent. | |||
| // | |||
| // assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") | |||
| func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.JSONEqf(t, expected, actual, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| @@ -920,7 +1100,7 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . | |||
| // Lessf asserts that the first element is less than the second | |||
| // | |||
| // assert.Lessf(t, 1, 2, "error message %s", "formatted") | |||
| // assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) | |||
| // assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") | |||
| // assert.Lessf(t, "a", "b", "error message %s", "formatted") | |||
| func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -932,6 +1112,62 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter | |||
| t.FailNow() | |||
| } | |||
| // Negative asserts that the specified element is negative | |||
| // | |||
| // assert.Negative(t, -1) | |||
| // assert.Negative(t, -1.23) | |||
| func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.Negative(t, e, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // Negativef asserts that the specified element is negative | |||
| // | |||
| // assert.Negativef(t, -1, "error message %s", "formatted") | |||
| // assert.Negativef(t, -1.23, "error message %s", "formatted") | |||
| func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.Negativef(t, e, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // Never asserts that the given condition doesn't satisfy in waitFor time, | |||
| // periodically checking the target function each tick. | |||
| // | |||
| // assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) | |||
| func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.Never(t, condition, waitFor, tick, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||
| // periodically checking the target function each tick. | |||
| // | |||
| // assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||
| func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.Neverf(t, condition, waitFor, tick, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // Nil asserts that the specified object is nil. | |||
| // | |||
| // assert.Nil(t, err) | |||
| @@ -958,6 +1194,30 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||
| t.FailNow() | |||
| } | |||
| // NoDirExists checks whether a directory does not exist in the given path. | |||
| // It fails if the path points to an existing _directory_ only. | |||
| func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.NoDirExists(t, path, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // NoDirExistsf checks whether a directory does not exist in the given path. | |||
| // It fails if the path points to an existing _directory_ only. | |||
| func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.NoDirExistsf(t, path, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // NoError asserts that a function returned no error (i.e. `nil`). | |||
| // | |||
| // actualObj, err := SomeFunction() | |||
| @@ -990,6 +1250,30 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { | |||
| t.FailNow() | |||
| } | |||
| // NoFileExists checks whether a file does not exist in a given path. It fails | |||
| // if the path points to an existing _file_ only. | |||
| func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.NoFileExists(t, path, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // NoFileExistsf checks whether a file does not exist in a given path. It fails | |||
| // if the path points to an existing _file_ only. | |||
| func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.NoFileExistsf(t, path, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | |||
| // specified substring or element. | |||
| // | |||
| @@ -1070,6 +1354,32 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . | |||
| t.FailNow() | |||
| } | |||
| // NotEqualValues asserts that two objects are not equal even when converted to the same type | |||
| // | |||
| // assert.NotEqualValues(t, obj1, obj2) | |||
| func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.NotEqualValues(t, expected, actual, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||
| // | |||
| // assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") | |||
| func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.NotEqualValuesf(t, expected, actual, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // NotEqualf asserts that the specified values are NOT equal. | |||
| // | |||
| // assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") | |||
| @@ -1086,6 +1396,30 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, | |||
| t.FailNow() | |||
| } | |||
| // NotErrorIs asserts that at none of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.NotErrorIs(t, err, target, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.NotErrorIsf(t, err, target, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // NotNil asserts that the specified object is not nil. | |||
| // | |||
| // assert.NotNil(t, err) | |||
| @@ -1154,7 +1488,7 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf | |||
| // NotRegexpf asserts that a specified regexp does not match a string. | |||
| // | |||
| // assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||
| // assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||
| // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") | |||
| func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -1166,6 +1500,38 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. | |||
| t.FailNow() | |||
| } | |||
| // NotSame asserts that two pointers do not reference the same object. | |||
| // | |||
| // assert.NotSame(t, ptr1, ptr2) | |||
| // | |||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||
| // determined based on the equality of both type and value. | |||
| func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.NotSame(t, expected, actual, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // NotSamef asserts that two pointers do not reference the same object. | |||
| // | |||
| // assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") | |||
| // | |||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||
| // determined based on the equality of both type and value. | |||
| func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.NotSamef(t, expected, actual, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // NotSubset asserts that the specified list(array, slice...) contains not all | |||
| // elements given in the specified subset(array, slice...). | |||
| // | |||
| @@ -1229,6 +1595,36 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||
| t.FailNow() | |||
| } | |||
| // PanicsWithError asserts that the code inside the specified PanicTestFunc | |||
| // panics, and that the recovered panic value is an error that satisfies the | |||
| // EqualError comparison. | |||
| // | |||
| // assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) | |||
| func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.PanicsWithError(t, errString, f, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||
| // panics, and that the recovered panic value is an error that satisfies the | |||
| // EqualError comparison. | |||
| // | |||
| // assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||
| func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.PanicsWithErrorf(t, errString, f, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | |||
| // the recovered panic value equals the expected panic value. | |||
| // | |||
| @@ -1270,6 +1666,34 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} | |||
| t.FailNow() | |||
| } | |||
| // Positive asserts that the specified element is positive | |||
| // | |||
| // assert.Positive(t, 1) | |||
| // assert.Positive(t, 1.23) | |||
| func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.Positive(t, e, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // Positivef asserts that the specified element is positive | |||
| // | |||
| // assert.Positivef(t, 1, "error message %s", "formatted") | |||
| // assert.Positivef(t, 1.23, "error message %s", "formatted") | |||
| func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.Positivef(t, e, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // Regexp asserts that a specified regexp matches a string. | |||
| // | |||
| // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") | |||
| @@ -1286,7 +1710,7 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface | |||
| // Regexpf asserts that a specified regexp matches a string. | |||
| // | |||
| // assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||
| // assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||
| // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") | |||
| func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -1410,6 +1834,28 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim | |||
| t.FailNow() | |||
| } | |||
| // YAMLEq asserts that two YAML strings are equivalent. | |||
| func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.YAMLEq(t, expected, actual, msgAndArgs...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||
| func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| if assert.YAMLEqf(t, expected, actual, msg, args...) { | |||
| return | |||
| } | |||
| t.FailNow() | |||
| } | |||
| // Zero asserts that i is the zero value for its type. | |||
| func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := t.(tHelper); ok { | |||
| @@ -54,7 +54,8 @@ func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, | |||
| Containsf(a.t, s, contains, msg, args...) | |||
| } | |||
| // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||
| // DirExists checks whether a directory exists in the given path. It also fails | |||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||
| func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -62,7 +63,8 @@ func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { | |||
| DirExists(a.t, path, msgAndArgs...) | |||
| } | |||
| // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||
| // DirExistsf checks whether a directory exists in the given path. It also fails | |||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||
| func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -168,7 +170,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn | |||
| // EqualValuesf asserts that two objects are equal or convertable to the same types | |||
| // and equal. | |||
| // | |||
| // a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) | |||
| // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") | |||
| func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -203,6 +205,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { | |||
| Error(a.t, err, msgAndArgs...) | |||
| } | |||
| // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||
| // This is a wrapper for errors.As. | |||
| func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| ErrorAs(a.t, err, target, msgAndArgs...) | |||
| } | |||
| // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||
| // This is a wrapper for errors.As. | |||
| func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| ErrorAsf(a.t, err, target, msg, args...) | |||
| } | |||
| // ErrorIs asserts that at least one of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| ErrorIs(a.t, err, target, msgAndArgs...) | |||
| } | |||
| // ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| ErrorIsf(a.t, err, target, msg, args...) | |||
| } | |||
| // Errorf asserts that a function returned an error (i.e. not `nil`). | |||
| // | |||
| // actualObj, err := SomeFunction() | |||
| @@ -250,7 +288,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg | |||
| // Exactlyf asserts that two objects are equal in value and type. | |||
| // | |||
| // a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) | |||
| // a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") | |||
| func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -310,7 +348,8 @@ func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { | |||
| Falsef(a.t, value, msg, args...) | |||
| } | |||
| // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||
| // FileExists checks whether a file exists in the given path. It also fails if | |||
| // the path points to a directory or there is an error when trying to check the file. | |||
| func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -318,7 +357,8 @@ func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { | |||
| FileExists(a.t, path, msgAndArgs...) | |||
| } | |||
| // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||
| // FileExistsf checks whether a file exists in the given path. It also fails if | |||
| // the path points to a directory or there is an error when trying to check the file. | |||
| func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -367,7 +407,7 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, | |||
| // Greaterf asserts that the first element is greater than the second | |||
| // | |||
| // a.Greaterf(2, 1, "error message %s", "formatted") | |||
| // a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) | |||
| // a.Greaterf(float64(2), float64(1), "error message %s", "formatted") | |||
| // a.Greaterf("b", "a", "error message %s", "formatted") | |||
| func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -444,7 +484,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri | |||
| // | |||
| // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | |||
| // | |||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -468,7 +508,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s | |||
| // | |||
| // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | |||
| // | |||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -476,6 +516,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url | |||
| HTTPRedirectf(a.t, handler, method, url, values, msg, args...) | |||
| } | |||
| // HTTPStatusCode asserts that a specified handler returns a specified status code. | |||
| // | |||
| // a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) | |||
| // | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) | |||
| } | |||
| // HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||
| // | |||
| // a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||
| // | |||
| // Returns whether the assertion was successful (true) or not (false). | |||
| func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) | |||
| } | |||
| // HTTPSuccess asserts that a specified handler returns a success status code. | |||
| // | |||
| // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | |||
| @@ -512,7 +576,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, | |||
| // Implementsf asserts that an object is implemented by the specified interface. | |||
| // | |||
| // a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||
| // a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||
| func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -522,7 +586,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} | |||
| // InDelta asserts that the two numerals are within delta of each other. | |||
| // | |||
| // a.InDelta(math.Pi, (22 / 7.0), 0.01) | |||
| // a.InDelta(math.Pi, 22/7.0, 0.01) | |||
| func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -564,7 +628,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del | |||
| // InDeltaf asserts that the two numerals are within delta of each other. | |||
| // | |||
| // a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||
| // a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||
| func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| @@ -604,6 +668,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo | |||
| InEpsilonf(a.t, expected, actual, epsilon, msg, args...) | |||
| } | |||
| // IsDecreasing asserts that the collection is decreasing | |||
| // | |||
| // a.IsDecreasing([]int{2, 1, 0}) | |||
| // a.IsDecreasing([]float{2, 1}) | |||
| // a.IsDecreasing([]string{"b", "a"}) | |||
| func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| IsDecreasing(a.t, object, msgAndArgs...) | |||
| } | |||
| // IsDecreasingf asserts that the collection is decreasing | |||
| // | |||
| // a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") | |||
| // a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") | |||
| // a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||
| func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| IsDecreasingf(a.t, object, msg, args...) | |||
| } | |||
| // IsIncreasing asserts that the collection is increasing | |||
| // | |||
| // a.IsIncreasing([]int{1, 2, 3}) | |||
| // a.IsIncreasing([]float{1, 2}) | |||
| // a.IsIncreasing([]string{"a", "b"}) | |||
| func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| IsIncreasing(a.t, object, msgAndArgs...) | |||
| } | |||
| // IsIncreasingf asserts that the collection is increasing | |||
| // | |||
| // a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") | |||
| // a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") | |||
| // a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||
| func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| IsIncreasingf(a.t, object, msg, args...) | |||
| } | |||
| // IsNonDecreasing asserts that the collection is not decreasing | |||
| // | |||
| // a.IsNonDecreasing([]int{1, 1, 2}) | |||
| // a.IsNonDecreasing([]float{1, 2}) | |||
| // a.IsNonDecreasing([]string{"a", "b"}) | |||
| func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| IsNonDecreasing(a.t, object, msgAndArgs...) | |||
| } | |||
| // IsNonDecreasingf asserts that the collection is not decreasing | |||
| // | |||
| // a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") | |||
| // a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") | |||
| // a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||
| func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| IsNonDecreasingf(a.t, object, msg, args...) | |||
| } | |||
| // IsNonIncreasing asserts that the collection is not increasing | |||
| // | |||
| // a.IsNonIncreasing([]int{2, 1, 1}) | |||
| // a.IsNonIncreasing([]float{2, 1}) | |||
| // a.IsNonIncreasing([]string{"b", "a"}) | |||
| func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| IsNonIncreasing(a.t, object, msgAndArgs...) | |||
| } | |||
| // IsNonIncreasingf asserts that the collection is not increasing | |||
| // | |||
| // a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") | |||
| // a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") | |||
| // a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||
| func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| IsNonIncreasingf(a.t, object, msg, args...) | |||
| } | |||
| // IsType asserts that the specified objects are of the same type. | |||
| func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -640,22 +800,6 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. | |||
| JSONEqf(a.t, expected, actual, msg, args...) | |||
| } | |||
| // YAMLEq asserts that two YAML strings are equivalent. | |||
| func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| YAMLEq(a.t, expected, actual, msgAndArgs...) | |||
| } | |||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||
| func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| YAMLEqf(a.t, expected, actual, msg, args...) | |||
| } | |||
| // Len asserts that the specified object has specific length. | |||
| // Len also fails if the object has a type that len() not accept. | |||
| // | |||
| @@ -719,7 +863,7 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar | |||
| // Lessf asserts that the first element is less than the second | |||
| // | |||
| // a.Lessf(1, 2, "error message %s", "formatted") | |||
| // a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) | |||
| // a.Lessf(float64(1), float64(2), "error message %s", "formatted") | |||
| // a.Lessf("a", "b", "error message %s", "formatted") | |||
| func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -728,6 +872,50 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i | |||
| Lessf(a.t, e1, e2, msg, args...) | |||
| } | |||
| // Negative asserts that the specified element is negative | |||
| // | |||
| // a.Negative(-1) | |||
| // a.Negative(-1.23) | |||
| func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| Negative(a.t, e, msgAndArgs...) | |||
| } | |||
| // Negativef asserts that the specified element is negative | |||
| // | |||
| // a.Negativef(-1, "error message %s", "formatted") | |||
| // a.Negativef(-1.23, "error message %s", "formatted") | |||
| func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| Negativef(a.t, e, msg, args...) | |||
| } | |||
| // Never asserts that the given condition doesn't satisfy in waitFor time, | |||
| // periodically checking the target function each tick. | |||
| // | |||
| // a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) | |||
| func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| Never(a.t, condition, waitFor, tick, msgAndArgs...) | |||
| } | |||
| // Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||
| // periodically checking the target function each tick. | |||
| // | |||
| // a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||
| func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| Neverf(a.t, condition, waitFor, tick, msg, args...) | |||
| } | |||
| // Nil asserts that the specified object is nil. | |||
| // | |||
| // a.Nil(err) | |||
| @@ -748,6 +936,24 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { | |||
| Nilf(a.t, object, msg, args...) | |||
| } | |||
| // NoDirExists checks whether a directory does not exist in the given path. | |||
| // It fails if the path points to an existing _directory_ only. | |||
| func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| NoDirExists(a.t, path, msgAndArgs...) | |||
| } | |||
| // NoDirExistsf checks whether a directory does not exist in the given path. | |||
| // It fails if the path points to an existing _directory_ only. | |||
| func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| NoDirExistsf(a.t, path, msg, args...) | |||
| } | |||
| // NoError asserts that a function returned no error (i.e. `nil`). | |||
| // | |||
| // actualObj, err := SomeFunction() | |||
| @@ -774,6 +980,24 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { | |||
| NoErrorf(a.t, err, msg, args...) | |||
| } | |||
| // NoFileExists checks whether a file does not exist in a given path. It fails | |||
| // if the path points to an existing _file_ only. | |||
| func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| NoFileExists(a.t, path, msgAndArgs...) | |||
| } | |||
| // NoFileExistsf checks whether a file does not exist in a given path. It fails | |||
| // if the path points to an existing _file_ only. | |||
| func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| NoFileExistsf(a.t, path, msg, args...) | |||
| } | |||
| // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | |||
| // specified substring or element. | |||
| // | |||
| @@ -839,6 +1063,26 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr | |||
| NotEqual(a.t, expected, actual, msgAndArgs...) | |||
| } | |||
| // NotEqualValues asserts that two objects are not equal even when converted to the same type | |||
| // | |||
| // a.NotEqualValues(obj1, obj2) | |||
| func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| NotEqualValues(a.t, expected, actual, msgAndArgs...) | |||
| } | |||
| // NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||
| // | |||
| // a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") | |||
| func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| NotEqualValuesf(a.t, expected, actual, msg, args...) | |||
| } | |||
| // NotEqualf asserts that the specified values are NOT equal. | |||
| // | |||
| // a.NotEqualf(obj1, obj2, "error message %s", "formatted") | |||
| @@ -852,6 +1096,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str | |||
| NotEqualf(a.t, expected, actual, msg, args...) | |||
| } | |||
| // NotErrorIs asserts that at none of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| NotErrorIs(a.t, err, target, msgAndArgs...) | |||
| } | |||
| // NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||
| // This is a wrapper for errors.Is. | |||
| func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| NotErrorIsf(a.t, err, target, msg, args...) | |||
| } | |||
| // NotNil asserts that the specified object is not nil. | |||
| // | |||
| // a.NotNil(err) | |||
| @@ -905,7 +1167,7 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in | |||
| // NotRegexpf asserts that a specified regexp does not match a string. | |||
| // | |||
| // a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||
| // a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||
| // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | |||
| func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -914,6 +1176,32 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg | |||
| NotRegexpf(a.t, rx, str, msg, args...) | |||
| } | |||
| // NotSame asserts that two pointers do not reference the same object. | |||
| // | |||
| // a.NotSame(ptr1, ptr2) | |||
| // | |||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||
| // determined based on the equality of both type and value. | |||
| func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| NotSame(a.t, expected, actual, msgAndArgs...) | |||
| } | |||
| // NotSamef asserts that two pointers do not reference the same object. | |||
| // | |||
| // a.NotSamef(ptr1, ptr2, "error message %s", "formatted") | |||
| // | |||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||
| // determined based on the equality of both type and value. | |||
| func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| NotSamef(a.t, expected, actual, msg, args...) | |||
| } | |||
| // NotSubset asserts that the specified list(array, slice...) contains not all | |||
| // elements given in the specified subset(array, slice...). | |||
| // | |||
| @@ -962,6 +1250,30 @@ func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||
| Panics(a.t, f, msgAndArgs...) | |||
| } | |||
| // PanicsWithError asserts that the code inside the specified PanicTestFunc | |||
| // panics, and that the recovered panic value is an error that satisfies the | |||
| // EqualError comparison. | |||
| // | |||
| // a.PanicsWithError("crazy error", func(){ GoCrazy() }) | |||
| func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| PanicsWithError(a.t, errString, f, msgAndArgs...) | |||
| } | |||
| // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||
| // panics, and that the recovered panic value is an error that satisfies the | |||
| // EqualError comparison. | |||
| // | |||
| // a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||
| func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| PanicsWithErrorf(a.t, errString, f, msg, args...) | |||
| } | |||
| // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | |||
| // the recovered panic value equals the expected panic value. | |||
| // | |||
| @@ -994,6 +1306,28 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa | |||
| Panicsf(a.t, f, msg, args...) | |||
| } | |||
| // Positive asserts that the specified element is positive | |||
| // | |||
| // a.Positive(1) | |||
| // a.Positive(1.23) | |||
| func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| Positive(a.t, e, msgAndArgs...) | |||
| } | |||
| // Positivef asserts that the specified element is positive | |||
| // | |||
| // a.Positivef(1, "error message %s", "formatted") | |||
| // a.Positivef(1.23, "error message %s", "formatted") | |||
| func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| Positivef(a.t, e, msg, args...) | |||
| } | |||
| // Regexp asserts that a specified regexp matches a string. | |||
| // | |||
| // a.Regexp(regexp.MustCompile("start"), "it's starting") | |||
| @@ -1007,7 +1341,7 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter | |||
| // Regexpf asserts that a specified regexp matches a string. | |||
| // | |||
| // a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||
| // a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||
| // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | |||
| func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -1104,6 +1438,22 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta | |||
| WithinDurationf(a.t, expected, actual, delta, msg, args...) | |||
| } | |||
| // YAMLEq asserts that two YAML strings are equivalent. | |||
| func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| YAMLEq(a.t, expected, actual, msgAndArgs...) | |||
| } | |||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||
| func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| h.Helper() | |||
| } | |||
| YAMLEqf(a.t, expected, actual, msg, args...) | |||
| } | |||
| // Zero asserts that i is the zero value for its type. | |||
| func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { | |||
| if h, ok := a.t.(tHelper); ok { | |||
| @@ -26,4 +26,4 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) | |||
| // for table driven tests. | |||
| type ErrorAssertionFunc func(TestingT, error, ...interface{}) | |||
| //go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl -include-format-funcs | |||
| //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" | |||
| @@ -0,0 +1,16 @@ | |||
| language: go | |||
| go: | |||
| - "1.4.x" | |||
| - "1.5.x" | |||
| - "1.6.x" | |||
| - "1.7.x" | |||
| - "1.8.x" | |||
| - "1.9.x" | |||
| - "1.10.x" | |||
| - "1.11.x" | |||
| - "1.12.x" | |||
| - "1.13.x" | |||
| - "tip" | |||
| go_import_path: gopkg.in/yaml.v3 | |||
| @@ -0,0 +1,50 @@ | |||
| This project is covered by two different licenses: MIT and Apache. | |||
| #### MIT License #### | |||
| The following files were ported to Go from C files of libyaml, and thus | |||
| are still covered by their original MIT license, with the additional | |||
| copyright staring in 2011 when the project was ported over: | |||
| apic.go emitterc.go parserc.go readerc.go scannerc.go | |||
| writerc.go yamlh.go yamlprivateh.go | |||
| Copyright (c) 2006-2010 Kirill Simonov | |||
| Copyright (c) 2006-2011 Kirill Simonov | |||
| 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. | |||
| ### Apache License ### | |||
| All the remaining project files are covered by the Apache license: | |||
| Copyright (c) 2011-2019 Canonical Ltd | |||
| Licensed under the Apache License, Version 2.0 (the "License"); | |||
| you may not use this file except in compliance with the License. | |||
| You may obtain a copy of the License at | |||
| http://www.apache.org/licenses/LICENSE-2.0 | |||
| Unless required by applicable law or agreed to in writing, software | |||
| distributed under the License is distributed on an "AS IS" BASIS, | |||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | |||
| limitations under the License. | |||
| @@ -0,0 +1,13 @@ | |||
| Copyright 2011-2016 Canonical Ltd. | |||
| Licensed under the Apache License, Version 2.0 (the "License"); | |||
| you may not use this file except in compliance with the License. | |||
| You may obtain a copy of the License at | |||
| http://www.apache.org/licenses/LICENSE-2.0 | |||
| Unless required by applicable law or agreed to in writing, software | |||
| distributed under the License is distributed on an "AS IS" BASIS, | |||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | |||
| limitations under the License. | |||
| @@ -0,0 +1,150 @@ | |||
| # YAML support for the Go language | |||
| Introduction | |||
| ------------ | |||
| The yaml package enables Go programs to comfortably encode and decode YAML | |||
| values. It was developed within [Canonical](https://www.canonical.com) as | |||
| part of the [juju](https://juju.ubuntu.com) project, and is based on a | |||
| pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) | |||
| C library to parse and generate YAML data quickly and reliably. | |||
| Compatibility | |||
| ------------- | |||
| The yaml package supports most of YAML 1.2, but preserves some behavior | |||
| from 1.1 for backwards compatibility. | |||
| Specifically, as of v3 of the yaml package: | |||
| - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being | |||
| decoded into a typed bool value. Otherwise they behave as a string. Booleans | |||
| in YAML 1.2 are _true/false_ only. | |||
| - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ | |||
| as specified in YAML 1.2, because most parsers still use the old format. | |||
| Octals in the _0o777_ format are supported though, so new files work. | |||
| - Does not support base-60 floats. These are gone from YAML 1.2, and were | |||
| actually never supported by this package as it's clearly a poor choice. | |||
| and offers backwards | |||
| compatibility with YAML 1.1 in some cases. | |||
| 1.2, including support for | |||
| anchors, tags, map merging, etc. Multi-document unmarshalling is not yet | |||
| implemented, and base-60 floats from YAML 1.1 are purposefully not | |||
| supported since they're a poor design and are gone in YAML 1.2. | |||
| Installation and usage | |||
| ---------------------- | |||
| The import path for the package is *gopkg.in/yaml.v3*. | |||
| To install it, run: | |||
| go get gopkg.in/yaml.v3 | |||
| API documentation | |||
| ----------------- | |||
| If opened in a browser, the import path itself leads to the API documentation: | |||
| - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) | |||
| API stability | |||
| ------------- | |||
| The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). | |||
| License | |||
| ------- | |||
| The yaml package is licensed under the MIT and Apache License 2.0 licenses. | |||
| Please see the LICENSE file for details. | |||
| Example | |||
| ------- | |||
| ```Go | |||
| package main | |||
| import ( | |||
| "fmt" | |||
| "log" | |||
| "gopkg.in/yaml.v3" | |||
| ) | |||
| var data = ` | |||
| a: Easy! | |||
| b: | |||
| c: 2 | |||
| d: [3, 4] | |||
| ` | |||
| // Note: struct fields must be public in order for unmarshal to | |||
| // correctly populate the data. | |||
| type T struct { | |||
| A string | |||
| B struct { | |||
| RenamedC int `yaml:"c"` | |||
| D []int `yaml:",flow"` | |||
| } | |||
| } | |||
| func main() { | |||
| t := T{} | |||
| err := yaml.Unmarshal([]byte(data), &t) | |||
| if err != nil { | |||
| log.Fatalf("error: %v", err) | |||
| } | |||
| fmt.Printf("--- t:\n%v\n\n", t) | |||
| d, err := yaml.Marshal(&t) | |||
| if err != nil { | |||
| log.Fatalf("error: %v", err) | |||
| } | |||
| fmt.Printf("--- t dump:\n%s\n\n", string(d)) | |||
| m := make(map[interface{}]interface{}) | |||
| err = yaml.Unmarshal([]byte(data), &m) | |||
| if err != nil { | |||
| log.Fatalf("error: %v", err) | |||
| } | |||
| fmt.Printf("--- m:\n%v\n\n", m) | |||
| d, err = yaml.Marshal(&m) | |||
| if err != nil { | |||
| log.Fatalf("error: %v", err) | |||
| } | |||
| fmt.Printf("--- m dump:\n%s\n\n", string(d)) | |||
| } | |||
| ``` | |||
| This example will generate the following output: | |||
| ``` | |||
| --- t: | |||
| {Easy! {2 [3 4]}} | |||
| --- t dump: | |||
| a: Easy! | |||
| b: | |||
| c: 2 | |||
| d: [3, 4] | |||
| --- m: | |||
| map[a:Easy! b:map[c:2 d:[3 4]]] | |||
| --- m dump: | |||
| a: Easy! | |||
| b: | |||
| c: 2 | |||
| d: | |||
| - 3 | |||
| - 4 | |||
| ``` | |||
| @@ -0,0 +1,746 @@ | |||
| // | |||
| // Copyright (c) 2011-2019 Canonical Ltd | |||
| // Copyright (c) 2006-2010 Kirill Simonov | |||
| // | |||
| // 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 yaml | |||
| import ( | |||
| "io" | |||
| ) | |||
| func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { | |||
| //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) | |||
| // Check if we can move the queue at the beginning of the buffer. | |||
| if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { | |||
| if parser.tokens_head != len(parser.tokens) { | |||
| copy(parser.tokens, parser.tokens[parser.tokens_head:]) | |||
| } | |||
| parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] | |||
| parser.tokens_head = 0 | |||
| } | |||
| parser.tokens = append(parser.tokens, *token) | |||
| if pos < 0 { | |||
| return | |||
| } | |||
| copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) | |||
| parser.tokens[parser.tokens_head+pos] = *token | |||
| } | |||
| // Create a new parser object. | |||
| func yaml_parser_initialize(parser *yaml_parser_t) bool { | |||
| *parser = yaml_parser_t{ | |||
| raw_buffer: make([]byte, 0, input_raw_buffer_size), | |||
| buffer: make([]byte, 0, input_buffer_size), | |||
| } | |||
| return true | |||
| } | |||
| // Destroy a parser object. | |||
| func yaml_parser_delete(parser *yaml_parser_t) { | |||
| *parser = yaml_parser_t{} | |||
| } | |||
| // String read handler. | |||
| func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { | |||
| if parser.input_pos == len(parser.input) { | |||
| return 0, io.EOF | |||
| } | |||
| n = copy(buffer, parser.input[parser.input_pos:]) | |||
| parser.input_pos += n | |||
| return n, nil | |||
| } | |||
| // Reader read handler. | |||
| func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { | |||
| return parser.input_reader.Read(buffer) | |||
| } | |||
| // Set a string input. | |||
| func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { | |||
| if parser.read_handler != nil { | |||
| panic("must set the input source only once") | |||
| } | |||
| parser.read_handler = yaml_string_read_handler | |||
| parser.input = input | |||
| parser.input_pos = 0 | |||
| } | |||
| // Set a file input. | |||
| func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { | |||
| if parser.read_handler != nil { | |||
| panic("must set the input source only once") | |||
| } | |||
| parser.read_handler = yaml_reader_read_handler | |||
| parser.input_reader = r | |||
| } | |||
| // Set the source encoding. | |||
| func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { | |||
| if parser.encoding != yaml_ANY_ENCODING { | |||
| panic("must set the encoding only once") | |||
| } | |||
| parser.encoding = encoding | |||
| } | |||
| // Create a new emitter object. | |||
| func yaml_emitter_initialize(emitter *yaml_emitter_t) { | |||
| *emitter = yaml_emitter_t{ | |||
| buffer: make([]byte, output_buffer_size), | |||
| raw_buffer: make([]byte, 0, output_raw_buffer_size), | |||
| states: make([]yaml_emitter_state_t, 0, initial_stack_size), | |||
| events: make([]yaml_event_t, 0, initial_queue_size), | |||
| } | |||
| } | |||
| // Destroy an emitter object. | |||
| func yaml_emitter_delete(emitter *yaml_emitter_t) { | |||
| *emitter = yaml_emitter_t{} | |||
| } | |||
| // String write handler. | |||
| func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { | |||
| *emitter.output_buffer = append(*emitter.output_buffer, buffer...) | |||
| return nil | |||
| } | |||
| // yaml_writer_write_handler uses emitter.output_writer to write the | |||
| // emitted text. | |||
| func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { | |||
| _, err := emitter.output_writer.Write(buffer) | |||
| return err | |||
| } | |||
| // Set a string output. | |||
| func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { | |||
| if emitter.write_handler != nil { | |||
| panic("must set the output target only once") | |||
| } | |||
| emitter.write_handler = yaml_string_write_handler | |||
| emitter.output_buffer = output_buffer | |||
| } | |||
| // Set a file output. | |||
| func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { | |||
| if emitter.write_handler != nil { | |||
| panic("must set the output target only once") | |||
| } | |||
| emitter.write_handler = yaml_writer_write_handler | |||
| emitter.output_writer = w | |||
| } | |||
| // Set the output encoding. | |||
| func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { | |||
| if emitter.encoding != yaml_ANY_ENCODING { | |||
| panic("must set the output encoding only once") | |||
| } | |||
| emitter.encoding = encoding | |||
| } | |||
| // Set the canonical output style. | |||
| func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { | |||
| emitter.canonical = canonical | |||
| } | |||
| // Set the indentation increment. | |||
| func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { | |||
| if indent < 2 || indent > 9 { | |||
| indent = 2 | |||
| } | |||
| emitter.best_indent = indent | |||
| } | |||
| // Set the preferred line width. | |||
| func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { | |||
| if width < 0 { | |||
| width = -1 | |||
| } | |||
| emitter.best_width = width | |||
| } | |||
| // Set if unescaped non-ASCII characters are allowed. | |||
| func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { | |||
| emitter.unicode = unicode | |||
| } | |||
| // Set the preferred line break character. | |||
| func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { | |||
| emitter.line_break = line_break | |||
| } | |||
| ///* | |||
| // * Destroy a token object. | |||
| // */ | |||
| // | |||
| //YAML_DECLARE(void) | |||
| //yaml_token_delete(yaml_token_t *token) | |||
| //{ | |||
| // assert(token); // Non-NULL token object expected. | |||
| // | |||
| // switch (token.type) | |||
| // { | |||
| // case YAML_TAG_DIRECTIVE_TOKEN: | |||
| // yaml_free(token.data.tag_directive.handle); | |||
| // yaml_free(token.data.tag_directive.prefix); | |||
| // break; | |||
| // | |||
| // case YAML_ALIAS_TOKEN: | |||
| // yaml_free(token.data.alias.value); | |||
| // break; | |||
| // | |||
| // case YAML_ANCHOR_TOKEN: | |||
| // yaml_free(token.data.anchor.value); | |||
| // break; | |||
| // | |||
| // case YAML_TAG_TOKEN: | |||
| // yaml_free(token.data.tag.handle); | |||
| // yaml_free(token.data.tag.suffix); | |||
| // break; | |||
| // | |||
| // case YAML_SCALAR_TOKEN: | |||
| // yaml_free(token.data.scalar.value); | |||
| // break; | |||
| // | |||
| // default: | |||
| // break; | |||
| // } | |||
| // | |||
| // memset(token, 0, sizeof(yaml_token_t)); | |||
| //} | |||
| // | |||
| ///* | |||
| // * Check if a string is a valid UTF-8 sequence. | |||
| // * | |||
| // * Check 'reader.c' for more details on UTF-8 encoding. | |||
| // */ | |||
| // | |||
| //static int | |||
| //yaml_check_utf8(yaml_char_t *start, size_t length) | |||
| //{ | |||
| // yaml_char_t *end = start+length; | |||
| // yaml_char_t *pointer = start; | |||
| // | |||
| // while (pointer < end) { | |||
| // unsigned char octet; | |||
| // unsigned int width; | |||
| // unsigned int value; | |||
| // size_t k; | |||
| // | |||
| // octet = pointer[0]; | |||
| // width = (octet & 0x80) == 0x00 ? 1 : | |||
| // (octet & 0xE0) == 0xC0 ? 2 : | |||
| // (octet & 0xF0) == 0xE0 ? 3 : | |||
| // (octet & 0xF8) == 0xF0 ? 4 : 0; | |||
| // value = (octet & 0x80) == 0x00 ? octet & 0x7F : | |||
| // (octet & 0xE0) == 0xC0 ? octet & 0x1F : | |||
| // (octet & 0xF0) == 0xE0 ? octet & 0x0F : | |||
| // (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; | |||
| // if (!width) return 0; | |||
| // if (pointer+width > end) return 0; | |||
| // for (k = 1; k < width; k ++) { | |||
| // octet = pointer[k]; | |||
| // if ((octet & 0xC0) != 0x80) return 0; | |||
| // value = (value << 6) + (octet & 0x3F); | |||
| // } | |||
| // if (!((width == 1) || | |||
| // (width == 2 && value >= 0x80) || | |||
| // (width == 3 && value >= 0x800) || | |||
| // (width == 4 && value >= 0x10000))) return 0; | |||
| // | |||
| // pointer += width; | |||
| // } | |||
| // | |||
| // return 1; | |||
| //} | |||
| // | |||
| // Create STREAM-START. | |||
| func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { | |||
| *event = yaml_event_t{ | |||
| typ: yaml_STREAM_START_EVENT, | |||
| encoding: encoding, | |||
| } | |||
| } | |||
| // Create STREAM-END. | |||
| func yaml_stream_end_event_initialize(event *yaml_event_t) { | |||
| *event = yaml_event_t{ | |||
| typ: yaml_STREAM_END_EVENT, | |||
| } | |||
| } | |||
| // Create DOCUMENT-START. | |||
| func yaml_document_start_event_initialize( | |||
| event *yaml_event_t, | |||
| version_directive *yaml_version_directive_t, | |||
| tag_directives []yaml_tag_directive_t, | |||
| implicit bool, | |||
| ) { | |||
| *event = yaml_event_t{ | |||
| typ: yaml_DOCUMENT_START_EVENT, | |||
| version_directive: version_directive, | |||
| tag_directives: tag_directives, | |||
| implicit: implicit, | |||
| } | |||
| } | |||
| // Create DOCUMENT-END. | |||
| func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { | |||
| *event = yaml_event_t{ | |||
| typ: yaml_DOCUMENT_END_EVENT, | |||
| implicit: implicit, | |||
| } | |||
| } | |||
| // Create ALIAS. | |||
| func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool { | |||
| *event = yaml_event_t{ | |||
| typ: yaml_ALIAS_EVENT, | |||
| anchor: anchor, | |||
| } | |||
| return true | |||
| } | |||
| // Create SCALAR. | |||
| func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { | |||
| *event = yaml_event_t{ | |||
| typ: yaml_SCALAR_EVENT, | |||
| anchor: anchor, | |||
| tag: tag, | |||
| value: value, | |||
| implicit: plain_implicit, | |||
| quoted_implicit: quoted_implicit, | |||
| style: yaml_style_t(style), | |||
| } | |||
| return true | |||
| } | |||
| // Create SEQUENCE-START. | |||
| func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { | |||
| *event = yaml_event_t{ | |||
| typ: yaml_SEQUENCE_START_EVENT, | |||
| anchor: anchor, | |||
| tag: tag, | |||
| implicit: implicit, | |||
| style: yaml_style_t(style), | |||
| } | |||
| return true | |||
| } | |||
| // Create SEQUENCE-END. | |||
| func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { | |||
| *event = yaml_event_t{ | |||
| typ: yaml_SEQUENCE_END_EVENT, | |||
| } | |||
| return true | |||
| } | |||
| // Create MAPPING-START. | |||
| func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { | |||
| *event = yaml_event_t{ | |||
| typ: yaml_MAPPING_START_EVENT, | |||
| anchor: anchor, | |||
| tag: tag, | |||
| implicit: implicit, | |||
| style: yaml_style_t(style), | |||
| } | |||
| } | |||
| // Create MAPPING-END. | |||
| func yaml_mapping_end_event_initialize(event *yaml_event_t) { | |||
| *event = yaml_event_t{ | |||
| typ: yaml_MAPPING_END_EVENT, | |||
| } | |||
| } | |||
| // Destroy an event object. | |||
| func yaml_event_delete(event *yaml_event_t) { | |||
| *event = yaml_event_t{} | |||
| } | |||
| ///* | |||
| // * Create a document object. | |||
| // */ | |||
| // | |||
| //YAML_DECLARE(int) | |||
| //yaml_document_initialize(document *yaml_document_t, | |||
| // version_directive *yaml_version_directive_t, | |||
| // tag_directives_start *yaml_tag_directive_t, | |||
| // tag_directives_end *yaml_tag_directive_t, | |||
| // start_implicit int, end_implicit int) | |||
| //{ | |||
| // struct { | |||
| // error yaml_error_type_t | |||
| // } context | |||
| // struct { | |||
| // start *yaml_node_t | |||
| // end *yaml_node_t | |||
| // top *yaml_node_t | |||
| // } nodes = { NULL, NULL, NULL } | |||
| // version_directive_copy *yaml_version_directive_t = NULL | |||
| // struct { | |||
| // start *yaml_tag_directive_t | |||
| // end *yaml_tag_directive_t | |||
| // top *yaml_tag_directive_t | |||
| // } tag_directives_copy = { NULL, NULL, NULL } | |||
| // value yaml_tag_directive_t = { NULL, NULL } | |||
| // mark yaml_mark_t = { 0, 0, 0 } | |||
| // | |||
| // assert(document) // Non-NULL document object is expected. | |||
| // assert((tag_directives_start && tag_directives_end) || | |||
| // (tag_directives_start == tag_directives_end)) | |||
| // // Valid tag directives are expected. | |||
| // | |||
| // if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error | |||
| // | |||
| // if (version_directive) { | |||
| // version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) | |||
| // if (!version_directive_copy) goto error | |||
| // version_directive_copy.major = version_directive.major | |||
| // version_directive_copy.minor = version_directive.minor | |||
| // } | |||
| // | |||
| // if (tag_directives_start != tag_directives_end) { | |||
| // tag_directive *yaml_tag_directive_t | |||
| // if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) | |||
| // goto error | |||
| // for (tag_directive = tag_directives_start | |||
| // tag_directive != tag_directives_end; tag_directive ++) { | |||
| // assert(tag_directive.handle) | |||
| // assert(tag_directive.prefix) | |||
| // if (!yaml_check_utf8(tag_directive.handle, | |||
| // strlen((char *)tag_directive.handle))) | |||
| // goto error | |||
| // if (!yaml_check_utf8(tag_directive.prefix, | |||
| // strlen((char *)tag_directive.prefix))) | |||
| // goto error | |||
| // value.handle = yaml_strdup(tag_directive.handle) | |||
| // value.prefix = yaml_strdup(tag_directive.prefix) | |||
| // if (!value.handle || !value.prefix) goto error | |||
| // if (!PUSH(&context, tag_directives_copy, value)) | |||
| // goto error | |||
| // value.handle = NULL | |||
| // value.prefix = NULL | |||
| // } | |||
| // } | |||
| // | |||
| // DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, | |||
| // tag_directives_copy.start, tag_directives_copy.top, | |||
| // start_implicit, end_implicit, mark, mark) | |||
| // | |||
| // return 1 | |||
| // | |||
| //error: | |||
| // STACK_DEL(&context, nodes) | |||
| // yaml_free(version_directive_copy) | |||
| // while (!STACK_EMPTY(&context, tag_directives_copy)) { | |||
| // value yaml_tag_directive_t = POP(&context, tag_directives_copy) | |||
| // yaml_free(value.handle) | |||
| // yaml_free(value.prefix) | |||
| // } | |||
| // STACK_DEL(&context, tag_directives_copy) | |||
| // yaml_free(value.handle) | |||
| // yaml_free(value.prefix) | |||
| // | |||
| // return 0 | |||
| //} | |||
| // | |||
| ///* | |||
| // * Destroy a document object. | |||
| // */ | |||
| // | |||
| //YAML_DECLARE(void) | |||
| //yaml_document_delete(document *yaml_document_t) | |||
| //{ | |||
| // struct { | |||
| // error yaml_error_type_t | |||
| // } context | |||
| // tag_directive *yaml_tag_directive_t | |||
| // | |||
| // context.error = YAML_NO_ERROR // Eliminate a compiler warning. | |||
| // | |||
| // assert(document) // Non-NULL document object is expected. | |||
| // | |||
| // while (!STACK_EMPTY(&context, document.nodes)) { | |||
| // node yaml_node_t = POP(&context, document.nodes) | |||
| // yaml_free(node.tag) | |||
| // switch (node.type) { | |||
| // case YAML_SCALAR_NODE: | |||
| // yaml_free(node.data.scalar.value) | |||
| // break | |||
| // case YAML_SEQUENCE_NODE: | |||
| // STACK_DEL(&context, node.data.sequence.items) | |||
| // break | |||
| // case YAML_MAPPING_NODE: | |||
| // STACK_DEL(&context, node.data.mapping.pairs) | |||
| // break | |||
| // default: | |||
| // assert(0) // Should not happen. | |||
| // } | |||
| // } | |||
| // STACK_DEL(&context, document.nodes) | |||
| // | |||
| // yaml_free(document.version_directive) | |||
| // for (tag_directive = document.tag_directives.start | |||
| // tag_directive != document.tag_directives.end | |||
| // tag_directive++) { | |||
| // yaml_free(tag_directive.handle) | |||
| // yaml_free(tag_directive.prefix) | |||
| // } | |||
| // yaml_free(document.tag_directives.start) | |||
| // | |||
| // memset(document, 0, sizeof(yaml_document_t)) | |||
| //} | |||
| // | |||
| ///** | |||
| // * Get a document node. | |||
| // */ | |||
| // | |||
| //YAML_DECLARE(yaml_node_t *) | |||
| //yaml_document_get_node(document *yaml_document_t, index int) | |||
| //{ | |||
| // assert(document) // Non-NULL document object is expected. | |||
| // | |||
| // if (index > 0 && document.nodes.start + index <= document.nodes.top) { | |||
| // return document.nodes.start + index - 1 | |||
| // } | |||
| // return NULL | |||
| //} | |||
| // | |||
| ///** | |||
| // * Get the root object. | |||
| // */ | |||
| // | |||
| //YAML_DECLARE(yaml_node_t *) | |||
| //yaml_document_get_root_node(document *yaml_document_t) | |||
| //{ | |||
| // assert(document) // Non-NULL document object is expected. | |||
| // | |||
| // if (document.nodes.top != document.nodes.start) { | |||
| // return document.nodes.start | |||
| // } | |||
| // return NULL | |||
| //} | |||
| // | |||
| ///* | |||
| // * Add a scalar node to a document. | |||
| // */ | |||
| // | |||
| //YAML_DECLARE(int) | |||
| //yaml_document_add_scalar(document *yaml_document_t, | |||
| // tag *yaml_char_t, value *yaml_char_t, length int, | |||
| // style yaml_scalar_style_t) | |||
| //{ | |||
| // struct { | |||
| // error yaml_error_type_t | |||
| // } context | |||
| // mark yaml_mark_t = { 0, 0, 0 } | |||
| // tag_copy *yaml_char_t = NULL | |||
| // value_copy *yaml_char_t = NULL | |||
| // node yaml_node_t | |||
| // | |||
| // assert(document) // Non-NULL document object is expected. | |||
| // assert(value) // Non-NULL value is expected. | |||
| // | |||
| // if (!tag) { | |||
| // tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG | |||
| // } | |||
| // | |||
| // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error | |||
| // tag_copy = yaml_strdup(tag) | |||
| // if (!tag_copy) goto error | |||
| // | |||
| // if (length < 0) { | |||
| // length = strlen((char *)value) | |||
| // } | |||
| // | |||
| // if (!yaml_check_utf8(value, length)) goto error | |||
| // value_copy = yaml_malloc(length+1) | |||
| // if (!value_copy) goto error | |||
| // memcpy(value_copy, value, length) | |||
| // value_copy[length] = '\0' | |||
| // | |||
| // SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) | |||
| // if (!PUSH(&context, document.nodes, node)) goto error | |||
| // | |||
| // return document.nodes.top - document.nodes.start | |||
| // | |||
| //error: | |||
| // yaml_free(tag_copy) | |||
| // yaml_free(value_copy) | |||
| // | |||
| // return 0 | |||
| //} | |||
| // | |||
| ///* | |||
| // * Add a sequence node to a document. | |||
| // */ | |||
| // | |||
| //YAML_DECLARE(int) | |||
| //yaml_document_add_sequence(document *yaml_document_t, | |||
| // tag *yaml_char_t, style yaml_sequence_style_t) | |||
| //{ | |||
| // struct { | |||
| // error yaml_error_type_t | |||
| // } context | |||
| // mark yaml_mark_t = { 0, 0, 0 } | |||
| // tag_copy *yaml_char_t = NULL | |||
| // struct { | |||
| // start *yaml_node_item_t | |||
| // end *yaml_node_item_t | |||
| // top *yaml_node_item_t | |||
| // } items = { NULL, NULL, NULL } | |||
| // node yaml_node_t | |||
| // | |||
| // assert(document) // Non-NULL document object is expected. | |||
| // | |||
| // if (!tag) { | |||
| // tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG | |||
| // } | |||
| // | |||
| // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error | |||
| // tag_copy = yaml_strdup(tag) | |||
| // if (!tag_copy) goto error | |||
| // | |||
| // if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error | |||
| // | |||
| // SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, | |||
| // style, mark, mark) | |||
| // if (!PUSH(&context, document.nodes, node)) goto error | |||
| // | |||
| // return document.nodes.top - document.nodes.start | |||
| // | |||
| //error: | |||
| // STACK_DEL(&context, items) | |||
| // yaml_free(tag_copy) | |||
| // | |||
| // return 0 | |||
| //} | |||
| // | |||
| ///* | |||
| // * Add a mapping node to a document. | |||
| // */ | |||
| // | |||
| //YAML_DECLARE(int) | |||
| //yaml_document_add_mapping(document *yaml_document_t, | |||
| // tag *yaml_char_t, style yaml_mapping_style_t) | |||
| //{ | |||
| // struct { | |||
| // error yaml_error_type_t | |||
| // } context | |||
| // mark yaml_mark_t = { 0, 0, 0 } | |||
| // tag_copy *yaml_char_t = NULL | |||
| // struct { | |||
| // start *yaml_node_pair_t | |||
| // end *yaml_node_pair_t | |||
| // top *yaml_node_pair_t | |||
| // } pairs = { NULL, NULL, NULL } | |||
| // node yaml_node_t | |||
| // | |||
| // assert(document) // Non-NULL document object is expected. | |||
| // | |||
| // if (!tag) { | |||
| // tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG | |||
| // } | |||
| // | |||
| // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error | |||
| // tag_copy = yaml_strdup(tag) | |||
| // if (!tag_copy) goto error | |||
| // | |||
| // if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error | |||
| // | |||
| // MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, | |||
| // style, mark, mark) | |||
| // if (!PUSH(&context, document.nodes, node)) goto error | |||
| // | |||
| // return document.nodes.top - document.nodes.start | |||
| // | |||
| //error: | |||
| // STACK_DEL(&context, pairs) | |||
| // yaml_free(tag_copy) | |||
| // | |||
| // return 0 | |||
| //} | |||
| // | |||
| ///* | |||
| // * Append an item to a sequence node. | |||
| // */ | |||
| // | |||
| //YAML_DECLARE(int) | |||
| //yaml_document_append_sequence_item(document *yaml_document_t, | |||
| // sequence int, item int) | |||
| //{ | |||
| // struct { | |||
| // error yaml_error_type_t | |||
| // } context | |||
| // | |||
| // assert(document) // Non-NULL document is required. | |||
| // assert(sequence > 0 | |||
| // && document.nodes.start + sequence <= document.nodes.top) | |||
| // // Valid sequence id is required. | |||
| // assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) | |||
| // // A sequence node is required. | |||
| // assert(item > 0 && document.nodes.start + item <= document.nodes.top) | |||
| // // Valid item id is required. | |||
| // | |||
| // if (!PUSH(&context, | |||
| // document.nodes.start[sequence-1].data.sequence.items, item)) | |||
| // return 0 | |||
| // | |||
| // return 1 | |||
| //} | |||
| // | |||
| ///* | |||
| // * Append a pair of a key and a value to a mapping node. | |||
| // */ | |||
| // | |||
| //YAML_DECLARE(int) | |||
| //yaml_document_append_mapping_pair(document *yaml_document_t, | |||
| // mapping int, key int, value int) | |||
| //{ | |||
| // struct { | |||
| // error yaml_error_type_t | |||
| // } context | |||
| // | |||
| // pair yaml_node_pair_t | |||
| // | |||
| // assert(document) // Non-NULL document is required. | |||
| // assert(mapping > 0 | |||
| // && document.nodes.start + mapping <= document.nodes.top) | |||
| // // Valid mapping id is required. | |||
| // assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) | |||
| // // A mapping node is required. | |||
| // assert(key > 0 && document.nodes.start + key <= document.nodes.top) | |||
| // // Valid key id is required. | |||
| // assert(value > 0 && document.nodes.start + value <= document.nodes.top) | |||
| // // Valid value id is required. | |||
| // | |||
| // pair.key = key | |||
| // pair.value = value | |||
| // | |||
| // if (!PUSH(&context, | |||
| // document.nodes.start[mapping-1].data.mapping.pairs, pair)) | |||
| // return 0 | |||
| // | |||
| // return 1 | |||
| //} | |||
| // | |||
| // | |||
| @@ -0,0 +1,931 @@ | |||
| // | |||
| // Copyright (c) 2011-2019 Canonical Ltd | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| package yaml | |||
| import ( | |||
| "encoding" | |||
| "encoding/base64" | |||
| "fmt" | |||
| "io" | |||
| "math" | |||
| "reflect" | |||
| "strconv" | |||
| "time" | |||
| ) | |||
| // ---------------------------------------------------------------------------- | |||
| // Parser, produces a node tree out of a libyaml event stream. | |||
| type parser struct { | |||
| parser yaml_parser_t | |||
| event yaml_event_t | |||
| doc *Node | |||
| anchors map[string]*Node | |||
| doneInit bool | |||
| } | |||
| func newParser(b []byte) *parser { | |||
| p := parser{} | |||
| if !yaml_parser_initialize(&p.parser) { | |||
| panic("failed to initialize YAML emitter") | |||
| } | |||
| if len(b) == 0 { | |||
| b = []byte{'\n'} | |||
| } | |||
| yaml_parser_set_input_string(&p.parser, b) | |||
| return &p | |||
| } | |||
| func newParserFromReader(r io.Reader) *parser { | |||
| p := parser{} | |||
| if !yaml_parser_initialize(&p.parser) { | |||
| panic("failed to initialize YAML emitter") | |||
| } | |||
| yaml_parser_set_input_reader(&p.parser, r) | |||
| return &p | |||
| } | |||
| func (p *parser) init() { | |||
| if p.doneInit { | |||
| return | |||
| } | |||
| p.anchors = make(map[string]*Node) | |||
| p.expect(yaml_STREAM_START_EVENT) | |||
| p.doneInit = true | |||
| } | |||
| func (p *parser) destroy() { | |||
| if p.event.typ != yaml_NO_EVENT { | |||
| yaml_event_delete(&p.event) | |||
| } | |||
| yaml_parser_delete(&p.parser) | |||
| } | |||
| // expect consumes an event from the event stream and | |||
| // checks that it's of the expected type. | |||
| func (p *parser) expect(e yaml_event_type_t) { | |||
| if p.event.typ == yaml_NO_EVENT { | |||
| if !yaml_parser_parse(&p.parser, &p.event) { | |||
| p.fail() | |||
| } | |||
| } | |||
| if p.event.typ == yaml_STREAM_END_EVENT { | |||
| failf("attempted to go past the end of stream; corrupted value?") | |||
| } | |||
| if p.event.typ != e { | |||
| p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) | |||
| p.fail() | |||
| } | |||
| yaml_event_delete(&p.event) | |||
| p.event.typ = yaml_NO_EVENT | |||
| } | |||
| // peek peeks at the next event in the event stream, | |||
| // puts the results into p.event and returns the event type. | |||
| func (p *parser) peek() yaml_event_type_t { | |||
| if p.event.typ != yaml_NO_EVENT { | |||
| return p.event.typ | |||
| } | |||
| if !yaml_parser_parse(&p.parser, &p.event) { | |||
| p.fail() | |||
| } | |||
| return p.event.typ | |||
| } | |||
| func (p *parser) fail() { | |||
| var where string | |||
| var line int | |||
| if p.parser.problem_mark.line != 0 { | |||
| line = p.parser.problem_mark.line | |||
| // Scanner errors don't iterate line before returning error | |||
| if p.parser.error == yaml_SCANNER_ERROR { | |||
| line++ | |||
| } | |||
| } else if p.parser.context_mark.line != 0 { | |||
| line = p.parser.context_mark.line | |||
| } | |||
| if line != 0 { | |||
| where = "line " + strconv.Itoa(line) + ": " | |||
| } | |||
| var msg string | |||
| if len(p.parser.problem) > 0 { | |||
| msg = p.parser.problem | |||
| } else { | |||
| msg = "unknown problem parsing YAML content" | |||
| } | |||
| failf("%s%s", where, msg) | |||
| } | |||
| func (p *parser) anchor(n *Node, anchor []byte) { | |||
| if anchor != nil { | |||
| n.Anchor = string(anchor) | |||
| p.anchors[n.Anchor] = n | |||
| } | |||
| } | |||
| func (p *parser) parse() *Node { | |||
| p.init() | |||
| switch p.peek() { | |||
| case yaml_SCALAR_EVENT: | |||
| return p.scalar() | |||
| case yaml_ALIAS_EVENT: | |||
| return p.alias() | |||
| case yaml_MAPPING_START_EVENT: | |||
| return p.mapping() | |||
| case yaml_SEQUENCE_START_EVENT: | |||
| return p.sequence() | |||
| case yaml_DOCUMENT_START_EVENT: | |||
| return p.document() | |||
| case yaml_STREAM_END_EVENT: | |||
| // Happens when attempting to decode an empty buffer. | |||
| return nil | |||
| case yaml_TAIL_COMMENT_EVENT: | |||
| panic("internal error: unexpected tail comment event (please report)") | |||
| default: | |||
| panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String()) | |||
| } | |||
| } | |||
| func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { | |||
| var style Style | |||
| if tag != "" && tag != "!" { | |||
| tag = shortTag(tag) | |||
| style = TaggedStyle | |||
| } else if defaultTag != "" { | |||
| tag = defaultTag | |||
| } else if kind == ScalarNode { | |||
| tag, _ = resolve("", value) | |||
| } | |||
| return &Node{ | |||
| Kind: kind, | |||
| Tag: tag, | |||
| Value: value, | |||
| Style: style, | |||
| Line: p.event.start_mark.line + 1, | |||
| Column: p.event.start_mark.column + 1, | |||
| HeadComment: string(p.event.head_comment), | |||
| LineComment: string(p.event.line_comment), | |||
| FootComment: string(p.event.foot_comment), | |||
| } | |||
| } | |||
| func (p *parser) parseChild(parent *Node) *Node { | |||
| child := p.parse() | |||
| parent.Content = append(parent.Content, child) | |||
| return child | |||
| } | |||
| func (p *parser) document() *Node { | |||
| n := p.node(DocumentNode, "", "", "") | |||
| p.doc = n | |||
| p.expect(yaml_DOCUMENT_START_EVENT) | |||
| p.parseChild(n) | |||
| if p.peek() == yaml_DOCUMENT_END_EVENT { | |||
| n.FootComment = string(p.event.foot_comment) | |||
| } | |||
| p.expect(yaml_DOCUMENT_END_EVENT) | |||
| return n | |||
| } | |||
| func (p *parser) alias() *Node { | |||
| n := p.node(AliasNode, "", "", string(p.event.anchor)) | |||
| n.Alias = p.anchors[n.Value] | |||
| if n.Alias == nil { | |||
| failf("unknown anchor '%s' referenced", n.Value) | |||
| } | |||
| p.expect(yaml_ALIAS_EVENT) | |||
| return n | |||
| } | |||
| func (p *parser) scalar() *Node { | |||
| var parsedStyle = p.event.scalar_style() | |||
| var nodeStyle Style | |||
| switch { | |||
| case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0: | |||
| nodeStyle = DoubleQuotedStyle | |||
| case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0: | |||
| nodeStyle = SingleQuotedStyle | |||
| case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0: | |||
| nodeStyle = LiteralStyle | |||
| case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0: | |||
| nodeStyle = FoldedStyle | |||
| } | |||
| var nodeValue = string(p.event.value) | |||
| var nodeTag = string(p.event.tag) | |||
| var defaultTag string | |||
| if nodeStyle == 0 { | |||
| if nodeValue == "<<" { | |||
| defaultTag = mergeTag | |||
| } | |||
| } else { | |||
| defaultTag = strTag | |||
| } | |||
| n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue) | |||
| n.Style |= nodeStyle | |||
| p.anchor(n, p.event.anchor) | |||
| p.expect(yaml_SCALAR_EVENT) | |||
| return n | |||
| } | |||
| func (p *parser) sequence() *Node { | |||
| n := p.node(SequenceNode, seqTag, string(p.event.tag), "") | |||
| if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 { | |||
| n.Style |= FlowStyle | |||
| } | |||
| p.anchor(n, p.event.anchor) | |||
| p.expect(yaml_SEQUENCE_START_EVENT) | |||
| for p.peek() != yaml_SEQUENCE_END_EVENT { | |||
| p.parseChild(n) | |||
| } | |||
| n.LineComment = string(p.event.line_comment) | |||
| n.FootComment = string(p.event.foot_comment) | |||
| p.expect(yaml_SEQUENCE_END_EVENT) | |||
| return n | |||
| } | |||
| func (p *parser) mapping() *Node { | |||
| n := p.node(MappingNode, mapTag, string(p.event.tag), "") | |||
| block := true | |||
| if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 { | |||
| block = false | |||
| n.Style |= FlowStyle | |||
| } | |||
| p.anchor(n, p.event.anchor) | |||
| p.expect(yaml_MAPPING_START_EVENT) | |||
| for p.peek() != yaml_MAPPING_END_EVENT { | |||
| k := p.parseChild(n) | |||
| if block && k.FootComment != "" { | |||
| // Must be a foot comment for the prior value when being dedented. | |||
| if len(n.Content) > 2 { | |||
| n.Content[len(n.Content)-3].FootComment = k.FootComment | |||
| k.FootComment = "" | |||
| } | |||
| } | |||
| v := p.parseChild(n) | |||
| if k.FootComment == "" && v.FootComment != "" { | |||
| k.FootComment = v.FootComment | |||
| v.FootComment = "" | |||
| } | |||
| if p.peek() == yaml_TAIL_COMMENT_EVENT { | |||
| if k.FootComment == "" { | |||
| k.FootComment = string(p.event.foot_comment) | |||
| } | |||
| p.expect(yaml_TAIL_COMMENT_EVENT) | |||
| } | |||
| } | |||
| n.LineComment = string(p.event.line_comment) | |||
| n.FootComment = string(p.event.foot_comment) | |||
| if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 { | |||
| n.Content[len(n.Content)-2].FootComment = n.FootComment | |||
| n.FootComment = "" | |||
| } | |||
| p.expect(yaml_MAPPING_END_EVENT) | |||
| return n | |||
| } | |||
| // ---------------------------------------------------------------------------- | |||
| // Decoder, unmarshals a node into a provided value. | |||
| type decoder struct { | |||
| doc *Node | |||
| aliases map[*Node]bool | |||
| terrors []string | |||
| stringMapType reflect.Type | |||
| generalMapType reflect.Type | |||
| knownFields bool | |||
| uniqueKeys bool | |||
| decodeCount int | |||
| aliasCount int | |||
| aliasDepth int | |||
| } | |||
| var ( | |||
| nodeType = reflect.TypeOf(Node{}) | |||
| durationType = reflect.TypeOf(time.Duration(0)) | |||
| stringMapType = reflect.TypeOf(map[string]interface{}{}) | |||
| generalMapType = reflect.TypeOf(map[interface{}]interface{}{}) | |||
| ifaceType = generalMapType.Elem() | |||
| timeType = reflect.TypeOf(time.Time{}) | |||
| ptrTimeType = reflect.TypeOf(&time.Time{}) | |||
| ) | |||
| func newDecoder() *decoder { | |||
| d := &decoder{ | |||
| stringMapType: stringMapType, | |||
| generalMapType: generalMapType, | |||
| uniqueKeys: true, | |||
| } | |||
| d.aliases = make(map[*Node]bool) | |||
| return d | |||
| } | |||
| func (d *decoder) terror(n *Node, tag string, out reflect.Value) { | |||
| if n.Tag != "" { | |||
| tag = n.Tag | |||
| } | |||
| value := n.Value | |||
| if tag != seqTag && tag != mapTag { | |||
| if len(value) > 10 { | |||
| value = " `" + value[:7] + "...`" | |||
| } else { | |||
| value = " `" + value + "`" | |||
| } | |||
| } | |||
| d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type())) | |||
| } | |||
| func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { | |||
| err := u.UnmarshalYAML(n) | |||
| if e, ok := err.(*TypeError); ok { | |||
| d.terrors = append(d.terrors, e.Errors...) | |||
| return false | |||
| } | |||
| if err != nil { | |||
| fail(err) | |||
| } | |||
| return true | |||
| } | |||
| func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) { | |||
| terrlen := len(d.terrors) | |||
| err := u.UnmarshalYAML(func(v interface{}) (err error) { | |||
| defer handleErr(&err) | |||
| d.unmarshal(n, reflect.ValueOf(v)) | |||
| if len(d.terrors) > terrlen { | |||
| issues := d.terrors[terrlen:] | |||
| d.terrors = d.terrors[:terrlen] | |||
| return &TypeError{issues} | |||
| } | |||
| return nil | |||
| }) | |||
| if e, ok := err.(*TypeError); ok { | |||
| d.terrors = append(d.terrors, e.Errors...) | |||
| return false | |||
| } | |||
| if err != nil { | |||
| fail(err) | |||
| } | |||
| return true | |||
| } | |||
| // d.prepare initializes and dereferences pointers and calls UnmarshalYAML | |||
| // if a value is found to implement it. | |||
| // It returns the initialized and dereferenced out value, whether | |||
| // unmarshalling was already done by UnmarshalYAML, and if so whether | |||
| // its types unmarshalled appropriately. | |||
| // | |||
| // If n holds a null value, prepare returns before doing anything. | |||
| func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { | |||
| if n.ShortTag() == nullTag { | |||
| return out, false, false | |||
| } | |||
| again := true | |||
| for again { | |||
| again = false | |||
| if out.Kind() == reflect.Ptr { | |||
| if out.IsNil() { | |||
| out.Set(reflect.New(out.Type().Elem())) | |||
| } | |||
| out = out.Elem() | |||
| again = true | |||
| } | |||
| if out.CanAddr() { | |||
| outi := out.Addr().Interface() | |||
| if u, ok := outi.(Unmarshaler); ok { | |||
| good = d.callUnmarshaler(n, u) | |||
| return out, true, good | |||
| } | |||
| if u, ok := outi.(obsoleteUnmarshaler); ok { | |||
| good = d.callObsoleteUnmarshaler(n, u) | |||
| return out, true, good | |||
| } | |||
| } | |||
| } | |||
| return out, false, false | |||
| } | |||
| func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) { | |||
| if n.ShortTag() == nullTag { | |||
| return reflect.Value{} | |||
| } | |||
| for _, num := range index { | |||
| for { | |||
| if v.Kind() == reflect.Ptr { | |||
| if v.IsNil() { | |||
| v.Set(reflect.New(v.Type().Elem())) | |||
| } | |||
| v = v.Elem() | |||
| continue | |||
| } | |||
| break | |||
| } | |||
| v = v.Field(num) | |||
| } | |||
| return v | |||
| } | |||
| const ( | |||
| // 400,000 decode operations is ~500kb of dense object declarations, or | |||
| // ~5kb of dense object declarations with 10000% alias expansion | |||
| alias_ratio_range_low = 400000 | |||
| // 4,000,000 decode operations is ~5MB of dense object declarations, or | |||
| // ~4.5MB of dense object declarations with 10% alias expansion | |||
| alias_ratio_range_high = 4000000 | |||
| // alias_ratio_range is the range over which we scale allowed alias ratios | |||
| alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) | |||
| ) | |||
| func allowedAliasRatio(decodeCount int) float64 { | |||
| switch { | |||
| case decodeCount <= alias_ratio_range_low: | |||
| // allow 99% to come from alias expansion for small-to-medium documents | |||
| return 0.99 | |||
| case decodeCount >= alias_ratio_range_high: | |||
| // allow 10% to come from alias expansion for very large documents | |||
| return 0.10 | |||
| default: | |||
| // scale smoothly from 99% down to 10% over the range. | |||
| // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. | |||
| // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). | |||
| return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) | |||
| } | |||
| } | |||
| func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { | |||
| d.decodeCount++ | |||
| if d.aliasDepth > 0 { | |||
| d.aliasCount++ | |||
| } | |||
| if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { | |||
| failf("document contains excessive aliasing") | |||
| } | |||
| if out.Type() == nodeType { | |||
| out.Set(reflect.ValueOf(n).Elem()) | |||
| return true | |||
| } | |||
| switch n.Kind { | |||
| case DocumentNode: | |||
| return d.document(n, out) | |||
| case AliasNode: | |||
| return d.alias(n, out) | |||
| } | |||
| out, unmarshaled, good := d.prepare(n, out) | |||
| if unmarshaled { | |||
| return good | |||
| } | |||
| switch n.Kind { | |||
| case ScalarNode: | |||
| good = d.scalar(n, out) | |||
| case MappingNode: | |||
| good = d.mapping(n, out) | |||
| case SequenceNode: | |||
| good = d.sequence(n, out) | |||
| default: | |||
| panic("internal error: unknown node kind: " + strconv.Itoa(int(n.Kind))) | |||
| } | |||
| return good | |||
| } | |||
| func (d *decoder) document(n *Node, out reflect.Value) (good bool) { | |||
| if len(n.Content) == 1 { | |||
| d.doc = n | |||
| d.unmarshal(n.Content[0], out) | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| func (d *decoder) alias(n *Node, out reflect.Value) (good bool) { | |||
| if d.aliases[n] { | |||
| // TODO this could actually be allowed in some circumstances. | |||
| failf("anchor '%s' value contains itself", n.Value) | |||
| } | |||
| d.aliases[n] = true | |||
| d.aliasDepth++ | |||
| good = d.unmarshal(n.Alias, out) | |||
| d.aliasDepth-- | |||
| delete(d.aliases, n) | |||
| return good | |||
| } | |||
| var zeroValue reflect.Value | |||
| func resetMap(out reflect.Value) { | |||
| for _, k := range out.MapKeys() { | |||
| out.SetMapIndex(k, zeroValue) | |||
| } | |||
| } | |||
| func (d *decoder) scalar(n *Node, out reflect.Value) bool { | |||
| var tag string | |||
| var resolved interface{} | |||
| if n.indicatedString() { | |||
| tag = strTag | |||
| resolved = n.Value | |||
| } else { | |||
| tag, resolved = resolve(n.Tag, n.Value) | |||
| if tag == binaryTag { | |||
| data, err := base64.StdEncoding.DecodeString(resolved.(string)) | |||
| if err != nil { | |||
| failf("!!binary value contains invalid base64 data") | |||
| } | |||
| resolved = string(data) | |||
| } | |||
| } | |||
| if resolved == nil { | |||
| if out.CanAddr() { | |||
| switch out.Kind() { | |||
| case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: | |||
| out.Set(reflect.Zero(out.Type())) | |||
| return true | |||
| } | |||
| } | |||
| return false | |||
| } | |||
| if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { | |||
| // We've resolved to exactly the type we want, so use that. | |||
| out.Set(resolvedv) | |||
| return true | |||
| } | |||
| // Perhaps we can use the value as a TextUnmarshaler to | |||
| // set its value. | |||
| if out.CanAddr() { | |||
| u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) | |||
| if ok { | |||
| var text []byte | |||
| if tag == binaryTag { | |||
| text = []byte(resolved.(string)) | |||
| } else { | |||
| // We let any value be unmarshaled into TextUnmarshaler. | |||
| // That might be more lax than we'd like, but the | |||
| // TextUnmarshaler itself should bowl out any dubious values. | |||
| text = []byte(n.Value) | |||
| } | |||
| err := u.UnmarshalText(text) | |||
| if err != nil { | |||
| fail(err) | |||
| } | |||
| return true | |||
| } | |||
| } | |||
| switch out.Kind() { | |||
| case reflect.String: | |||
| if tag == binaryTag { | |||
| out.SetString(resolved.(string)) | |||
| return true | |||
| } | |||
| out.SetString(n.Value) | |||
| return true | |||
| case reflect.Interface: | |||
| out.Set(reflect.ValueOf(resolved)) | |||
| return true | |||
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||
| // This used to work in v2, but it's very unfriendly. | |||
| isDuration := out.Type() == durationType | |||
| switch resolved := resolved.(type) { | |||
| case int: | |||
| if !isDuration && !out.OverflowInt(int64(resolved)) { | |||
| out.SetInt(int64(resolved)) | |||
| return true | |||
| } | |||
| case int64: | |||
| if !isDuration && !out.OverflowInt(resolved) { | |||
| out.SetInt(resolved) | |||
| return true | |||
| } | |||
| case uint64: | |||
| if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | |||
| out.SetInt(int64(resolved)) | |||
| return true | |||
| } | |||
| case float64: | |||
| if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | |||
| out.SetInt(int64(resolved)) | |||
| return true | |||
| } | |||
| case string: | |||
| if out.Type() == durationType { | |||
| d, err := time.ParseDuration(resolved) | |||
| if err == nil { | |||
| out.SetInt(int64(d)) | |||
| return true | |||
| } | |||
| } | |||
| } | |||
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||
| switch resolved := resolved.(type) { | |||
| case int: | |||
| if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { | |||
| out.SetUint(uint64(resolved)) | |||
| return true | |||
| } | |||
| case int64: | |||
| if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { | |||
| out.SetUint(uint64(resolved)) | |||
| return true | |||
| } | |||
| case uint64: | |||
| if !out.OverflowUint(uint64(resolved)) { | |||
| out.SetUint(uint64(resolved)) | |||
| return true | |||
| } | |||
| case float64: | |||
| if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { | |||
| out.SetUint(uint64(resolved)) | |||
| return true | |||
| } | |||
| } | |||
| case reflect.Bool: | |||
| switch resolved := resolved.(type) { | |||
| case bool: | |||
| out.SetBool(resolved) | |||
| return true | |||
| case string: | |||
| // This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). | |||
| // It only works if explicitly attempting to unmarshal into a typed bool value. | |||
| switch resolved { | |||
| case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": | |||
| out.SetBool(true) | |||
| return true | |||
| case "n", "N", "no", "No", "NO", "off", "Off", "OFF": | |||
| out.SetBool(false) | |||
| return true | |||
| } | |||
| } | |||
| case reflect.Float32, reflect.Float64: | |||
| switch resolved := resolved.(type) { | |||
| case int: | |||
| out.SetFloat(float64(resolved)) | |||
| return true | |||
| case int64: | |||
| out.SetFloat(float64(resolved)) | |||
| return true | |||
| case uint64: | |||
| out.SetFloat(float64(resolved)) | |||
| return true | |||
| case float64: | |||
| out.SetFloat(resolved) | |||
| return true | |||
| } | |||
| case reflect.Struct: | |||
| if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { | |||
| out.Set(resolvedv) | |||
| return true | |||
| } | |||
| case reflect.Ptr: | |||
| panic("yaml internal error: please report the issue") | |||
| } | |||
| d.terror(n, tag, out) | |||
| return false | |||
| } | |||
| func settableValueOf(i interface{}) reflect.Value { | |||
| v := reflect.ValueOf(i) | |||
| sv := reflect.New(v.Type()).Elem() | |||
| sv.Set(v) | |||
| return sv | |||
| } | |||
| func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { | |||
| l := len(n.Content) | |||
| var iface reflect.Value | |||
| switch out.Kind() { | |||
| case reflect.Slice: | |||
| out.Set(reflect.MakeSlice(out.Type(), l, l)) | |||
| case reflect.Array: | |||
| if l != out.Len() { | |||
| failf("invalid array: want %d elements but got %d", out.Len(), l) | |||
| } | |||
| case reflect.Interface: | |||
| // No type hints. Will have to use a generic sequence. | |||
| iface = out | |||
| out = settableValueOf(make([]interface{}, l)) | |||
| default: | |||
| d.terror(n, seqTag, out) | |||
| return false | |||
| } | |||
| et := out.Type().Elem() | |||
| j := 0 | |||
| for i := 0; i < l; i++ { | |||
| e := reflect.New(et).Elem() | |||
| if ok := d.unmarshal(n.Content[i], e); ok { | |||
| out.Index(j).Set(e) | |||
| j++ | |||
| } | |||
| } | |||
| if out.Kind() != reflect.Array { | |||
| out.Set(out.Slice(0, j)) | |||
| } | |||
| if iface.IsValid() { | |||
| iface.Set(out) | |||
| } | |||
| return true | |||
| } | |||
| func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { | |||
| l := len(n.Content) | |||
| if d.uniqueKeys { | |||
| nerrs := len(d.terrors) | |||
| for i := 0; i < l; i += 2 { | |||
| ni := n.Content[i] | |||
| for j := i + 2; j < l; j += 2 { | |||
| nj := n.Content[j] | |||
| if ni.Kind == nj.Kind && ni.Value == nj.Value { | |||
| d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line)) | |||
| } | |||
| } | |||
| } | |||
| if len(d.terrors) > nerrs { | |||
| return false | |||
| } | |||
| } | |||
| switch out.Kind() { | |||
| case reflect.Struct: | |||
| return d.mappingStruct(n, out) | |||
| case reflect.Map: | |||
| // okay | |||
| case reflect.Interface: | |||
| iface := out | |||
| if isStringMap(n) { | |||
| out = reflect.MakeMap(d.stringMapType) | |||
| } else { | |||
| out = reflect.MakeMap(d.generalMapType) | |||
| } | |||
| iface.Set(out) | |||
| default: | |||
| d.terror(n, mapTag, out) | |||
| return false | |||
| } | |||
| outt := out.Type() | |||
| kt := outt.Key() | |||
| et := outt.Elem() | |||
| stringMapType := d.stringMapType | |||
| generalMapType := d.generalMapType | |||
| if outt.Elem() == ifaceType { | |||
| if outt.Key().Kind() == reflect.String { | |||
| d.stringMapType = outt | |||
| } else if outt.Key() == ifaceType { | |||
| d.generalMapType = outt | |||
| } | |||
| } | |||
| if out.IsNil() { | |||
| out.Set(reflect.MakeMap(outt)) | |||
| } | |||
| for i := 0; i < l; i += 2 { | |||
| if isMerge(n.Content[i]) { | |||
| d.merge(n.Content[i+1], out) | |||
| continue | |||
| } | |||
| k := reflect.New(kt).Elem() | |||
| if d.unmarshal(n.Content[i], k) { | |||
| kkind := k.Kind() | |||
| if kkind == reflect.Interface { | |||
| kkind = k.Elem().Kind() | |||
| } | |||
| if kkind == reflect.Map || kkind == reflect.Slice { | |||
| failf("invalid map key: %#v", k.Interface()) | |||
| } | |||
| e := reflect.New(et).Elem() | |||
| if d.unmarshal(n.Content[i+1], e) { | |||
| out.SetMapIndex(k, e) | |||
| } | |||
| } | |||
| } | |||
| d.stringMapType = stringMapType | |||
| d.generalMapType = generalMapType | |||
| return true | |||
| } | |||
| func isStringMap(n *Node) bool { | |||
| if n.Kind != MappingNode { | |||
| return false | |||
| } | |||
| l := len(n.Content) | |||
| for i := 0; i < l; i += 2 { | |||
| if n.Content[i].ShortTag() != strTag { | |||
| return false | |||
| } | |||
| } | |||
| return true | |||
| } | |||
| func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { | |||
| sinfo, err := getStructInfo(out.Type()) | |||
| if err != nil { | |||
| panic(err) | |||
| } | |||
| var inlineMap reflect.Value | |||
| var elemType reflect.Type | |||
| if sinfo.InlineMap != -1 { | |||
| inlineMap = out.Field(sinfo.InlineMap) | |||
| inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) | |||
| elemType = inlineMap.Type().Elem() | |||
| } | |||
| for _, index := range sinfo.InlineUnmarshalers { | |||
| field := d.fieldByIndex(n, out, index) | |||
| d.prepare(n, field) | |||
| } | |||
| var doneFields []bool | |||
| if d.uniqueKeys { | |||
| doneFields = make([]bool, len(sinfo.FieldsList)) | |||
| } | |||
| name := settableValueOf("") | |||
| l := len(n.Content) | |||
| for i := 0; i < l; i += 2 { | |||
| ni := n.Content[i] | |||
| if isMerge(ni) { | |||
| d.merge(n.Content[i+1], out) | |||
| continue | |||
| } | |||
| if !d.unmarshal(ni, name) { | |||
| continue | |||
| } | |||
| if info, ok := sinfo.FieldsMap[name.String()]; ok { | |||
| if d.uniqueKeys { | |||
| if doneFields[info.Id] { | |||
| d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) | |||
| continue | |||
| } | |||
| doneFields[info.Id] = true | |||
| } | |||
| var field reflect.Value | |||
| if info.Inline == nil { | |||
| field = out.Field(info.Num) | |||
| } else { | |||
| field = d.fieldByIndex(n, out, info.Inline) | |||
| } | |||
| d.unmarshal(n.Content[i+1], field) | |||
| } else if sinfo.InlineMap != -1 { | |||
| if inlineMap.IsNil() { | |||
| inlineMap.Set(reflect.MakeMap(inlineMap.Type())) | |||
| } | |||
| value := reflect.New(elemType).Elem() | |||
| d.unmarshal(n.Content[i+1], value) | |||
| inlineMap.SetMapIndex(name, value) | |||
| } else if d.knownFields { | |||
| d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) | |||
| } | |||
| } | |||
| return true | |||
| } | |||
| func failWantMap() { | |||
| failf("map merge requires map or sequence of maps as the value") | |||
| } | |||
| func (d *decoder) merge(n *Node, out reflect.Value) { | |||
| switch n.Kind { | |||
| case MappingNode: | |||
| d.unmarshal(n, out) | |||
| case AliasNode: | |||
| if n.Alias != nil && n.Alias.Kind != MappingNode { | |||
| failWantMap() | |||
| } | |||
| d.unmarshal(n, out) | |||
| case SequenceNode: | |||
| // Step backwards as earlier nodes take precedence. | |||
| for i := len(n.Content) - 1; i >= 0; i-- { | |||
| ni := n.Content[i] | |||
| if ni.Kind == AliasNode { | |||
| if ni.Alias != nil && ni.Alias.Kind != MappingNode { | |||
| failWantMap() | |||
| } | |||
| } else if ni.Kind != MappingNode { | |||
| failWantMap() | |||
| } | |||
| d.unmarshal(ni, out) | |||
| } | |||
| default: | |||
| failWantMap() | |||
| } | |||
| } | |||
| func isMerge(n *Node) bool { | |||
| return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag) | |||
| } | |||
| @@ -0,0 +1,561 @@ | |||
| // | |||
| // Copyright (c) 2011-2019 Canonical Ltd | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| package yaml | |||
| import ( | |||
| "encoding" | |||
| "fmt" | |||
| "io" | |||
| "reflect" | |||
| "regexp" | |||
| "sort" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| "unicode/utf8" | |||
| ) | |||
| type encoder struct { | |||
| emitter yaml_emitter_t | |||
| event yaml_event_t | |||
| out []byte | |||
| flow bool | |||
| indent int | |||
| doneInit bool | |||
| } | |||
| func newEncoder() *encoder { | |||
| e := &encoder{} | |||
| yaml_emitter_initialize(&e.emitter) | |||
| yaml_emitter_set_output_string(&e.emitter, &e.out) | |||
| yaml_emitter_set_unicode(&e.emitter, true) | |||
| return e | |||
| } | |||
| func newEncoderWithWriter(w io.Writer) *encoder { | |||
| e := &encoder{} | |||
| yaml_emitter_initialize(&e.emitter) | |||
| yaml_emitter_set_output_writer(&e.emitter, w) | |||
| yaml_emitter_set_unicode(&e.emitter, true) | |||
| return e | |||
| } | |||
| func (e *encoder) init() { | |||
| if e.doneInit { | |||
| return | |||
| } | |||
| if e.indent == 0 { | |||
| e.indent = 4 | |||
| } | |||
| e.emitter.best_indent = e.indent | |||
| yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) | |||
| e.emit() | |||
| e.doneInit = true | |||
| } | |||
| func (e *encoder) finish() { | |||
| e.emitter.open_ended = false | |||
| yaml_stream_end_event_initialize(&e.event) | |||
| e.emit() | |||
| } | |||
| func (e *encoder) destroy() { | |||
| yaml_emitter_delete(&e.emitter) | |||
| } | |||
| func (e *encoder) emit() { | |||
| // This will internally delete the e.event value. | |||
| e.must(yaml_emitter_emit(&e.emitter, &e.event)) | |||
| } | |||
| func (e *encoder) must(ok bool) { | |||
| if !ok { | |||
| msg := e.emitter.problem | |||
| if msg == "" { | |||
| msg = "unknown problem generating YAML content" | |||
| } | |||
| failf("%s", msg) | |||
| } | |||
| } | |||
| func (e *encoder) marshalDoc(tag string, in reflect.Value) { | |||
| e.init() | |||
| var node *Node | |||
| if in.IsValid() { | |||
| node, _ = in.Interface().(*Node) | |||
| } | |||
| if node != nil && node.Kind == DocumentNode { | |||
| e.nodev(in) | |||
| } else { | |||
| yaml_document_start_event_initialize(&e.event, nil, nil, true) | |||
| e.emit() | |||
| e.marshal(tag, in) | |||
| yaml_document_end_event_initialize(&e.event, true) | |||
| e.emit() | |||
| } | |||
| } | |||
| func (e *encoder) marshal(tag string, in reflect.Value) { | |||
| tag = shortTag(tag) | |||
| if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { | |||
| e.nilv() | |||
| return | |||
| } | |||
| iface := in.Interface() | |||
| switch value := iface.(type) { | |||
| case *Node: | |||
| e.nodev(in) | |||
| return | |||
| case time.Time: | |||
| e.timev(tag, in) | |||
| return | |||
| case *time.Time: | |||
| e.timev(tag, in.Elem()) | |||
| return | |||
| case time.Duration: | |||
| e.stringv(tag, reflect.ValueOf(value.String())) | |||
| return | |||
| case Marshaler: | |||
| v, err := value.MarshalYAML() | |||
| if err != nil { | |||
| fail(err) | |||
| } | |||
| if v == nil { | |||
| e.nilv() | |||
| return | |||
| } | |||
| e.marshal(tag, reflect.ValueOf(v)) | |||
| return | |||
| case encoding.TextMarshaler: | |||
| text, err := value.MarshalText() | |||
| if err != nil { | |||
| fail(err) | |||
| } | |||
| in = reflect.ValueOf(string(text)) | |||
| case nil: | |||
| e.nilv() | |||
| return | |||
| } | |||
| switch in.Kind() { | |||
| case reflect.Interface: | |||
| e.marshal(tag, in.Elem()) | |||
| case reflect.Map: | |||
| e.mapv(tag, in) | |||
| case reflect.Ptr: | |||
| e.marshal(tag, in.Elem()) | |||
| case reflect.Struct: | |||
| e.structv(tag, in) | |||
| case reflect.Slice, reflect.Array: | |||
| e.slicev(tag, in) | |||
| case reflect.String: | |||
| e.stringv(tag, in) | |||
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||
| e.intv(tag, in) | |||
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||
| e.uintv(tag, in) | |||
| case reflect.Float32, reflect.Float64: | |||
| e.floatv(tag, in) | |||
| case reflect.Bool: | |||
| e.boolv(tag, in) | |||
| default: | |||
| panic("cannot marshal type: " + in.Type().String()) | |||
| } | |||
| } | |||
| func (e *encoder) mapv(tag string, in reflect.Value) { | |||
| e.mappingv(tag, func() { | |||
| keys := keyList(in.MapKeys()) | |||
| sort.Sort(keys) | |||
| for _, k := range keys { | |||
| e.marshal("", k) | |||
| e.marshal("", in.MapIndex(k)) | |||
| } | |||
| }) | |||
| } | |||
| func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { | |||
| for _, num := range index { | |||
| for { | |||
| if v.Kind() == reflect.Ptr { | |||
| if v.IsNil() { | |||
| return reflect.Value{} | |||
| } | |||
| v = v.Elem() | |||
| continue | |||
| } | |||
| break | |||
| } | |||
| v = v.Field(num) | |||
| } | |||
| return v | |||
| } | |||
| func (e *encoder) structv(tag string, in reflect.Value) { | |||
| sinfo, err := getStructInfo(in.Type()) | |||
| if err != nil { | |||
| panic(err) | |||
| } | |||
| e.mappingv(tag, func() { | |||
| for _, info := range sinfo.FieldsList { | |||
| var value reflect.Value | |||
| if info.Inline == nil { | |||
| value = in.Field(info.Num) | |||
| } else { | |||
| value = e.fieldByIndex(in, info.Inline) | |||
| if !value.IsValid() { | |||
| continue | |||
| } | |||
| } | |||
| if info.OmitEmpty && isZero(value) { | |||
| continue | |||
| } | |||
| e.marshal("", reflect.ValueOf(info.Key)) | |||
| e.flow = info.Flow | |||
| e.marshal("", value) | |||
| } | |||
| if sinfo.InlineMap >= 0 { | |||
| m := in.Field(sinfo.InlineMap) | |||
| if m.Len() > 0 { | |||
| e.flow = false | |||
| keys := keyList(m.MapKeys()) | |||
| sort.Sort(keys) | |||
| for _, k := range keys { | |||
| if _, found := sinfo.FieldsMap[k.String()]; found { | |||
| panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) | |||
| } | |||
| e.marshal("", k) | |||
| e.flow = false | |||
| e.marshal("", m.MapIndex(k)) | |||
| } | |||
| } | |||
| } | |||
| }) | |||
| } | |||
| func (e *encoder) mappingv(tag string, f func()) { | |||
| implicit := tag == "" | |||
| style := yaml_BLOCK_MAPPING_STYLE | |||
| if e.flow { | |||
| e.flow = false | |||
| style = yaml_FLOW_MAPPING_STYLE | |||
| } | |||
| yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) | |||
| e.emit() | |||
| f() | |||
| yaml_mapping_end_event_initialize(&e.event) | |||
| e.emit() | |||
| } | |||
| func (e *encoder) slicev(tag string, in reflect.Value) { | |||
| implicit := tag == "" | |||
| style := yaml_BLOCK_SEQUENCE_STYLE | |||
| if e.flow { | |||
| e.flow = false | |||
| style = yaml_FLOW_SEQUENCE_STYLE | |||
| } | |||
| e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) | |||
| e.emit() | |||
| n := in.Len() | |||
| for i := 0; i < n; i++ { | |||
| e.marshal("", in.Index(i)) | |||
| } | |||
| e.must(yaml_sequence_end_event_initialize(&e.event)) | |||
| e.emit() | |||
| } | |||
| // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. | |||
| // | |||
| // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported | |||
| // in YAML 1.2 and by this package, but these should be marshalled quoted for | |||
| // the time being for compatibility with other parsers. | |||
| func isBase60Float(s string) (result bool) { | |||
| // Fast path. | |||
| if s == "" { | |||
| return false | |||
| } | |||
| c := s[0] | |||
| if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { | |||
| return false | |||
| } | |||
| // Do the full match. | |||
| return base60float.MatchString(s) | |||
| } | |||
| // From http://yaml.org/type/float.html, except the regular expression there | |||
| // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. | |||
| var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) | |||
| // isOldBool returns whether s is bool notation as defined in YAML 1.1. | |||
| // | |||
| // We continue to force strings that YAML 1.1 would interpret as booleans to be | |||
| // rendered as quotes strings so that the marshalled output valid for YAML 1.1 | |||
| // parsing. | |||
| func isOldBool(s string) (result bool) { | |||
| switch s { | |||
| case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", | |||
| "n", "N", "no", "No", "NO", "off", "Off", "OFF": | |||
| return true | |||
| default: | |||
| return false | |||
| } | |||
| } | |||
| func (e *encoder) stringv(tag string, in reflect.Value) { | |||
| var style yaml_scalar_style_t | |||
| s := in.String() | |||
| canUsePlain := true | |||
| switch { | |||
| case !utf8.ValidString(s): | |||
| if tag == binaryTag { | |||
| failf("explicitly tagged !!binary data must be base64-encoded") | |||
| } | |||
| if tag != "" { | |||
| failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) | |||
| } | |||
| // It can't be encoded directly as YAML so use a binary tag | |||
| // and encode it as base64. | |||
| tag = binaryTag | |||
| s = encodeBase64(s) | |||
| case tag == "": | |||
| // Check to see if it would resolve to a specific | |||
| // tag when encoded unquoted. If it doesn't, | |||
| // there's no need to quote it. | |||
| rtag, _ := resolve("", s) | |||
| canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) | |||
| } | |||
| // Note: it's possible for user code to emit invalid YAML | |||
| // if they explicitly specify a tag and a string containing | |||
| // text that's incompatible with that tag. | |||
| switch { | |||
| case strings.Contains(s, "\n"): | |||
| if e.flow { | |||
| style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||
| } else { | |||
| style = yaml_LITERAL_SCALAR_STYLE | |||
| } | |||
| case canUsePlain: | |||
| style = yaml_PLAIN_SCALAR_STYLE | |||
| default: | |||
| style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||
| } | |||
| e.emitScalar(s, "", tag, style, nil, nil, nil, nil) | |||
| } | |||
| func (e *encoder) boolv(tag string, in reflect.Value) { | |||
| var s string | |||
| if in.Bool() { | |||
| s = "true" | |||
| } else { | |||
| s = "false" | |||
| } | |||
| e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||
| } | |||
| func (e *encoder) intv(tag string, in reflect.Value) { | |||
| s := strconv.FormatInt(in.Int(), 10) | |||
| e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||
| } | |||
| func (e *encoder) uintv(tag string, in reflect.Value) { | |||
| s := strconv.FormatUint(in.Uint(), 10) | |||
| e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||
| } | |||
| func (e *encoder) timev(tag string, in reflect.Value) { | |||
| t := in.Interface().(time.Time) | |||
| s := t.Format(time.RFC3339Nano) | |||
| e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||
| } | |||
| func (e *encoder) floatv(tag string, in reflect.Value) { | |||
| // Issue #352: When formatting, use the precision of the underlying value | |||
| precision := 64 | |||
| if in.Kind() == reflect.Float32 { | |||
| precision = 32 | |||
| } | |||
| s := strconv.FormatFloat(in.Float(), 'g', -1, precision) | |||
| switch s { | |||
| case "+Inf": | |||
| s = ".inf" | |||
| case "-Inf": | |||
| s = "-.inf" | |||
| case "NaN": | |||
| s = ".nan" | |||
| } | |||
| e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||
| } | |||
| func (e *encoder) nilv() { | |||
| e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||
| } | |||
| func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { | |||
| // TODO Kill this function. Replace all initialize calls by their underlining Go literals. | |||
| implicit := tag == "" | |||
| if !implicit { | |||
| tag = longTag(tag) | |||
| } | |||
| e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) | |||
| e.event.head_comment = head | |||
| e.event.line_comment = line | |||
| e.event.foot_comment = foot | |||
| e.event.tail_comment = tail | |||
| e.emit() | |||
| } | |||
| func (e *encoder) nodev(in reflect.Value) { | |||
| e.node(in.Interface().(*Node), "") | |||
| } | |||
| func (e *encoder) node(node *Node, tail string) { | |||
| // If the tag was not explicitly requested, and dropping it won't change the | |||
| // implicit tag of the value, don't include it in the presentation. | |||
| var tag = node.Tag | |||
| var stag = shortTag(tag) | |||
| var rtag string | |||
| var forceQuoting bool | |||
| if tag != "" && node.Style&TaggedStyle == 0 { | |||
| if node.Kind == ScalarNode { | |||
| if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { | |||
| tag = "" | |||
| } else { | |||
| rtag, _ = resolve("", node.Value) | |||
| if rtag == stag { | |||
| tag = "" | |||
| } else if stag == strTag { | |||
| tag = "" | |||
| forceQuoting = true | |||
| } | |||
| } | |||
| } else { | |||
| switch node.Kind { | |||
| case MappingNode: | |||
| rtag = mapTag | |||
| case SequenceNode: | |||
| rtag = seqTag | |||
| } | |||
| if rtag == stag { | |||
| tag = "" | |||
| } | |||
| } | |||
| } | |||
| switch node.Kind { | |||
| case DocumentNode: | |||
| yaml_document_start_event_initialize(&e.event, nil, nil, true) | |||
| e.event.head_comment = []byte(node.HeadComment) | |||
| e.emit() | |||
| for _, node := range node.Content { | |||
| e.node(node, "") | |||
| } | |||
| yaml_document_end_event_initialize(&e.event, true) | |||
| e.event.foot_comment = []byte(node.FootComment) | |||
| e.emit() | |||
| case SequenceNode: | |||
| style := yaml_BLOCK_SEQUENCE_STYLE | |||
| if node.Style&FlowStyle != 0 { | |||
| style = yaml_FLOW_SEQUENCE_STYLE | |||
| } | |||
| e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style)) | |||
| e.event.head_comment = []byte(node.HeadComment) | |||
| e.emit() | |||
| for _, node := range node.Content { | |||
| e.node(node, "") | |||
| } | |||
| e.must(yaml_sequence_end_event_initialize(&e.event)) | |||
| e.event.line_comment = []byte(node.LineComment) | |||
| e.event.foot_comment = []byte(node.FootComment) | |||
| e.emit() | |||
| case MappingNode: | |||
| style := yaml_BLOCK_MAPPING_STYLE | |||
| if node.Style&FlowStyle != 0 { | |||
| style = yaml_FLOW_MAPPING_STYLE | |||
| } | |||
| yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style) | |||
| e.event.tail_comment = []byte(tail) | |||
| e.event.head_comment = []byte(node.HeadComment) | |||
| e.emit() | |||
| // The tail logic below moves the foot comment of prior keys to the following key, | |||
| // since the value for each key may be a nested structure and the foot needs to be | |||
| // processed only the entirety of the value is streamed. The last tail is processed | |||
| // with the mapping end event. | |||
| var tail string | |||
| for i := 0; i+1 < len(node.Content); i += 2 { | |||
| k := node.Content[i] | |||
| foot := k.FootComment | |||
| if foot != "" { | |||
| kopy := *k | |||
| kopy.FootComment = "" | |||
| k = &kopy | |||
| } | |||
| e.node(k, tail) | |||
| tail = foot | |||
| v := node.Content[i+1] | |||
| e.node(v, "") | |||
| } | |||
| yaml_mapping_end_event_initialize(&e.event) | |||
| e.event.tail_comment = []byte(tail) | |||
| e.event.line_comment = []byte(node.LineComment) | |||
| e.event.foot_comment = []byte(node.FootComment) | |||
| e.emit() | |||
| case AliasNode: | |||
| yaml_alias_event_initialize(&e.event, []byte(node.Value)) | |||
| e.event.head_comment = []byte(node.HeadComment) | |||
| e.event.line_comment = []byte(node.LineComment) | |||
| e.event.foot_comment = []byte(node.FootComment) | |||
| e.emit() | |||
| case ScalarNode: | |||
| value := node.Value | |||
| if !utf8.ValidString(value) { | |||
| if tag == binaryTag { | |||
| failf("explicitly tagged !!binary data must be base64-encoded") | |||
| } | |||
| if tag != "" { | |||
| failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) | |||
| } | |||
| // It can't be encoded directly as YAML so use a binary tag | |||
| // and encode it as base64. | |||
| tag = binaryTag | |||
| value = encodeBase64(value) | |||
| } | |||
| style := yaml_PLAIN_SCALAR_STYLE | |||
| switch { | |||
| case node.Style&DoubleQuotedStyle != 0: | |||
| style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||
| case node.Style&SingleQuotedStyle != 0: | |||
| style = yaml_SINGLE_QUOTED_SCALAR_STYLE | |||
| case node.Style&LiteralStyle != 0: | |||
| style = yaml_LITERAL_SCALAR_STYLE | |||
| case node.Style&FoldedStyle != 0: | |||
| style = yaml_FOLDED_SCALAR_STYLE | |||
| case strings.Contains(value, "\n"): | |||
| style = yaml_LITERAL_SCALAR_STYLE | |||
| case forceQuoting: | |||
| style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||
| } | |||
| e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) | |||
| } | |||
| } | |||
| @@ -0,0 +1,5 @@ | |||
| module "gopkg.in/yaml.v3" | |||
| require ( | |||
| "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 | |||
| ) | |||
| @@ -0,0 +1,434 @@ | |||
| // | |||
| // Copyright (c) 2011-2019 Canonical Ltd | |||
| // Copyright (c) 2006-2010 Kirill Simonov | |||
| // | |||
| // 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 yaml | |||
| import ( | |||
| "io" | |||
| ) | |||
| // Set the reader error and return 0. | |||
| func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { | |||
| parser.error = yaml_READER_ERROR | |||
| parser.problem = problem | |||
| parser.problem_offset = offset | |||
| parser.problem_value = value | |||
| return false | |||
| } | |||
| // Byte order marks. | |||
| const ( | |||
| bom_UTF8 = "\xef\xbb\xbf" | |||
| bom_UTF16LE = "\xff\xfe" | |||
| bom_UTF16BE = "\xfe\xff" | |||
| ) | |||
| // Determine the input stream encoding by checking the BOM symbol. If no BOM is | |||
| // found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. | |||
| func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { | |||
| // Ensure that we had enough bytes in the raw buffer. | |||
| for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { | |||
| if !yaml_parser_update_raw_buffer(parser) { | |||
| return false | |||
| } | |||
| } | |||
| // Determine the encoding. | |||
| buf := parser.raw_buffer | |||
| pos := parser.raw_buffer_pos | |||
| avail := len(buf) - pos | |||
| if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { | |||
| parser.encoding = yaml_UTF16LE_ENCODING | |||
| parser.raw_buffer_pos += 2 | |||
| parser.offset += 2 | |||
| } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { | |||
| parser.encoding = yaml_UTF16BE_ENCODING | |||
| parser.raw_buffer_pos += 2 | |||
| parser.offset += 2 | |||
| } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { | |||
| parser.encoding = yaml_UTF8_ENCODING | |||
| parser.raw_buffer_pos += 3 | |||
| parser.offset += 3 | |||
| } else { | |||
| parser.encoding = yaml_UTF8_ENCODING | |||
| } | |||
| return true | |||
| } | |||
| // Update the raw buffer. | |||
| func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { | |||
| size_read := 0 | |||
| // Return if the raw buffer is full. | |||
| if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { | |||
| return true | |||
| } | |||
| // Return on EOF. | |||
| if parser.eof { | |||
| return true | |||
| } | |||
| // Move the remaining bytes in the raw buffer to the beginning. | |||
| if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { | |||
| copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) | |||
| } | |||
| parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] | |||
| parser.raw_buffer_pos = 0 | |||
| // Call the read handler to fill the buffer. | |||
| size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) | |||
| parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] | |||
| if err == io.EOF { | |||
| parser.eof = true | |||
| } else if err != nil { | |||
| return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) | |||
| } | |||
| return true | |||
| } | |||
| // Ensure that the buffer contains at least `length` characters. | |||
| // Return true on success, false on failure. | |||
| // | |||
| // The length is supposed to be significantly less that the buffer size. | |||
| func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { | |||
| if parser.read_handler == nil { | |||
| panic("read handler must be set") | |||
| } | |||
| // [Go] This function was changed to guarantee the requested length size at EOF. | |||
| // The fact we need to do this is pretty awful, but the description above implies | |||
| // for that to be the case, and there are tests | |||
| // If the EOF flag is set and the raw buffer is empty, do nothing. | |||
| if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { | |||
| // [Go] ACTUALLY! Read the documentation of this function above. | |||
| // This is just broken. To return true, we need to have the | |||
| // given length in the buffer. Not doing that means every single | |||
| // check that calls this function to make sure the buffer has a | |||
| // given length is Go) panicking; or C) accessing invalid memory. | |||
| //return true | |||
| } | |||
| // Return if the buffer contains enough characters. | |||
| if parser.unread >= length { | |||
| return true | |||
| } | |||
| // Determine the input encoding if it is not known yet. | |||
| if parser.encoding == yaml_ANY_ENCODING { | |||
| if !yaml_parser_determine_encoding(parser) { | |||
| return false | |||
| } | |||
| } | |||
| // Move the unread characters to the beginning of the buffer. | |||
| buffer_len := len(parser.buffer) | |||
| if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { | |||
| copy(parser.buffer, parser.buffer[parser.buffer_pos:]) | |||
| buffer_len -= parser.buffer_pos | |||
| parser.buffer_pos = 0 | |||
| } else if parser.buffer_pos == buffer_len { | |||
| buffer_len = 0 | |||
| parser.buffer_pos = 0 | |||
| } | |||
| // Open the whole buffer for writing, and cut it before returning. | |||
| parser.buffer = parser.buffer[:cap(parser.buffer)] | |||
| // Fill the buffer until it has enough characters. | |||
| first := true | |||
| for parser.unread < length { | |||
| // Fill the raw buffer if necessary. | |||
| if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { | |||
| if !yaml_parser_update_raw_buffer(parser) { | |||
| parser.buffer = parser.buffer[:buffer_len] | |||
| return false | |||
| } | |||
| } | |||
| first = false | |||
| // Decode the raw buffer. | |||
| inner: | |||
| for parser.raw_buffer_pos != len(parser.raw_buffer) { | |||
| var value rune | |||
| var width int | |||
| raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos | |||
| // Decode the next character. | |||
| switch parser.encoding { | |||
| case yaml_UTF8_ENCODING: | |||
| // Decode a UTF-8 character. Check RFC 3629 | |||
| // (http://www.ietf.org/rfc/rfc3629.txt) for more details. | |||
| // | |||
| // The following table (taken from the RFC) is used for | |||
| // decoding. | |||
| // | |||
| // Char. number range | UTF-8 octet sequence | |||
| // (hexadecimal) | (binary) | |||
| // --------------------+------------------------------------ | |||
| // 0000 0000-0000 007F | 0xxxxxxx | |||
| // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx | |||
| // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx | |||
| // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | |||
| // | |||
| // Additionally, the characters in the range 0xD800-0xDFFF | |||
| // are prohibited as they are reserved for use with UTF-16 | |||
| // surrogate pairs. | |||
| // Determine the length of the UTF-8 sequence. | |||
| octet := parser.raw_buffer[parser.raw_buffer_pos] | |||
| switch { | |||
| case octet&0x80 == 0x00: | |||
| width = 1 | |||
| case octet&0xE0 == 0xC0: | |||
| width = 2 | |||
| case octet&0xF0 == 0xE0: | |||
| width = 3 | |||
| case octet&0xF8 == 0xF0: | |||
| width = 4 | |||
| default: | |||
| // The leading octet is invalid. | |||
| return yaml_parser_set_reader_error(parser, | |||
| "invalid leading UTF-8 octet", | |||
| parser.offset, int(octet)) | |||
| } | |||
| // Check if the raw buffer contains an incomplete character. | |||
| if width > raw_unread { | |||
| if parser.eof { | |||
| return yaml_parser_set_reader_error(parser, | |||
| "incomplete UTF-8 octet sequence", | |||
| parser.offset, -1) | |||
| } | |||
| break inner | |||
| } | |||
| // Decode the leading octet. | |||
| switch { | |||
| case octet&0x80 == 0x00: | |||
| value = rune(octet & 0x7F) | |||
| case octet&0xE0 == 0xC0: | |||
| value = rune(octet & 0x1F) | |||
| case octet&0xF0 == 0xE0: | |||
| value = rune(octet & 0x0F) | |||
| case octet&0xF8 == 0xF0: | |||
| value = rune(octet & 0x07) | |||
| default: | |||
| value = 0 | |||
| } | |||
| // Check and decode the trailing octets. | |||
| for k := 1; k < width; k++ { | |||
| octet = parser.raw_buffer[parser.raw_buffer_pos+k] | |||
| // Check if the octet is valid. | |||
| if (octet & 0xC0) != 0x80 { | |||
| return yaml_parser_set_reader_error(parser, | |||
| "invalid trailing UTF-8 octet", | |||
| parser.offset+k, int(octet)) | |||
| } | |||
| // Decode the octet. | |||
| value = (value << 6) + rune(octet&0x3F) | |||
| } | |||
| // Check the length of the sequence against the value. | |||
| switch { | |||
| case width == 1: | |||
| case width == 2 && value >= 0x80: | |||
| case width == 3 && value >= 0x800: | |||
| case width == 4 && value >= 0x10000: | |||
| default: | |||
| return yaml_parser_set_reader_error(parser, | |||
| "invalid length of a UTF-8 sequence", | |||
| parser.offset, -1) | |||
| } | |||
| // Check the range of the value. | |||
| if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { | |||
| return yaml_parser_set_reader_error(parser, | |||
| "invalid Unicode character", | |||
| parser.offset, int(value)) | |||
| } | |||
| case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: | |||
| var low, high int | |||
| if parser.encoding == yaml_UTF16LE_ENCODING { | |||
| low, high = 0, 1 | |||
| } else { | |||
| low, high = 1, 0 | |||
| } | |||
| // The UTF-16 encoding is not as simple as one might | |||
| // naively think. Check RFC 2781 | |||
| // (http://www.ietf.org/rfc/rfc2781.txt). | |||
| // | |||
| // Normally, two subsequent bytes describe a Unicode | |||
| // character. However a special technique (called a | |||
| // surrogate pair) is used for specifying character | |||
| // values larger than 0xFFFF. | |||
| // | |||
| // A surrogate pair consists of two pseudo-characters: | |||
| // high surrogate area (0xD800-0xDBFF) | |||
| // low surrogate area (0xDC00-0xDFFF) | |||
| // | |||
| // The following formulas are used for decoding | |||
| // and encoding characters using surrogate pairs: | |||
| // | |||
| // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) | |||
| // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) | |||
| // W1 = 110110yyyyyyyyyy | |||
| // W2 = 110111xxxxxxxxxx | |||
| // | |||
| // where U is the character value, W1 is the high surrogate | |||
| // area, W2 is the low surrogate area. | |||
| // Check for incomplete UTF-16 character. | |||
| if raw_unread < 2 { | |||
| if parser.eof { | |||
| return yaml_parser_set_reader_error(parser, | |||
| "incomplete UTF-16 character", | |||
| parser.offset, -1) | |||
| } | |||
| break inner | |||
| } | |||
| // Get the character. | |||
| value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + | |||
| (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) | |||
| // Check for unexpected low surrogate area. | |||
| if value&0xFC00 == 0xDC00 { | |||
| return yaml_parser_set_reader_error(parser, | |||
| "unexpected low surrogate area", | |||
| parser.offset, int(value)) | |||
| } | |||
| // Check for a high surrogate area. | |||
| if value&0xFC00 == 0xD800 { | |||
| width = 4 | |||
| // Check for incomplete surrogate pair. | |||
| if raw_unread < 4 { | |||
| if parser.eof { | |||
| return yaml_parser_set_reader_error(parser, | |||
| "incomplete UTF-16 surrogate pair", | |||
| parser.offset, -1) | |||
| } | |||
| break inner | |||
| } | |||
| // Get the next character. | |||
| value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + | |||
| (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) | |||
| // Check for a low surrogate area. | |||
| if value2&0xFC00 != 0xDC00 { | |||
| return yaml_parser_set_reader_error(parser, | |||
| "expected low surrogate area", | |||
| parser.offset+2, int(value2)) | |||
| } | |||
| // Generate the value of the surrogate pair. | |||
| value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) | |||
| } else { | |||
| width = 2 | |||
| } | |||
| default: | |||
| panic("impossible") | |||
| } | |||
| // Check if the character is in the allowed range: | |||
| // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) | |||
| // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) | |||
| // | [#x10000-#x10FFFF] (32 bit) | |||
| switch { | |||
| case value == 0x09: | |||
| case value == 0x0A: | |||
| case value == 0x0D: | |||
| case value >= 0x20 && value <= 0x7E: | |||
| case value == 0x85: | |||
| case value >= 0xA0 && value <= 0xD7FF: | |||
| case value >= 0xE000 && value <= 0xFFFD: | |||
| case value >= 0x10000 && value <= 0x10FFFF: | |||
| default: | |||
| return yaml_parser_set_reader_error(parser, | |||
| "control characters are not allowed", | |||
| parser.offset, int(value)) | |||
| } | |||
| // Move the raw pointers. | |||
| parser.raw_buffer_pos += width | |||
| parser.offset += width | |||
| // Finally put the character into the buffer. | |||
| if value <= 0x7F { | |||
| // 0000 0000-0000 007F . 0xxxxxxx | |||
| parser.buffer[buffer_len+0] = byte(value) | |||
| buffer_len += 1 | |||
| } else if value <= 0x7FF { | |||
| // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx | |||
| parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) | |||
| parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) | |||
| buffer_len += 2 | |||
| } else if value <= 0xFFFF { | |||
| // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx | |||
| parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) | |||
| parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) | |||
| parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) | |||
| buffer_len += 3 | |||
| } else { | |||
| // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | |||
| parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) | |||
| parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) | |||
| parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) | |||
| parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) | |||
| buffer_len += 4 | |||
| } | |||
| parser.unread++ | |||
| } | |||
| // On EOF, put NUL into the buffer and return. | |||
| if parser.eof { | |||
| parser.buffer[buffer_len] = 0 | |||
| buffer_len++ | |||
| parser.unread++ | |||
| break | |||
| } | |||
| } | |||
| // [Go] Read the documentation of this function above. To return true, | |||
| // we need to have the given length in the buffer. Not doing that means | |||
| // every single check that calls this function to make sure the buffer | |||
| // has a given length is Go) panicking; or C) accessing invalid memory. | |||
| // This happens here due to the EOF above breaking early. | |||
| for buffer_len < length { | |||
| parser.buffer[buffer_len] = 0 | |||
| buffer_len++ | |||
| } | |||
| parser.buffer = parser.buffer[:buffer_len] | |||
| return true | |||
| } | |||
| @@ -0,0 +1,326 @@ | |||
| // | |||
| // Copyright (c) 2011-2019 Canonical Ltd | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| package yaml | |||
| import ( | |||
| "encoding/base64" | |||
| "math" | |||
| "regexp" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| ) | |||
| type resolveMapItem struct { | |||
| value interface{} | |||
| tag string | |||
| } | |||
| var resolveTable = make([]byte, 256) | |||
| var resolveMap = make(map[string]resolveMapItem) | |||
| func init() { | |||
| t := resolveTable | |||
| t[int('+')] = 'S' // Sign | |||
| t[int('-')] = 'S' | |||
| for _, c := range "0123456789" { | |||
| t[int(c)] = 'D' // Digit | |||
| } | |||
| for _, c := range "yYnNtTfFoO~" { | |||
| t[int(c)] = 'M' // In map | |||
| } | |||
| t[int('.')] = '.' // Float (potentially in map) | |||
| var resolveMapList = []struct { | |||
| v interface{} | |||
| tag string | |||
| l []string | |||
| }{ | |||
| {true, boolTag, []string{"true", "True", "TRUE"}}, | |||
| {false, boolTag, []string{"false", "False", "FALSE"}}, | |||
| {nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, | |||
| {math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, | |||
| {math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, | |||
| {math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, | |||
| {math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, | |||
| {"<<", mergeTag, []string{"<<"}}, | |||
| } | |||
| m := resolveMap | |||
| for _, item := range resolveMapList { | |||
| for _, s := range item.l { | |||
| m[s] = resolveMapItem{item.v, item.tag} | |||
| } | |||
| } | |||
| } | |||
| const ( | |||
| nullTag = "!!null" | |||
| boolTag = "!!bool" | |||
| strTag = "!!str" | |||
| intTag = "!!int" | |||
| floatTag = "!!float" | |||
| timestampTag = "!!timestamp" | |||
| seqTag = "!!seq" | |||
| mapTag = "!!map" | |||
| binaryTag = "!!binary" | |||
| mergeTag = "!!merge" | |||
| ) | |||
| var longTags = make(map[string]string) | |||
| var shortTags = make(map[string]string) | |||
| func init() { | |||
| for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { | |||
| ltag := longTag(stag) | |||
| longTags[stag] = ltag | |||
| shortTags[ltag] = stag | |||
| } | |||
| } | |||
| const longTagPrefix = "tag:yaml.org,2002:" | |||
| func shortTag(tag string) string { | |||
| if strings.HasPrefix(tag, longTagPrefix) { | |||
| if stag, ok := shortTags[tag]; ok { | |||
| return stag | |||
| } | |||
| return "!!" + tag[len(longTagPrefix):] | |||
| } | |||
| return tag | |||
| } | |||
| func longTag(tag string) string { | |||
| if strings.HasPrefix(tag, "!!") { | |||
| if ltag, ok := longTags[tag]; ok { | |||
| return ltag | |||
| } | |||
| return longTagPrefix + tag[2:] | |||
| } | |||
| return tag | |||
| } | |||
| func resolvableTag(tag string) bool { | |||
| switch tag { | |||
| case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) | |||
| func resolve(tag string, in string) (rtag string, out interface{}) { | |||
| tag = shortTag(tag) | |||
| if !resolvableTag(tag) { | |||
| return tag, in | |||
| } | |||
| defer func() { | |||
| switch tag { | |||
| case "", rtag, strTag, binaryTag: | |||
| return | |||
| case floatTag: | |||
| if rtag == intTag { | |||
| switch v := out.(type) { | |||
| case int64: | |||
| rtag = floatTag | |||
| out = float64(v) | |||
| return | |||
| case int: | |||
| rtag = floatTag | |||
| out = float64(v) | |||
| return | |||
| } | |||
| } | |||
| } | |||
| failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) | |||
| }() | |||
| // Any data is accepted as a !!str or !!binary. | |||
| // Otherwise, the prefix is enough of a hint about what it might be. | |||
| hint := byte('N') | |||
| if in != "" { | |||
| hint = resolveTable[in[0]] | |||
| } | |||
| if hint != 0 && tag != strTag && tag != binaryTag { | |||
| // Handle things we can lookup in a map. | |||
| if item, ok := resolveMap[in]; ok { | |||
| return item.tag, item.value | |||
| } | |||
| // Base 60 floats are a bad idea, were dropped in YAML 1.2, and | |||
| // are purposefully unsupported here. They're still quoted on | |||
| // the way out for compatibility with other parser, though. | |||
| switch hint { | |||
| case 'M': | |||
| // We've already checked the map above. | |||
| case '.': | |||
| // Not in the map, so maybe a normal float. | |||
| floatv, err := strconv.ParseFloat(in, 64) | |||
| if err == nil { | |||
| return floatTag, floatv | |||
| } | |||
| case 'D', 'S': | |||
| // Int, float, or timestamp. | |||
| // Only try values as a timestamp if the value is unquoted or there's an explicit | |||
| // !!timestamp tag. | |||
| if tag == "" || tag == timestampTag { | |||
| t, ok := parseTimestamp(in) | |||
| if ok { | |||
| return timestampTag, t | |||
| } | |||
| } | |||
| plain := strings.Replace(in, "_", "", -1) | |||
| intv, err := strconv.ParseInt(plain, 0, 64) | |||
| if err == nil { | |||
| if intv == int64(int(intv)) { | |||
| return intTag, int(intv) | |||
| } else { | |||
| return intTag, intv | |||
| } | |||
| } | |||
| uintv, err := strconv.ParseUint(plain, 0, 64) | |||
| if err == nil { | |||
| return intTag, uintv | |||
| } | |||
| if yamlStyleFloat.MatchString(plain) { | |||
| floatv, err := strconv.ParseFloat(plain, 64) | |||
| if err == nil { | |||
| return floatTag, floatv | |||
| } | |||
| } | |||
| if strings.HasPrefix(plain, "0b") { | |||
| intv, err := strconv.ParseInt(plain[2:], 2, 64) | |||
| if err == nil { | |||
| if intv == int64(int(intv)) { | |||
| return intTag, int(intv) | |||
| } else { | |||
| return intTag, intv | |||
| } | |||
| } | |||
| uintv, err := strconv.ParseUint(plain[2:], 2, 64) | |||
| if err == nil { | |||
| return intTag, uintv | |||
| } | |||
| } else if strings.HasPrefix(plain, "-0b") { | |||
| intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) | |||
| if err == nil { | |||
| if true || intv == int64(int(intv)) { | |||
| return intTag, int(intv) | |||
| } else { | |||
| return intTag, intv | |||
| } | |||
| } | |||
| } | |||
| // Octals as introduced in version 1.2 of the spec. | |||
| // Octals from the 1.1 spec, spelled as 0777, are still | |||
| // decoded by default in v3 as well for compatibility. | |||
| // May be dropped in v4 depending on how usage evolves. | |||
| if strings.HasPrefix(plain, "0o") { | |||
| intv, err := strconv.ParseInt(plain[2:], 8, 64) | |||
| if err == nil { | |||
| if intv == int64(int(intv)) { | |||
| return intTag, int(intv) | |||
| } else { | |||
| return intTag, intv | |||
| } | |||
| } | |||
| uintv, err := strconv.ParseUint(plain[2:], 8, 64) | |||
| if err == nil { | |||
| return intTag, uintv | |||
| } | |||
| } else if strings.HasPrefix(plain, "-0o") { | |||
| intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) | |||
| if err == nil { | |||
| if true || intv == int64(int(intv)) { | |||
| return intTag, int(intv) | |||
| } else { | |||
| return intTag, intv | |||
| } | |||
| } | |||
| } | |||
| default: | |||
| panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") | |||
| } | |||
| } | |||
| return strTag, in | |||
| } | |||
| // encodeBase64 encodes s as base64 that is broken up into multiple lines | |||
| // as appropriate for the resulting length. | |||
| func encodeBase64(s string) string { | |||
| const lineLen = 70 | |||
| encLen := base64.StdEncoding.EncodedLen(len(s)) | |||
| lines := encLen/lineLen + 1 | |||
| buf := make([]byte, encLen*2+lines) | |||
| in := buf[0:encLen] | |||
| out := buf[encLen:] | |||
| base64.StdEncoding.Encode(in, []byte(s)) | |||
| k := 0 | |||
| for i := 0; i < len(in); i += lineLen { | |||
| j := i + lineLen | |||
| if j > len(in) { | |||
| j = len(in) | |||
| } | |||
| k += copy(out[k:], in[i:j]) | |||
| if lines > 1 { | |||
| out[k] = '\n' | |||
| k++ | |||
| } | |||
| } | |||
| return string(out[:k]) | |||
| } | |||
| // This is a subset of the formats allowed by the regular expression | |||
| // defined at http://yaml.org/type/timestamp.html. | |||
| var allowedTimestampFormats = []string{ | |||
| "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. | |||
| "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". | |||
| "2006-1-2 15:4:5.999999999", // space separated with no time zone | |||
| "2006-1-2", // date only | |||
| // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" | |||
| // from the set of examples. | |||
| } | |||
| // parseTimestamp parses s as a timestamp string and | |||
| // returns the timestamp and reports whether it succeeded. | |||
| // Timestamp formats are defined at http://yaml.org/type/timestamp.html | |||
| func parseTimestamp(s string) (time.Time, bool) { | |||
| // TODO write code to check all the formats supported by | |||
| // http://yaml.org/type/timestamp.html instead of using time.Parse. | |||
| // Quick check: all date formats start with YYYY-. | |||
| i := 0 | |||
| for ; i < len(s); i++ { | |||
| if c := s[i]; c < '0' || c > '9' { | |||
| break | |||
| } | |||
| } | |||
| if i != 4 || i == len(s) || s[i] != '-' { | |||
| return time.Time{}, false | |||
| } | |||
| for _, format := range allowedTimestampFormats { | |||
| if t, err := time.Parse(format, s); err == nil { | |||
| return t, true | |||
| } | |||
| } | |||
| return time.Time{}, false | |||
| } | |||
| @@ -0,0 +1,134 @@ | |||
| // | |||
| // Copyright (c) 2011-2019 Canonical Ltd | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| package yaml | |||
| import ( | |||
| "reflect" | |||
| "unicode" | |||
| ) | |||
| type keyList []reflect.Value | |||
| func (l keyList) Len() int { return len(l) } | |||
| func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } | |||
| func (l keyList) Less(i, j int) bool { | |||
| a := l[i] | |||
| b := l[j] | |||
| ak := a.Kind() | |||
| bk := b.Kind() | |||
| for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { | |||
| a = a.Elem() | |||
| ak = a.Kind() | |||
| } | |||
| for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { | |||
| b = b.Elem() | |||
| bk = b.Kind() | |||
| } | |||
| af, aok := keyFloat(a) | |||
| bf, bok := keyFloat(b) | |||
| if aok && bok { | |||
| if af != bf { | |||
| return af < bf | |||
| } | |||
| if ak != bk { | |||
| return ak < bk | |||
| } | |||
| return numLess(a, b) | |||
| } | |||
| if ak != reflect.String || bk != reflect.String { | |||
| return ak < bk | |||
| } | |||
| ar, br := []rune(a.String()), []rune(b.String()) | |||
| digits := false | |||
| for i := 0; i < len(ar) && i < len(br); i++ { | |||
| if ar[i] == br[i] { | |||
| digits = unicode.IsDigit(ar[i]) | |||
| continue | |||
| } | |||
| al := unicode.IsLetter(ar[i]) | |||
| bl := unicode.IsLetter(br[i]) | |||
| if al && bl { | |||
| return ar[i] < br[i] | |||
| } | |||
| if al || bl { | |||
| if digits { | |||
| return al | |||
| } else { | |||
| return bl | |||
| } | |||
| } | |||
| var ai, bi int | |||
| var an, bn int64 | |||
| if ar[i] == '0' || br[i] == '0' { | |||
| for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { | |||
| if ar[j] != '0' { | |||
| an = 1 | |||
| bn = 1 | |||
| break | |||
| } | |||
| } | |||
| } | |||
| for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { | |||
| an = an*10 + int64(ar[ai]-'0') | |||
| } | |||
| for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { | |||
| bn = bn*10 + int64(br[bi]-'0') | |||
| } | |||
| if an != bn { | |||
| return an < bn | |||
| } | |||
| if ai != bi { | |||
| return ai < bi | |||
| } | |||
| return ar[i] < br[i] | |||
| } | |||
| return len(ar) < len(br) | |||
| } | |||
| // keyFloat returns a float value for v if it is a number/bool | |||
| // and whether it is a number/bool or not. | |||
| func keyFloat(v reflect.Value) (f float64, ok bool) { | |||
| switch v.Kind() { | |||
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||
| return float64(v.Int()), true | |||
| case reflect.Float32, reflect.Float64: | |||
| return v.Float(), true | |||
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||
| return float64(v.Uint()), true | |||
| case reflect.Bool: | |||
| if v.Bool() { | |||
| return 1, true | |||
| } | |||
| return 0, true | |||
| } | |||
| return 0, false | |||
| } | |||
| // numLess returns whether a < b. | |||
| // a and b must necessarily have the same kind. | |||
| func numLess(a, b reflect.Value) bool { | |||
| switch a.Kind() { | |||
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||
| return a.Int() < b.Int() | |||
| case reflect.Float32, reflect.Float64: | |||
| return a.Float() < b.Float() | |||
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||
| return a.Uint() < b.Uint() | |||
| case reflect.Bool: | |||
| return !a.Bool() && b.Bool() | |||
| } | |||
| panic("not a number") | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| // | |||
| // Copyright (c) 2011-2019 Canonical Ltd | |||
| // Copyright (c) 2006-2010 Kirill Simonov | |||
| // | |||
| // 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 yaml | |||
| // Set the writer error and return false. | |||
| func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { | |||
| emitter.error = yaml_WRITER_ERROR | |||
| emitter.problem = problem | |||
| return false | |||
| } | |||
| // Flush the output buffer. | |||
| func yaml_emitter_flush(emitter *yaml_emitter_t) bool { | |||
| if emitter.write_handler == nil { | |||
| panic("write handler not set") | |||
| } | |||
| // Check if the buffer is empty. | |||
| if emitter.buffer_pos == 0 { | |||
| return true | |||
| } | |||
| if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { | |||
| return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) | |||
| } | |||
| emitter.buffer_pos = 0 | |||
| return true | |||
| } | |||
| @@ -0,0 +1,662 @@ | |||
| // | |||
| // Copyright (c) 2011-2019 Canonical Ltd | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // Package yaml implements YAML support for the Go language. | |||
| // | |||
| // Source code and other details for the project are available at GitHub: | |||
| // | |||
| // https://github.com/go-yaml/yaml | |||
| // | |||
| package yaml | |||
| import ( | |||
| "errors" | |||
| "fmt" | |||
| "io" | |||
| "reflect" | |||
| "strings" | |||
| "sync" | |||
| "unicode/utf8" | |||
| ) | |||
| // The Unmarshaler interface may be implemented by types to customize their | |||
| // behavior when being unmarshaled from a YAML document. | |||
| type Unmarshaler interface { | |||
| UnmarshalYAML(value *Node) error | |||
| } | |||
| type obsoleteUnmarshaler interface { | |||
| UnmarshalYAML(unmarshal func(interface{}) error) error | |||
| } | |||
| // The Marshaler interface may be implemented by types to customize their | |||
| // behavior when being marshaled into a YAML document. The returned value | |||
| // is marshaled in place of the original value implementing Marshaler. | |||
| // | |||
| // If an error is returned by MarshalYAML, the marshaling procedure stops | |||
| // and returns with the provided error. | |||
| type Marshaler interface { | |||
| MarshalYAML() (interface{}, error) | |||
| } | |||
| // Unmarshal decodes the first document found within the in byte slice | |||
| // and assigns decoded values into the out value. | |||
| // | |||
| // Maps and pointers (to a struct, string, int, etc) are accepted as out | |||
| // values. If an internal pointer within a struct is not initialized, | |||
| // the yaml package will initialize it if necessary for unmarshalling | |||
| // the provided data. The out parameter must not be nil. | |||
| // | |||
| // The type of the decoded values should be compatible with the respective | |||
| // values in out. If one or more values cannot be decoded due to a type | |||
| // mismatches, decoding continues partially until the end of the YAML | |||
| // content, and a *yaml.TypeError is returned with details for all | |||
| // missed values. | |||
| // | |||
| // Struct fields are only unmarshalled if they are exported (have an | |||
| // upper case first letter), and are unmarshalled using the field name | |||
| // lowercased as the default key. Custom keys may be defined via the | |||
| // "yaml" name in the field tag: the content preceding the first comma | |||
| // is used as the key, and the following comma-separated options are | |||
| // used to tweak the marshalling process (see Marshal). | |||
| // Conflicting names result in a runtime error. | |||
| // | |||
| // For example: | |||
| // | |||
| // type T struct { | |||
| // F int `yaml:"a,omitempty"` | |||
| // B int | |||
| // } | |||
| // var t T | |||
| // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) | |||
| // | |||
| // See the documentation of Marshal for the format of tags and a list of | |||
| // supported tag options. | |||
| // | |||
| func Unmarshal(in []byte, out interface{}) (err error) { | |||
| return unmarshal(in, out, false) | |||
| } | |||
| // A Decorder reads and decodes YAML values from an input stream. | |||
| type Decoder struct { | |||
| parser *parser | |||
| knownFields bool | |||
| } | |||
| // NewDecoder returns a new decoder that reads from r. | |||
| // | |||
| // The decoder introduces its own buffering and may read | |||
| // data from r beyond the YAML values requested. | |||
| func NewDecoder(r io.Reader) *Decoder { | |||
| return &Decoder{ | |||
| parser: newParserFromReader(r), | |||
| } | |||
| } | |||
| // KnownFields ensures that the keys in decoded mappings to | |||
| // exist as fields in the struct being decoded into. | |||
| func (dec *Decoder) KnownFields(enable bool) { | |||
| dec.knownFields = enable | |||
| } | |||
| // Decode reads the next YAML-encoded value from its input | |||
| // and stores it in the value pointed to by v. | |||
| // | |||
| // See the documentation for Unmarshal for details about the | |||
| // conversion of YAML into a Go value. | |||
| func (dec *Decoder) Decode(v interface{}) (err error) { | |||
| d := newDecoder() | |||
| d.knownFields = dec.knownFields | |||
| defer handleErr(&err) | |||
| node := dec.parser.parse() | |||
| if node == nil { | |||
| return io.EOF | |||
| } | |||
| out := reflect.ValueOf(v) | |||
| if out.Kind() == reflect.Ptr && !out.IsNil() { | |||
| out = out.Elem() | |||
| } | |||
| d.unmarshal(node, out) | |||
| if len(d.terrors) > 0 { | |||
| return &TypeError{d.terrors} | |||
| } | |||
| return nil | |||
| } | |||
| // Decode decodes the node and stores its data into the value pointed to by v. | |||
| // | |||
| // See the documentation for Unmarshal for details about the | |||
| // conversion of YAML into a Go value. | |||
| func (n *Node) Decode(v interface{}) (err error) { | |||
| d := newDecoder() | |||
| defer handleErr(&err) | |||
| out := reflect.ValueOf(v) | |||
| if out.Kind() == reflect.Ptr && !out.IsNil() { | |||
| out = out.Elem() | |||
| } | |||
| d.unmarshal(n, out) | |||
| if len(d.terrors) > 0 { | |||
| return &TypeError{d.terrors} | |||
| } | |||
| return nil | |||
| } | |||
| func unmarshal(in []byte, out interface{}, strict bool) (err error) { | |||
| defer handleErr(&err) | |||
| d := newDecoder() | |||
| p := newParser(in) | |||
| defer p.destroy() | |||
| node := p.parse() | |||
| if node != nil { | |||
| v := reflect.ValueOf(out) | |||
| if v.Kind() == reflect.Ptr && !v.IsNil() { | |||
| v = v.Elem() | |||
| } | |||
| d.unmarshal(node, v) | |||
| } | |||
| if len(d.terrors) > 0 { | |||
| return &TypeError{d.terrors} | |||
| } | |||
| return nil | |||
| } | |||
| // Marshal serializes the value provided into a YAML document. The structure | |||
| // of the generated document will reflect the structure of the value itself. | |||
| // Maps and pointers (to struct, string, int, etc) are accepted as the in value. | |||
| // | |||
| // Struct fields are only marshalled if they are exported (have an upper case | |||
| // first letter), and are marshalled using the field name lowercased as the | |||
| // default key. Custom keys may be defined via the "yaml" name in the field | |||
| // tag: the content preceding the first comma is used as the key, and the | |||
| // following comma-separated options are used to tweak the marshalling process. | |||
| // Conflicting names result in a runtime error. | |||
| // | |||
| // The field tag format accepted is: | |||
| // | |||
| // `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)` | |||
| // | |||
| // The following flags are currently supported: | |||
| // | |||
| // omitempty Only include the field if it's not set to the zero | |||
| // value for the type or to empty slices or maps. | |||
| // Zero valued structs will be omitted if all their public | |||
| // fields are zero, unless they implement an IsZero | |||
| // method (see the IsZeroer interface type), in which | |||
| // case the field will be included if that method returns true. | |||
| // | |||
| // flow Marshal using a flow style (useful for structs, | |||
| // sequences and maps). | |||
| // | |||
| // inline Inline the field, which must be a struct or a map, | |||
| // causing all of its fields or keys to be processed as if | |||
| // they were part of the outer struct. For maps, keys must | |||
| // not conflict with the yaml keys of other struct fields. | |||
| // | |||
| // In addition, if the key is "-", the field is ignored. | |||
| // | |||
| // For example: | |||
| // | |||
| // type T struct { | |||
| // F int `yaml:"a,omitempty"` | |||
| // B int | |||
| // } | |||
| // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" | |||
| // yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" | |||
| // | |||
| func Marshal(in interface{}) (out []byte, err error) { | |||
| defer handleErr(&err) | |||
| e := newEncoder() | |||
| defer e.destroy() | |||
| e.marshalDoc("", reflect.ValueOf(in)) | |||
| e.finish() | |||
| out = e.out | |||
| return | |||
| } | |||
| // An Encoder writes YAML values to an output stream. | |||
| type Encoder struct { | |||
| encoder *encoder | |||
| } | |||
| // NewEncoder returns a new encoder that writes to w. | |||
| // The Encoder should be closed after use to flush all data | |||
| // to w. | |||
| func NewEncoder(w io.Writer) *Encoder { | |||
| return &Encoder{ | |||
| encoder: newEncoderWithWriter(w), | |||
| } | |||
| } | |||
| // Encode writes the YAML encoding of v to the stream. | |||
| // If multiple items are encoded to the stream, the | |||
| // second and subsequent document will be preceded | |||
| // with a "---" document separator, but the first will not. | |||
| // | |||
| // See the documentation for Marshal for details about the conversion of Go | |||
| // values to YAML. | |||
| func (e *Encoder) Encode(v interface{}) (err error) { | |||
| defer handleErr(&err) | |||
| e.encoder.marshalDoc("", reflect.ValueOf(v)) | |||
| return nil | |||
| } | |||
| // SetIndent changes the used indentation used when encoding. | |||
| func (e *Encoder) SetIndent(spaces int) { | |||
| if spaces < 0 { | |||
| panic("yaml: cannot indent to a negative number of spaces") | |||
| } | |||
| e.encoder.indent = spaces | |||
| } | |||
| // Close closes the encoder by writing any remaining data. | |||
| // It does not write a stream terminating string "...". | |||
| func (e *Encoder) Close() (err error) { | |||
| defer handleErr(&err) | |||
| e.encoder.finish() | |||
| return nil | |||
| } | |||
| func handleErr(err *error) { | |||
| if v := recover(); v != nil { | |||
| if e, ok := v.(yamlError); ok { | |||
| *err = e.err | |||
| } else { | |||
| panic(v) | |||
| } | |||
| } | |||
| } | |||
| type yamlError struct { | |||
| err error | |||
| } | |||
| func fail(err error) { | |||
| panic(yamlError{err}) | |||
| } | |||
| func failf(format string, args ...interface{}) { | |||
| panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) | |||
| } | |||
| // A TypeError is returned by Unmarshal when one or more fields in | |||
| // the YAML document cannot be properly decoded into the requested | |||
| // types. When this error is returned, the value is still | |||
| // unmarshaled partially. | |||
| type TypeError struct { | |||
| Errors []string | |||
| } | |||
| func (e *TypeError) Error() string { | |||
| return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) | |||
| } | |||
| type Kind uint32 | |||
| const ( | |||
| DocumentNode Kind = 1 << iota | |||
| SequenceNode | |||
| MappingNode | |||
| ScalarNode | |||
| AliasNode | |||
| ) | |||
| type Style uint32 | |||
| const ( | |||
| TaggedStyle Style = 1 << iota | |||
| DoubleQuotedStyle | |||
| SingleQuotedStyle | |||
| LiteralStyle | |||
| FoldedStyle | |||
| FlowStyle | |||
| ) | |||
| // Node represents an element in the YAML document hierarchy. While documents | |||
| // are typically encoded and decoded into higher level types, such as structs | |||
| // and maps, Node is an intermediate representation that allows detailed | |||
| // control over the content being decoded or encoded. | |||
| // | |||
| // Values that make use of the Node type interact with the yaml package in the | |||
| // same way any other type would do, by encoding and decoding yaml data | |||
| // directly or indirectly into them. | |||
| // | |||
| // For example: | |||
| // | |||
| // var person struct { | |||
| // Name string | |||
| // Address yaml.Node | |||
| // } | |||
| // err := yaml.Unmarshal(data, &person) | |||
| // | |||
| // Or by itself: | |||
| // | |||
| // var person Node | |||
| // err := yaml.Unmarshal(data, &person) | |||
| // | |||
| type Node struct { | |||
| // Kind defines whether the node is a document, a mapping, a sequence, | |||
| // a scalar value, or an alias to another node. The specific data type of | |||
| // scalar nodes may be obtained via the ShortTag and LongTag methods. | |||
| Kind Kind | |||
| // Style allows customizing the apperance of the node in the tree. | |||
| Style Style | |||
| // Tag holds the YAML tag defining the data type for the value. | |||
| // When decoding, this field will always be set to the resolved tag, | |||
| // even when it wasn't explicitly provided in the YAML content. | |||
| // When encoding, if this field is unset the value type will be | |||
| // implied from the node properties, and if it is set, it will only | |||
| // be serialized into the representation if TaggedStyle is used or | |||
| // the implicit tag diverges from the provided one. | |||
| Tag string | |||
| // Value holds the unescaped and unquoted represenation of the value. | |||
| Value string | |||
| // Anchor holds the anchor name for this node, which allows aliases to point to it. | |||
| Anchor string | |||
| // Alias holds the node that this alias points to. Only valid when Kind is AliasNode. | |||
| Alias *Node | |||
| // Content holds contained nodes for documents, mappings, and sequences. | |||
| Content []*Node | |||
| // HeadComment holds any comments in the lines preceding the node and | |||
| // not separated by an empty line. | |||
| HeadComment string | |||
| // LineComment holds any comments at the end of the line where the node is in. | |||
| LineComment string | |||
| // FootComment holds any comments following the node and before empty lines. | |||
| FootComment string | |||
| // Line and Column hold the node position in the decoded YAML text. | |||
| // These fields are not respected when encoding the node. | |||
| Line int | |||
| Column int | |||
| } | |||
| // LongTag returns the long form of the tag that indicates the data type for | |||
| // the node. If the Tag field isn't explicitly defined, one will be computed | |||
| // based on the node properties. | |||
| func (n *Node) LongTag() string { | |||
| return longTag(n.ShortTag()) | |||
| } | |||
| // ShortTag returns the short form of the YAML tag that indicates data type for | |||
| // the node. If the Tag field isn't explicitly defined, one will be computed | |||
| // based on the node properties. | |||
| func (n *Node) ShortTag() string { | |||
| if n.indicatedString() { | |||
| return strTag | |||
| } | |||
| if n.Tag == "" || n.Tag == "!" { | |||
| switch n.Kind { | |||
| case MappingNode: | |||
| return mapTag | |||
| case SequenceNode: | |||
| return seqTag | |||
| case AliasNode: | |||
| if n.Alias != nil { | |||
| return n.Alias.ShortTag() | |||
| } | |||
| case ScalarNode: | |||
| tag, _ := resolve("", n.Value) | |||
| return tag | |||
| } | |||
| return "" | |||
| } | |||
| return shortTag(n.Tag) | |||
| } | |||
| func (n *Node) indicatedString() bool { | |||
| return n.Kind == ScalarNode && | |||
| (shortTag(n.Tag) == strTag || | |||
| (n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) | |||
| } | |||
| // SetString is a convenience function that sets the node to a string value | |||
| // and defines its style in a pleasant way depending on its content. | |||
| func (n *Node) SetString(s string) { | |||
| n.Kind = ScalarNode | |||
| if utf8.ValidString(s) { | |||
| n.Value = s | |||
| n.Tag = strTag | |||
| } else { | |||
| n.Value = encodeBase64(s) | |||
| n.Tag = binaryTag | |||
| } | |||
| if strings.Contains(n.Value, "\n") { | |||
| n.Style = LiteralStyle | |||
| } | |||
| } | |||
| // -------------------------------------------------------------------------- | |||
| // Maintain a mapping of keys to structure field indexes | |||
| // The code in this section was copied from mgo/bson. | |||
| // structInfo holds details for the serialization of fields of | |||
| // a given struct. | |||
| type structInfo struct { | |||
| FieldsMap map[string]fieldInfo | |||
| FieldsList []fieldInfo | |||
| // InlineMap is the number of the field in the struct that | |||
| // contains an ,inline map, or -1 if there's none. | |||
| InlineMap int | |||
| // InlineUnmarshalers holds indexes to inlined fields that | |||
| // contain unmarshaler values. | |||
| InlineUnmarshalers [][]int | |||
| } | |||
| type fieldInfo struct { | |||
| Key string | |||
| Num int | |||
| OmitEmpty bool | |||
| Flow bool | |||
| // Id holds the unique field identifier, so we can cheaply | |||
| // check for field duplicates without maintaining an extra map. | |||
| Id int | |||
| // Inline holds the field index if the field is part of an inlined struct. | |||
| Inline []int | |||
| } | |||
| var structMap = make(map[reflect.Type]*structInfo) | |||
| var fieldMapMutex sync.RWMutex | |||
| var unmarshalerType reflect.Type | |||
| func init() { | |||
| var v Unmarshaler | |||
| unmarshalerType = reflect.ValueOf(&v).Elem().Type() | |||
| } | |||
| func getStructInfo(st reflect.Type) (*structInfo, error) { | |||
| fieldMapMutex.RLock() | |||
| sinfo, found := structMap[st] | |||
| fieldMapMutex.RUnlock() | |||
| if found { | |||
| return sinfo, nil | |||
| } | |||
| n := st.NumField() | |||
| fieldsMap := make(map[string]fieldInfo) | |||
| fieldsList := make([]fieldInfo, 0, n) | |||
| inlineMap := -1 | |||
| inlineUnmarshalers := [][]int(nil) | |||
| for i := 0; i != n; i++ { | |||
| field := st.Field(i) | |||
| if field.PkgPath != "" && !field.Anonymous { | |||
| continue // Private field | |||
| } | |||
| info := fieldInfo{Num: i} | |||
| tag := field.Tag.Get("yaml") | |||
| if tag == "" && strings.Index(string(field.Tag), ":") < 0 { | |||
| tag = string(field.Tag) | |||
| } | |||
| if tag == "-" { | |||
| continue | |||
| } | |||
| inline := false | |||
| fields := strings.Split(tag, ",") | |||
| if len(fields) > 1 { | |||
| for _, flag := range fields[1:] { | |||
| switch flag { | |||
| case "omitempty": | |||
| info.OmitEmpty = true | |||
| case "flow": | |||
| info.Flow = true | |||
| case "inline": | |||
| inline = true | |||
| default: | |||
| return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st)) | |||
| } | |||
| } | |||
| tag = fields[0] | |||
| } | |||
| if inline { | |||
| switch field.Type.Kind() { | |||
| case reflect.Map: | |||
| if inlineMap >= 0 { | |||
| return nil, errors.New("multiple ,inline maps in struct " + st.String()) | |||
| } | |||
| if field.Type.Key() != reflect.TypeOf("") { | |||
| return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String()) | |||
| } | |||
| inlineMap = info.Num | |||
| case reflect.Struct, reflect.Ptr: | |||
| ftype := field.Type | |||
| for ftype.Kind() == reflect.Ptr { | |||
| ftype = ftype.Elem() | |||
| } | |||
| if ftype.Kind() != reflect.Struct { | |||
| return nil, errors.New("option ,inline may only be used on a struct or map field") | |||
| } | |||
| if reflect.PtrTo(ftype).Implements(unmarshalerType) { | |||
| inlineUnmarshalers = append(inlineUnmarshalers, []int{i}) | |||
| } else { | |||
| sinfo, err := getStructInfo(ftype) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| for _, index := range sinfo.InlineUnmarshalers { | |||
| inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...)) | |||
| } | |||
| for _, finfo := range sinfo.FieldsList { | |||
| if _, found := fieldsMap[finfo.Key]; found { | |||
| msg := "duplicated key '" + finfo.Key + "' in struct " + st.String() | |||
| return nil, errors.New(msg) | |||
| } | |||
| if finfo.Inline == nil { | |||
| finfo.Inline = []int{i, finfo.Num} | |||
| } else { | |||
| finfo.Inline = append([]int{i}, finfo.Inline...) | |||
| } | |||
| finfo.Id = len(fieldsList) | |||
| fieldsMap[finfo.Key] = finfo | |||
| fieldsList = append(fieldsList, finfo) | |||
| } | |||
| } | |||
| default: | |||
| return nil, errors.New("option ,inline may only be used on a struct or map field") | |||
| } | |||
| continue | |||
| } | |||
| if tag != "" { | |||
| info.Key = tag | |||
| } else { | |||
| info.Key = strings.ToLower(field.Name) | |||
| } | |||
| if _, found = fieldsMap[info.Key]; found { | |||
| msg := "duplicated key '" + info.Key + "' in struct " + st.String() | |||
| return nil, errors.New(msg) | |||
| } | |||
| info.Id = len(fieldsList) | |||
| fieldsList = append(fieldsList, info) | |||
| fieldsMap[info.Key] = info | |||
| } | |||
| sinfo = &structInfo{ | |||
| FieldsMap: fieldsMap, | |||
| FieldsList: fieldsList, | |||
| InlineMap: inlineMap, | |||
| InlineUnmarshalers: inlineUnmarshalers, | |||
| } | |||
| fieldMapMutex.Lock() | |||
| structMap[st] = sinfo | |||
| fieldMapMutex.Unlock() | |||
| return sinfo, nil | |||
| } | |||
| // IsZeroer is used to check whether an object is zero to | |||
| // determine whether it should be omitted when marshaling | |||
| // with the omitempty flag. One notable implementation | |||
| // is time.Time. | |||
| type IsZeroer interface { | |||
| IsZero() bool | |||
| } | |||
| func isZero(v reflect.Value) bool { | |||
| kind := v.Kind() | |||
| if z, ok := v.Interface().(IsZeroer); ok { | |||
| if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { | |||
| return true | |||
| } | |||
| return z.IsZero() | |||
| } | |||
| switch kind { | |||
| case reflect.String: | |||
| return len(v.String()) == 0 | |||
| case reflect.Interface, reflect.Ptr: | |||
| return v.IsNil() | |||
| case reflect.Slice: | |||
| return v.Len() == 0 | |||
| case reflect.Map: | |||
| return v.Len() == 0 | |||
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||
| return v.Int() == 0 | |||
| case reflect.Float32, reflect.Float64: | |||
| return v.Float() == 0 | |||
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||
| return v.Uint() == 0 | |||
| case reflect.Bool: | |||
| return !v.Bool() | |||
| case reflect.Struct: | |||
| vt := v.Type() | |||
| for i := v.NumField() - 1; i >= 0; i-- { | |||
| if vt.Field(i).PkgPath != "" { | |||
| continue // Private field | |||
| } | |||
| if !isZero(v.Field(i)) { | |||
| return false | |||
| } | |||
| } | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| @@ -0,0 +1,805 @@ | |||
| // | |||
| // Copyright (c) 2011-2019 Canonical Ltd | |||
| // Copyright (c) 2006-2010 Kirill Simonov | |||
| // | |||
| // 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 yaml | |||
| import ( | |||
| "fmt" | |||
| "io" | |||
| ) | |||
| // The version directive data. | |||
| type yaml_version_directive_t struct { | |||
| major int8 // The major version number. | |||
| minor int8 // The minor version number. | |||
| } | |||
| // The tag directive data. | |||
| type yaml_tag_directive_t struct { | |||
| handle []byte // The tag handle. | |||
| prefix []byte // The tag prefix. | |||
| } | |||
| type yaml_encoding_t int | |||
| // The stream encoding. | |||
| const ( | |||
| // Let the parser choose the encoding. | |||
| yaml_ANY_ENCODING yaml_encoding_t = iota | |||
| yaml_UTF8_ENCODING // The default UTF-8 encoding. | |||
| yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. | |||
| yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. | |||
| ) | |||
| type yaml_break_t int | |||
| // Line break types. | |||
| const ( | |||
| // Let the parser choose the break type. | |||
| yaml_ANY_BREAK yaml_break_t = iota | |||
| yaml_CR_BREAK // Use CR for line breaks (Mac style). | |||
| yaml_LN_BREAK // Use LN for line breaks (Unix style). | |||
| yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). | |||
| ) | |||
| type yaml_error_type_t int | |||
| // Many bad things could happen with the parser and emitter. | |||
| const ( | |||
| // No error is produced. | |||
| yaml_NO_ERROR yaml_error_type_t = iota | |||
| yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. | |||
| yaml_READER_ERROR // Cannot read or decode the input stream. | |||
| yaml_SCANNER_ERROR // Cannot scan the input stream. | |||
| yaml_PARSER_ERROR // Cannot parse the input stream. | |||
| yaml_COMPOSER_ERROR // Cannot compose a YAML document. | |||
| yaml_WRITER_ERROR // Cannot write to the output stream. | |||
| yaml_EMITTER_ERROR // Cannot emit a YAML stream. | |||
| ) | |||
| // The pointer position. | |||
| type yaml_mark_t struct { | |||
| index int // The position index. | |||
| line int // The position line. | |||
| column int // The position column. | |||
| } | |||
| // Node Styles | |||
| type yaml_style_t int8 | |||
| type yaml_scalar_style_t yaml_style_t | |||
| // Scalar styles. | |||
| const ( | |||
| // Let the emitter choose the style. | |||
| yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0 | |||
| yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style. | |||
| yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. | |||
| yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. | |||
| yaml_LITERAL_SCALAR_STYLE // The literal scalar style. | |||
| yaml_FOLDED_SCALAR_STYLE // The folded scalar style. | |||
| ) | |||
| type yaml_sequence_style_t yaml_style_t | |||
| // Sequence styles. | |||
| const ( | |||
| // Let the emitter choose the style. | |||
| yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota | |||
| yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. | |||
| yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. | |||
| ) | |||
| type yaml_mapping_style_t yaml_style_t | |||
| // Mapping styles. | |||
| const ( | |||
| // Let the emitter choose the style. | |||
| yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota | |||
| yaml_BLOCK_MAPPING_STYLE // The block mapping style. | |||
| yaml_FLOW_MAPPING_STYLE // The flow mapping style. | |||
| ) | |||
| // Tokens | |||
| type yaml_token_type_t int | |||
| // Token types. | |||
| const ( | |||
| // An empty token. | |||
| yaml_NO_TOKEN yaml_token_type_t = iota | |||
| yaml_STREAM_START_TOKEN // A STREAM-START token. | |||
| yaml_STREAM_END_TOKEN // A STREAM-END token. | |||
| yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. | |||
| yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. | |||
| yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. | |||
| yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. | |||
| yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. | |||
| yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. | |||
| yaml_BLOCK_END_TOKEN // A BLOCK-END token. | |||
| yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. | |||
| yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. | |||
| yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. | |||
| yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. | |||
| yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. | |||
| yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. | |||
| yaml_KEY_TOKEN // A KEY token. | |||
| yaml_VALUE_TOKEN // A VALUE token. | |||
| yaml_ALIAS_TOKEN // An ALIAS token. | |||
| yaml_ANCHOR_TOKEN // An ANCHOR token. | |||
| yaml_TAG_TOKEN // A TAG token. | |||
| yaml_SCALAR_TOKEN // A SCALAR token. | |||
| ) | |||
| func (tt yaml_token_type_t) String() string { | |||
| switch tt { | |||
| case yaml_NO_TOKEN: | |||
| return "yaml_NO_TOKEN" | |||
| case yaml_STREAM_START_TOKEN: | |||
| return "yaml_STREAM_START_TOKEN" | |||
| case yaml_STREAM_END_TOKEN: | |||
| return "yaml_STREAM_END_TOKEN" | |||
| case yaml_VERSION_DIRECTIVE_TOKEN: | |||
| return "yaml_VERSION_DIRECTIVE_TOKEN" | |||
| case yaml_TAG_DIRECTIVE_TOKEN: | |||
| return "yaml_TAG_DIRECTIVE_TOKEN" | |||
| case yaml_DOCUMENT_START_TOKEN: | |||
| return "yaml_DOCUMENT_START_TOKEN" | |||
| case yaml_DOCUMENT_END_TOKEN: | |||
| return "yaml_DOCUMENT_END_TOKEN" | |||
| case yaml_BLOCK_SEQUENCE_START_TOKEN: | |||
| return "yaml_BLOCK_SEQUENCE_START_TOKEN" | |||
| case yaml_BLOCK_MAPPING_START_TOKEN: | |||
| return "yaml_BLOCK_MAPPING_START_TOKEN" | |||
| case yaml_BLOCK_END_TOKEN: | |||
| return "yaml_BLOCK_END_TOKEN" | |||
| case yaml_FLOW_SEQUENCE_START_TOKEN: | |||
| return "yaml_FLOW_SEQUENCE_START_TOKEN" | |||
| case yaml_FLOW_SEQUENCE_END_TOKEN: | |||
| return "yaml_FLOW_SEQUENCE_END_TOKEN" | |||
| case yaml_FLOW_MAPPING_START_TOKEN: | |||
| return "yaml_FLOW_MAPPING_START_TOKEN" | |||
| case yaml_FLOW_MAPPING_END_TOKEN: | |||
| return "yaml_FLOW_MAPPING_END_TOKEN" | |||
| case yaml_BLOCK_ENTRY_TOKEN: | |||
| return "yaml_BLOCK_ENTRY_TOKEN" | |||
| case yaml_FLOW_ENTRY_TOKEN: | |||
| return "yaml_FLOW_ENTRY_TOKEN" | |||
| case yaml_KEY_TOKEN: | |||
| return "yaml_KEY_TOKEN" | |||
| case yaml_VALUE_TOKEN: | |||
| return "yaml_VALUE_TOKEN" | |||
| case yaml_ALIAS_TOKEN: | |||
| return "yaml_ALIAS_TOKEN" | |||
| case yaml_ANCHOR_TOKEN: | |||
| return "yaml_ANCHOR_TOKEN" | |||
| case yaml_TAG_TOKEN: | |||
| return "yaml_TAG_TOKEN" | |||
| case yaml_SCALAR_TOKEN: | |||
| return "yaml_SCALAR_TOKEN" | |||
| } | |||
| return "<unknown token>" | |||
| } | |||
| // The token structure. | |||
| type yaml_token_t struct { | |||
| // The token type. | |||
| typ yaml_token_type_t | |||
| // The start/end of the token. | |||
| start_mark, end_mark yaml_mark_t | |||
| // The stream encoding (for yaml_STREAM_START_TOKEN). | |||
| encoding yaml_encoding_t | |||
| // The alias/anchor/scalar value or tag/tag directive handle | |||
| // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). | |||
| value []byte | |||
| // The tag suffix (for yaml_TAG_TOKEN). | |||
| suffix []byte | |||
| // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). | |||
| prefix []byte | |||
| // The scalar style (for yaml_SCALAR_TOKEN). | |||
| style yaml_scalar_style_t | |||
| // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). | |||
| major, minor int8 | |||
| } | |||
| // Events | |||
| type yaml_event_type_t int8 | |||
| // Event types. | |||
| const ( | |||
| // An empty event. | |||
| yaml_NO_EVENT yaml_event_type_t = iota | |||
| yaml_STREAM_START_EVENT // A STREAM-START event. | |||
| yaml_STREAM_END_EVENT // A STREAM-END event. | |||
| yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. | |||
| yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. | |||
| yaml_ALIAS_EVENT // An ALIAS event. | |||
| yaml_SCALAR_EVENT // A SCALAR event. | |||
| yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. | |||
| yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. | |||
| yaml_MAPPING_START_EVENT // A MAPPING-START event. | |||
| yaml_MAPPING_END_EVENT // A MAPPING-END event. | |||
| yaml_TAIL_COMMENT_EVENT | |||
| ) | |||
| var eventStrings = []string{ | |||
| yaml_NO_EVENT: "none", | |||
| yaml_STREAM_START_EVENT: "stream start", | |||
| yaml_STREAM_END_EVENT: "stream end", | |||
| yaml_DOCUMENT_START_EVENT: "document start", | |||
| yaml_DOCUMENT_END_EVENT: "document end", | |||
| yaml_ALIAS_EVENT: "alias", | |||
| yaml_SCALAR_EVENT: "scalar", | |||
| yaml_SEQUENCE_START_EVENT: "sequence start", | |||
| yaml_SEQUENCE_END_EVENT: "sequence end", | |||
| yaml_MAPPING_START_EVENT: "mapping start", | |||
| yaml_MAPPING_END_EVENT: "mapping end", | |||
| yaml_TAIL_COMMENT_EVENT: "tail comment", | |||
| } | |||
| func (e yaml_event_type_t) String() string { | |||
| if e < 0 || int(e) >= len(eventStrings) { | |||
| return fmt.Sprintf("unknown event %d", e) | |||
| } | |||
| return eventStrings[e] | |||
| } | |||
| // The event structure. | |||
| type yaml_event_t struct { | |||
| // The event type. | |||
| typ yaml_event_type_t | |||
| // The start and end of the event. | |||
| start_mark, end_mark yaml_mark_t | |||
| // The document encoding (for yaml_STREAM_START_EVENT). | |||
| encoding yaml_encoding_t | |||
| // The version directive (for yaml_DOCUMENT_START_EVENT). | |||
| version_directive *yaml_version_directive_t | |||
| // The list of tag directives (for yaml_DOCUMENT_START_EVENT). | |||
| tag_directives []yaml_tag_directive_t | |||
| // The comments | |||
| head_comment []byte | |||
| line_comment []byte | |||
| foot_comment []byte | |||
| tail_comment []byte | |||
| // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). | |||
| anchor []byte | |||
| // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). | |||
| tag []byte | |||
| // The scalar value (for yaml_SCALAR_EVENT). | |||
| value []byte | |||
| // Is the document start/end indicator implicit, or the tag optional? | |||
| // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). | |||
| implicit bool | |||
| // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). | |||
| quoted_implicit bool | |||
| // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). | |||
| style yaml_style_t | |||
| } | |||
| func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } | |||
| func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } | |||
| func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } | |||
| // Nodes | |||
| const ( | |||
| yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. | |||
| yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. | |||
| yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. | |||
| yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. | |||
| yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. | |||
| yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. | |||
| yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. | |||
| yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. | |||
| // Not in original libyaml. | |||
| yaml_BINARY_TAG = "tag:yaml.org,2002:binary" | |||
| yaml_MERGE_TAG = "tag:yaml.org,2002:merge" | |||
| yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. | |||
| yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. | |||
| yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. | |||
| ) | |||
| type yaml_node_type_t int | |||
| // Node types. | |||
| const ( | |||
| // An empty node. | |||
| yaml_NO_NODE yaml_node_type_t = iota | |||
| yaml_SCALAR_NODE // A scalar node. | |||
| yaml_SEQUENCE_NODE // A sequence node. | |||
| yaml_MAPPING_NODE // A mapping node. | |||
| ) | |||
| // An element of a sequence node. | |||
| type yaml_node_item_t int | |||
| // An element of a mapping node. | |||
| type yaml_node_pair_t struct { | |||
| key int // The key of the element. | |||
| value int // The value of the element. | |||
| } | |||
| // The node structure. | |||
| type yaml_node_t struct { | |||
| typ yaml_node_type_t // The node type. | |||
| tag []byte // The node tag. | |||
| // The node data. | |||
| // The scalar parameters (for yaml_SCALAR_NODE). | |||
| scalar struct { | |||
| value []byte // The scalar value. | |||
| length int // The length of the scalar value. | |||
| style yaml_scalar_style_t // The scalar style. | |||
| } | |||
| // The sequence parameters (for YAML_SEQUENCE_NODE). | |||
| sequence struct { | |||
| items_data []yaml_node_item_t // The stack of sequence items. | |||
| style yaml_sequence_style_t // The sequence style. | |||
| } | |||
| // The mapping parameters (for yaml_MAPPING_NODE). | |||
| mapping struct { | |||
| pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). | |||
| pairs_start *yaml_node_pair_t // The beginning of the stack. | |||
| pairs_end *yaml_node_pair_t // The end of the stack. | |||
| pairs_top *yaml_node_pair_t // The top of the stack. | |||
| style yaml_mapping_style_t // The mapping style. | |||
| } | |||
| start_mark yaml_mark_t // The beginning of the node. | |||
| end_mark yaml_mark_t // The end of the node. | |||
| } | |||
| // The document structure. | |||
| type yaml_document_t struct { | |||
| // The document nodes. | |||
| nodes []yaml_node_t | |||
| // The version directive. | |||
| version_directive *yaml_version_directive_t | |||
| // The list of tag directives. | |||
| tag_directives_data []yaml_tag_directive_t | |||
| tag_directives_start int // The beginning of the tag directives list. | |||
| tag_directives_end int // The end of the tag directives list. | |||
| start_implicit int // Is the document start indicator implicit? | |||
| end_implicit int // Is the document end indicator implicit? | |||
| // The start/end of the document. | |||
| start_mark, end_mark yaml_mark_t | |||
| } | |||
| // The prototype of a read handler. | |||
| // | |||
| // The read handler is called when the parser needs to read more bytes from the | |||
| // source. The handler should write not more than size bytes to the buffer. | |||
| // The number of written bytes should be set to the size_read variable. | |||
| // | |||
| // [in,out] data A pointer to an application data specified by | |||
| // yaml_parser_set_input(). | |||
| // [out] buffer The buffer to write the data from the source. | |||
| // [in] size The size of the buffer. | |||
| // [out] size_read The actual number of bytes read from the source. | |||
| // | |||
| // On success, the handler should return 1. If the handler failed, | |||
| // the returned value should be 0. On EOF, the handler should set the | |||
| // size_read to 0 and return 1. | |||
| type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) | |||
| // This structure holds information about a potential simple key. | |||
| type yaml_simple_key_t struct { | |||
| possible bool // Is a simple key possible? | |||
| required bool // Is a simple key required? | |||
| token_number int // The number of the token. | |||
| mark yaml_mark_t // The position mark. | |||
| } | |||
| // The states of the parser. | |||
| type yaml_parser_state_t int | |||
| const ( | |||
| yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota | |||
| yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. | |||
| yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. | |||
| yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. | |||
| yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. | |||
| yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. | |||
| yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. | |||
| yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. | |||
| yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. | |||
| yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. | |||
| yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. | |||
| yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. | |||
| yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. | |||
| yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. | |||
| yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. | |||
| yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. | |||
| yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. | |||
| yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. | |||
| yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. | |||
| yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. | |||
| yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. | |||
| yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. | |||
| yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. | |||
| yaml_PARSE_END_STATE // Expect nothing. | |||
| ) | |||
| func (ps yaml_parser_state_t) String() string { | |||
| switch ps { | |||
| case yaml_PARSE_STREAM_START_STATE: | |||
| return "yaml_PARSE_STREAM_START_STATE" | |||
| case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: | |||
| return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" | |||
| case yaml_PARSE_DOCUMENT_START_STATE: | |||
| return "yaml_PARSE_DOCUMENT_START_STATE" | |||
| case yaml_PARSE_DOCUMENT_CONTENT_STATE: | |||
| return "yaml_PARSE_DOCUMENT_CONTENT_STATE" | |||
| case yaml_PARSE_DOCUMENT_END_STATE: | |||
| return "yaml_PARSE_DOCUMENT_END_STATE" | |||
| case yaml_PARSE_BLOCK_NODE_STATE: | |||
| return "yaml_PARSE_BLOCK_NODE_STATE" | |||
| case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: | |||
| return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" | |||
| case yaml_PARSE_FLOW_NODE_STATE: | |||
| return "yaml_PARSE_FLOW_NODE_STATE" | |||
| case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: | |||
| return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" | |||
| case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: | |||
| return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" | |||
| case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: | |||
| return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" | |||
| case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: | |||
| return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" | |||
| case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: | |||
| return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" | |||
| case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: | |||
| return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" | |||
| case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: | |||
| return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" | |||
| case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: | |||
| return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" | |||
| case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: | |||
| return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" | |||
| case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: | |||
| return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" | |||
| case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: | |||
| return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" | |||
| case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: | |||
| return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" | |||
| case yaml_PARSE_FLOW_MAPPING_KEY_STATE: | |||
| return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" | |||
| case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: | |||
| return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" | |||
| case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: | |||
| return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" | |||
| case yaml_PARSE_END_STATE: | |||
| return "yaml_PARSE_END_STATE" | |||
| } | |||
| return "<unknown parser state>" | |||
| } | |||
| // This structure holds aliases data. | |||
| type yaml_alias_data_t struct { | |||
| anchor []byte // The anchor. | |||
| index int // The node id. | |||
| mark yaml_mark_t // The anchor mark. | |||
| } | |||
| // The parser structure. | |||
| // | |||
| // All members are internal. Manage the structure using the | |||
| // yaml_parser_ family of functions. | |||
| type yaml_parser_t struct { | |||
| // Error handling | |||
| error yaml_error_type_t // Error type. | |||
| problem string // Error description. | |||
| // The byte about which the problem occurred. | |||
| problem_offset int | |||
| problem_value int | |||
| problem_mark yaml_mark_t | |||
| // The error context. | |||
| context string | |||
| context_mark yaml_mark_t | |||
| // Reader stuff | |||
| read_handler yaml_read_handler_t // Read handler. | |||
| input_reader io.Reader // File input data. | |||
| input []byte // String input data. | |||
| input_pos int | |||
| eof bool // EOF flag | |||
| buffer []byte // The working buffer. | |||
| buffer_pos int // The current position of the buffer. | |||
| unread int // The number of unread characters in the buffer. | |||
| newlines int // The number of line breaks since last non-break/non-blank character | |||
| raw_buffer []byte // The raw buffer. | |||
| raw_buffer_pos int // The current position of the buffer. | |||
| encoding yaml_encoding_t // The input encoding. | |||
| offset int // The offset of the current position (in bytes). | |||
| mark yaml_mark_t // The mark of the current position. | |||
| // Comments | |||
| head_comment []byte // The current head comments | |||
| line_comment []byte // The current line comments | |||
| foot_comment []byte // The current foot comments | |||
| tail_comment []byte // Foot comment that happens at the end of a block. | |||
| stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc) | |||
| comments []yaml_comment_t // The folded comments for all parsed tokens | |||
| comments_head int | |||
| // Scanner stuff | |||
| stream_start_produced bool // Have we started to scan the input stream? | |||
| stream_end_produced bool // Have we reached the end of the input stream? | |||
| flow_level int // The number of unclosed '[' and '{' indicators. | |||
| tokens []yaml_token_t // The tokens queue. | |||
| tokens_head int // The head of the tokens queue. | |||
| tokens_parsed int // The number of tokens fetched from the queue. | |||
| token_available bool // Does the tokens queue contain a token ready for dequeueing. | |||
| indent int // The current indentation level. | |||
| indents []int // The indentation levels stack. | |||
| simple_key_allowed bool // May a simple key occur at the current position? | |||
| simple_keys []yaml_simple_key_t // The stack of simple keys. | |||
| simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number | |||
| // Parser stuff | |||
| state yaml_parser_state_t // The current parser state. | |||
| states []yaml_parser_state_t // The parser states stack. | |||
| marks []yaml_mark_t // The stack of marks. | |||
| tag_directives []yaml_tag_directive_t // The list of TAG directives. | |||
| // Dumper stuff | |||
| aliases []yaml_alias_data_t // The alias data. | |||
| document *yaml_document_t // The currently parsed document. | |||
| } | |||
| type yaml_comment_t struct { | |||
| scan_mark yaml_mark_t // Position where scanning for comments started | |||
| token_mark yaml_mark_t // Position after which tokens will be associated with this comment | |||
| start_mark yaml_mark_t // Position of '#' comment mark | |||
| end_mark yaml_mark_t // Position where comment terminated | |||
| head []byte | |||
| line []byte | |||
| foot []byte | |||
| } | |||
| // Emitter Definitions | |||
| // The prototype of a write handler. | |||
| // | |||
| // The write handler is called when the emitter needs to flush the accumulated | |||
| // characters to the output. The handler should write @a size bytes of the | |||
| // @a buffer to the output. | |||
| // | |||
| // @param[in,out] data A pointer to an application data specified by | |||
| // yaml_emitter_set_output(). | |||
| // @param[in] buffer The buffer with bytes to be written. | |||
| // @param[in] size The size of the buffer. | |||
| // | |||
| // @returns On success, the handler should return @c 1. If the handler failed, | |||
| // the returned value should be @c 0. | |||
| // | |||
| type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error | |||
| type yaml_emitter_state_t int | |||
| // The emitter states. | |||
| const ( | |||
| // Expect STREAM-START. | |||
| yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota | |||
| yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. | |||
| yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. | |||
| yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. | |||
| yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. | |||
| yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. | |||
| yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out | |||
| yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. | |||
| yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. | |||
| yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out | |||
| yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. | |||
| yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. | |||
| yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. | |||
| yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. | |||
| yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. | |||
| yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. | |||
| yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. | |||
| yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. | |||
| yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. | |||
| yaml_EMIT_END_STATE // Expect nothing. | |||
| ) | |||
| // The emitter structure. | |||
| // | |||
| // All members are internal. Manage the structure using the @c yaml_emitter_ | |||
| // family of functions. | |||
| type yaml_emitter_t struct { | |||
| // Error handling | |||
| error yaml_error_type_t // Error type. | |||
| problem string // Error description. | |||
| // Writer stuff | |||
| write_handler yaml_write_handler_t // Write handler. | |||
| output_buffer *[]byte // String output data. | |||
| output_writer io.Writer // File output data. | |||
| buffer []byte // The working buffer. | |||
| buffer_pos int // The current position of the buffer. | |||
| raw_buffer []byte // The raw buffer. | |||
| raw_buffer_pos int // The current position of the buffer. | |||
| encoding yaml_encoding_t // The stream encoding. | |||
| // Emitter stuff | |||
| canonical bool // If the output is in the canonical style? | |||
| best_indent int // The number of indentation spaces. | |||
| best_width int // The preferred width of the output lines. | |||
| unicode bool // Allow unescaped non-ASCII characters? | |||
| line_break yaml_break_t // The preferred line break. | |||
| state yaml_emitter_state_t // The current emitter state. | |||
| states []yaml_emitter_state_t // The stack of states. | |||
| events []yaml_event_t // The event queue. | |||
| events_head int // The head of the event queue. | |||
| indents []int // The stack of indentation levels. | |||
| tag_directives []yaml_tag_directive_t // The list of tag directives. | |||
| indent int // The current indentation level. | |||
| flow_level int // The current flow level. | |||
| root_context bool // Is it the document root context? | |||
| sequence_context bool // Is it a sequence context? | |||
| mapping_context bool // Is it a mapping context? | |||
| simple_key_context bool // Is it a simple mapping key context? | |||
| line int // The current line. | |||
| column int // The current column. | |||
| whitespace bool // If the last character was a whitespace? | |||
| indention bool // If the last character was an indentation character (' ', '-', '?', ':')? | |||
| open_ended bool // If an explicit document end is required? | |||
| space_above bool // Is there's an empty line above? | |||
| foot_indent int // The indent used to write the foot comment above, or -1 if none. | |||
| // Anchor analysis. | |||
| anchor_data struct { | |||
| anchor []byte // The anchor value. | |||
| alias bool // Is it an alias? | |||
| } | |||
| // Tag analysis. | |||
| tag_data struct { | |||
| handle []byte // The tag handle. | |||
| suffix []byte // The tag suffix. | |||
| } | |||
| // Scalar analysis. | |||
| scalar_data struct { | |||
| value []byte // The scalar value. | |||
| multiline bool // Does the scalar contain line breaks? | |||
| flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? | |||
| block_plain_allowed bool // Can the scalar be expressed in the block plain style? | |||
| single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? | |||
| block_allowed bool // Can the scalar be expressed in the literal or folded styles? | |||
| style yaml_scalar_style_t // The output style. | |||
| } | |||
| // Comments | |||
| head_comment []byte | |||
| line_comment []byte | |||
| foot_comment []byte | |||
| tail_comment []byte | |||
| // Dumper stuff | |||
| opened bool // If the stream was already opened? | |||
| closed bool // If the stream was already closed? | |||
| // The information associated with the document nodes. | |||
| anchors *struct { | |||
| references int // The number of references. | |||
| anchor int // The anchor id. | |||
| serialized bool // If the node has been emitted? | |||
| } | |||
| last_anchor_id int // The last assigned anchor id. | |||
| document *yaml_document_t // The currently emitted document. | |||
| } | |||
| @@ -0,0 +1,198 @@ | |||
| // | |||
| // Copyright (c) 2011-2019 Canonical Ltd | |||
| // Copyright (c) 2006-2010 Kirill Simonov | |||
| // | |||
| // 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 yaml | |||
| const ( | |||
| // The size of the input raw buffer. | |||
| input_raw_buffer_size = 512 | |||
| // The size of the input buffer. | |||
| // It should be possible to decode the whole raw buffer. | |||
| input_buffer_size = input_raw_buffer_size * 3 | |||
| // The size of the output buffer. | |||
| output_buffer_size = 128 | |||
| // The size of the output raw buffer. | |||
| // It should be possible to encode the whole output buffer. | |||
| output_raw_buffer_size = (output_buffer_size*2 + 2) | |||
| // The size of other stacks and queues. | |||
| initial_stack_size = 16 | |||
| initial_queue_size = 16 | |||
| initial_string_size = 16 | |||
| ) | |||
| // Check if the character at the specified position is an alphabetical | |||
| // character, a digit, '_', or '-'. | |||
| func is_alpha(b []byte, i int) bool { | |||
| return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' | |||
| } | |||
| // Check if the character at the specified position is a digit. | |||
| func is_digit(b []byte, i int) bool { | |||
| return b[i] >= '0' && b[i] <= '9' | |||
| } | |||
| // Get the value of a digit. | |||
| func as_digit(b []byte, i int) int { | |||
| return int(b[i]) - '0' | |||
| } | |||
| // Check if the character at the specified position is a hex-digit. | |||
| func is_hex(b []byte, i int) bool { | |||
| return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' | |||
| } | |||
| // Get the value of a hex-digit. | |||
| func as_hex(b []byte, i int) int { | |||
| bi := b[i] | |||
| if bi >= 'A' && bi <= 'F' { | |||
| return int(bi) - 'A' + 10 | |||
| } | |||
| if bi >= 'a' && bi <= 'f' { | |||
| return int(bi) - 'a' + 10 | |||
| } | |||
| return int(bi) - '0' | |||
| } | |||
| // Check if the character is ASCII. | |||
| func is_ascii(b []byte, i int) bool { | |||
| return b[i] <= 0x7F | |||
| } | |||
| // Check if the character at the start of the buffer can be printed unescaped. | |||
| func is_printable(b []byte, i int) bool { | |||
| return ((b[i] == 0x0A) || // . == #x0A | |||
| (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E | |||
| (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF | |||
| (b[i] > 0xC2 && b[i] < 0xED) || | |||
| (b[i] == 0xED && b[i+1] < 0xA0) || | |||
| (b[i] == 0xEE) || | |||
| (b[i] == 0xEF && // #xE000 <= . <= #xFFFD | |||
| !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF | |||
| !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) | |||
| } | |||
| // Check if the character at the specified position is NUL. | |||
| func is_z(b []byte, i int) bool { | |||
| return b[i] == 0x00 | |||
| } | |||
| // Check if the beginning of the buffer is a BOM. | |||
| func is_bom(b []byte, i int) bool { | |||
| return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF | |||
| } | |||
| // Check if the character at the specified position is space. | |||
| func is_space(b []byte, i int) bool { | |||
| return b[i] == ' ' | |||
| } | |||
| // Check if the character at the specified position is tab. | |||
| func is_tab(b []byte, i int) bool { | |||
| return b[i] == '\t' | |||
| } | |||
| // Check if the character at the specified position is blank (space or tab). | |||
| func is_blank(b []byte, i int) bool { | |||
| //return is_space(b, i) || is_tab(b, i) | |||
| return b[i] == ' ' || b[i] == '\t' | |||
| } | |||
| // Check if the character at the specified position is a line break. | |||
| func is_break(b []byte, i int) bool { | |||
| return (b[i] == '\r' || // CR (#xD) | |||
| b[i] == '\n' || // LF (#xA) | |||
| b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) | |||
| b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) | |||
| b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) | |||
| } | |||
| func is_crlf(b []byte, i int) bool { | |||
| return b[i] == '\r' && b[i+1] == '\n' | |||
| } | |||
| // Check if the character is a line break or NUL. | |||
| func is_breakz(b []byte, i int) bool { | |||
| //return is_break(b, i) || is_z(b, i) | |||
| return ( | |||
| // is_break: | |||
| b[i] == '\r' || // CR (#xD) | |||
| b[i] == '\n' || // LF (#xA) | |||
| b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) | |||
| b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) | |||
| b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) | |||
| // is_z: | |||
| b[i] == 0) | |||
| } | |||
| // Check if the character is a line break, space, or NUL. | |||
| func is_spacez(b []byte, i int) bool { | |||
| //return is_space(b, i) || is_breakz(b, i) | |||
| return ( | |||
| // is_space: | |||
| b[i] == ' ' || | |||
| // is_breakz: | |||
| b[i] == '\r' || // CR (#xD) | |||
| b[i] == '\n' || // LF (#xA) | |||
| b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) | |||
| b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) | |||
| b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) | |||
| b[i] == 0) | |||
| } | |||
| // Check if the character is a line break, space, tab, or NUL. | |||
| func is_blankz(b []byte, i int) bool { | |||
| //return is_blank(b, i) || is_breakz(b, i) | |||
| return ( | |||
| // is_blank: | |||
| b[i] == ' ' || b[i] == '\t' || | |||
| // is_breakz: | |||
| b[i] == '\r' || // CR (#xD) | |||
| b[i] == '\n' || // LF (#xA) | |||
| b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) | |||
| b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) | |||
| b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) | |||
| b[i] == 0) | |||
| } | |||
| // Determine the width of the character. | |||
| func width(b byte) int { | |||
| // Don't replace these by a switch without first | |||
| // confirming that it is being inlined. | |||
| if b&0x80 == 0x00 { | |||
| return 1 | |||
| } | |||
| if b&0xE0 == 0xC0 { | |||
| return 2 | |||
| } | |||
| if b&0xF0 == 0xE0 { | |||
| return 3 | |||
| } | |||
| if b&0xF8 == 0xF0 { | |||
| return 4 | |||
| } | |||
| return 0 | |||
| } | |||
| @@ -253,6 +253,9 @@ github.com/dustin/go-humanize | |||
| # github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 | |||
| ## explicit | |||
| github.com/editorconfig/editorconfig-core-go/v2 | |||
| # github.com/elliotchance/orderedmap v1.4.0 | |||
| ## explicit | |||
| github.com/elliotchance/orderedmap | |||
| # github.com/emirpasic/gods v1.12.0 | |||
| ## explicit | |||
| github.com/emirpasic/gods/containers | |||
| @@ -753,7 +756,7 @@ github.com/spf13/viper | |||
| github.com/steveyen/gtreap | |||
| # github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9 | |||
| github.com/streadway/amqp | |||
| # github.com/stretchr/testify v1.4.0 | |||
| # github.com/stretchr/testify v1.7.0 | |||
| ## explicit | |||
| github.com/stretchr/testify/assert | |||
| github.com/stretchr/testify/require | |||
| @@ -1134,6 +1137,8 @@ gopkg.in/warnings.v0 | |||
| # gopkg.in/yaml.v2 v2.2.8 | |||
| ## explicit | |||
| gopkg.in/yaml.v2 | |||
| # gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c | |||
| gopkg.in/yaml.v3 | |||
| # mvdan.cc/xurls/v2 v2.1.0 | |||
| ## explicit | |||
| mvdan.cc/xurls/v2 | |||