mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-21 16:24:22 +01:00
vendor: Add vendor checkmetrics dependencies
This PR adds the vendor for the checkmetrics. Signed-off-by: Gabriela Cervantes <gabriela.cervantes.tellez@intel.com>
This commit is contained in:
22
tests/metrics/cmd/checkmetrics/go.mod
Normal file
22
tests/metrics/cmd/checkmetrics/go.mod
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
module example.com/m
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/BurntSushi/toml v1.3.2
|
||||||
|
github.com/montanaflynn/stats v0.7.1
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
github.com/stretchr/testify v1.8.4
|
||||||
|
github.com/urfave/cli v1.22.14
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
37
tests/metrics/cmd/checkmetrics/go.sum
Normal file
37
tests/metrics/cmd/checkmetrics/go.sum
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||||
|
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||||
|
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
|
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/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||||
|
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
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.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -120,7 +120,7 @@ func TestLoad(t *testing.T) {
|
|||||||
err = CreateBadFile(badFileName)
|
err = CreateBadFile(badFileName)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
|
||||||
// Set up our basci metrics struct
|
// Set up our basic metrics struct
|
||||||
var m = metrics{
|
var m = metrics{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Description: "desc",
|
Description: "desc",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ results, stored in JSON files, against a set of baseline metrics
|
|||||||
|
|
||||||
It returns non zero if any of the TOML metrics are not met.
|
It returns non zero if any of the TOML metrics are not met.
|
||||||
|
|
||||||
It prints out a tabluated report summary at the end of the run.
|
It prints out a tabulated report summary at the end of the run.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|||||||
2
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/.gitignore
generated
vendored
Normal file
2
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/toml.test
|
||||||
|
/toml-test
|
||||||
21
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/COPYING
generated
vendored
Normal file
21
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/COPYING
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 TOML authors
|
||||||
|
|
||||||
|
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.
|
||||||
120
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/README.md
generated
vendored
Normal file
120
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/README.md
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
|
||||||
|
reflection interface similar to Go's standard library `json` and `xml` packages.
|
||||||
|
|
||||||
|
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
|
||||||
|
|
||||||
|
Documentation: https://godocs.io/github.com/BurntSushi/toml
|
||||||
|
|
||||||
|
See the [releases page](https://github.com/BurntSushi/toml/releases) for a
|
||||||
|
changelog; this information is also in the git tag annotations (e.g. `git show
|
||||||
|
v0.4.0`).
|
||||||
|
|
||||||
|
This library requires Go 1.13 or newer; add it to your go.mod with:
|
||||||
|
|
||||||
|
% go get github.com/BurntSushi/toml@latest
|
||||||
|
|
||||||
|
It also comes with a TOML validator CLI tool:
|
||||||
|
|
||||||
|
% go install github.com/BurntSushi/toml/cmd/tomlv@latest
|
||||||
|
% tomlv some-toml-file.toml
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
For the simplest example, consider some TOML file as just a list of keys and
|
||||||
|
values:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
Age = 25
|
||||||
|
Cats = [ "Cauchy", "Plato" ]
|
||||||
|
Pi = 3.14
|
||||||
|
Perfection = [ 6, 28, 496, 8128 ]
|
||||||
|
DOB = 1987-07-05T05:45:00Z
|
||||||
|
```
|
||||||
|
|
||||||
|
Which can be decoded with:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Age int
|
||||||
|
Cats []string
|
||||||
|
Pi float64
|
||||||
|
Perfection []int
|
||||||
|
DOB time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf Config
|
||||||
|
_, err := toml.Decode(tomlData, &conf)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use struct tags if your struct field name doesn't map to a TOML key
|
||||||
|
value directly:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
some_key_NAME = "wat"
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type TOML struct {
|
||||||
|
ObscureKey string `toml:"some_key_NAME"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Beware that like other decoders **only exported fields** are considered when
|
||||||
|
encoding and decoding; private fields are silently ignored.
|
||||||
|
|
||||||
|
### Using the `Marshaler` and `encoding.TextUnmarshaler` interfaces
|
||||||
|
Here's an example that automatically parses values in a `mail.Address`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
contacts = [
|
||||||
|
"Donald Duck <donald@duckburg.com>",
|
||||||
|
"Scrooge McDuck <scrooge@duckburg.com>",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Can be decoded with:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create address type which satisfies the encoding.TextUnmarshaler interface.
|
||||||
|
type address struct {
|
||||||
|
*mail.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *address) UnmarshalText(text []byte) error {
|
||||||
|
var err error
|
||||||
|
a.Address, err = mail.ParseAddress(string(text))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode it.
|
||||||
|
func decode() {
|
||||||
|
blob := `
|
||||||
|
contacts = [
|
||||||
|
"Donald Duck <donald@duckburg.com>",
|
||||||
|
"Scrooge McDuck <scrooge@duckburg.com>",
|
||||||
|
]
|
||||||
|
`
|
||||||
|
|
||||||
|
var contacts struct {
|
||||||
|
Contacts []address
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := toml.Decode(blob, &contacts)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range contacts.Contacts {
|
||||||
|
fmt.Printf("%#v\n", c.Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// &mail.Address{Name:"Donald Duck", Address:"donald@duckburg.com"}
|
||||||
|
// &mail.Address{Name:"Scrooge McDuck", Address:"scrooge@duckburg.com"}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To target TOML specifically you can implement `UnmarshalTOML` TOML interface in
|
||||||
|
a similar way.
|
||||||
|
|
||||||
|
### More complex usage
|
||||||
|
See the [`_example/`](/_example) directory for a more complex example.
|
||||||
602
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
Normal file
602
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
Normal file
@@ -0,0 +1,602 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unmarshaler is the interface implemented by objects that can unmarshal a
|
||||||
|
// TOML description of themselves.
|
||||||
|
type Unmarshaler interface {
|
||||||
|
UnmarshalTOML(interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal decodes the contents of data in TOML format into a pointer v.
|
||||||
|
//
|
||||||
|
// See [Decoder] for a description of the decoding process.
|
||||||
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
|
_, err := NewDecoder(bytes.NewReader(data)).Decode(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the TOML data in to the pointer v.
|
||||||
|
//
|
||||||
|
// See [Decoder] for a description of the decoding process.
|
||||||
|
func Decode(data string, v interface{}) (MetaData, error) {
|
||||||
|
return NewDecoder(strings.NewReader(data)).Decode(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFile reads the contents of a file and decodes it with [Decode].
|
||||||
|
func DecodeFile(path string, v interface{}) (MetaData, error) {
|
||||||
|
fp, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return MetaData{}, err
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
return NewDecoder(fp).Decode(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Primitive is a TOML value that hasn't been decoded into a Go value.
|
||||||
|
//
|
||||||
|
// This type can be used for any value, which will cause decoding to be delayed.
|
||||||
|
// You can use [PrimitiveDecode] to "manually" decode these values.
|
||||||
|
//
|
||||||
|
// NOTE: The underlying representation of a `Primitive` value is subject to
|
||||||
|
// change. Do not rely on it.
|
||||||
|
//
|
||||||
|
// NOTE: Primitive values are still parsed, so using them will only avoid the
|
||||||
|
// overhead of reflection. They can be useful when you don't know the exact type
|
||||||
|
// of TOML data until runtime.
|
||||||
|
type Primitive struct {
|
||||||
|
undecoded interface{}
|
||||||
|
context Key
|
||||||
|
}
|
||||||
|
|
||||||
|
// The significand precision for float32 and float64 is 24 and 53 bits; this is
|
||||||
|
// the range a natural number can be stored in a float without loss of data.
|
||||||
|
const (
|
||||||
|
maxSafeFloat32Int = 16777215 // 2^24-1
|
||||||
|
maxSafeFloat64Int = int64(9007199254740991) // 2^53-1
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decoder decodes TOML data.
|
||||||
|
//
|
||||||
|
// TOML tables correspond to Go structs or maps; they can be used
|
||||||
|
// interchangeably, but structs offer better type safety.
|
||||||
|
//
|
||||||
|
// TOML table arrays correspond to either a slice of structs or a slice of maps.
|
||||||
|
//
|
||||||
|
// TOML datetimes correspond to [time.Time]. Local datetimes are parsed in the
|
||||||
|
// local timezone.
|
||||||
|
//
|
||||||
|
// [time.Duration] types are treated as nanoseconds if the TOML value is an
|
||||||
|
// integer, or they're parsed with time.ParseDuration() if they're strings.
|
||||||
|
//
|
||||||
|
// All other TOML types (float, string, int, bool and array) correspond to the
|
||||||
|
// obvious Go types.
|
||||||
|
//
|
||||||
|
// An exception to the above rules is if a type implements the TextUnmarshaler
|
||||||
|
// interface, in which case any primitive TOML value (floats, strings, integers,
|
||||||
|
// booleans, datetimes) will be converted to a []byte and given to the value's
|
||||||
|
// UnmarshalText method. See the Unmarshaler example for a demonstration with
|
||||||
|
// email addresses.
|
||||||
|
//
|
||||||
|
// # Key mapping
|
||||||
|
//
|
||||||
|
// TOML keys can map to either keys in a Go map or field names in a Go struct.
|
||||||
|
// The special `toml` struct tag can be used to map TOML keys to struct fields
|
||||||
|
// that don't match the key name exactly (see the example). A case insensitive
|
||||||
|
// match to struct names will be tried if an exact match can't be found.
|
||||||
|
//
|
||||||
|
// The mapping between TOML values and Go values is loose. That is, there may
|
||||||
|
// exist TOML values that cannot be placed into your representation, and there
|
||||||
|
// may be parts of your representation that do not correspond to TOML values.
|
||||||
|
// This loose mapping can be made stricter by using the IsDefined and/or
|
||||||
|
// Undecoded methods on the MetaData returned.
|
||||||
|
//
|
||||||
|
// This decoder does not handle cyclic types. Decode will not terminate if a
|
||||||
|
// cyclic type is passed.
|
||||||
|
type Decoder struct {
|
||||||
|
r io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder creates a new Decoder.
|
||||||
|
func NewDecoder(r io.Reader) *Decoder {
|
||||||
|
return &Decoder{r: r}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
unmarshalToml = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||||
|
unmarshalText = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||||
|
primitiveType = reflect.TypeOf((*Primitive)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decode TOML data in to the pointer `v`.
|
||||||
|
func (dec *Decoder) Decode(v interface{}) (MetaData, error) {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.Kind() != reflect.Ptr {
|
||||||
|
s := "%q"
|
||||||
|
if reflect.TypeOf(v) == nil {
|
||||||
|
s = "%v"
|
||||||
|
}
|
||||||
|
|
||||||
|
return MetaData{}, fmt.Errorf("toml: cannot decode to non-pointer "+s, reflect.TypeOf(v))
|
||||||
|
}
|
||||||
|
if rv.IsNil() {
|
||||||
|
return MetaData{}, fmt.Errorf("toml: cannot decode to nil value of %q", reflect.TypeOf(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a supported type: struct, map, interface{}, or something
|
||||||
|
// that implements UnmarshalTOML or UnmarshalText.
|
||||||
|
rv = indirect(rv)
|
||||||
|
rt := rv.Type()
|
||||||
|
if rv.Kind() != reflect.Struct && rv.Kind() != reflect.Map &&
|
||||||
|
!(rv.Kind() == reflect.Interface && rv.NumMethod() == 0) &&
|
||||||
|
!rt.Implements(unmarshalToml) && !rt.Implements(unmarshalText) {
|
||||||
|
return MetaData{}, fmt.Errorf("toml: cannot decode to type %s", rt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: parser should read from io.Reader? Or at the very least, make it
|
||||||
|
// read from []byte rather than string
|
||||||
|
data, err := ioutil.ReadAll(dec.r)
|
||||||
|
if err != nil {
|
||||||
|
return MetaData{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := parse(string(data))
|
||||||
|
if err != nil {
|
||||||
|
return MetaData{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
md := MetaData{
|
||||||
|
mapping: p.mapping,
|
||||||
|
keyInfo: p.keyInfo,
|
||||||
|
keys: p.ordered,
|
||||||
|
decoded: make(map[string]struct{}, len(p.ordered)),
|
||||||
|
context: nil,
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
return md, md.unify(p.mapping, rv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrimitiveDecode is just like the other Decode* functions, except it decodes a
|
||||||
|
// TOML value that has already been parsed. Valid primitive values can *only* be
|
||||||
|
// obtained from values filled by the decoder functions, including this method.
|
||||||
|
// (i.e., v may contain more [Primitive] values.)
|
||||||
|
//
|
||||||
|
// Meta data for primitive values is included in the meta data returned by the
|
||||||
|
// Decode* functions with one exception: keys returned by the Undecoded method
|
||||||
|
// will only reflect keys that were decoded. Namely, any keys hidden behind a
|
||||||
|
// Primitive will be considered undecoded. Executing this method will update the
|
||||||
|
// undecoded keys in the meta data. (See the example.)
|
||||||
|
func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
|
||||||
|
md.context = primValue.context
|
||||||
|
defer func() { md.context = nil }()
|
||||||
|
return md.unify(primValue.undecoded, rvalue(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// unify performs a sort of type unification based on the structure of `rv`,
|
||||||
|
// which is the client representation.
|
||||||
|
//
|
||||||
|
// Any type mismatch produces an error. Finding a type that we don't know
|
||||||
|
// how to handle produces an unsupported type error.
|
||||||
|
func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
|
||||||
|
// Special case. Look for a `Primitive` value.
|
||||||
|
// TODO: #76 would make this superfluous after implemented.
|
||||||
|
if rv.Type() == primitiveType {
|
||||||
|
// Save the undecoded data and the key context into the primitive
|
||||||
|
// value.
|
||||||
|
context := make(Key, len(md.context))
|
||||||
|
copy(context, md.context)
|
||||||
|
rv.Set(reflect.ValueOf(Primitive{
|
||||||
|
undecoded: data,
|
||||||
|
context: context,
|
||||||
|
}))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rvi := rv.Interface()
|
||||||
|
if v, ok := rvi.(Unmarshaler); ok {
|
||||||
|
return v.UnmarshalTOML(data)
|
||||||
|
}
|
||||||
|
if v, ok := rvi.(encoding.TextUnmarshaler); ok {
|
||||||
|
return md.unifyText(data, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// The behavior here is incorrect whenever a Go type satisfies the
|
||||||
|
// encoding.TextUnmarshaler interface but also corresponds to a TOML hash or
|
||||||
|
// array. In particular, the unmarshaler should only be applied to primitive
|
||||||
|
// TOML values. But at this point, it will be applied to all kinds of values
|
||||||
|
// and produce an incorrect error whenever those values are hashes or arrays
|
||||||
|
// (including arrays of tables).
|
||||||
|
|
||||||
|
k := rv.Kind()
|
||||||
|
|
||||||
|
if k >= reflect.Int && k <= reflect.Uint64 {
|
||||||
|
return md.unifyInt(data, rv)
|
||||||
|
}
|
||||||
|
switch k {
|
||||||
|
case reflect.Ptr:
|
||||||
|
elem := reflect.New(rv.Type().Elem())
|
||||||
|
err := md.unify(data, reflect.Indirect(elem))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rv.Set(elem)
|
||||||
|
return nil
|
||||||
|
case reflect.Struct:
|
||||||
|
return md.unifyStruct(data, rv)
|
||||||
|
case reflect.Map:
|
||||||
|
return md.unifyMap(data, rv)
|
||||||
|
case reflect.Array:
|
||||||
|
return md.unifyArray(data, rv)
|
||||||
|
case reflect.Slice:
|
||||||
|
return md.unifySlice(data, rv)
|
||||||
|
case reflect.String:
|
||||||
|
return md.unifyString(data, rv)
|
||||||
|
case reflect.Bool:
|
||||||
|
return md.unifyBool(data, rv)
|
||||||
|
case reflect.Interface:
|
||||||
|
if rv.NumMethod() > 0 { /// Only empty interfaces are supported.
|
||||||
|
return md.e("unsupported type %s", rv.Type())
|
||||||
|
}
|
||||||
|
return md.unifyAnything(data, rv)
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return md.unifyFloat64(data, rv)
|
||||||
|
}
|
||||||
|
return md.e("unsupported type %s", rv.Kind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
|
||||||
|
tmap, ok := mapping.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
if mapping == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return md.e("type mismatch for %s: expected table but found %T",
|
||||||
|
rv.Type().String(), mapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, datum := range tmap {
|
||||||
|
var f *field
|
||||||
|
fields := cachedTypeFields(rv.Type())
|
||||||
|
for i := range fields {
|
||||||
|
ff := &fields[i]
|
||||||
|
if ff.name == key {
|
||||||
|
f = ff
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if f == nil && strings.EqualFold(ff.name, key) {
|
||||||
|
f = ff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f != nil {
|
||||||
|
subv := rv
|
||||||
|
for _, i := range f.index {
|
||||||
|
subv = indirect(subv.Field(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
if isUnifiable(subv) {
|
||||||
|
md.decoded[md.context.add(key).String()] = struct{}{}
|
||||||
|
md.context = append(md.context, key)
|
||||||
|
|
||||||
|
err := md.unify(datum, subv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
md.context = md.context[0 : len(md.context)-1]
|
||||||
|
} else if f.name != "" {
|
||||||
|
return md.e("cannot write unexported field %s.%s", rv.Type().String(), f.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
|
||||||
|
keyType := rv.Type().Key().Kind()
|
||||||
|
if keyType != reflect.String && keyType != reflect.Interface {
|
||||||
|
return fmt.Errorf("toml: cannot decode to a map with non-string key type (%s in %q)",
|
||||||
|
keyType, rv.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
tmap, ok := mapping.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
if tmap == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return md.badtype("map", mapping)
|
||||||
|
}
|
||||||
|
if rv.IsNil() {
|
||||||
|
rv.Set(reflect.MakeMap(rv.Type()))
|
||||||
|
}
|
||||||
|
for k, v := range tmap {
|
||||||
|
md.decoded[md.context.add(k).String()] = struct{}{}
|
||||||
|
md.context = append(md.context, k)
|
||||||
|
|
||||||
|
rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
|
||||||
|
|
||||||
|
err := md.unify(v, indirect(rvval))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
md.context = md.context[0 : len(md.context)-1]
|
||||||
|
|
||||||
|
rvkey := indirect(reflect.New(rv.Type().Key()))
|
||||||
|
|
||||||
|
switch keyType {
|
||||||
|
case reflect.Interface:
|
||||||
|
rvkey.Set(reflect.ValueOf(k))
|
||||||
|
case reflect.String:
|
||||||
|
rvkey.SetString(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
rv.SetMapIndex(rvkey, rvval)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
|
||||||
|
datav := reflect.ValueOf(data)
|
||||||
|
if datav.Kind() != reflect.Slice {
|
||||||
|
if !datav.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return md.badtype("slice", data)
|
||||||
|
}
|
||||||
|
if l := datav.Len(); l != rv.Len() {
|
||||||
|
return md.e("expected array length %d; got TOML array of length %d", rv.Len(), l)
|
||||||
|
}
|
||||||
|
return md.unifySliceArray(datav, rv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error {
|
||||||
|
datav := reflect.ValueOf(data)
|
||||||
|
if datav.Kind() != reflect.Slice {
|
||||||
|
if !datav.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return md.badtype("slice", data)
|
||||||
|
}
|
||||||
|
n := datav.Len()
|
||||||
|
if rv.IsNil() || rv.Cap() < n {
|
||||||
|
rv.Set(reflect.MakeSlice(rv.Type(), n, n))
|
||||||
|
}
|
||||||
|
rv.SetLen(n)
|
||||||
|
return md.unifySliceArray(datav, rv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
|
||||||
|
l := data.Len()
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
err := md.unify(data.Index(i).Interface(), indirect(rv.Index(i)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
|
||||||
|
_, ok := rv.Interface().(json.Number)
|
||||||
|
if ok {
|
||||||
|
if i, ok := data.(int64); ok {
|
||||||
|
rv.SetString(strconv.FormatInt(i, 10))
|
||||||
|
} else if f, ok := data.(float64); ok {
|
||||||
|
rv.SetString(strconv.FormatFloat(f, 'f', -1, 64))
|
||||||
|
} else {
|
||||||
|
return md.badtype("string", data)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if s, ok := data.(string); ok {
|
||||||
|
rv.SetString(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return md.badtype("string", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
|
||||||
|
rvk := rv.Kind()
|
||||||
|
|
||||||
|
if num, ok := data.(float64); ok {
|
||||||
|
switch rvk {
|
||||||
|
case reflect.Float32:
|
||||||
|
if num < -math.MaxFloat32 || num > math.MaxFloat32 {
|
||||||
|
return md.parseErr(errParseRange{i: num, size: rvk.String()})
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case reflect.Float64:
|
||||||
|
rv.SetFloat(num)
|
||||||
|
default:
|
||||||
|
panic("bug")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if num, ok := data.(int64); ok {
|
||||||
|
if (rvk == reflect.Float32 && (num < -maxSafeFloat32Int || num > maxSafeFloat32Int)) ||
|
||||||
|
(rvk == reflect.Float64 && (num < -maxSafeFloat64Int || num > maxSafeFloat64Int)) {
|
||||||
|
return md.parseErr(errParseRange{i: num, size: rvk.String()})
|
||||||
|
}
|
||||||
|
rv.SetFloat(float64(num))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return md.badtype("float", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
|
||||||
|
_, ok := rv.Interface().(time.Duration)
|
||||||
|
if ok {
|
||||||
|
// Parse as string duration, and fall back to regular integer parsing
|
||||||
|
// (as nanosecond) if this is not a string.
|
||||||
|
if s, ok := data.(string); ok {
|
||||||
|
dur, err := time.ParseDuration(s)
|
||||||
|
if err != nil {
|
||||||
|
return md.parseErr(errParseDuration{s})
|
||||||
|
}
|
||||||
|
rv.SetInt(int64(dur))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
num, ok := data.(int64)
|
||||||
|
if !ok {
|
||||||
|
return md.badtype("integer", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
rvk := rv.Kind()
|
||||||
|
switch {
|
||||||
|
case rvk >= reflect.Int && rvk <= reflect.Int64:
|
||||||
|
if (rvk == reflect.Int8 && (num < math.MinInt8 || num > math.MaxInt8)) ||
|
||||||
|
(rvk == reflect.Int16 && (num < math.MinInt16 || num > math.MaxInt16)) ||
|
||||||
|
(rvk == reflect.Int32 && (num < math.MinInt32 || num > math.MaxInt32)) {
|
||||||
|
return md.parseErr(errParseRange{i: num, size: rvk.String()})
|
||||||
|
}
|
||||||
|
rv.SetInt(num)
|
||||||
|
case rvk >= reflect.Uint && rvk <= reflect.Uint64:
|
||||||
|
unum := uint64(num)
|
||||||
|
if rvk == reflect.Uint8 && (num < 0 || unum > math.MaxUint8) ||
|
||||||
|
rvk == reflect.Uint16 && (num < 0 || unum > math.MaxUint16) ||
|
||||||
|
rvk == reflect.Uint32 && (num < 0 || unum > math.MaxUint32) {
|
||||||
|
return md.parseErr(errParseRange{i: num, size: rvk.String()})
|
||||||
|
}
|
||||||
|
rv.SetUint(unum)
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
|
||||||
|
if b, ok := data.(bool); ok {
|
||||||
|
rv.SetBool(b)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return md.badtype("boolean", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
|
||||||
|
rv.Set(reflect.ValueOf(data))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) error {
|
||||||
|
var s string
|
||||||
|
switch sdata := data.(type) {
|
||||||
|
case Marshaler:
|
||||||
|
text, err := sdata.MarshalTOML()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s = string(text)
|
||||||
|
case encoding.TextMarshaler:
|
||||||
|
text, err := sdata.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s = string(text)
|
||||||
|
case fmt.Stringer:
|
||||||
|
s = sdata.String()
|
||||||
|
case string:
|
||||||
|
s = sdata
|
||||||
|
case bool:
|
||||||
|
s = fmt.Sprintf("%v", sdata)
|
||||||
|
case int64:
|
||||||
|
s = fmt.Sprintf("%d", sdata)
|
||||||
|
case float64:
|
||||||
|
s = fmt.Sprintf("%f", sdata)
|
||||||
|
default:
|
||||||
|
return md.badtype("primitive (string-like)", data)
|
||||||
|
}
|
||||||
|
if err := v.UnmarshalText([]byte(s)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) badtype(dst string, data interface{}) error {
|
||||||
|
return md.e("incompatible types: TOML value has type %T; destination has type %s", data, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) parseErr(err error) error {
|
||||||
|
k := md.context.String()
|
||||||
|
return ParseError{
|
||||||
|
LastKey: k,
|
||||||
|
Position: md.keyInfo[k].pos,
|
||||||
|
Line: md.keyInfo[k].pos.Line,
|
||||||
|
err: err,
|
||||||
|
input: string(md.data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (md *MetaData) e(format string, args ...interface{}) error {
|
||||||
|
f := "toml: "
|
||||||
|
if len(md.context) > 0 {
|
||||||
|
f = fmt.Sprintf("toml: (last key %q): ", md.context)
|
||||||
|
p := md.keyInfo[md.context.String()].pos
|
||||||
|
if p.Line > 0 {
|
||||||
|
f = fmt.Sprintf("toml: line %d (last key %q): ", p.Line, md.context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf(f+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rvalue returns a reflect.Value of `v`. All pointers are resolved.
|
||||||
|
func rvalue(v interface{}) reflect.Value {
|
||||||
|
return indirect(reflect.ValueOf(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// indirect returns the value pointed to by a pointer.
|
||||||
|
//
|
||||||
|
// Pointers are followed until the value is not a pointer. New values are
|
||||||
|
// allocated for each nil pointer.
|
||||||
|
//
|
||||||
|
// An exception to this rule is if the value satisfies an interface of interest
|
||||||
|
// to us (like encoding.TextUnmarshaler).
|
||||||
|
func indirect(v reflect.Value) reflect.Value {
|
||||||
|
if v.Kind() != reflect.Ptr {
|
||||||
|
if v.CanSet() {
|
||||||
|
pv := v.Addr()
|
||||||
|
pvi := pv.Interface()
|
||||||
|
if _, ok := pvi.(encoding.TextUnmarshaler); ok {
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
if _, ok := pvi.(Unmarshaler); ok {
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
if v.IsNil() {
|
||||||
|
v.Set(reflect.New(v.Type().Elem()))
|
||||||
|
}
|
||||||
|
return indirect(reflect.Indirect(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUnifiable(rv reflect.Value) bool {
|
||||||
|
if rv.CanSet() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
rvi := rv.Interface()
|
||||||
|
if _, ok := rvi.(encoding.TextUnmarshaler); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := rvi.(Unmarshaler); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
19
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/decode_go116.go
generated
vendored
Normal file
19
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/decode_go116.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//go:build go1.16
|
||||||
|
// +build go1.16
|
||||||
|
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecodeFS reads the contents of a file from [fs.FS] and decodes it with
|
||||||
|
// [Decode].
|
||||||
|
func DecodeFS(fsys fs.FS, path string, v interface{}) (MetaData, error) {
|
||||||
|
fp, err := fsys.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return MetaData{}, err
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
return NewDecoder(fp).Decode(v)
|
||||||
|
}
|
||||||
29
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/deprecated.go
generated
vendored
Normal file
29
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/deprecated.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TextMarshaler is an alias for encoding.TextMarshaler.
|
||||||
|
//
|
||||||
|
// Deprecated: use encoding.TextMarshaler
|
||||||
|
type TextMarshaler encoding.TextMarshaler
|
||||||
|
|
||||||
|
// TextUnmarshaler is an alias for encoding.TextUnmarshaler.
|
||||||
|
//
|
||||||
|
// Deprecated: use encoding.TextUnmarshaler
|
||||||
|
type TextUnmarshaler encoding.TextUnmarshaler
|
||||||
|
|
||||||
|
// PrimitiveDecode is an alias for MetaData.PrimitiveDecode().
|
||||||
|
//
|
||||||
|
// Deprecated: use MetaData.PrimitiveDecode.
|
||||||
|
func PrimitiveDecode(primValue Primitive, v interface{}) error {
|
||||||
|
md := MetaData{decoded: make(map[string]struct{})}
|
||||||
|
return md.unify(primValue.undecoded, rvalue(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeReader is an alias for NewDecoder(r).Decode(v).
|
||||||
|
//
|
||||||
|
// Deprecated: use NewDecoder(reader).Decode(&value).
|
||||||
|
func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { return NewDecoder(r).Decode(v) }
|
||||||
11
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
Normal file
11
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Package toml implements decoding and encoding of TOML files.
|
||||||
|
//
|
||||||
|
// This package supports TOML v1.0.0, as specified at https://toml.io
|
||||||
|
//
|
||||||
|
// There is also support for delaying decoding with the Primitive type, and
|
||||||
|
// querying the set of keys in a TOML document with the MetaData type.
|
||||||
|
//
|
||||||
|
// The github.com/BurntSushi/toml/cmd/tomlv package implements a TOML validator,
|
||||||
|
// and can be used to verify if TOML document is valid. It can also be used to
|
||||||
|
// print the type of each key.
|
||||||
|
package toml
|
||||||
759
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
Normal file
759
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
Normal file
@@ -0,0 +1,759 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tomlEncodeError struct{ error }
|
||||||
|
|
||||||
|
var (
|
||||||
|
errArrayNilElement = errors.New("toml: cannot encode array with nil element")
|
||||||
|
errNonString = errors.New("toml: cannot encode a map with non-string key type")
|
||||||
|
errNoKey = errors.New("toml: top-level values must be Go maps or structs")
|
||||||
|
errAnything = errors.New("") // used in testing
|
||||||
|
)
|
||||||
|
|
||||||
|
var dblQuotedReplacer = strings.NewReplacer(
|
||||||
|
"\"", "\\\"",
|
||||||
|
"\\", "\\\\",
|
||||||
|
"\x00", `\u0000`,
|
||||||
|
"\x01", `\u0001`,
|
||||||
|
"\x02", `\u0002`,
|
||||||
|
"\x03", `\u0003`,
|
||||||
|
"\x04", `\u0004`,
|
||||||
|
"\x05", `\u0005`,
|
||||||
|
"\x06", `\u0006`,
|
||||||
|
"\x07", `\u0007`,
|
||||||
|
"\b", `\b`,
|
||||||
|
"\t", `\t`,
|
||||||
|
"\n", `\n`,
|
||||||
|
"\x0b", `\u000b`,
|
||||||
|
"\f", `\f`,
|
||||||
|
"\r", `\r`,
|
||||||
|
"\x0e", `\u000e`,
|
||||||
|
"\x0f", `\u000f`,
|
||||||
|
"\x10", `\u0010`,
|
||||||
|
"\x11", `\u0011`,
|
||||||
|
"\x12", `\u0012`,
|
||||||
|
"\x13", `\u0013`,
|
||||||
|
"\x14", `\u0014`,
|
||||||
|
"\x15", `\u0015`,
|
||||||
|
"\x16", `\u0016`,
|
||||||
|
"\x17", `\u0017`,
|
||||||
|
"\x18", `\u0018`,
|
||||||
|
"\x19", `\u0019`,
|
||||||
|
"\x1a", `\u001a`,
|
||||||
|
"\x1b", `\u001b`,
|
||||||
|
"\x1c", `\u001c`,
|
||||||
|
"\x1d", `\u001d`,
|
||||||
|
"\x1e", `\u001e`,
|
||||||
|
"\x1f", `\u001f`,
|
||||||
|
"\x7f", `\u007f`,
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
marshalToml = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||||
|
marshalText = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||||
|
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Marshaler is the interface implemented by types that can marshal themselves
|
||||||
|
// into valid TOML.
|
||||||
|
type Marshaler interface {
|
||||||
|
MarshalTOML() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoder encodes a Go to a TOML document.
|
||||||
|
//
|
||||||
|
// The mapping between Go values and TOML values should be precisely the same as
|
||||||
|
// for [Decode].
|
||||||
|
//
|
||||||
|
// time.Time is encoded as a RFC 3339 string, and time.Duration as its string
|
||||||
|
// representation.
|
||||||
|
//
|
||||||
|
// The [Marshaler] and [encoding.TextMarshaler] interfaces are supported to
|
||||||
|
// encoding the value as custom TOML.
|
||||||
|
//
|
||||||
|
// If you want to write arbitrary binary data then you will need to use
|
||||||
|
// something like base64 since TOML does not have any binary types.
|
||||||
|
//
|
||||||
|
// When encoding TOML hashes (Go maps or structs), keys without any sub-hashes
|
||||||
|
// are encoded first.
|
||||||
|
//
|
||||||
|
// Go maps will be sorted alphabetically by key for deterministic output.
|
||||||
|
//
|
||||||
|
// The toml struct tag can be used to provide the key name; if omitted the
|
||||||
|
// struct field name will be used. If the "omitempty" option is present the
|
||||||
|
// following value will be skipped:
|
||||||
|
//
|
||||||
|
// - arrays, slices, maps, and string with len of 0
|
||||||
|
// - struct with all zero values
|
||||||
|
// - bool false
|
||||||
|
//
|
||||||
|
// If omitzero is given all int and float types with a value of 0 will be
|
||||||
|
// skipped.
|
||||||
|
//
|
||||||
|
// Encoding Go values without a corresponding TOML representation will return an
|
||||||
|
// error. Examples of this includes maps with non-string keys, slices with nil
|
||||||
|
// elements, embedded non-struct types, and nested slices containing maps or
|
||||||
|
// structs. (e.g. [][]map[string]string is not allowed but []map[string]string
|
||||||
|
// is okay, as is []map[string][]string).
|
||||||
|
//
|
||||||
|
// NOTE: only exported keys are encoded due to the use of reflection. Unexported
|
||||||
|
// keys are silently discarded.
|
||||||
|
type Encoder struct {
|
||||||
|
// String to use for a single indentation level; default is two spaces.
|
||||||
|
Indent string
|
||||||
|
|
||||||
|
w *bufio.Writer
|
||||||
|
hasWritten bool // written any output to w yet?
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder create a new Encoder.
|
||||||
|
func NewEncoder(w io.Writer) *Encoder {
|
||||||
|
return &Encoder{
|
||||||
|
w: bufio.NewWriter(w),
|
||||||
|
Indent: " ",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode writes a TOML representation of the Go value to the [Encoder]'s writer.
|
||||||
|
//
|
||||||
|
// An error is returned if the value given cannot be encoded to a valid TOML
|
||||||
|
// document.
|
||||||
|
func (enc *Encoder) Encode(v interface{}) error {
|
||||||
|
rv := eindirect(reflect.ValueOf(v))
|
||||||
|
err := enc.safeEncode(Key([]string{}), rv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return enc.w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if terr, ok := r.(tomlEncodeError); ok {
|
||||||
|
err = terr.error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
enc.encode(key, rv)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) encode(key Key, rv reflect.Value) {
|
||||||
|
// If we can marshal the type to text, then we use that. This prevents the
|
||||||
|
// encoder for handling these types as generic structs (or whatever the
|
||||||
|
// underlying type of a TextMarshaler is).
|
||||||
|
switch {
|
||||||
|
case isMarshaler(rv):
|
||||||
|
enc.writeKeyValue(key, rv, false)
|
||||||
|
return
|
||||||
|
case rv.Type() == primitiveType: // TODO: #76 would make this superfluous after implemented.
|
||||||
|
enc.encode(key, reflect.ValueOf(rv.Interface().(Primitive).undecoded))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
k := rv.Kind()
|
||||||
|
switch k {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
||||||
|
reflect.Int64,
|
||||||
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
||||||
|
reflect.Uint64,
|
||||||
|
reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
|
||||||
|
enc.writeKeyValue(key, rv, false)
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
|
||||||
|
enc.eArrayOfTables(key, rv)
|
||||||
|
} else {
|
||||||
|
enc.writeKeyValue(key, rv, false)
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
if rv.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enc.encode(key, rv.Elem())
|
||||||
|
case reflect.Map:
|
||||||
|
if rv.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enc.eTable(key, rv)
|
||||||
|
case reflect.Ptr:
|
||||||
|
if rv.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enc.encode(key, rv.Elem())
|
||||||
|
case reflect.Struct:
|
||||||
|
enc.eTable(key, rv)
|
||||||
|
default:
|
||||||
|
encPanic(fmt.Errorf("unsupported type for key '%s': %s", key, k))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eElement encodes any value that can be an array element.
|
||||||
|
func (enc *Encoder) eElement(rv reflect.Value) {
|
||||||
|
switch v := rv.Interface().(type) {
|
||||||
|
case time.Time: // Using TextMarshaler adds extra quotes, which we don't want.
|
||||||
|
format := time.RFC3339Nano
|
||||||
|
switch v.Location() {
|
||||||
|
case internal.LocalDatetime:
|
||||||
|
format = "2006-01-02T15:04:05.999999999"
|
||||||
|
case internal.LocalDate:
|
||||||
|
format = "2006-01-02"
|
||||||
|
case internal.LocalTime:
|
||||||
|
format = "15:04:05.999999999"
|
||||||
|
}
|
||||||
|
switch v.Location() {
|
||||||
|
default:
|
||||||
|
enc.wf(v.Format(format))
|
||||||
|
case internal.LocalDatetime, internal.LocalDate, internal.LocalTime:
|
||||||
|
enc.wf(v.In(time.UTC).Format(format))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case Marshaler:
|
||||||
|
s, err := v.MarshalTOML()
|
||||||
|
if err != nil {
|
||||||
|
encPanic(err)
|
||||||
|
}
|
||||||
|
if s == nil {
|
||||||
|
encPanic(errors.New("MarshalTOML returned nil and no error"))
|
||||||
|
}
|
||||||
|
enc.w.Write(s)
|
||||||
|
return
|
||||||
|
case encoding.TextMarshaler:
|
||||||
|
s, err := v.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
encPanic(err)
|
||||||
|
}
|
||||||
|
if s == nil {
|
||||||
|
encPanic(errors.New("MarshalText returned nil and no error"))
|
||||||
|
}
|
||||||
|
enc.writeQuoted(string(s))
|
||||||
|
return
|
||||||
|
case time.Duration:
|
||||||
|
enc.writeQuoted(v.String())
|
||||||
|
return
|
||||||
|
case json.Number:
|
||||||
|
n, _ := rv.Interface().(json.Number)
|
||||||
|
|
||||||
|
if n == "" { /// Useful zero value.
|
||||||
|
enc.w.WriteByte('0')
|
||||||
|
return
|
||||||
|
} else if v, err := n.Int64(); err == nil {
|
||||||
|
enc.eElement(reflect.ValueOf(v))
|
||||||
|
return
|
||||||
|
} else if v, err := n.Float64(); err == nil {
|
||||||
|
enc.eElement(reflect.ValueOf(v))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
encPanic(fmt.Errorf("unable to convert %q to int64 or float64", n))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
enc.eElement(rv.Elem())
|
||||||
|
return
|
||||||
|
case reflect.String:
|
||||||
|
enc.writeQuoted(rv.String())
|
||||||
|
case reflect.Bool:
|
||||||
|
enc.wf(strconv.FormatBool(rv.Bool()))
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
enc.wf(strconv.FormatInt(rv.Int(), 10))
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
enc.wf(strconv.FormatUint(rv.Uint(), 10))
|
||||||
|
case reflect.Float32:
|
||||||
|
f := rv.Float()
|
||||||
|
if math.IsNaN(f) {
|
||||||
|
enc.wf("nan")
|
||||||
|
} else if math.IsInf(f, 0) {
|
||||||
|
enc.wf("%cinf", map[bool]byte{true: '-', false: '+'}[math.Signbit(f)])
|
||||||
|
} else {
|
||||||
|
enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 32)))
|
||||||
|
}
|
||||||
|
case reflect.Float64:
|
||||||
|
f := rv.Float()
|
||||||
|
if math.IsNaN(f) {
|
||||||
|
enc.wf("nan")
|
||||||
|
} else if math.IsInf(f, 0) {
|
||||||
|
enc.wf("%cinf", map[bool]byte{true: '-', false: '+'}[math.Signbit(f)])
|
||||||
|
} else {
|
||||||
|
enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 64)))
|
||||||
|
}
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
enc.eArrayOrSliceElement(rv)
|
||||||
|
case reflect.Struct:
|
||||||
|
enc.eStruct(nil, rv, true)
|
||||||
|
case reflect.Map:
|
||||||
|
enc.eMap(nil, rv, true)
|
||||||
|
case reflect.Interface:
|
||||||
|
enc.eElement(rv.Elem())
|
||||||
|
default:
|
||||||
|
encPanic(fmt.Errorf("unexpected type: %T", rv.Interface()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// By the TOML spec, all floats must have a decimal with at least one number on
|
||||||
|
// either side.
|
||||||
|
func floatAddDecimal(fstr string) string {
|
||||||
|
if !strings.Contains(fstr, ".") {
|
||||||
|
return fstr + ".0"
|
||||||
|
}
|
||||||
|
return fstr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) writeQuoted(s string) {
|
||||||
|
enc.wf("\"%s\"", dblQuotedReplacer.Replace(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
|
||||||
|
length := rv.Len()
|
||||||
|
enc.wf("[")
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
elem := eindirect(rv.Index(i))
|
||||||
|
enc.eElement(elem)
|
||||||
|
if i != length-1 {
|
||||||
|
enc.wf(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enc.wf("]")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
||||||
|
if len(key) == 0 {
|
||||||
|
encPanic(errNoKey)
|
||||||
|
}
|
||||||
|
for i := 0; i < rv.Len(); i++ {
|
||||||
|
trv := eindirect(rv.Index(i))
|
||||||
|
if isNil(trv) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
enc.newline()
|
||||||
|
enc.wf("%s[[%s]]", enc.indentStr(key), key)
|
||||||
|
enc.newline()
|
||||||
|
enc.eMapOrStruct(key, trv, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) eTable(key Key, rv reflect.Value) {
|
||||||
|
if len(key) == 1 {
|
||||||
|
// Output an extra newline between top-level tables.
|
||||||
|
// (The newline isn't written if nothing else has been written though.)
|
||||||
|
enc.newline()
|
||||||
|
}
|
||||||
|
if len(key) > 0 {
|
||||||
|
enc.wf("%s[%s]", enc.indentStr(key), key)
|
||||||
|
enc.newline()
|
||||||
|
}
|
||||||
|
enc.eMapOrStruct(key, rv, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value, inline bool) {
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Map:
|
||||||
|
enc.eMap(key, rv, inline)
|
||||||
|
case reflect.Struct:
|
||||||
|
enc.eStruct(key, rv, inline)
|
||||||
|
default:
|
||||||
|
// Should never happen?
|
||||||
|
panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
|
||||||
|
rt := rv.Type()
|
||||||
|
if rt.Key().Kind() != reflect.String {
|
||||||
|
encPanic(errNonString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort keys so that we have deterministic output. And write keys directly
|
||||||
|
// underneath this key first, before writing sub-structs or sub-maps.
|
||||||
|
var mapKeysDirect, mapKeysSub []string
|
||||||
|
for _, mapKey := range rv.MapKeys() {
|
||||||
|
k := mapKey.String()
|
||||||
|
if typeIsTable(tomlTypeOfGo(eindirect(rv.MapIndex(mapKey)))) {
|
||||||
|
mapKeysSub = append(mapKeysSub, k)
|
||||||
|
} else {
|
||||||
|
mapKeysDirect = append(mapKeysDirect, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var writeMapKeys = func(mapKeys []string, trailC bool) {
|
||||||
|
sort.Strings(mapKeys)
|
||||||
|
for i, mapKey := range mapKeys {
|
||||||
|
val := eindirect(rv.MapIndex(reflect.ValueOf(mapKey)))
|
||||||
|
if isNil(val) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if inline {
|
||||||
|
enc.writeKeyValue(Key{mapKey}, val, true)
|
||||||
|
if trailC || i != len(mapKeys)-1 {
|
||||||
|
enc.wf(", ")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
enc.encode(key.add(mapKey), val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inline {
|
||||||
|
enc.wf("{")
|
||||||
|
}
|
||||||
|
writeMapKeys(mapKeysDirect, len(mapKeysSub) > 0)
|
||||||
|
writeMapKeys(mapKeysSub, false)
|
||||||
|
if inline {
|
||||||
|
enc.wf("}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const is32Bit = (32 << (^uint(0) >> 63)) == 32
|
||||||
|
|
||||||
|
func pointerTo(t reflect.Type) reflect.Type {
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
return pointerTo(t.Elem())
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
|
||||||
|
// Write keys for fields directly under this key first, because if we write
|
||||||
|
// a field that creates a new table then all keys under it will be in that
|
||||||
|
// table (not the one we're writing here).
|
||||||
|
//
|
||||||
|
// Fields is a [][]int: for fieldsDirect this always has one entry (the
|
||||||
|
// struct index). For fieldsSub it contains two entries: the parent field
|
||||||
|
// index from tv, and the field indexes for the fields of the sub.
|
||||||
|
var (
|
||||||
|
rt = rv.Type()
|
||||||
|
fieldsDirect, fieldsSub [][]int
|
||||||
|
addFields func(rt reflect.Type, rv reflect.Value, start []int)
|
||||||
|
)
|
||||||
|
addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
|
||||||
|
for i := 0; i < rt.NumField(); i++ {
|
||||||
|
f := rt.Field(i)
|
||||||
|
isEmbed := f.Anonymous && pointerTo(f.Type).Kind() == reflect.Struct
|
||||||
|
if f.PkgPath != "" && !isEmbed { /// Skip unexported fields.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
opts := getOptions(f.Tag)
|
||||||
|
if opts.skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
frv := eindirect(rv.Field(i))
|
||||||
|
|
||||||
|
if is32Bit {
|
||||||
|
// Copy so it works correct on 32bit archs; not clear why this
|
||||||
|
// is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4
|
||||||
|
// This also works fine on 64bit, but 32bit archs are somewhat
|
||||||
|
// rare and this is a wee bit faster.
|
||||||
|
copyStart := make([]int, len(start))
|
||||||
|
copy(copyStart, start)
|
||||||
|
start = copyStart
|
||||||
|
}
|
||||||
|
|
||||||
|
// Treat anonymous struct fields with tag names as though they are
|
||||||
|
// not anonymous, like encoding/json does.
|
||||||
|
//
|
||||||
|
// Non-struct anonymous fields use the normal encoding logic.
|
||||||
|
if isEmbed {
|
||||||
|
if getOptions(f.Tag).name == "" && frv.Kind() == reflect.Struct {
|
||||||
|
addFields(frv.Type(), frv, append(start, f.Index...))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if typeIsTable(tomlTypeOfGo(frv)) {
|
||||||
|
fieldsSub = append(fieldsSub, append(start, f.Index...))
|
||||||
|
} else {
|
||||||
|
fieldsDirect = append(fieldsDirect, append(start, f.Index...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addFields(rt, rv, nil)
|
||||||
|
|
||||||
|
writeFields := func(fields [][]int) {
|
||||||
|
for _, fieldIndex := range fields {
|
||||||
|
fieldType := rt.FieldByIndex(fieldIndex)
|
||||||
|
fieldVal := rv.FieldByIndex(fieldIndex)
|
||||||
|
|
||||||
|
opts := getOptions(fieldType.Tag)
|
||||||
|
if opts.skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if opts.omitempty && isEmpty(fieldVal) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldVal = eindirect(fieldVal)
|
||||||
|
|
||||||
|
if isNil(fieldVal) { /// Don't write anything for nil fields.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
keyName := fieldType.Name
|
||||||
|
if opts.name != "" {
|
||||||
|
keyName = opts.name
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.omitzero && isZero(fieldVal) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if inline {
|
||||||
|
enc.writeKeyValue(Key{keyName}, fieldVal, true)
|
||||||
|
if fieldIndex[0] != len(fields)-1 {
|
||||||
|
enc.wf(", ")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
enc.encode(key.add(keyName), fieldVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inline {
|
||||||
|
enc.wf("{")
|
||||||
|
}
|
||||||
|
writeFields(fieldsDirect)
|
||||||
|
writeFields(fieldsSub)
|
||||||
|
if inline {
|
||||||
|
enc.wf("}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tomlTypeOfGo returns the TOML type name of the Go value's type.
|
||||||
|
//
|
||||||
|
// It is used to determine whether the types of array elements are mixed (which
|
||||||
|
// is forbidden). If the Go value is nil, then it is illegal for it to be an
|
||||||
|
// array element, and valueIsNil is returned as true.
|
||||||
|
//
|
||||||
|
// The type may be `nil`, which means no concrete TOML type could be found.
|
||||||
|
func tomlTypeOfGo(rv reflect.Value) tomlType {
|
||||||
|
if isNil(rv) || !rv.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if rv.Kind() == reflect.Struct {
|
||||||
|
if rv.Type() == timeType {
|
||||||
|
return tomlDatetime
|
||||||
|
}
|
||||||
|
if isMarshaler(rv) {
|
||||||
|
return tomlString
|
||||||
|
}
|
||||||
|
return tomlHash
|
||||||
|
}
|
||||||
|
|
||||||
|
if isMarshaler(rv) {
|
||||||
|
return tomlString
|
||||||
|
}
|
||||||
|
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return tomlBool
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
||||||
|
reflect.Int64,
|
||||||
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
||||||
|
reflect.Uint64:
|
||||||
|
return tomlInteger
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return tomlFloat
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
if isTableArray(rv) {
|
||||||
|
return tomlArrayHash
|
||||||
|
}
|
||||||
|
return tomlArray
|
||||||
|
case reflect.Ptr, reflect.Interface:
|
||||||
|
return tomlTypeOfGo(rv.Elem())
|
||||||
|
case reflect.String:
|
||||||
|
return tomlString
|
||||||
|
case reflect.Map:
|
||||||
|
return tomlHash
|
||||||
|
default:
|
||||||
|
encPanic(errors.New("unsupported type: " + rv.Kind().String()))
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMarshaler(rv reflect.Value) bool {
|
||||||
|
return rv.Type().Implements(marshalText) || rv.Type().Implements(marshalToml)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTableArray reports if all entries in the array or slice are a table.
|
||||||
|
func isTableArray(arr reflect.Value) bool {
|
||||||
|
if isNil(arr) || !arr.IsValid() || arr.Len() == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := true
|
||||||
|
for i := 0; i < arr.Len(); i++ {
|
||||||
|
tt := tomlTypeOfGo(eindirect(arr.Index(i)))
|
||||||
|
// Don't allow nil.
|
||||||
|
if tt == nil {
|
||||||
|
encPanic(errArrayNilElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret && !typeEqual(tomlHash, tt) {
|
||||||
|
ret = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagOptions struct {
|
||||||
|
skip bool // "-"
|
||||||
|
name string
|
||||||
|
omitempty bool
|
||||||
|
omitzero bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOptions(tag reflect.StructTag) tagOptions {
|
||||||
|
t := tag.Get("toml")
|
||||||
|
if t == "-" {
|
||||||
|
return tagOptions{skip: true}
|
||||||
|
}
|
||||||
|
var opts tagOptions
|
||||||
|
parts := strings.Split(t, ",")
|
||||||
|
opts.name = parts[0]
|
||||||
|
for _, s := range parts[1:] {
|
||||||
|
switch s {
|
||||||
|
case "omitempty":
|
||||||
|
opts.omitempty = true
|
||||||
|
case "omitzero":
|
||||||
|
opts.omitzero = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func isZero(rv reflect.Value) bool {
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return rv.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
return rv.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return rv.Float() == 0.0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEmpty(rv reflect.Value) bool {
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
||||||
|
return rv.Len() == 0
|
||||||
|
case reflect.Struct:
|
||||||
|
if rv.Type().Comparable() {
|
||||||
|
return reflect.Zero(rv.Type()).Interface() == rv.Interface()
|
||||||
|
}
|
||||||
|
// Need to also check if all the fields are empty, otherwise something
|
||||||
|
// like this with uncomparable types will always return true:
|
||||||
|
//
|
||||||
|
// type a struct{ field b }
|
||||||
|
// type b struct{ s []string }
|
||||||
|
// s := a{field: b{s: []string{"AAA"}}}
|
||||||
|
for i := 0; i < rv.NumField(); i++ {
|
||||||
|
if !isEmpty(rv.Field(i)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case reflect.Bool:
|
||||||
|
return !rv.Bool()
|
||||||
|
case reflect.Ptr:
|
||||||
|
return rv.IsNil()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) newline() {
|
||||||
|
if enc.hasWritten {
|
||||||
|
enc.wf("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a key/value pair:
|
||||||
|
//
|
||||||
|
// key = <any value>
|
||||||
|
//
|
||||||
|
// This is also used for "k = v" in inline tables; so something like this will
|
||||||
|
// be written in three calls:
|
||||||
|
//
|
||||||
|
// ┌───────────────────┐
|
||||||
|
// │ ┌───┐ ┌────┐│
|
||||||
|
// v v v v vv
|
||||||
|
// key = {k = 1, k2 = 2}
|
||||||
|
func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
|
||||||
|
/// Marshaler used on top-level document; call eElement() to just call
|
||||||
|
/// Marshal{TOML,Text}.
|
||||||
|
if len(key) == 0 {
|
||||||
|
enc.eElement(val)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
|
||||||
|
enc.eElement(val)
|
||||||
|
if !inline {
|
||||||
|
enc.newline()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) wf(format string, v ...interface{}) {
|
||||||
|
_, err := fmt.Fprintf(enc.w, format, v...)
|
||||||
|
if err != nil {
|
||||||
|
encPanic(err)
|
||||||
|
}
|
||||||
|
enc.hasWritten = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *Encoder) indentStr(key Key) string {
|
||||||
|
return strings.Repeat(enc.Indent, len(key)-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encPanic(err error) {
|
||||||
|
panic(tomlEncodeError{err})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve any level of pointers to the actual value (e.g. **string → string).
|
||||||
|
func eindirect(v reflect.Value) reflect.Value {
|
||||||
|
if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
|
||||||
|
if isMarshaler(v) {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
if v.CanAddr() { /// Special case for marshalers; see #358.
|
||||||
|
if pv := v.Addr(); isMarshaler(pv) {
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.IsNil() {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
return eindirect(v.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNil(rv reflect.Value) bool {
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||||
|
return rv.IsNil()
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
279
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/error.go
generated
vendored
Normal file
279
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/error.go
generated
vendored
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseError is returned when there is an error parsing the TOML syntax such as
|
||||||
|
// invalid syntax, duplicate keys, etc.
|
||||||
|
//
|
||||||
|
// In addition to the error message itself, you can also print detailed location
|
||||||
|
// information with context by using [ErrorWithPosition]:
|
||||||
|
//
|
||||||
|
// toml: error: Key 'fruit' was already created and cannot be used as an array.
|
||||||
|
//
|
||||||
|
// At line 4, column 2-7:
|
||||||
|
//
|
||||||
|
// 2 | fruit = []
|
||||||
|
// 3 |
|
||||||
|
// 4 | [[fruit]] # Not allowed
|
||||||
|
// ^^^^^
|
||||||
|
//
|
||||||
|
// [ErrorWithUsage] can be used to print the above with some more detailed usage
|
||||||
|
// guidance:
|
||||||
|
//
|
||||||
|
// toml: error: newlines not allowed within inline tables
|
||||||
|
//
|
||||||
|
// At line 1, column 18:
|
||||||
|
//
|
||||||
|
// 1 | x = [{ key = 42 #
|
||||||
|
// ^
|
||||||
|
//
|
||||||
|
// Error help:
|
||||||
|
//
|
||||||
|
// Inline tables must always be on a single line:
|
||||||
|
//
|
||||||
|
// table = {key = 42, second = 43}
|
||||||
|
//
|
||||||
|
// It is invalid to split them over multiple lines like so:
|
||||||
|
//
|
||||||
|
// # INVALID
|
||||||
|
// table = {
|
||||||
|
// key = 42,
|
||||||
|
// second = 43
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Use regular for this:
|
||||||
|
//
|
||||||
|
// [table]
|
||||||
|
// key = 42
|
||||||
|
// second = 43
|
||||||
|
type ParseError struct {
|
||||||
|
Message string // Short technical message.
|
||||||
|
Usage string // Longer message with usage guidance; may be blank.
|
||||||
|
Position Position // Position of the error
|
||||||
|
LastKey string // Last parsed key, may be blank.
|
||||||
|
|
||||||
|
// Line the error occurred.
|
||||||
|
//
|
||||||
|
// Deprecated: use [Position].
|
||||||
|
Line int
|
||||||
|
|
||||||
|
err error
|
||||||
|
input string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position of an error.
|
||||||
|
type Position struct {
|
||||||
|
Line int // Line number, starting at 1.
|
||||||
|
Start int // Start of error, as byte offset starting at 0.
|
||||||
|
Len int // Lenght in bytes.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe ParseError) Error() string {
|
||||||
|
msg := pe.Message
|
||||||
|
if msg == "" { // Error from errorf()
|
||||||
|
msg = pe.err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if pe.LastKey == "" {
|
||||||
|
return fmt.Sprintf("toml: line %d: %s", pe.Position.Line, msg)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("toml: line %d (last key %q): %s",
|
||||||
|
pe.Position.Line, pe.LastKey, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorWithPosition returns the error with detailed location context.
|
||||||
|
//
|
||||||
|
// See the documentation on [ParseError].
|
||||||
|
func (pe ParseError) ErrorWithPosition() string {
|
||||||
|
if pe.input == "" { // Should never happen, but just in case.
|
||||||
|
return pe.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
lines = strings.Split(pe.input, "\n")
|
||||||
|
col = pe.column(lines)
|
||||||
|
b = new(strings.Builder)
|
||||||
|
)
|
||||||
|
|
||||||
|
msg := pe.Message
|
||||||
|
if msg == "" {
|
||||||
|
msg = pe.err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: don't show control characters as literals? This may not show up
|
||||||
|
// well everywhere.
|
||||||
|
|
||||||
|
if pe.Position.Len == 1 {
|
||||||
|
fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d:\n\n",
|
||||||
|
msg, pe.Position.Line, col+1)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d-%d:\n\n",
|
||||||
|
msg, pe.Position.Line, col, col+pe.Position.Len)
|
||||||
|
}
|
||||||
|
if pe.Position.Line > 2 {
|
||||||
|
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, lines[pe.Position.Line-3])
|
||||||
|
}
|
||||||
|
if pe.Position.Line > 1 {
|
||||||
|
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-1, lines[pe.Position.Line-2])
|
||||||
|
}
|
||||||
|
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line, lines[pe.Position.Line-1])
|
||||||
|
fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", col), strings.Repeat("^", pe.Position.Len))
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorWithUsage returns the error with detailed location context and usage
|
||||||
|
// guidance.
|
||||||
|
//
|
||||||
|
// See the documentation on [ParseError].
|
||||||
|
func (pe ParseError) ErrorWithUsage() string {
|
||||||
|
m := pe.ErrorWithPosition()
|
||||||
|
if u, ok := pe.err.(interface{ Usage() string }); ok && u.Usage() != "" {
|
||||||
|
lines := strings.Split(strings.TrimSpace(u.Usage()), "\n")
|
||||||
|
for i := range lines {
|
||||||
|
if lines[i] != "" {
|
||||||
|
lines[i] = " " + lines[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m + "Error help:\n\n" + strings.Join(lines, "\n") + "\n"
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe ParseError) column(lines []string) int {
|
||||||
|
var pos, col int
|
||||||
|
for i := range lines {
|
||||||
|
ll := len(lines[i]) + 1 // +1 for the removed newline
|
||||||
|
if pos+ll >= pe.Position.Start {
|
||||||
|
col = pe.Position.Start - pos
|
||||||
|
if col < 0 { // Should never happen, but just in case.
|
||||||
|
col = 0
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pos += ll
|
||||||
|
}
|
||||||
|
|
||||||
|
return col
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
errLexControl struct{ r rune }
|
||||||
|
errLexEscape struct{ r rune }
|
||||||
|
errLexUTF8 struct{ b byte }
|
||||||
|
errLexInvalidNum struct{ v string }
|
||||||
|
errLexInvalidDate struct{ v string }
|
||||||
|
errLexInlineTableNL struct{}
|
||||||
|
errLexStringNL struct{}
|
||||||
|
errParseRange struct {
|
||||||
|
i interface{} // int or float
|
||||||
|
size string // "int64", "uint16", etc.
|
||||||
|
}
|
||||||
|
errParseDuration struct{ d string }
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e errLexControl) Error() string {
|
||||||
|
return fmt.Sprintf("TOML files cannot contain control characters: '0x%02x'", e.r)
|
||||||
|
}
|
||||||
|
func (e errLexControl) Usage() string { return "" }
|
||||||
|
|
||||||
|
func (e errLexEscape) Error() string { return fmt.Sprintf(`invalid escape in string '\%c'`, e.r) }
|
||||||
|
func (e errLexEscape) Usage() string { return usageEscape }
|
||||||
|
func (e errLexUTF8) Error() string { return fmt.Sprintf("invalid UTF-8 byte: 0x%02x", e.b) }
|
||||||
|
func (e errLexUTF8) Usage() string { return "" }
|
||||||
|
func (e errLexInvalidNum) Error() string { return fmt.Sprintf("invalid number: %q", e.v) }
|
||||||
|
func (e errLexInvalidNum) Usage() string { return "" }
|
||||||
|
func (e errLexInvalidDate) Error() string { return fmt.Sprintf("invalid date: %q", e.v) }
|
||||||
|
func (e errLexInvalidDate) Usage() string { return "" }
|
||||||
|
func (e errLexInlineTableNL) Error() string { return "newlines not allowed within inline tables" }
|
||||||
|
func (e errLexInlineTableNL) Usage() string { return usageInlineNewline }
|
||||||
|
func (e errLexStringNL) Error() string { return "strings cannot contain newlines" }
|
||||||
|
func (e errLexStringNL) Usage() string { return usageStringNewline }
|
||||||
|
func (e errParseRange) Error() string { return fmt.Sprintf("%v is out of range for %s", e.i, e.size) }
|
||||||
|
func (e errParseRange) Usage() string { return usageIntOverflow }
|
||||||
|
func (e errParseDuration) Error() string { return fmt.Sprintf("invalid duration: %q", e.d) }
|
||||||
|
func (e errParseDuration) Usage() string { return usageDuration }
|
||||||
|
|
||||||
|
const usageEscape = `
|
||||||
|
A '\' inside a "-delimited string is interpreted as an escape character.
|
||||||
|
|
||||||
|
The following escape sequences are supported:
|
||||||
|
\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX
|
||||||
|
|
||||||
|
To prevent a '\' from being recognized as an escape character, use either:
|
||||||
|
|
||||||
|
- a ' or '''-delimited string; escape characters aren't processed in them; or
|
||||||
|
- write two backslashes to get a single backslash: '\\'.
|
||||||
|
|
||||||
|
If you're trying to add a Windows path (e.g. "C:\Users\martin") then using '/'
|
||||||
|
instead of '\' will usually also work: "C:/Users/martin".
|
||||||
|
`
|
||||||
|
|
||||||
|
const usageInlineNewline = `
|
||||||
|
Inline tables must always be on a single line:
|
||||||
|
|
||||||
|
table = {key = 42, second = 43}
|
||||||
|
|
||||||
|
It is invalid to split them over multiple lines like so:
|
||||||
|
|
||||||
|
# INVALID
|
||||||
|
table = {
|
||||||
|
key = 42,
|
||||||
|
second = 43
|
||||||
|
}
|
||||||
|
|
||||||
|
Use regular for this:
|
||||||
|
|
||||||
|
[table]
|
||||||
|
key = 42
|
||||||
|
second = 43
|
||||||
|
`
|
||||||
|
|
||||||
|
const usageStringNewline = `
|
||||||
|
Strings must always be on a single line, and cannot span more than one line:
|
||||||
|
|
||||||
|
# INVALID
|
||||||
|
string = "Hello,
|
||||||
|
world!"
|
||||||
|
|
||||||
|
Instead use """ or ''' to split strings over multiple lines:
|
||||||
|
|
||||||
|
string = """Hello,
|
||||||
|
world!"""
|
||||||
|
`
|
||||||
|
|
||||||
|
const usageIntOverflow = `
|
||||||
|
This number is too large; this may be an error in the TOML, but it can also be a
|
||||||
|
bug in the program that uses too small of an integer.
|
||||||
|
|
||||||
|
The maximum and minimum values are:
|
||||||
|
|
||||||
|
size │ lowest │ highest
|
||||||
|
───────┼────────────────┼──────────
|
||||||
|
int8 │ -128 │ 127
|
||||||
|
int16 │ -32,768 │ 32,767
|
||||||
|
int32 │ -2,147,483,648 │ 2,147,483,647
|
||||||
|
int64 │ -9.2 × 10¹⁷ │ 9.2 × 10¹⁷
|
||||||
|
uint8 │ 0 │ 255
|
||||||
|
uint16 │ 0 │ 65535
|
||||||
|
uint32 │ 0 │ 4294967295
|
||||||
|
uint64 │ 0 │ 1.8 × 10¹⁸
|
||||||
|
|
||||||
|
int refers to int32 on 32-bit systems and int64 on 64-bit systems.
|
||||||
|
`
|
||||||
|
|
||||||
|
const usageDuration = `
|
||||||
|
A duration must be as "number<unit>", without any spaces. Valid units are:
|
||||||
|
|
||||||
|
ns nanoseconds (billionth of a second)
|
||||||
|
us, µs microseconds (millionth of a second)
|
||||||
|
ms milliseconds (thousands of a second)
|
||||||
|
s seconds
|
||||||
|
m minutes
|
||||||
|
h hours
|
||||||
|
|
||||||
|
You can combine multiple units; for example "5m10s" for 5 minutes and 10
|
||||||
|
seconds.
|
||||||
|
`
|
||||||
36
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/internal/tz.go
generated
vendored
Normal file
36
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/internal/tz.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Timezones used for local datetime, date, and time TOML types.
|
||||||
|
//
|
||||||
|
// The exact way times and dates without a timezone should be interpreted is not
|
||||||
|
// well-defined in the TOML specification and left to the implementation. These
|
||||||
|
// defaults to current local timezone offset of the computer, but this can be
|
||||||
|
// changed by changing these variables before decoding.
|
||||||
|
//
|
||||||
|
// TODO:
|
||||||
|
// Ideally we'd like to offer people the ability to configure the used timezone
|
||||||
|
// by setting Decoder.Timezone and Encoder.Timezone; however, this is a bit
|
||||||
|
// tricky: the reason we use three different variables for this is to support
|
||||||
|
// round-tripping – without these specific TZ names we wouldn't know which
|
||||||
|
// format to use.
|
||||||
|
//
|
||||||
|
// There isn't a good way to encode this right now though, and passing this sort
|
||||||
|
// of information also ties in to various related issues such as string format
|
||||||
|
// encoding, encoding of comments, etc.
|
||||||
|
//
|
||||||
|
// So, for the time being, just put this in internal until we can write a good
|
||||||
|
// comprehensive API for doing all of this.
|
||||||
|
//
|
||||||
|
// The reason they're exported is because they're referred from in e.g.
|
||||||
|
// internal/tag.
|
||||||
|
//
|
||||||
|
// Note that this behaviour is valid according to the TOML spec as the exact
|
||||||
|
// behaviour is left up to implementations.
|
||||||
|
var (
|
||||||
|
localOffset = func() int { _, o := time.Now().Zone(); return o }()
|
||||||
|
LocalDatetime = time.FixedZone("datetime-local", localOffset)
|
||||||
|
LocalDate = time.FixedZone("date-local", localOffset)
|
||||||
|
LocalTime = time.FixedZone("time-local", localOffset)
|
||||||
|
)
|
||||||
1283
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
Normal file
1283
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
121
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/meta.go
generated
vendored
Normal file
121
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/meta.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MetaData allows access to meta information about TOML data that's not
|
||||||
|
// accessible otherwise.
|
||||||
|
//
|
||||||
|
// It allows checking if a key is defined in the TOML data, whether any keys
|
||||||
|
// were undecoded, and the TOML type of a key.
|
||||||
|
type MetaData struct {
|
||||||
|
context Key // Used only during decoding.
|
||||||
|
|
||||||
|
keyInfo map[string]keyInfo
|
||||||
|
mapping map[string]interface{}
|
||||||
|
keys []Key
|
||||||
|
decoded map[string]struct{}
|
||||||
|
data []byte // Input file; for errors.
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDefined reports if the key exists in the TOML data.
|
||||||
|
//
|
||||||
|
// The key should be specified hierarchically, for example to access the TOML
|
||||||
|
// key "a.b.c" you would use IsDefined("a", "b", "c"). Keys are case sensitive.
|
||||||
|
//
|
||||||
|
// Returns false for an empty key.
|
||||||
|
func (md *MetaData) IsDefined(key ...string) bool {
|
||||||
|
if len(key) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
hash map[string]interface{}
|
||||||
|
ok bool
|
||||||
|
hashOrVal interface{} = md.mapping
|
||||||
|
)
|
||||||
|
for _, k := range key {
|
||||||
|
if hash, ok = hashOrVal.(map[string]interface{}); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if hashOrVal, ok = hash[k]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns a string representation of the type of the key specified.
|
||||||
|
//
|
||||||
|
// Type will return the empty string if given an empty key or a key that does
|
||||||
|
// not exist. Keys are case sensitive.
|
||||||
|
func (md *MetaData) Type(key ...string) string {
|
||||||
|
if ki, ok := md.keyInfo[Key(key).String()]; ok {
|
||||||
|
return ki.tomlType.typeString()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns a slice of every key in the TOML data, including key groups.
|
||||||
|
//
|
||||||
|
// Each key is itself a slice, where the first element is the top of the
|
||||||
|
// hierarchy and the last is the most specific. The list will have the same
|
||||||
|
// order as the keys appeared in the TOML data.
|
||||||
|
//
|
||||||
|
// All keys returned are non-empty.
|
||||||
|
func (md *MetaData) Keys() []Key {
|
||||||
|
return md.keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undecoded returns all keys that have not been decoded in the order in which
|
||||||
|
// they appear in the original TOML document.
|
||||||
|
//
|
||||||
|
// This includes keys that haven't been decoded because of a [Primitive] value.
|
||||||
|
// Once the Primitive value is decoded, the keys will be considered decoded.
|
||||||
|
//
|
||||||
|
// Also note that decoding into an empty interface will result in no decoding,
|
||||||
|
// and so no keys will be considered decoded.
|
||||||
|
//
|
||||||
|
// In this sense, the Undecoded keys correspond to keys in the TOML document
|
||||||
|
// that do not have a concrete type in your representation.
|
||||||
|
func (md *MetaData) Undecoded() []Key {
|
||||||
|
undecoded := make([]Key, 0, len(md.keys))
|
||||||
|
for _, key := range md.keys {
|
||||||
|
if _, ok := md.decoded[key.String()]; !ok {
|
||||||
|
undecoded = append(undecoded, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undecoded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key represents any TOML key, including key groups. Use [MetaData.Keys] to get
|
||||||
|
// values of this type.
|
||||||
|
type Key []string
|
||||||
|
|
||||||
|
func (k Key) String() string {
|
||||||
|
ss := make([]string, len(k))
|
||||||
|
for i := range k {
|
||||||
|
ss[i] = k.maybeQuoted(i)
|
||||||
|
}
|
||||||
|
return strings.Join(ss, ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) maybeQuoted(i int) string {
|
||||||
|
if k[i] == "" {
|
||||||
|
return `""`
|
||||||
|
}
|
||||||
|
for _, c := range k[i] {
|
||||||
|
if !isBareKeyChar(c, false) {
|
||||||
|
return `"` + dblQuotedReplacer.Replace(k[i]) + `"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return k[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) add(piece string) Key {
|
||||||
|
newKey := make(Key, len(k)+1)
|
||||||
|
copy(newKey, k)
|
||||||
|
newKey[len(k)] = piece
|
||||||
|
return newKey
|
||||||
|
}
|
||||||
811
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
Normal file
811
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
Normal file
@@ -0,0 +1,811 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type parser struct {
|
||||||
|
lx *lexer
|
||||||
|
context Key // Full key for the current hash in scope.
|
||||||
|
currentKey string // Base key name for everything except hashes.
|
||||||
|
pos Position // Current position in the TOML file.
|
||||||
|
tomlNext bool
|
||||||
|
|
||||||
|
ordered []Key // List of keys in the order that they appear in the TOML data.
|
||||||
|
|
||||||
|
keyInfo map[string]keyInfo // Map keyname → info about the TOML key.
|
||||||
|
mapping map[string]interface{} // Map keyname → key value.
|
||||||
|
implicits map[string]struct{} // Record implicit keys (e.g. "key.group.names").
|
||||||
|
}
|
||||||
|
|
||||||
|
type keyInfo struct {
|
||||||
|
pos Position
|
||||||
|
tomlType tomlType
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(data string) (p *parser, err error) {
|
||||||
|
_, tomlNext := os.LookupEnv("BURNTSUSHI_TOML_110")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if pErr, ok := r.(ParseError); ok {
|
||||||
|
pErr.input = data
|
||||||
|
err = pErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Read over BOM; do this here as the lexer calls utf8.DecodeRuneInString()
|
||||||
|
// which mangles stuff. UTF-16 BOM isn't strictly valid, but some tools add
|
||||||
|
// it anyway.
|
||||||
|
if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { // UTF-16
|
||||||
|
data = data[2:]
|
||||||
|
} else if strings.HasPrefix(data, "\xef\xbb\xbf") { // UTF-8
|
||||||
|
data = data[3:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Examine first few bytes for NULL bytes; this probably means it's a UTF-16
|
||||||
|
// file (second byte in surrogate pair being NULL). Again, do this here to
|
||||||
|
// avoid having to deal with UTF-8/16 stuff in the lexer.
|
||||||
|
ex := 6
|
||||||
|
if len(data) < 6 {
|
||||||
|
ex = len(data)
|
||||||
|
}
|
||||||
|
if i := strings.IndexRune(data[:ex], 0); i > -1 {
|
||||||
|
return nil, ParseError{
|
||||||
|
Message: "files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8",
|
||||||
|
Position: Position{Line: 1, Start: i, Len: 1},
|
||||||
|
Line: 1,
|
||||||
|
input: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p = &parser{
|
||||||
|
keyInfo: make(map[string]keyInfo),
|
||||||
|
mapping: make(map[string]interface{}),
|
||||||
|
lx: lex(data, tomlNext),
|
||||||
|
ordered: make([]Key, 0),
|
||||||
|
implicits: make(map[string]struct{}),
|
||||||
|
tomlNext: tomlNext,
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
item := p.next()
|
||||||
|
if item.typ == itemEOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.topLevel(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) panicErr(it item, err error) {
|
||||||
|
panic(ParseError{
|
||||||
|
err: err,
|
||||||
|
Position: it.pos,
|
||||||
|
Line: it.pos.Len,
|
||||||
|
LastKey: p.current(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) panicItemf(it item, format string, v ...interface{}) {
|
||||||
|
panic(ParseError{
|
||||||
|
Message: fmt.Sprintf(format, v...),
|
||||||
|
Position: it.pos,
|
||||||
|
Line: it.pos.Len,
|
||||||
|
LastKey: p.current(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) panicf(format string, v ...interface{}) {
|
||||||
|
panic(ParseError{
|
||||||
|
Message: fmt.Sprintf(format, v...),
|
||||||
|
Position: p.pos,
|
||||||
|
Line: p.pos.Line,
|
||||||
|
LastKey: p.current(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) next() item {
|
||||||
|
it := p.lx.nextItem()
|
||||||
|
//fmt.Printf("ITEM %-18s line %-3d │ %q\n", it.typ, it.pos.Line, it.val)
|
||||||
|
if it.typ == itemError {
|
||||||
|
if it.err != nil {
|
||||||
|
panic(ParseError{
|
||||||
|
Position: it.pos,
|
||||||
|
Line: it.pos.Line,
|
||||||
|
LastKey: p.current(),
|
||||||
|
err: it.err,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
p.panicItemf(it, "%s", it.val)
|
||||||
|
}
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) nextPos() item {
|
||||||
|
it := p.next()
|
||||||
|
p.pos = it.pos
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) bug(format string, v ...interface{}) {
|
||||||
|
panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) expect(typ itemType) item {
|
||||||
|
it := p.next()
|
||||||
|
p.assertEqual(typ, it.typ)
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) assertEqual(expected, got itemType) {
|
||||||
|
if expected != got {
|
||||||
|
p.bug("Expected '%s' but got '%s'.", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) topLevel(item item) {
|
||||||
|
switch item.typ {
|
||||||
|
case itemCommentStart: // # ..
|
||||||
|
p.expect(itemText)
|
||||||
|
case itemTableStart: // [ .. ]
|
||||||
|
name := p.nextPos()
|
||||||
|
|
||||||
|
var key Key
|
||||||
|
for ; name.typ != itemTableEnd && name.typ != itemEOF; name = p.next() {
|
||||||
|
key = append(key, p.keyString(name))
|
||||||
|
}
|
||||||
|
p.assertEqual(itemTableEnd, name.typ)
|
||||||
|
|
||||||
|
p.addContext(key, false)
|
||||||
|
p.setType("", tomlHash, item.pos)
|
||||||
|
p.ordered = append(p.ordered, key)
|
||||||
|
case itemArrayTableStart: // [[ .. ]]
|
||||||
|
name := p.nextPos()
|
||||||
|
|
||||||
|
var key Key
|
||||||
|
for ; name.typ != itemArrayTableEnd && name.typ != itemEOF; name = p.next() {
|
||||||
|
key = append(key, p.keyString(name))
|
||||||
|
}
|
||||||
|
p.assertEqual(itemArrayTableEnd, name.typ)
|
||||||
|
|
||||||
|
p.addContext(key, true)
|
||||||
|
p.setType("", tomlArrayHash, item.pos)
|
||||||
|
p.ordered = append(p.ordered, key)
|
||||||
|
case itemKeyStart: // key = ..
|
||||||
|
outerContext := p.context
|
||||||
|
/// Read all the key parts (e.g. 'a' and 'b' in 'a.b')
|
||||||
|
k := p.nextPos()
|
||||||
|
var key Key
|
||||||
|
for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() {
|
||||||
|
key = append(key, p.keyString(k))
|
||||||
|
}
|
||||||
|
p.assertEqual(itemKeyEnd, k.typ)
|
||||||
|
|
||||||
|
/// The current key is the last part.
|
||||||
|
p.currentKey = key[len(key)-1]
|
||||||
|
|
||||||
|
/// All the other parts (if any) are the context; need to set each part
|
||||||
|
/// as implicit.
|
||||||
|
context := key[:len(key)-1]
|
||||||
|
for i := range context {
|
||||||
|
p.addImplicitContext(append(p.context, context[i:i+1]...))
|
||||||
|
}
|
||||||
|
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
||||||
|
|
||||||
|
/// Set value.
|
||||||
|
vItem := p.next()
|
||||||
|
val, typ := p.value(vItem, false)
|
||||||
|
p.set(p.currentKey, val, typ, vItem.pos)
|
||||||
|
|
||||||
|
/// Remove the context we added (preserving any context from [tbl] lines).
|
||||||
|
p.context = outerContext
|
||||||
|
p.currentKey = ""
|
||||||
|
default:
|
||||||
|
p.bug("Unexpected type at top level: %s", item.typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets a string for a key (or part of a key in a table name).
|
||||||
|
func (p *parser) keyString(it item) string {
|
||||||
|
switch it.typ {
|
||||||
|
case itemText:
|
||||||
|
return it.val
|
||||||
|
case itemString, itemMultilineString,
|
||||||
|
itemRawString, itemRawMultilineString:
|
||||||
|
s, _ := p.value(it, false)
|
||||||
|
return s.(string)
|
||||||
|
default:
|
||||||
|
p.bug("Unexpected key type: %s", it.typ)
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var datetimeRepl = strings.NewReplacer(
|
||||||
|
"z", "Z",
|
||||||
|
"t", "T",
|
||||||
|
" ", "T")
|
||||||
|
|
||||||
|
// value translates an expected value from the lexer into a Go value wrapped
|
||||||
|
// as an empty interface.
|
||||||
|
func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) {
|
||||||
|
switch it.typ {
|
||||||
|
case itemString:
|
||||||
|
return p.replaceEscapes(it, it.val), p.typeOfPrimitive(it)
|
||||||
|
case itemMultilineString:
|
||||||
|
return p.replaceEscapes(it, p.stripEscapedNewlines(stripFirstNewline(it.val))), p.typeOfPrimitive(it)
|
||||||
|
case itemRawString:
|
||||||
|
return it.val, p.typeOfPrimitive(it)
|
||||||
|
case itemRawMultilineString:
|
||||||
|
return stripFirstNewline(it.val), p.typeOfPrimitive(it)
|
||||||
|
case itemInteger:
|
||||||
|
return p.valueInteger(it)
|
||||||
|
case itemFloat:
|
||||||
|
return p.valueFloat(it)
|
||||||
|
case itemBool:
|
||||||
|
switch it.val {
|
||||||
|
case "true":
|
||||||
|
return true, p.typeOfPrimitive(it)
|
||||||
|
case "false":
|
||||||
|
return false, p.typeOfPrimitive(it)
|
||||||
|
default:
|
||||||
|
p.bug("Expected boolean value, but got '%s'.", it.val)
|
||||||
|
}
|
||||||
|
case itemDatetime:
|
||||||
|
return p.valueDatetime(it)
|
||||||
|
case itemArray:
|
||||||
|
return p.valueArray(it)
|
||||||
|
case itemInlineTableStart:
|
||||||
|
return p.valueInlineTable(it, parentIsArray)
|
||||||
|
default:
|
||||||
|
p.bug("Unexpected value type: %s", it.typ)
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) valueInteger(it item) (interface{}, tomlType) {
|
||||||
|
if !numUnderscoresOK(it.val) {
|
||||||
|
p.panicItemf(it, "Invalid integer %q: underscores must be surrounded by digits", it.val)
|
||||||
|
}
|
||||||
|
if numHasLeadingZero(it.val) {
|
||||||
|
p.panicItemf(it, "Invalid integer %q: cannot have leading zeroes", it.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
num, err := strconv.ParseInt(it.val, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
// Distinguish integer values. Normally, it'd be a bug if the lexer
|
||||||
|
// provides an invalid integer, but it's possible that the number is
|
||||||
|
// out of range of valid values (which the lexer cannot determine).
|
||||||
|
// So mark the former as a bug but the latter as a legitimate user
|
||||||
|
// error.
|
||||||
|
if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
|
||||||
|
p.panicErr(it, errParseRange{i: it.val, size: "int64"})
|
||||||
|
} else {
|
||||||
|
p.bug("Expected integer value, but got '%s'.", it.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num, p.typeOfPrimitive(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) valueFloat(it item) (interface{}, tomlType) {
|
||||||
|
parts := strings.FieldsFunc(it.val, func(r rune) bool {
|
||||||
|
switch r {
|
||||||
|
case '.', 'e', 'E':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
for _, part := range parts {
|
||||||
|
if !numUnderscoresOK(part) {
|
||||||
|
p.panicItemf(it, "Invalid float %q: underscores must be surrounded by digits", it.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(parts) > 0 && numHasLeadingZero(parts[0]) {
|
||||||
|
p.panicItemf(it, "Invalid float %q: cannot have leading zeroes", it.val)
|
||||||
|
}
|
||||||
|
if !numPeriodsOK(it.val) {
|
||||||
|
// As a special case, numbers like '123.' or '1.e2',
|
||||||
|
// which are valid as far as Go/strconv are concerned,
|
||||||
|
// must be rejected because TOML says that a fractional
|
||||||
|
// part consists of '.' followed by 1+ digits.
|
||||||
|
p.panicItemf(it, "Invalid float %q: '.' must be followed by one or more digits", it.val)
|
||||||
|
}
|
||||||
|
val := strings.Replace(it.val, "_", "", -1)
|
||||||
|
if val == "+nan" || val == "-nan" { // Go doesn't support this, but TOML spec does.
|
||||||
|
val = "nan"
|
||||||
|
}
|
||||||
|
num, err := strconv.ParseFloat(val, 64)
|
||||||
|
if err != nil {
|
||||||
|
if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
|
||||||
|
p.panicErr(it, errParseRange{i: it.val, size: "float64"})
|
||||||
|
} else {
|
||||||
|
p.panicItemf(it, "Invalid float value: %q", it.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num, p.typeOfPrimitive(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dtTypes = []struct {
|
||||||
|
fmt string
|
||||||
|
zone *time.Location
|
||||||
|
next bool
|
||||||
|
}{
|
||||||
|
{time.RFC3339Nano, time.Local, false},
|
||||||
|
{"2006-01-02T15:04:05.999999999", internal.LocalDatetime, false},
|
||||||
|
{"2006-01-02", internal.LocalDate, false},
|
||||||
|
{"15:04:05.999999999", internal.LocalTime, false},
|
||||||
|
|
||||||
|
// tomlNext
|
||||||
|
{"2006-01-02T15:04Z07:00", time.Local, true},
|
||||||
|
{"2006-01-02T15:04", internal.LocalDatetime, true},
|
||||||
|
{"15:04", internal.LocalTime, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) valueDatetime(it item) (interface{}, tomlType) {
|
||||||
|
it.val = datetimeRepl.Replace(it.val)
|
||||||
|
var (
|
||||||
|
t time.Time
|
||||||
|
ok bool
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
for _, dt := range dtTypes {
|
||||||
|
if dt.next && !p.tomlNext {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone)
|
||||||
|
if err == nil {
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
p.panicItemf(it, "Invalid TOML Datetime: %q.", it.val)
|
||||||
|
}
|
||||||
|
return t, p.typeOfPrimitive(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) valueArray(it item) (interface{}, tomlType) {
|
||||||
|
p.setType(p.currentKey, tomlArray, it.pos)
|
||||||
|
|
||||||
|
var (
|
||||||
|
types []tomlType
|
||||||
|
|
||||||
|
// Initialize to a non-nil empty slice. This makes it consistent with
|
||||||
|
// how S = [] decodes into a non-nil slice inside something like struct
|
||||||
|
// { S []string }. See #338
|
||||||
|
array = []interface{}{}
|
||||||
|
)
|
||||||
|
for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
|
||||||
|
if it.typ == itemCommentStart {
|
||||||
|
p.expect(itemText)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val, typ := p.value(it, true)
|
||||||
|
array = append(array, val)
|
||||||
|
types = append(types, typ)
|
||||||
|
|
||||||
|
// XXX: types isn't used here, we need it to record the accurate type
|
||||||
|
// information.
|
||||||
|
//
|
||||||
|
// Not entirely sure how to best store this; could use "key[0]",
|
||||||
|
// "key[1]" notation, or maybe store it on the Array type?
|
||||||
|
_ = types
|
||||||
|
}
|
||||||
|
return array, tomlArray
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tomlType) {
|
||||||
|
var (
|
||||||
|
hash = make(map[string]interface{})
|
||||||
|
outerContext = p.context
|
||||||
|
outerKey = p.currentKey
|
||||||
|
)
|
||||||
|
|
||||||
|
p.context = append(p.context, p.currentKey)
|
||||||
|
prevContext := p.context
|
||||||
|
p.currentKey = ""
|
||||||
|
|
||||||
|
p.addImplicit(p.context)
|
||||||
|
p.addContext(p.context, parentIsArray)
|
||||||
|
|
||||||
|
/// Loop over all table key/value pairs.
|
||||||
|
for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
|
||||||
|
if it.typ == itemCommentStart {
|
||||||
|
p.expect(itemText)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read all key parts.
|
||||||
|
k := p.nextPos()
|
||||||
|
var key Key
|
||||||
|
for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() {
|
||||||
|
key = append(key, p.keyString(k))
|
||||||
|
}
|
||||||
|
p.assertEqual(itemKeyEnd, k.typ)
|
||||||
|
|
||||||
|
/// The current key is the last part.
|
||||||
|
p.currentKey = key[len(key)-1]
|
||||||
|
|
||||||
|
/// All the other parts (if any) are the context; need to set each part
|
||||||
|
/// as implicit.
|
||||||
|
context := key[:len(key)-1]
|
||||||
|
for i := range context {
|
||||||
|
p.addImplicitContext(append(p.context, context[i:i+1]...))
|
||||||
|
}
|
||||||
|
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
||||||
|
|
||||||
|
/// Set the value.
|
||||||
|
val, typ := p.value(p.next(), false)
|
||||||
|
p.set(p.currentKey, val, typ, it.pos)
|
||||||
|
hash[p.currentKey] = val
|
||||||
|
|
||||||
|
/// Restore context.
|
||||||
|
p.context = prevContext
|
||||||
|
}
|
||||||
|
p.context = outerContext
|
||||||
|
p.currentKey = outerKey
|
||||||
|
return hash, tomlHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// numHasLeadingZero checks if this number has leading zeroes, allowing for '0',
|
||||||
|
// +/- signs, and base prefixes.
|
||||||
|
func numHasLeadingZero(s string) bool {
|
||||||
|
if len(s) > 1 && s[0] == '0' && !(s[1] == 'b' || s[1] == 'o' || s[1] == 'x') { // Allow 0b, 0o, 0x
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(s) > 2 && (s[0] == '-' || s[0] == '+') && s[1] == '0' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// numUnderscoresOK checks whether each underscore in s is surrounded by
|
||||||
|
// characters that are not underscores.
|
||||||
|
func numUnderscoresOK(s string) bool {
|
||||||
|
switch s {
|
||||||
|
case "nan", "+nan", "-nan", "inf", "-inf", "+inf":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
accept := false
|
||||||
|
for _, r := range s {
|
||||||
|
if r == '_' {
|
||||||
|
if !accept {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isHexadecimal is a superset of all the permissable characters
|
||||||
|
// surrounding an underscore.
|
||||||
|
accept = isHexadecimal(r)
|
||||||
|
}
|
||||||
|
return accept
|
||||||
|
}
|
||||||
|
|
||||||
|
// numPeriodsOK checks whether every period in s is followed by a digit.
|
||||||
|
func numPeriodsOK(s string) bool {
|
||||||
|
period := false
|
||||||
|
for _, r := range s {
|
||||||
|
if period && !isDigit(r) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
period = r == '.'
|
||||||
|
}
|
||||||
|
return !period
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the current context of the parser, where the context is either a hash or
|
||||||
|
// an array of hashes, depending on the value of the `array` parameter.
|
||||||
|
//
|
||||||
|
// Establishing the context also makes sure that the key isn't a duplicate, and
|
||||||
|
// will create implicit hashes automatically.
|
||||||
|
func (p *parser) addContext(key Key, array bool) {
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
// Always start at the top level and drill down for our context.
|
||||||
|
hashContext := p.mapping
|
||||||
|
keyContext := make(Key, 0)
|
||||||
|
|
||||||
|
// We only need implicit hashes for key[0:-1]
|
||||||
|
for _, k := range key[0 : len(key)-1] {
|
||||||
|
_, ok = hashContext[k]
|
||||||
|
keyContext = append(keyContext, k)
|
||||||
|
|
||||||
|
// No key? Make an implicit hash and move on.
|
||||||
|
if !ok {
|
||||||
|
p.addImplicit(keyContext)
|
||||||
|
hashContext[k] = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the hash context is actually an array of tables, then set
|
||||||
|
// the hash context to the last element in that array.
|
||||||
|
//
|
||||||
|
// Otherwise, it better be a table, since this MUST be a key group (by
|
||||||
|
// virtue of it not being the last element in a key).
|
||||||
|
switch t := hashContext[k].(type) {
|
||||||
|
case []map[string]interface{}:
|
||||||
|
hashContext = t[len(t)-1]
|
||||||
|
case map[string]interface{}:
|
||||||
|
hashContext = t
|
||||||
|
default:
|
||||||
|
p.panicf("Key '%s' was already created as a hash.", keyContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.context = keyContext
|
||||||
|
if array {
|
||||||
|
// If this is the first element for this array, then allocate a new
|
||||||
|
// list of tables for it.
|
||||||
|
k := key[len(key)-1]
|
||||||
|
if _, ok := hashContext[k]; !ok {
|
||||||
|
hashContext[k] = make([]map[string]interface{}, 0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new table. But make sure the key hasn't already been used
|
||||||
|
// for something else.
|
||||||
|
if hash, ok := hashContext[k].([]map[string]interface{}); ok {
|
||||||
|
hashContext[k] = append(hash, make(map[string]interface{}))
|
||||||
|
} else {
|
||||||
|
p.panicf("Key '%s' was already created and cannot be used as an array.", key)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.setValue(key[len(key)-1], make(map[string]interface{}))
|
||||||
|
}
|
||||||
|
p.context = append(p.context, key[len(key)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// set calls setValue and setType.
|
||||||
|
func (p *parser) set(key string, val interface{}, typ tomlType, pos Position) {
|
||||||
|
p.setValue(key, val)
|
||||||
|
p.setType(key, typ, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setValue sets the given key to the given value in the current context.
|
||||||
|
// It will make sure that the key hasn't already been defined, account for
|
||||||
|
// implicit key groups.
|
||||||
|
func (p *parser) setValue(key string, value interface{}) {
|
||||||
|
var (
|
||||||
|
tmpHash interface{}
|
||||||
|
ok bool
|
||||||
|
hash = p.mapping
|
||||||
|
keyContext Key
|
||||||
|
)
|
||||||
|
for _, k := range p.context {
|
||||||
|
keyContext = append(keyContext, k)
|
||||||
|
if tmpHash, ok = hash[k]; !ok {
|
||||||
|
p.bug("Context for key '%s' has not been established.", keyContext)
|
||||||
|
}
|
||||||
|
switch t := tmpHash.(type) {
|
||||||
|
case []map[string]interface{}:
|
||||||
|
// The context is a table of hashes. Pick the most recent table
|
||||||
|
// defined as the current hash.
|
||||||
|
hash = t[len(t)-1]
|
||||||
|
case map[string]interface{}:
|
||||||
|
hash = t
|
||||||
|
default:
|
||||||
|
p.panicf("Key '%s' has already been defined.", keyContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyContext = append(keyContext, key)
|
||||||
|
|
||||||
|
if _, ok := hash[key]; ok {
|
||||||
|
// Normally redefining keys isn't allowed, but the key could have been
|
||||||
|
// defined implicitly and it's allowed to be redefined concretely. (See
|
||||||
|
// the `valid/implicit-and-explicit-after.toml` in toml-test)
|
||||||
|
//
|
||||||
|
// But we have to make sure to stop marking it as an implicit. (So that
|
||||||
|
// another redefinition provokes an error.)
|
||||||
|
//
|
||||||
|
// Note that since it has already been defined (as a hash), we don't
|
||||||
|
// want to overwrite it. So our business is done.
|
||||||
|
if p.isArray(keyContext) {
|
||||||
|
p.removeImplicit(keyContext)
|
||||||
|
hash[key] = value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p.isImplicit(keyContext) {
|
||||||
|
p.removeImplicit(keyContext)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we have a concrete key trying to override a previous
|
||||||
|
// key, which is *always* wrong.
|
||||||
|
p.panicf("Key '%s' has already been defined.", keyContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// setType sets the type of a particular value at a given key. It should be
|
||||||
|
// called immediately AFTER setValue.
|
||||||
|
//
|
||||||
|
// Note that if `key` is empty, then the type given will be applied to the
|
||||||
|
// current context (which is either a table or an array of tables).
|
||||||
|
func (p *parser) setType(key string, typ tomlType, pos Position) {
|
||||||
|
keyContext := make(Key, 0, len(p.context)+1)
|
||||||
|
keyContext = append(keyContext, p.context...)
|
||||||
|
if len(key) > 0 { // allow type setting for hashes
|
||||||
|
keyContext = append(keyContext, key)
|
||||||
|
}
|
||||||
|
// Special case to make empty keys ("" = 1) work.
|
||||||
|
// Without it it will set "" rather than `""`.
|
||||||
|
// TODO: why is this needed? And why is this only needed here?
|
||||||
|
if len(keyContext) == 0 {
|
||||||
|
keyContext = Key{""}
|
||||||
|
}
|
||||||
|
p.keyInfo[keyContext.String()] = keyInfo{tomlType: typ, pos: pos}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and
|
||||||
|
// "[a.b.c]" (the "a", "b", and "c" hashes are never created explicitly).
|
||||||
|
func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} }
|
||||||
|
func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) }
|
||||||
|
func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok }
|
||||||
|
func (p *parser) isArray(key Key) bool { return p.keyInfo[key.String()].tomlType == tomlArray }
|
||||||
|
func (p *parser) addImplicitContext(key Key) { p.addImplicit(key); p.addContext(key, false) }
|
||||||
|
|
||||||
|
// current returns the full key name of the current context.
|
||||||
|
func (p *parser) current() string {
|
||||||
|
if len(p.currentKey) == 0 {
|
||||||
|
return p.context.String()
|
||||||
|
}
|
||||||
|
if len(p.context) == 0 {
|
||||||
|
return p.currentKey
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s.%s", p.context, p.currentKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stripFirstNewline(s string) string {
|
||||||
|
if len(s) > 0 && s[0] == '\n' {
|
||||||
|
return s[1:]
|
||||||
|
}
|
||||||
|
if len(s) > 1 && s[0] == '\r' && s[1] == '\n' {
|
||||||
|
return s[2:]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// stripEscapedNewlines removes whitespace after line-ending backslashes in
|
||||||
|
// multiline strings.
|
||||||
|
//
|
||||||
|
// A line-ending backslash is an unescaped \ followed only by whitespace until
|
||||||
|
// the next newline. After a line-ending backslash, all whitespace is removed
|
||||||
|
// until the next non-whitespace character.
|
||||||
|
func (p *parser) stripEscapedNewlines(s string) string {
|
||||||
|
var b strings.Builder
|
||||||
|
var i int
|
||||||
|
for {
|
||||||
|
ix := strings.Index(s[i:], `\`)
|
||||||
|
if ix < 0 {
|
||||||
|
b.WriteString(s)
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
i += ix
|
||||||
|
|
||||||
|
if len(s) > i+1 && s[i+1] == '\\' {
|
||||||
|
// Escaped backslash.
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Scan until the next non-whitespace.
|
||||||
|
j := i + 1
|
||||||
|
whitespaceLoop:
|
||||||
|
for ; j < len(s); j++ {
|
||||||
|
switch s[j] {
|
||||||
|
case ' ', '\t', '\r', '\n':
|
||||||
|
default:
|
||||||
|
break whitespaceLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if j == i+1 {
|
||||||
|
// Not a whitespace escape.
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !strings.Contains(s[i:j], "\n") {
|
||||||
|
// This is not a line-ending backslash.
|
||||||
|
// (It's a bad escape sequence, but we can let
|
||||||
|
// replaceEscapes catch it.)
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b.WriteString(s[:i])
|
||||||
|
s = s[j:]
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) replaceEscapes(it item, str string) string {
|
||||||
|
replaced := make([]rune, 0, len(str))
|
||||||
|
s := []byte(str)
|
||||||
|
r := 0
|
||||||
|
for r < len(s) {
|
||||||
|
if s[r] != '\\' {
|
||||||
|
c, size := utf8.DecodeRune(s[r:])
|
||||||
|
r += size
|
||||||
|
replaced = append(replaced, c)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r += 1
|
||||||
|
if r >= len(s) {
|
||||||
|
p.bug("Escape sequence at end of string.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
switch s[r] {
|
||||||
|
default:
|
||||||
|
p.bug("Expected valid escape code after \\, but got %q.", s[r])
|
||||||
|
case ' ', '\t':
|
||||||
|
p.panicItemf(it, "invalid escape: '\\%c'", s[r])
|
||||||
|
case 'b':
|
||||||
|
replaced = append(replaced, rune(0x0008))
|
||||||
|
r += 1
|
||||||
|
case 't':
|
||||||
|
replaced = append(replaced, rune(0x0009))
|
||||||
|
r += 1
|
||||||
|
case 'n':
|
||||||
|
replaced = append(replaced, rune(0x000A))
|
||||||
|
r += 1
|
||||||
|
case 'f':
|
||||||
|
replaced = append(replaced, rune(0x000C))
|
||||||
|
r += 1
|
||||||
|
case 'r':
|
||||||
|
replaced = append(replaced, rune(0x000D))
|
||||||
|
r += 1
|
||||||
|
case 'e':
|
||||||
|
if p.tomlNext {
|
||||||
|
replaced = append(replaced, rune(0x001B))
|
||||||
|
r += 1
|
||||||
|
}
|
||||||
|
case '"':
|
||||||
|
replaced = append(replaced, rune(0x0022))
|
||||||
|
r += 1
|
||||||
|
case '\\':
|
||||||
|
replaced = append(replaced, rune(0x005C))
|
||||||
|
r += 1
|
||||||
|
case 'x':
|
||||||
|
if p.tomlNext {
|
||||||
|
escaped := p.asciiEscapeToUnicode(it, s[r+1:r+3])
|
||||||
|
replaced = append(replaced, escaped)
|
||||||
|
r += 3
|
||||||
|
}
|
||||||
|
case 'u':
|
||||||
|
// At this point, we know we have a Unicode escape of the form
|
||||||
|
// `uXXXX` at [r, r+5). (Because the lexer guarantees this
|
||||||
|
// for us.)
|
||||||
|
escaped := p.asciiEscapeToUnicode(it, s[r+1:r+5])
|
||||||
|
replaced = append(replaced, escaped)
|
||||||
|
r += 5
|
||||||
|
case 'U':
|
||||||
|
// At this point, we know we have a Unicode escape of the form
|
||||||
|
// `uXXXX` at [r, r+9). (Because the lexer guarantees this
|
||||||
|
// for us.)
|
||||||
|
escaped := p.asciiEscapeToUnicode(it, s[r+1:r+9])
|
||||||
|
replaced = append(replaced, escaped)
|
||||||
|
r += 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(replaced)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) asciiEscapeToUnicode(it item, bs []byte) rune {
|
||||||
|
s := string(bs)
|
||||||
|
hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
|
||||||
|
if err != nil {
|
||||||
|
p.bug("Could not parse '%s' as a hexadecimal number, but the lexer claims it's OK: %s", s, err)
|
||||||
|
}
|
||||||
|
if !utf8.ValidRune(rune(hex)) {
|
||||||
|
p.panicItemf(it, "Escaped character '\\u%s' is not valid UTF-8.", s)
|
||||||
|
}
|
||||||
|
return rune(hex)
|
||||||
|
}
|
||||||
242
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/type_fields.go
generated
vendored
Normal file
242
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/type_fields.go
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
// Struct field handling is adapted from code in encoding/json:
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the Go distribution.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A field represents a single field found in a struct.
|
||||||
|
type field struct {
|
||||||
|
name string // the name of the field (`toml` tag included)
|
||||||
|
tag bool // whether field has a `toml` tag
|
||||||
|
index []int // represents the depth of an anonymous field
|
||||||
|
typ reflect.Type // the type of the field
|
||||||
|
}
|
||||||
|
|
||||||
|
// byName sorts field by name, breaking ties with depth,
|
||||||
|
// then breaking ties with "name came from toml tag", then
|
||||||
|
// breaking ties with index sequence.
|
||||||
|
type byName []field
|
||||||
|
|
||||||
|
func (x byName) Len() int { return len(x) }
|
||||||
|
|
||||||
|
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||||
|
|
||||||
|
func (x byName) Less(i, j int) bool {
|
||||||
|
if x[i].name != x[j].name {
|
||||||
|
return x[i].name < x[j].name
|
||||||
|
}
|
||||||
|
if len(x[i].index) != len(x[j].index) {
|
||||||
|
return len(x[i].index) < len(x[j].index)
|
||||||
|
}
|
||||||
|
if x[i].tag != x[j].tag {
|
||||||
|
return x[i].tag
|
||||||
|
}
|
||||||
|
return byIndex(x).Less(i, j)
|
||||||
|
}
|
||||||
|
|
||||||
|
// byIndex sorts field by index sequence.
|
||||||
|
type byIndex []field
|
||||||
|
|
||||||
|
func (x byIndex) Len() int { return len(x) }
|
||||||
|
|
||||||
|
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||||
|
|
||||||
|
func (x byIndex) Less(i, j int) bool {
|
||||||
|
for k, xik := range x[i].index {
|
||||||
|
if k >= len(x[j].index) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if xik != x[j].index[k] {
|
||||||
|
return xik < x[j].index[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(x[i].index) < len(x[j].index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeFields returns a list of fields that TOML should recognize for the given
|
||||||
|
// type. The algorithm is breadth-first search over the set of structs to
|
||||||
|
// include - the top struct and then any reachable anonymous structs.
|
||||||
|
func typeFields(t reflect.Type) []field {
|
||||||
|
// Anonymous fields to explore at the current level and the next.
|
||||||
|
current := []field{}
|
||||||
|
next := []field{{typ: t}}
|
||||||
|
|
||||||
|
// Count of queued names for current level and the next.
|
||||||
|
var count map[reflect.Type]int
|
||||||
|
var nextCount map[reflect.Type]int
|
||||||
|
|
||||||
|
// Types already visited at an earlier level.
|
||||||
|
visited := map[reflect.Type]bool{}
|
||||||
|
|
||||||
|
// Fields found.
|
||||||
|
var fields []field
|
||||||
|
|
||||||
|
for len(next) > 0 {
|
||||||
|
current, next = next, current[:0]
|
||||||
|
count, nextCount = nextCount, map[reflect.Type]int{}
|
||||||
|
|
||||||
|
for _, f := range current {
|
||||||
|
if visited[f.typ] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
visited[f.typ] = true
|
||||||
|
|
||||||
|
// Scan f.typ for fields to include.
|
||||||
|
for i := 0; i < f.typ.NumField(); i++ {
|
||||||
|
sf := f.typ.Field(i)
|
||||||
|
if sf.PkgPath != "" && !sf.Anonymous { // unexported
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
opts := getOptions(sf.Tag)
|
||||||
|
if opts.skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
index := make([]int, len(f.index)+1)
|
||||||
|
copy(index, f.index)
|
||||||
|
index[len(f.index)] = i
|
||||||
|
|
||||||
|
ft := sf.Type
|
||||||
|
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
|
||||||
|
// Follow pointer.
|
||||||
|
ft = ft.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record found field and index sequence.
|
||||||
|
if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
||||||
|
tagged := opts.name != ""
|
||||||
|
name := opts.name
|
||||||
|
if name == "" {
|
||||||
|
name = sf.Name
|
||||||
|
}
|
||||||
|
fields = append(fields, field{name, tagged, index, ft})
|
||||||
|
if count[f.typ] > 1 {
|
||||||
|
// If there were multiple instances, add a second,
|
||||||
|
// so that the annihilation code will see a duplicate.
|
||||||
|
// It only cares about the distinction between 1 or 2,
|
||||||
|
// so don't bother generating any more copies.
|
||||||
|
fields = append(fields, fields[len(fields)-1])
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record new anonymous struct to explore in next round.
|
||||||
|
nextCount[ft]++
|
||||||
|
if nextCount[ft] == 1 {
|
||||||
|
f := field{name: ft.Name(), index: index, typ: ft}
|
||||||
|
next = append(next, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(byName(fields))
|
||||||
|
|
||||||
|
// Delete all fields that are hidden by the Go rules for embedded fields,
|
||||||
|
// except that fields with TOML tags are promoted.
|
||||||
|
|
||||||
|
// The fields are sorted in primary order of name, secondary order
|
||||||
|
// of field index length. Loop over names; for each name, delete
|
||||||
|
// hidden fields by choosing the one dominant field that survives.
|
||||||
|
out := fields[:0]
|
||||||
|
for advance, i := 0, 0; i < len(fields); i += advance {
|
||||||
|
// One iteration per name.
|
||||||
|
// Find the sequence of fields with the name of this first field.
|
||||||
|
fi := fields[i]
|
||||||
|
name := fi.name
|
||||||
|
for advance = 1; i+advance < len(fields); advance++ {
|
||||||
|
fj := fields[i+advance]
|
||||||
|
if fj.name != name {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if advance == 1 { // Only one field with this name
|
||||||
|
out = append(out, fi)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dominant, ok := dominantField(fields[i : i+advance])
|
||||||
|
if ok {
|
||||||
|
out = append(out, dominant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = out
|
||||||
|
sort.Sort(byIndex(fields))
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// dominantField looks through the fields, all of which are known to
|
||||||
|
// have the same name, to find the single field that dominates the
|
||||||
|
// others using Go's embedding rules, modified by the presence of
|
||||||
|
// TOML tags. If there are multiple top-level fields, the boolean
|
||||||
|
// will be false: This condition is an error in Go and we skip all
|
||||||
|
// the fields.
|
||||||
|
func dominantField(fields []field) (field, bool) {
|
||||||
|
// The fields are sorted in increasing index-length order. The winner
|
||||||
|
// must therefore be one with the shortest index length. Drop all
|
||||||
|
// longer entries, which is easy: just truncate the slice.
|
||||||
|
length := len(fields[0].index)
|
||||||
|
tagged := -1 // Index of first tagged field.
|
||||||
|
for i, f := range fields {
|
||||||
|
if len(f.index) > length {
|
||||||
|
fields = fields[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if f.tag {
|
||||||
|
if tagged >= 0 {
|
||||||
|
// Multiple tagged fields at the same level: conflict.
|
||||||
|
// Return no field.
|
||||||
|
return field{}, false
|
||||||
|
}
|
||||||
|
tagged = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tagged >= 0 {
|
||||||
|
return fields[tagged], true
|
||||||
|
}
|
||||||
|
// All remaining fields have the same length. If there's more than one,
|
||||||
|
// we have a conflict (two fields named "X" at the same level) and we
|
||||||
|
// return no field.
|
||||||
|
if len(fields) > 1 {
|
||||||
|
return field{}, false
|
||||||
|
}
|
||||||
|
return fields[0], true
|
||||||
|
}
|
||||||
|
|
||||||
|
var fieldCache struct {
|
||||||
|
sync.RWMutex
|
||||||
|
m map[reflect.Type][]field
|
||||||
|
}
|
||||||
|
|
||||||
|
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
||||||
|
func cachedTypeFields(t reflect.Type) []field {
|
||||||
|
fieldCache.RLock()
|
||||||
|
f := fieldCache.m[t]
|
||||||
|
fieldCache.RUnlock()
|
||||||
|
if f != nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute fields without lock.
|
||||||
|
// Might duplicate effort but won't hold other computations back.
|
||||||
|
f = typeFields(t)
|
||||||
|
if f == nil {
|
||||||
|
f = []field{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldCache.Lock()
|
||||||
|
if fieldCache.m == nil {
|
||||||
|
fieldCache.m = map[reflect.Type][]field{}
|
||||||
|
}
|
||||||
|
fieldCache.m[t] = f
|
||||||
|
fieldCache.Unlock()
|
||||||
|
return f
|
||||||
|
}
|
||||||
70
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/type_toml.go
generated
vendored
Normal file
70
tests/metrics/cmd/checkmetrics/vendor/github.com/BurntSushi/toml/type_toml.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
// tomlType represents any Go type that corresponds to a TOML type.
|
||||||
|
// While the first draft of the TOML spec has a simplistic type system that
|
||||||
|
// probably doesn't need this level of sophistication, we seem to be militating
|
||||||
|
// toward adding real composite types.
|
||||||
|
type tomlType interface {
|
||||||
|
typeString() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeEqual accepts any two types and returns true if they are equal.
|
||||||
|
func typeEqual(t1, t2 tomlType) bool {
|
||||||
|
if t1 == nil || t2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return t1.typeString() == t2.typeString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeIsTable(t tomlType) bool {
|
||||||
|
return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
type tomlBaseType string
|
||||||
|
|
||||||
|
func (btype tomlBaseType) typeString() string {
|
||||||
|
return string(btype)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (btype tomlBaseType) String() string {
|
||||||
|
return btype.typeString()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
tomlInteger tomlBaseType = "Integer"
|
||||||
|
tomlFloat tomlBaseType = "Float"
|
||||||
|
tomlDatetime tomlBaseType = "Datetime"
|
||||||
|
tomlString tomlBaseType = "String"
|
||||||
|
tomlBool tomlBaseType = "Bool"
|
||||||
|
tomlArray tomlBaseType = "Array"
|
||||||
|
tomlHash tomlBaseType = "Hash"
|
||||||
|
tomlArrayHash tomlBaseType = "ArrayHash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// typeOfPrimitive returns a tomlType of any primitive value in TOML.
|
||||||
|
// Primitive values are: Integer, Float, Datetime, String and Bool.
|
||||||
|
//
|
||||||
|
// Passing a lexer item other than the following will cause a BUG message
|
||||||
|
// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime.
|
||||||
|
func (p *parser) typeOfPrimitive(lexItem item) tomlType {
|
||||||
|
switch lexItem.typ {
|
||||||
|
case itemInteger:
|
||||||
|
return tomlInteger
|
||||||
|
case itemFloat:
|
||||||
|
return tomlFloat
|
||||||
|
case itemDatetime:
|
||||||
|
return tomlDatetime
|
||||||
|
case itemString:
|
||||||
|
return tomlString
|
||||||
|
case itemMultilineString:
|
||||||
|
return tomlString
|
||||||
|
case itemRawString:
|
||||||
|
return tomlString
|
||||||
|
case itemRawMultilineString:
|
||||||
|
return tomlString
|
||||||
|
case itemBool:
|
||||||
|
return tomlBool
|
||||||
|
}
|
||||||
|
p.bug("Cannot infer primitive type of lex item '%s'.", lexItem)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
21
tests/metrics/cmd/checkmetrics/vendor/github.com/cpuguy83/go-md2man/v2/LICENSE.md
generated
vendored
Normal file
21
tests/metrics/cmd/checkmetrics/vendor/github.com/cpuguy83/go-md2man/v2/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Brian Goff
|
||||||
|
|
||||||
|
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.
|
||||||
14
tests/metrics/cmd/checkmetrics/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go
generated
vendored
Normal file
14
tests/metrics/cmd/checkmetrics/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package md2man
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/russross/blackfriday/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Render converts a markdown document into a roff formatted document.
|
||||||
|
func Render(doc []byte) []byte {
|
||||||
|
renderer := NewRoffRenderer()
|
||||||
|
|
||||||
|
return blackfriday.Run(doc,
|
||||||
|
[]blackfriday.Option{blackfriday.WithRenderer(renderer),
|
||||||
|
blackfriday.WithExtensions(renderer.GetExtensions())}...)
|
||||||
|
}
|
||||||
336
tests/metrics/cmd/checkmetrics/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go
generated
vendored
Normal file
336
tests/metrics/cmd/checkmetrics/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go
generated
vendored
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
package md2man
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/russross/blackfriday/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// roffRenderer implements the blackfriday.Renderer interface for creating
|
||||||
|
// roff format (manpages) from markdown text
|
||||||
|
type roffRenderer struct {
|
||||||
|
extensions blackfriday.Extensions
|
||||||
|
listCounters []int
|
||||||
|
firstHeader bool
|
||||||
|
firstDD bool
|
||||||
|
listDepth int
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
titleHeader = ".TH "
|
||||||
|
topLevelHeader = "\n\n.SH "
|
||||||
|
secondLevelHdr = "\n.SH "
|
||||||
|
otherHeader = "\n.SS "
|
||||||
|
crTag = "\n"
|
||||||
|
emphTag = "\\fI"
|
||||||
|
emphCloseTag = "\\fP"
|
||||||
|
strongTag = "\\fB"
|
||||||
|
strongCloseTag = "\\fP"
|
||||||
|
breakTag = "\n.br\n"
|
||||||
|
paraTag = "\n.PP\n"
|
||||||
|
hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n"
|
||||||
|
linkTag = "\n\\[la]"
|
||||||
|
linkCloseTag = "\\[ra]"
|
||||||
|
codespanTag = "\\fB\\fC"
|
||||||
|
codespanCloseTag = "\\fR"
|
||||||
|
codeTag = "\n.PP\n.RS\n\n.nf\n"
|
||||||
|
codeCloseTag = "\n.fi\n.RE\n"
|
||||||
|
quoteTag = "\n.PP\n.RS\n"
|
||||||
|
quoteCloseTag = "\n.RE\n"
|
||||||
|
listTag = "\n.RS\n"
|
||||||
|
listCloseTag = "\n.RE\n"
|
||||||
|
dtTag = "\n.TP\n"
|
||||||
|
dd2Tag = "\n"
|
||||||
|
tableStart = "\n.TS\nallbox;\n"
|
||||||
|
tableEnd = ".TE\n"
|
||||||
|
tableCellStart = "T{\n"
|
||||||
|
tableCellEnd = "\nT}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
|
||||||
|
// from markdown
|
||||||
|
func NewRoffRenderer() *roffRenderer { // nolint: golint
|
||||||
|
var extensions blackfriday.Extensions
|
||||||
|
|
||||||
|
extensions |= blackfriday.NoIntraEmphasis
|
||||||
|
extensions |= blackfriday.Tables
|
||||||
|
extensions |= blackfriday.FencedCode
|
||||||
|
extensions |= blackfriday.SpaceHeadings
|
||||||
|
extensions |= blackfriday.Footnotes
|
||||||
|
extensions |= blackfriday.Titleblock
|
||||||
|
extensions |= blackfriday.DefinitionLists
|
||||||
|
return &roffRenderer{
|
||||||
|
extensions: extensions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns the list of extensions used by this renderer implementation
|
||||||
|
func (r *roffRenderer) GetExtensions() blackfriday.Extensions {
|
||||||
|
return r.extensions
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderHeader handles outputting the header at document start
|
||||||
|
func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) {
|
||||||
|
// disable hyphenation
|
||||||
|
out(w, ".nh\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderFooter handles outputting the footer at the document end; the roff
|
||||||
|
// renderer has no footer information
|
||||||
|
func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderNode is called for each node in a markdown document; based on the node
|
||||||
|
// type the equivalent roff output is sent to the writer
|
||||||
|
func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||||
|
|
||||||
|
var walkAction = blackfriday.GoToNext
|
||||||
|
|
||||||
|
switch node.Type {
|
||||||
|
case blackfriday.Text:
|
||||||
|
escapeSpecialChars(w, node.Literal)
|
||||||
|
case blackfriday.Softbreak:
|
||||||
|
out(w, crTag)
|
||||||
|
case blackfriday.Hardbreak:
|
||||||
|
out(w, breakTag)
|
||||||
|
case blackfriday.Emph:
|
||||||
|
if entering {
|
||||||
|
out(w, emphTag)
|
||||||
|
} else {
|
||||||
|
out(w, emphCloseTag)
|
||||||
|
}
|
||||||
|
case blackfriday.Strong:
|
||||||
|
if entering {
|
||||||
|
out(w, strongTag)
|
||||||
|
} else {
|
||||||
|
out(w, strongCloseTag)
|
||||||
|
}
|
||||||
|
case blackfriday.Link:
|
||||||
|
if !entering {
|
||||||
|
out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag)
|
||||||
|
}
|
||||||
|
case blackfriday.Image:
|
||||||
|
// ignore images
|
||||||
|
walkAction = blackfriday.SkipChildren
|
||||||
|
case blackfriday.Code:
|
||||||
|
out(w, codespanTag)
|
||||||
|
escapeSpecialChars(w, node.Literal)
|
||||||
|
out(w, codespanCloseTag)
|
||||||
|
case blackfriday.Document:
|
||||||
|
break
|
||||||
|
case blackfriday.Paragraph:
|
||||||
|
// roff .PP markers break lists
|
||||||
|
if r.listDepth > 0 {
|
||||||
|
return blackfriday.GoToNext
|
||||||
|
}
|
||||||
|
if entering {
|
||||||
|
out(w, paraTag)
|
||||||
|
} else {
|
||||||
|
out(w, crTag)
|
||||||
|
}
|
||||||
|
case blackfriday.BlockQuote:
|
||||||
|
if entering {
|
||||||
|
out(w, quoteTag)
|
||||||
|
} else {
|
||||||
|
out(w, quoteCloseTag)
|
||||||
|
}
|
||||||
|
case blackfriday.Heading:
|
||||||
|
r.handleHeading(w, node, entering)
|
||||||
|
case blackfriday.HorizontalRule:
|
||||||
|
out(w, hruleTag)
|
||||||
|
case blackfriday.List:
|
||||||
|
r.handleList(w, node, entering)
|
||||||
|
case blackfriday.Item:
|
||||||
|
r.handleItem(w, node, entering)
|
||||||
|
case blackfriday.CodeBlock:
|
||||||
|
out(w, codeTag)
|
||||||
|
escapeSpecialChars(w, node.Literal)
|
||||||
|
out(w, codeCloseTag)
|
||||||
|
case blackfriday.Table:
|
||||||
|
r.handleTable(w, node, entering)
|
||||||
|
case blackfriday.TableHead:
|
||||||
|
case blackfriday.TableBody:
|
||||||
|
case blackfriday.TableRow:
|
||||||
|
// no action as cell entries do all the nroff formatting
|
||||||
|
return blackfriday.GoToNext
|
||||||
|
case blackfriday.TableCell:
|
||||||
|
r.handleTableCell(w, node, entering)
|
||||||
|
case blackfriday.HTMLSpan:
|
||||||
|
// ignore other HTML tags
|
||||||
|
default:
|
||||||
|
fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
|
||||||
|
}
|
||||||
|
return walkAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||||
|
if entering {
|
||||||
|
switch node.Level {
|
||||||
|
case 1:
|
||||||
|
if !r.firstHeader {
|
||||||
|
out(w, titleHeader)
|
||||||
|
r.firstHeader = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
out(w, topLevelHeader)
|
||||||
|
case 2:
|
||||||
|
out(w, secondLevelHdr)
|
||||||
|
default:
|
||||||
|
out(w, otherHeader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *roffRenderer) handleList(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||||
|
openTag := listTag
|
||||||
|
closeTag := listCloseTag
|
||||||
|
if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
|
||||||
|
// tags for definition lists handled within Item node
|
||||||
|
openTag = ""
|
||||||
|
closeTag = ""
|
||||||
|
}
|
||||||
|
if entering {
|
||||||
|
r.listDepth++
|
||||||
|
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
|
||||||
|
r.listCounters = append(r.listCounters, 1)
|
||||||
|
}
|
||||||
|
out(w, openTag)
|
||||||
|
} else {
|
||||||
|
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
|
||||||
|
r.listCounters = r.listCounters[:len(r.listCounters)-1]
|
||||||
|
}
|
||||||
|
out(w, closeTag)
|
||||||
|
r.listDepth--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||||
|
if entering {
|
||||||
|
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
|
||||||
|
out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1]))
|
||||||
|
r.listCounters[len(r.listCounters)-1]++
|
||||||
|
} else if node.ListFlags&blackfriday.ListTypeTerm != 0 {
|
||||||
|
// DT (definition term): line just before DD (see below).
|
||||||
|
out(w, dtTag)
|
||||||
|
r.firstDD = true
|
||||||
|
} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
|
||||||
|
// DD (definition description): line that starts with ": ".
|
||||||
|
//
|
||||||
|
// We have to distinguish between the first DD and the
|
||||||
|
// subsequent ones, as there should be no vertical
|
||||||
|
// whitespace between the DT and the first DD.
|
||||||
|
if r.firstDD {
|
||||||
|
r.firstDD = false
|
||||||
|
} else {
|
||||||
|
out(w, dd2Tag)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out(w, ".IP \\(bu 2\n")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out(w, "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||||
|
if entering {
|
||||||
|
out(w, tableStart)
|
||||||
|
// call walker to count cells (and rows?) so format section can be produced
|
||||||
|
columns := countColumns(node)
|
||||||
|
out(w, strings.Repeat("l ", columns)+"\n")
|
||||||
|
out(w, strings.Repeat("l ", columns)+".\n")
|
||||||
|
} else {
|
||||||
|
out(w, tableEnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||||
|
if entering {
|
||||||
|
var start string
|
||||||
|
if node.Prev != nil && node.Prev.Type == blackfriday.TableCell {
|
||||||
|
start = "\t"
|
||||||
|
}
|
||||||
|
if node.IsHeader {
|
||||||
|
start += codespanTag
|
||||||
|
} else if nodeLiteralSize(node) > 30 {
|
||||||
|
start += tableCellStart
|
||||||
|
}
|
||||||
|
out(w, start)
|
||||||
|
} else {
|
||||||
|
var end string
|
||||||
|
if node.IsHeader {
|
||||||
|
end = codespanCloseTag
|
||||||
|
} else if nodeLiteralSize(node) > 30 {
|
||||||
|
end = tableCellEnd
|
||||||
|
}
|
||||||
|
if node.Next == nil && end != tableCellEnd {
|
||||||
|
// Last cell: need to carriage return if we are at the end of the
|
||||||
|
// header row and content isn't wrapped in a "tablecell"
|
||||||
|
end += crTag
|
||||||
|
}
|
||||||
|
out(w, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeLiteralSize(node *blackfriday.Node) int {
|
||||||
|
total := 0
|
||||||
|
for n := node.FirstChild; n != nil; n = n.FirstChild {
|
||||||
|
total += len(n.Literal)
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// because roff format requires knowing the column count before outputting any table
|
||||||
|
// data we need to walk a table tree and count the columns
|
||||||
|
func countColumns(node *blackfriday.Node) int {
|
||||||
|
var columns int
|
||||||
|
|
||||||
|
node.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||||
|
switch node.Type {
|
||||||
|
case blackfriday.TableRow:
|
||||||
|
if !entering {
|
||||||
|
return blackfriday.Terminate
|
||||||
|
}
|
||||||
|
case blackfriday.TableCell:
|
||||||
|
if entering {
|
||||||
|
columns++
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return blackfriday.GoToNext
|
||||||
|
})
|
||||||
|
return columns
|
||||||
|
}
|
||||||
|
|
||||||
|
func out(w io.Writer, output string) {
|
||||||
|
io.WriteString(w, output) // nolint: errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeSpecialChars(w io.Writer, text []byte) {
|
||||||
|
for i := 0; i < len(text); i++ {
|
||||||
|
// escape initial apostrophe or period
|
||||||
|
if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') {
|
||||||
|
out(w, "\\&")
|
||||||
|
}
|
||||||
|
|
||||||
|
// directly copy normal characters
|
||||||
|
org := i
|
||||||
|
|
||||||
|
for i < len(text) && text[i] != '\\' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i > org {
|
||||||
|
w.Write(text[org:i]) // nolint: errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
// escape a character
|
||||||
|
if i >= len(text) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte{'\\', text[i]}) // nolint: errcheck
|
||||||
|
}
|
||||||
|
}
|
||||||
15
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal file
15
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
145
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
Normal file
145
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||||
|
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||||
|
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||||
|
// tag is deprecated and thus should not be used.
|
||||||
|
// Go versions prior to 1.4 are disabled because they use a different layout
|
||||||
|
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
||||||
|
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||||
|
// not access to the unsafe package is available.
|
||||||
|
UnsafeDisabled = false
|
||||||
|
|
||||||
|
// ptrSize is the size of a pointer on the current arch.
|
||||||
|
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||||
|
)
|
||||||
|
|
||||||
|
type flag uintptr
|
||||||
|
|
||||||
|
var (
|
||||||
|
// flagRO indicates whether the value field of a reflect.Value
|
||||||
|
// is read-only.
|
||||||
|
flagRO flag
|
||||||
|
|
||||||
|
// flagAddr indicates whether the address of the reflect.Value's
|
||||||
|
// value may be taken.
|
||||||
|
flagAddr flag
|
||||||
|
)
|
||||||
|
|
||||||
|
// flagKindMask holds the bits that make up the kind
|
||||||
|
// part of the flags field. In all the supported versions,
|
||||||
|
// it is in the lower 5 bits.
|
||||||
|
const flagKindMask = flag(0x1f)
|
||||||
|
|
||||||
|
// Different versions of Go have used different
|
||||||
|
// bit layouts for the flags type. This table
|
||||||
|
// records the known combinations.
|
||||||
|
var okFlags = []struct {
|
||||||
|
ro, addr flag
|
||||||
|
}{{
|
||||||
|
// From Go 1.4 to 1.5
|
||||||
|
ro: 1 << 5,
|
||||||
|
addr: 1 << 7,
|
||||||
|
}, {
|
||||||
|
// Up to Go tip.
|
||||||
|
ro: 1<<5 | 1<<6,
|
||||||
|
addr: 1 << 8,
|
||||||
|
}}
|
||||||
|
|
||||||
|
var flagValOffset = func() uintptr {
|
||||||
|
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||||
|
if !ok {
|
||||||
|
panic("reflect.Value has no flag field")
|
||||||
|
}
|
||||||
|
return field.Offset
|
||||||
|
}()
|
||||||
|
|
||||||
|
// flagField returns a pointer to the flag field of a reflect.Value.
|
||||||
|
func flagField(v *reflect.Value) *flag {
|
||||||
|
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||||
|
// the typical safety restrictions preventing access to unaddressable and
|
||||||
|
// unexported data. It works by digging the raw pointer to the underlying
|
||||||
|
// value out of the protected value and generating a new unprotected (unsafe)
|
||||||
|
// reflect.Value to it.
|
||||||
|
//
|
||||||
|
// This allows us to check for implementations of the Stringer and error
|
||||||
|
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||||
|
// inaccessible values such as unexported struct fields.
|
||||||
|
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||||
|
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
flagFieldPtr := flagField(&v)
|
||||||
|
*flagFieldPtr &^= flagRO
|
||||||
|
*flagFieldPtr |= flagAddr
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity checks against future reflect package changes
|
||||||
|
// to the type or semantics of the Value.flag field.
|
||||||
|
func init() {
|
||||||
|
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||||
|
if !ok {
|
||||||
|
panic("reflect.Value has no flag field")
|
||||||
|
}
|
||||||
|
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
|
||||||
|
panic("reflect.Value flag field has changed kind")
|
||||||
|
}
|
||||||
|
type t0 int
|
||||||
|
var t struct {
|
||||||
|
A t0
|
||||||
|
// t0 will have flagEmbedRO set.
|
||||||
|
t0
|
||||||
|
// a will have flagStickyRO set
|
||||||
|
a t0
|
||||||
|
}
|
||||||
|
vA := reflect.ValueOf(t).FieldByName("A")
|
||||||
|
va := reflect.ValueOf(t).FieldByName("a")
|
||||||
|
vt0 := reflect.ValueOf(t).FieldByName("t0")
|
||||||
|
|
||||||
|
// Infer flagRO from the difference between the flags
|
||||||
|
// for the (otherwise identical) fields in t.
|
||||||
|
flagPublic := *flagField(&vA)
|
||||||
|
flagWithRO := *flagField(&va) | *flagField(&vt0)
|
||||||
|
flagRO = flagPublic ^ flagWithRO
|
||||||
|
|
||||||
|
// Infer flagAddr from the difference between a value
|
||||||
|
// taken from a pointer and not.
|
||||||
|
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
|
||||||
|
flagNoPtr := *flagField(&vA)
|
||||||
|
flagPtr := *flagField(&vPtrA)
|
||||||
|
flagAddr = flagNoPtr ^ flagPtr
|
||||||
|
|
||||||
|
// Check that the inferred flags tally with one of the known versions.
|
||||||
|
for _, f := range okFlags {
|
||||||
|
if flagRO == f.ro && flagAddr == f.addr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("reflect.Value read-only flag has changed semantics")
|
||||||
|
}
|
||||||
38
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
Normal file
38
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||||
|
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||||
|
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||||
|
// tag is deprecated and thus should not be used.
|
||||||
|
// +build js appengine safe disableunsafe !go1.4
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||||
|
// not access to the unsafe package is available.
|
||||||
|
UnsafeDisabled = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// unsafeReflectValue typically converts the passed reflect.Value into a one
|
||||||
|
// that bypasses the typical safety restrictions preventing access to
|
||||||
|
// unaddressable and unexported data. However, doing this relies on access to
|
||||||
|
// the unsafe package. This is a stub version which simply returns the passed
|
||||||
|
// reflect.Value when the unsafe package is not available.
|
||||||
|
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||||
|
return v
|
||||||
|
}
|
||||||
341
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
Normal file
341
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Some constants in the form of bytes to avoid string overhead. This mirrors
|
||||||
|
// the technique used in the fmt package.
|
||||||
|
var (
|
||||||
|
panicBytes = []byte("(PANIC=")
|
||||||
|
plusBytes = []byte("+")
|
||||||
|
iBytes = []byte("i")
|
||||||
|
trueBytes = []byte("true")
|
||||||
|
falseBytes = []byte("false")
|
||||||
|
interfaceBytes = []byte("(interface {})")
|
||||||
|
commaNewlineBytes = []byte(",\n")
|
||||||
|
newlineBytes = []byte("\n")
|
||||||
|
openBraceBytes = []byte("{")
|
||||||
|
openBraceNewlineBytes = []byte("{\n")
|
||||||
|
closeBraceBytes = []byte("}")
|
||||||
|
asteriskBytes = []byte("*")
|
||||||
|
colonBytes = []byte(":")
|
||||||
|
colonSpaceBytes = []byte(": ")
|
||||||
|
openParenBytes = []byte("(")
|
||||||
|
closeParenBytes = []byte(")")
|
||||||
|
spaceBytes = []byte(" ")
|
||||||
|
pointerChainBytes = []byte("->")
|
||||||
|
nilAngleBytes = []byte("<nil>")
|
||||||
|
maxNewlineBytes = []byte("<max depth reached>\n")
|
||||||
|
maxShortBytes = []byte("<max>")
|
||||||
|
circularBytes = []byte("<already shown>")
|
||||||
|
circularShortBytes = []byte("<shown>")
|
||||||
|
invalidAngleBytes = []byte("<invalid>")
|
||||||
|
openBracketBytes = []byte("[")
|
||||||
|
closeBracketBytes = []byte("]")
|
||||||
|
percentBytes = []byte("%")
|
||||||
|
precisionBytes = []byte(".")
|
||||||
|
openAngleBytes = []byte("<")
|
||||||
|
closeAngleBytes = []byte(">")
|
||||||
|
openMapBytes = []byte("map[")
|
||||||
|
closeMapBytes = []byte("]")
|
||||||
|
lenEqualsBytes = []byte("len=")
|
||||||
|
capEqualsBytes = []byte("cap=")
|
||||||
|
)
|
||||||
|
|
||||||
|
// hexDigits is used to map a decimal value to a hex digit.
|
||||||
|
var hexDigits = "0123456789abcdef"
|
||||||
|
|
||||||
|
// catchPanic handles any panics that might occur during the handleMethods
|
||||||
|
// calls.
|
||||||
|
func catchPanic(w io.Writer, v reflect.Value) {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
w.Write(panicBytes)
|
||||||
|
fmt.Fprintf(w, "%v", err)
|
||||||
|
w.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleMethods attempts to call the Error and String methods on the underlying
|
||||||
|
// type the passed reflect.Value represents and outputes the result to Writer w.
|
||||||
|
//
|
||||||
|
// It handles panics in any called methods by catching and displaying the error
|
||||||
|
// as the formatted value.
|
||||||
|
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
|
||||||
|
// We need an interface to check if the type implements the error or
|
||||||
|
// Stringer interface. However, the reflect package won't give us an
|
||||||
|
// interface on certain things like unexported struct fields in order
|
||||||
|
// to enforce visibility rules. We use unsafe, when it's available,
|
||||||
|
// to bypass these restrictions since this package does not mutate the
|
||||||
|
// values.
|
||||||
|
if !v.CanInterface() {
|
||||||
|
if UnsafeDisabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
v = unsafeReflectValue(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose whether or not to do error and Stringer interface lookups against
|
||||||
|
// the base type or a pointer to the base type depending on settings.
|
||||||
|
// Technically calling one of these methods with a pointer receiver can
|
||||||
|
// mutate the value, however, types which choose to satisify an error or
|
||||||
|
// Stringer interface with a pointer receiver should not be mutating their
|
||||||
|
// state inside these interface methods.
|
||||||
|
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
|
||||||
|
v = unsafeReflectValue(v)
|
||||||
|
}
|
||||||
|
if v.CanAddr() {
|
||||||
|
v = v.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is it an error or Stringer?
|
||||||
|
switch iface := v.Interface().(type) {
|
||||||
|
case error:
|
||||||
|
defer catchPanic(w, v)
|
||||||
|
if cs.ContinueOnMethod {
|
||||||
|
w.Write(openParenBytes)
|
||||||
|
w.Write([]byte(iface.Error()))
|
||||||
|
w.Write(closeParenBytes)
|
||||||
|
w.Write(spaceBytes)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte(iface.Error()))
|
||||||
|
return true
|
||||||
|
|
||||||
|
case fmt.Stringer:
|
||||||
|
defer catchPanic(w, v)
|
||||||
|
if cs.ContinueOnMethod {
|
||||||
|
w.Write(openParenBytes)
|
||||||
|
w.Write([]byte(iface.String()))
|
||||||
|
w.Write(closeParenBytes)
|
||||||
|
w.Write(spaceBytes)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
w.Write([]byte(iface.String()))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// printBool outputs a boolean value as true or false to Writer w.
|
||||||
|
func printBool(w io.Writer, val bool) {
|
||||||
|
if val {
|
||||||
|
w.Write(trueBytes)
|
||||||
|
} else {
|
||||||
|
w.Write(falseBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printInt outputs a signed integer value to Writer w.
|
||||||
|
func printInt(w io.Writer, val int64, base int) {
|
||||||
|
w.Write([]byte(strconv.FormatInt(val, base)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// printUint outputs an unsigned integer value to Writer w.
|
||||||
|
func printUint(w io.Writer, val uint64, base int) {
|
||||||
|
w.Write([]byte(strconv.FormatUint(val, base)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// printFloat outputs a floating point value using the specified precision,
|
||||||
|
// which is expected to be 32 or 64bit, to Writer w.
|
||||||
|
func printFloat(w io.Writer, val float64, precision int) {
|
||||||
|
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// printComplex outputs a complex value using the specified float precision
|
||||||
|
// for the real and imaginary parts to Writer w.
|
||||||
|
func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
||||||
|
r := real(c)
|
||||||
|
w.Write(openParenBytes)
|
||||||
|
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
|
||||||
|
i := imag(c)
|
||||||
|
if i >= 0 {
|
||||||
|
w.Write(plusBytes)
|
||||||
|
}
|
||||||
|
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
|
||||||
|
w.Write(iBytes)
|
||||||
|
w.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
|
||||||
|
// prefix to Writer w.
|
||||||
|
func printHexPtr(w io.Writer, p uintptr) {
|
||||||
|
// Null pointer.
|
||||||
|
num := uint64(p)
|
||||||
|
if num == 0 {
|
||||||
|
w.Write(nilAngleBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
|
||||||
|
buf := make([]byte, 18)
|
||||||
|
|
||||||
|
// It's simpler to construct the hex string right to left.
|
||||||
|
base := uint64(16)
|
||||||
|
i := len(buf) - 1
|
||||||
|
for num >= base {
|
||||||
|
buf[i] = hexDigits[num%base]
|
||||||
|
num /= base
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
buf[i] = hexDigits[num]
|
||||||
|
|
||||||
|
// Add '0x' prefix.
|
||||||
|
i--
|
||||||
|
buf[i] = 'x'
|
||||||
|
i--
|
||||||
|
buf[i] = '0'
|
||||||
|
|
||||||
|
// Strip unused leading bytes.
|
||||||
|
buf = buf[i:]
|
||||||
|
w.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
|
||||||
|
// elements to be sorted.
|
||||||
|
type valuesSorter struct {
|
||||||
|
values []reflect.Value
|
||||||
|
strings []string // either nil or same len and values
|
||||||
|
cs *ConfigState
|
||||||
|
}
|
||||||
|
|
||||||
|
// newValuesSorter initializes a valuesSorter instance, which holds a set of
|
||||||
|
// surrogate keys on which the data should be sorted. It uses flags in
|
||||||
|
// ConfigState to decide if and how to populate those surrogate keys.
|
||||||
|
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
|
||||||
|
vs := &valuesSorter{values: values, cs: cs}
|
||||||
|
if canSortSimply(vs.values[0].Kind()) {
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
if !cs.DisableMethods {
|
||||||
|
vs.strings = make([]string, len(values))
|
||||||
|
for i := range vs.values {
|
||||||
|
b := bytes.Buffer{}
|
||||||
|
if !handleMethods(cs, &b, vs.values[i]) {
|
||||||
|
vs.strings = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
vs.strings[i] = b.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if vs.strings == nil && cs.SpewKeys {
|
||||||
|
vs.strings = make([]string, len(values))
|
||||||
|
for i := range vs.values {
|
||||||
|
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
|
||||||
|
// directly, or whether it should be considered for sorting by surrogate keys
|
||||||
|
// (if the ConfigState allows it).
|
||||||
|
func canSortSimply(kind reflect.Kind) bool {
|
||||||
|
// This switch parallels valueSortLess, except for the default case.
|
||||||
|
switch kind {
|
||||||
|
case reflect.Bool:
|
||||||
|
return true
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
return true
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||||
|
return true
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return true
|
||||||
|
case reflect.String:
|
||||||
|
return true
|
||||||
|
case reflect.Uintptr:
|
||||||
|
return true
|
||||||
|
case reflect.Array:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of values in the slice. It is part of the
|
||||||
|
// sort.Interface implementation.
|
||||||
|
func (s *valuesSorter) Len() int {
|
||||||
|
return len(s.values)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps the values at the passed indices. It is part of the
|
||||||
|
// sort.Interface implementation.
|
||||||
|
func (s *valuesSorter) Swap(i, j int) {
|
||||||
|
s.values[i], s.values[j] = s.values[j], s.values[i]
|
||||||
|
if s.strings != nil {
|
||||||
|
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// valueSortLess returns whether the first value should sort before the second
|
||||||
|
// value. It is used by valueSorter.Less as part of the sort.Interface
|
||||||
|
// implementation.
|
||||||
|
func valueSortLess(a, b reflect.Value) bool {
|
||||||
|
switch a.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return !a.Bool() && b.Bool()
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
return a.Int() < b.Int()
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||||
|
return a.Uint() < b.Uint()
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return a.Float() < b.Float()
|
||||||
|
case reflect.String:
|
||||||
|
return a.String() < b.String()
|
||||||
|
case reflect.Uintptr:
|
||||||
|
return a.Uint() < b.Uint()
|
||||||
|
case reflect.Array:
|
||||||
|
// Compare the contents of both arrays.
|
||||||
|
l := a.Len()
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
av := a.Index(i)
|
||||||
|
bv := b.Index(i)
|
||||||
|
if av.Interface() == bv.Interface() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return valueSortLess(av, bv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a.String() < b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less returns whether the value at index i should sort before the
|
||||||
|
// value at index j. It is part of the sort.Interface implementation.
|
||||||
|
func (s *valuesSorter) Less(i, j int) bool {
|
||||||
|
if s.strings == nil {
|
||||||
|
return valueSortLess(s.values[i], s.values[j])
|
||||||
|
}
|
||||||
|
return s.strings[i] < s.strings[j]
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortValues is a sort function that handles both native types and any type that
|
||||||
|
// can be converted to error or Stringer. Other inputs are sorted according to
|
||||||
|
// their Value.String() value to ensure display stability.
|
||||||
|
func sortValues(values []reflect.Value, cs *ConfigState) {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sort.Sort(newValuesSorter(values, cs))
|
||||||
|
}
|
||||||
306
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
Normal file
306
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigState houses the configuration options used by spew to format and
|
||||||
|
// display values. There is a global instance, Config, that is used to control
|
||||||
|
// all top-level Formatter and Dump functionality. Each ConfigState instance
|
||||||
|
// provides methods equivalent to the top-level functions.
|
||||||
|
//
|
||||||
|
// The zero value for ConfigState provides no indentation. You would typically
|
||||||
|
// want to set it to a space or a tab.
|
||||||
|
//
|
||||||
|
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
|
||||||
|
// with default settings. See the documentation of NewDefaultConfig for default
|
||||||
|
// values.
|
||||||
|
type ConfigState struct {
|
||||||
|
// Indent specifies the string to use for each indentation level. The
|
||||||
|
// global config instance that all top-level functions use set this to a
|
||||||
|
// single space by default. If you would like more indentation, you might
|
||||||
|
// set this to a tab with "\t" or perhaps two spaces with " ".
|
||||||
|
Indent string
|
||||||
|
|
||||||
|
// MaxDepth controls the maximum number of levels to descend into nested
|
||||||
|
// data structures. The default, 0, means there is no limit.
|
||||||
|
//
|
||||||
|
// NOTE: Circular data structures are properly detected, so it is not
|
||||||
|
// necessary to set this value unless you specifically want to limit deeply
|
||||||
|
// nested data structures.
|
||||||
|
MaxDepth int
|
||||||
|
|
||||||
|
// DisableMethods specifies whether or not error and Stringer interfaces are
|
||||||
|
// invoked for types that implement them.
|
||||||
|
DisableMethods bool
|
||||||
|
|
||||||
|
// DisablePointerMethods specifies whether or not to check for and invoke
|
||||||
|
// error and Stringer interfaces on types which only accept a pointer
|
||||||
|
// receiver when the current type is not a pointer.
|
||||||
|
//
|
||||||
|
// NOTE: This might be an unsafe action since calling one of these methods
|
||||||
|
// with a pointer receiver could technically mutate the value, however,
|
||||||
|
// in practice, types which choose to satisify an error or Stringer
|
||||||
|
// interface with a pointer receiver should not be mutating their state
|
||||||
|
// inside these interface methods. As a result, this option relies on
|
||||||
|
// access to the unsafe package, so it will not have any effect when
|
||||||
|
// running in environments without access to the unsafe package such as
|
||||||
|
// Google App Engine or with the "safe" build tag specified.
|
||||||
|
DisablePointerMethods bool
|
||||||
|
|
||||||
|
// DisablePointerAddresses specifies whether to disable the printing of
|
||||||
|
// pointer addresses. This is useful when diffing data structures in tests.
|
||||||
|
DisablePointerAddresses bool
|
||||||
|
|
||||||
|
// DisableCapacities specifies whether to disable the printing of capacities
|
||||||
|
// for arrays, slices, maps and channels. This is useful when diffing
|
||||||
|
// data structures in tests.
|
||||||
|
DisableCapacities bool
|
||||||
|
|
||||||
|
// ContinueOnMethod specifies whether or not recursion should continue once
|
||||||
|
// a custom error or Stringer interface is invoked. The default, false,
|
||||||
|
// means it will print the results of invoking the custom error or Stringer
|
||||||
|
// interface and return immediately instead of continuing to recurse into
|
||||||
|
// the internals of the data type.
|
||||||
|
//
|
||||||
|
// NOTE: This flag does not have any effect if method invocation is disabled
|
||||||
|
// via the DisableMethods or DisablePointerMethods options.
|
||||||
|
ContinueOnMethod bool
|
||||||
|
|
||||||
|
// SortKeys specifies map keys should be sorted before being printed. Use
|
||||||
|
// this to have a more deterministic, diffable output. Note that only
|
||||||
|
// native types (bool, int, uint, floats, uintptr and string) and types
|
||||||
|
// that support the error or Stringer interfaces (if methods are
|
||||||
|
// enabled) are supported, with other types sorted according to the
|
||||||
|
// reflect.Value.String() output which guarantees display stability.
|
||||||
|
SortKeys bool
|
||||||
|
|
||||||
|
// SpewKeys specifies that, as a last resort attempt, map keys should
|
||||||
|
// be spewed to strings and sorted by those strings. This is only
|
||||||
|
// considered if SortKeys is true.
|
||||||
|
SpewKeys bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config is the active configuration of the top-level functions.
|
||||||
|
// The configuration can be changed by modifying the contents of spew.Config.
|
||||||
|
var Config = ConfigState{Indent: " "}
|
||||||
|
|
||||||
|
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the formatted string as a value that satisfies error. See NewFormatter
|
||||||
|
// for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
|
||||||
|
return fmt.Errorf(format, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprint(w, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintf(w, format, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintln(w, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Print(c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Printf(format, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Println(c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Sprint(a ...interface{}) string {
|
||||||
|
return fmt.Sprint(c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
|
||||||
|
return fmt.Sprintf(format, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||||
|
// were passed with a Formatter interface returned by c.NewFormatter. It
|
||||||
|
// returns the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Sprintln(a ...interface{}) string {
|
||||||
|
return fmt.Sprintln(c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||||
|
interface. As a result, it integrates cleanly with standard fmt package
|
||||||
|
printing functions. The formatter is useful for inline printing of smaller data
|
||||||
|
types similar to the standard %v format specifier.
|
||||||
|
|
||||||
|
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||||
|
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
|
||||||
|
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||||
|
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||||
|
the width and precision arguments (however they will still work on the format
|
||||||
|
specifiers not handled by the custom formatter).
|
||||||
|
|
||||||
|
Typically this function shouldn't be called directly. It is much easier to make
|
||||||
|
use of the custom formatter by calling one of the convenience functions such as
|
||||||
|
c.Printf, c.Println, or c.Printf.
|
||||||
|
*/
|
||||||
|
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
|
||||||
|
return newFormatter(c, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||||
|
// exactly the same as Dump.
|
||||||
|
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
|
||||||
|
fdump(c, w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dump displays the passed parameters to standard out with newlines, customizable
|
||||||
|
indentation, and additional debug information such as complete types and all
|
||||||
|
pointer addresses used to indirect to the final value. It provides the
|
||||||
|
following features over the built-in printing facilities provided by the fmt
|
||||||
|
package:
|
||||||
|
|
||||||
|
* Pointers are dereferenced and followed
|
||||||
|
* Circular data structures are detected and handled properly
|
||||||
|
* Custom Stringer/error interfaces are optionally invoked, including
|
||||||
|
on unexported types
|
||||||
|
* Custom types which only implement the Stringer/error interfaces via
|
||||||
|
a pointer receiver are optionally invoked when passing non-pointer
|
||||||
|
variables
|
||||||
|
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||||
|
includes offsets, byte values in hex, and ASCII output
|
||||||
|
|
||||||
|
The configuration options are controlled by modifying the public members
|
||||||
|
of c. See ConfigState for options documentation.
|
||||||
|
|
||||||
|
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||||
|
get the formatted result as a string.
|
||||||
|
*/
|
||||||
|
func (c *ConfigState) Dump(a ...interface{}) {
|
||||||
|
fdump(c, os.Stdout, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||||
|
// as Dump.
|
||||||
|
func (c *ConfigState) Sdump(a ...interface{}) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fdump(c, &buf, a...)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||||
|
// length with each argument converted to a spew Formatter interface using
|
||||||
|
// the ConfigState associated with s.
|
||||||
|
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
|
||||||
|
formatters = make([]interface{}, len(args))
|
||||||
|
for index, arg := range args {
|
||||||
|
formatters[index] = newFormatter(c, arg)
|
||||||
|
}
|
||||||
|
return formatters
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a ConfigState with the following default settings.
|
||||||
|
//
|
||||||
|
// Indent: " "
|
||||||
|
// MaxDepth: 0
|
||||||
|
// DisableMethods: false
|
||||||
|
// DisablePointerMethods: false
|
||||||
|
// ContinueOnMethod: false
|
||||||
|
// SortKeys: false
|
||||||
|
func NewDefaultConfig() *ConfigState {
|
||||||
|
return &ConfigState{Indent: " "}
|
||||||
|
}
|
||||||
211
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
Normal file
211
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package spew implements a deep pretty printer for Go data structures to aid in
|
||||||
|
debugging.
|
||||||
|
|
||||||
|
A quick overview of the additional features spew provides over the built-in
|
||||||
|
printing facilities for Go data types are as follows:
|
||||||
|
|
||||||
|
* Pointers are dereferenced and followed
|
||||||
|
* Circular data structures are detected and handled properly
|
||||||
|
* Custom Stringer/error interfaces are optionally invoked, including
|
||||||
|
on unexported types
|
||||||
|
* Custom types which only implement the Stringer/error interfaces via
|
||||||
|
a pointer receiver are optionally invoked when passing non-pointer
|
||||||
|
variables
|
||||||
|
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||||
|
includes offsets, byte values in hex, and ASCII output (only when using
|
||||||
|
Dump style)
|
||||||
|
|
||||||
|
There are two different approaches spew allows for dumping Go data structures:
|
||||||
|
|
||||||
|
* Dump style which prints with newlines, customizable indentation,
|
||||||
|
and additional debug information such as types and all pointer addresses
|
||||||
|
used to indirect to the final value
|
||||||
|
* A custom Formatter interface that integrates cleanly with the standard fmt
|
||||||
|
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
|
||||||
|
similar to the default %v while providing the additional functionality
|
||||||
|
outlined above and passing unsupported format verbs such as %x and %q
|
||||||
|
along to fmt
|
||||||
|
|
||||||
|
Quick Start
|
||||||
|
|
||||||
|
This section demonstrates how to quickly get started with spew. See the
|
||||||
|
sections below for further details on formatting and configuration options.
|
||||||
|
|
||||||
|
To dump a variable with full newlines, indentation, type, and pointer
|
||||||
|
information use Dump, Fdump, or Sdump:
|
||||||
|
spew.Dump(myVar1, myVar2, ...)
|
||||||
|
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||||
|
str := spew.Sdump(myVar1, myVar2, ...)
|
||||||
|
|
||||||
|
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||||
|
printing style, use the convenience wrappers Printf, Fprintf, etc with
|
||||||
|
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
|
||||||
|
%#+v (adds types and pointer addresses):
|
||||||
|
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
|
||||||
|
Configuration Options
|
||||||
|
|
||||||
|
Configuration of spew is handled by fields in the ConfigState type. For
|
||||||
|
convenience, all of the top-level functions use a global state available
|
||||||
|
via the spew.Config global.
|
||||||
|
|
||||||
|
It is also possible to create a ConfigState instance that provides methods
|
||||||
|
equivalent to the top-level functions. This allows concurrent configuration
|
||||||
|
options. See the ConfigState documentation for more details.
|
||||||
|
|
||||||
|
The following configuration options are available:
|
||||||
|
* Indent
|
||||||
|
String to use for each indentation level for Dump functions.
|
||||||
|
It is a single space by default. A popular alternative is "\t".
|
||||||
|
|
||||||
|
* MaxDepth
|
||||||
|
Maximum number of levels to descend into nested data structures.
|
||||||
|
There is no limit by default.
|
||||||
|
|
||||||
|
* DisableMethods
|
||||||
|
Disables invocation of error and Stringer interface methods.
|
||||||
|
Method invocation is enabled by default.
|
||||||
|
|
||||||
|
* DisablePointerMethods
|
||||||
|
Disables invocation of error and Stringer interface methods on types
|
||||||
|
which only accept pointer receivers from non-pointer variables.
|
||||||
|
Pointer method invocation is enabled by default.
|
||||||
|
|
||||||
|
* DisablePointerAddresses
|
||||||
|
DisablePointerAddresses specifies whether to disable the printing of
|
||||||
|
pointer addresses. This is useful when diffing data structures in tests.
|
||||||
|
|
||||||
|
* DisableCapacities
|
||||||
|
DisableCapacities specifies whether to disable the printing of
|
||||||
|
capacities for arrays, slices, maps and channels. This is useful when
|
||||||
|
diffing data structures in tests.
|
||||||
|
|
||||||
|
* ContinueOnMethod
|
||||||
|
Enables recursion into types after invoking error and Stringer interface
|
||||||
|
methods. Recursion after method invocation is disabled by default.
|
||||||
|
|
||||||
|
* SortKeys
|
||||||
|
Specifies map keys should be sorted before being printed. Use
|
||||||
|
this to have a more deterministic, diffable output. Note that
|
||||||
|
only native types (bool, int, uint, floats, uintptr and string)
|
||||||
|
and types which implement error or Stringer interfaces are
|
||||||
|
supported with other types sorted according to the
|
||||||
|
reflect.Value.String() output which guarantees display
|
||||||
|
stability. Natural map order is used by default.
|
||||||
|
|
||||||
|
* SpewKeys
|
||||||
|
Specifies that, as a last resort attempt, map keys should be
|
||||||
|
spewed to strings and sorted by those strings. This is only
|
||||||
|
considered if SortKeys is true.
|
||||||
|
|
||||||
|
Dump Usage
|
||||||
|
|
||||||
|
Simply call spew.Dump with a list of variables you want to dump:
|
||||||
|
|
||||||
|
spew.Dump(myVar1, myVar2, ...)
|
||||||
|
|
||||||
|
You may also call spew.Fdump if you would prefer to output to an arbitrary
|
||||||
|
io.Writer. For example, to dump to standard error:
|
||||||
|
|
||||||
|
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
|
||||||
|
|
||||||
|
A third option is to call spew.Sdump to get the formatted output as a string:
|
||||||
|
|
||||||
|
str := spew.Sdump(myVar1, myVar2, ...)
|
||||||
|
|
||||||
|
Sample Dump Output
|
||||||
|
|
||||||
|
See the Dump example for details on the setup of the types and variables being
|
||||||
|
shown here.
|
||||||
|
|
||||||
|
(main.Foo) {
|
||||||
|
unexportedField: (*main.Bar)(0xf84002e210)({
|
||||||
|
flag: (main.Flag) flagTwo,
|
||||||
|
data: (uintptr) <nil>
|
||||||
|
}),
|
||||||
|
ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||||
|
(string) (len=3) "one": (bool) true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
|
||||||
|
command as shown.
|
||||||
|
([]uint8) (len=32 cap=32) {
|
||||||
|
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||||
|
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||||
|
00000020 31 32 |12|
|
||||||
|
}
|
||||||
|
|
||||||
|
Custom Formatter
|
||||||
|
|
||||||
|
Spew provides a custom formatter that implements the fmt.Formatter interface
|
||||||
|
so that it integrates cleanly with standard fmt package printing functions. The
|
||||||
|
formatter is useful for inline printing of smaller data types similar to the
|
||||||
|
standard %v format specifier.
|
||||||
|
|
||||||
|
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||||
|
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||||
|
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||||
|
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||||
|
the width and precision arguments (however they will still work on the format
|
||||||
|
specifiers not handled by the custom formatter).
|
||||||
|
|
||||||
|
Custom Formatter Usage
|
||||||
|
|
||||||
|
The simplest way to make use of the spew custom formatter is to call one of the
|
||||||
|
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
|
||||||
|
functions have syntax you are most likely already familiar with:
|
||||||
|
|
||||||
|
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
spew.Println(myVar, myVar2)
|
||||||
|
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
|
||||||
|
See the Index for the full list convenience functions.
|
||||||
|
|
||||||
|
Sample Formatter Output
|
||||||
|
|
||||||
|
Double pointer to a uint8:
|
||||||
|
%v: <**>5
|
||||||
|
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||||
|
%#v: (**uint8)5
|
||||||
|
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||||
|
|
||||||
|
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||||
|
%v: <*>{1 <*><shown>}
|
||||||
|
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||||
|
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||||
|
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||||
|
|
||||||
|
See the Printf example for details on the setup of variables being shown
|
||||||
|
here.
|
||||||
|
|
||||||
|
Errors
|
||||||
|
|
||||||
|
Since it is possible for custom Stringer/error interfaces to panic, spew
|
||||||
|
detects them and handles them internally by printing the panic information
|
||||||
|
inline with the output. Since spew is intended to provide deep pretty printing
|
||||||
|
capabilities on structures, it intentionally does not return any errors.
|
||||||
|
*/
|
||||||
|
package spew
|
||||||
509
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
Normal file
509
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
Normal file
@@ -0,0 +1,509 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// uint8Type is a reflect.Type representing a uint8. It is used to
|
||||||
|
// convert cgo types to uint8 slices for hexdumping.
|
||||||
|
uint8Type = reflect.TypeOf(uint8(0))
|
||||||
|
|
||||||
|
// cCharRE is a regular expression that matches a cgo char.
|
||||||
|
// It is used to detect character arrays to hexdump them.
|
||||||
|
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
|
||||||
|
|
||||||
|
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||||
|
// char. It is used to detect unsigned character arrays to hexdump
|
||||||
|
// them.
|
||||||
|
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
|
||||||
|
|
||||||
|
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||||
|
// It is used to detect uint8_t arrays to hexdump them.
|
||||||
|
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// dumpState contains information about the state of a dump operation.
|
||||||
|
type dumpState struct {
|
||||||
|
w io.Writer
|
||||||
|
depth int
|
||||||
|
pointers map[uintptr]int
|
||||||
|
ignoreNextType bool
|
||||||
|
ignoreNextIndent bool
|
||||||
|
cs *ConfigState
|
||||||
|
}
|
||||||
|
|
||||||
|
// indent performs indentation according to the depth level and cs.Indent
|
||||||
|
// option.
|
||||||
|
func (d *dumpState) indent() {
|
||||||
|
if d.ignoreNextIndent {
|
||||||
|
d.ignoreNextIndent = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpackValue returns values inside of non-nil interfaces when possible.
|
||||||
|
// This is useful for data types like structs, arrays, slices, and maps which
|
||||||
|
// can contain varying types packed inside an interface.
|
||||||
|
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
|
||||||
|
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
||||||
|
func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||||
|
// Remove pointers at or below the current depth from map used to detect
|
||||||
|
// circular refs.
|
||||||
|
for k, depth := range d.pointers {
|
||||||
|
if depth >= d.depth {
|
||||||
|
delete(d.pointers, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep list of all dereferenced pointers to show later.
|
||||||
|
pointerChain := make([]uintptr, 0)
|
||||||
|
|
||||||
|
// Figure out how many levels of indirection there are by dereferencing
|
||||||
|
// pointers and unpacking interfaces down the chain while detecting circular
|
||||||
|
// references.
|
||||||
|
nilFound := false
|
||||||
|
cycleFound := false
|
||||||
|
indirects := 0
|
||||||
|
ve := v
|
||||||
|
for ve.Kind() == reflect.Ptr {
|
||||||
|
if ve.IsNil() {
|
||||||
|
nilFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
indirects++
|
||||||
|
addr := ve.Pointer()
|
||||||
|
pointerChain = append(pointerChain, addr)
|
||||||
|
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
|
||||||
|
cycleFound = true
|
||||||
|
indirects--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d.pointers[addr] = d.depth
|
||||||
|
|
||||||
|
ve = ve.Elem()
|
||||||
|
if ve.Kind() == reflect.Interface {
|
||||||
|
if ve.IsNil() {
|
||||||
|
nilFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ve = ve.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display type information.
|
||||||
|
d.w.Write(openParenBytes)
|
||||||
|
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||||
|
d.w.Write([]byte(ve.Type().String()))
|
||||||
|
d.w.Write(closeParenBytes)
|
||||||
|
|
||||||
|
// Display pointer information.
|
||||||
|
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
|
||||||
|
d.w.Write(openParenBytes)
|
||||||
|
for i, addr := range pointerChain {
|
||||||
|
if i > 0 {
|
||||||
|
d.w.Write(pointerChainBytes)
|
||||||
|
}
|
||||||
|
printHexPtr(d.w, addr)
|
||||||
|
}
|
||||||
|
d.w.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display dereferenced value.
|
||||||
|
d.w.Write(openParenBytes)
|
||||||
|
switch {
|
||||||
|
case nilFound:
|
||||||
|
d.w.Write(nilAngleBytes)
|
||||||
|
|
||||||
|
case cycleFound:
|
||||||
|
d.w.Write(circularBytes)
|
||||||
|
|
||||||
|
default:
|
||||||
|
d.ignoreNextType = true
|
||||||
|
d.dump(ve)
|
||||||
|
}
|
||||||
|
d.w.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
||||||
|
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
||||||
|
func (d *dumpState) dumpSlice(v reflect.Value) {
|
||||||
|
// Determine whether this type should be hex dumped or not. Also,
|
||||||
|
// for types which should be hexdumped, try to use the underlying data
|
||||||
|
// first, then fall back to trying to convert them to a uint8 slice.
|
||||||
|
var buf []uint8
|
||||||
|
doConvert := false
|
||||||
|
doHexDump := false
|
||||||
|
numEntries := v.Len()
|
||||||
|
if numEntries > 0 {
|
||||||
|
vt := v.Index(0).Type()
|
||||||
|
vts := vt.String()
|
||||||
|
switch {
|
||||||
|
// C types that need to be converted.
|
||||||
|
case cCharRE.MatchString(vts):
|
||||||
|
fallthrough
|
||||||
|
case cUnsignedCharRE.MatchString(vts):
|
||||||
|
fallthrough
|
||||||
|
case cUint8tCharRE.MatchString(vts):
|
||||||
|
doConvert = true
|
||||||
|
|
||||||
|
// Try to use existing uint8 slices and fall back to converting
|
||||||
|
// and copying if that fails.
|
||||||
|
case vt.Kind() == reflect.Uint8:
|
||||||
|
// We need an addressable interface to convert the type
|
||||||
|
// to a byte slice. However, the reflect package won't
|
||||||
|
// give us an interface on certain things like
|
||||||
|
// unexported struct fields in order to enforce
|
||||||
|
// visibility rules. We use unsafe, when available, to
|
||||||
|
// bypass these restrictions since this package does not
|
||||||
|
// mutate the values.
|
||||||
|
vs := v
|
||||||
|
if !vs.CanInterface() || !vs.CanAddr() {
|
||||||
|
vs = unsafeReflectValue(vs)
|
||||||
|
}
|
||||||
|
if !UnsafeDisabled {
|
||||||
|
vs = vs.Slice(0, numEntries)
|
||||||
|
|
||||||
|
// Use the existing uint8 slice if it can be
|
||||||
|
// type asserted.
|
||||||
|
iface := vs.Interface()
|
||||||
|
if slice, ok := iface.([]uint8); ok {
|
||||||
|
buf = slice
|
||||||
|
doHexDump = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The underlying data needs to be converted if it can't
|
||||||
|
// be type asserted to a uint8 slice.
|
||||||
|
doConvert = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy and convert the underlying type if needed.
|
||||||
|
if doConvert && vt.ConvertibleTo(uint8Type) {
|
||||||
|
// Convert and copy each element into a uint8 byte
|
||||||
|
// slice.
|
||||||
|
buf = make([]uint8, numEntries)
|
||||||
|
for i := 0; i < numEntries; i++ {
|
||||||
|
vv := v.Index(i)
|
||||||
|
buf[i] = uint8(vv.Convert(uint8Type).Uint())
|
||||||
|
}
|
||||||
|
doHexDump = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hexdump the entire slice as needed.
|
||||||
|
if doHexDump {
|
||||||
|
indent := strings.Repeat(d.cs.Indent, d.depth)
|
||||||
|
str := indent + hex.Dump(buf)
|
||||||
|
str = strings.Replace(str, "\n", "\n"+indent, -1)
|
||||||
|
str = strings.TrimRight(str, d.cs.Indent)
|
||||||
|
d.w.Write([]byte(str))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively call dump for each item.
|
||||||
|
for i := 0; i < numEntries; i++ {
|
||||||
|
d.dump(d.unpackValue(v.Index(i)))
|
||||||
|
if i < (numEntries - 1) {
|
||||||
|
d.w.Write(commaNewlineBytes)
|
||||||
|
} else {
|
||||||
|
d.w.Write(newlineBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
||||||
|
// value to figure out what kind of object we are dealing with and formats it
|
||||||
|
// appropriately. It is a recursive function, however circular data structures
|
||||||
|
// are detected and handled properly.
|
||||||
|
func (d *dumpState) dump(v reflect.Value) {
|
||||||
|
// Handle invalid reflect values immediately.
|
||||||
|
kind := v.Kind()
|
||||||
|
if kind == reflect.Invalid {
|
||||||
|
d.w.Write(invalidAngleBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle pointers specially.
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
d.indent()
|
||||||
|
d.dumpPtr(v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print type information unless already handled elsewhere.
|
||||||
|
if !d.ignoreNextType {
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(openParenBytes)
|
||||||
|
d.w.Write([]byte(v.Type().String()))
|
||||||
|
d.w.Write(closeParenBytes)
|
||||||
|
d.w.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
d.ignoreNextType = false
|
||||||
|
|
||||||
|
// Display length and capacity if the built-in len and cap functions
|
||||||
|
// work with the value's kind and the len/cap itself is non-zero.
|
||||||
|
valueLen, valueCap := 0, 0
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Array, reflect.Slice, reflect.Chan:
|
||||||
|
valueLen, valueCap = v.Len(), v.Cap()
|
||||||
|
case reflect.Map, reflect.String:
|
||||||
|
valueLen = v.Len()
|
||||||
|
}
|
||||||
|
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
|
||||||
|
d.w.Write(openParenBytes)
|
||||||
|
if valueLen != 0 {
|
||||||
|
d.w.Write(lenEqualsBytes)
|
||||||
|
printInt(d.w, int64(valueLen), 10)
|
||||||
|
}
|
||||||
|
if !d.cs.DisableCapacities && valueCap != 0 {
|
||||||
|
if valueLen != 0 {
|
||||||
|
d.w.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
d.w.Write(capEqualsBytes)
|
||||||
|
printInt(d.w, int64(valueCap), 10)
|
||||||
|
}
|
||||||
|
d.w.Write(closeParenBytes)
|
||||||
|
d.w.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call Stringer/error interfaces if they exist and the handle methods flag
|
||||||
|
// is enabled
|
||||||
|
if !d.cs.DisableMethods {
|
||||||
|
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||||
|
if handled := handleMethods(d.cs, d.w, v); handled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch kind {
|
||||||
|
case reflect.Invalid:
|
||||||
|
// Do nothing. We should never get here since invalid has already
|
||||||
|
// been handled above.
|
||||||
|
|
||||||
|
case reflect.Bool:
|
||||||
|
printBool(d.w, v.Bool())
|
||||||
|
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
printInt(d.w, v.Int(), 10)
|
||||||
|
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||||
|
printUint(d.w, v.Uint(), 10)
|
||||||
|
|
||||||
|
case reflect.Float32:
|
||||||
|
printFloat(d.w, v.Float(), 32)
|
||||||
|
|
||||||
|
case reflect.Float64:
|
||||||
|
printFloat(d.w, v.Float(), 64)
|
||||||
|
|
||||||
|
case reflect.Complex64:
|
||||||
|
printComplex(d.w, v.Complex(), 32)
|
||||||
|
|
||||||
|
case reflect.Complex128:
|
||||||
|
printComplex(d.w, v.Complex(), 64)
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
if v.IsNil() {
|
||||||
|
d.w.Write(nilAngleBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case reflect.Array:
|
||||||
|
d.w.Write(openBraceNewlineBytes)
|
||||||
|
d.depth++
|
||||||
|
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(maxNewlineBytes)
|
||||||
|
} else {
|
||||||
|
d.dumpSlice(v)
|
||||||
|
}
|
||||||
|
d.depth--
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(closeBraceBytes)
|
||||||
|
|
||||||
|
case reflect.String:
|
||||||
|
d.w.Write([]byte(strconv.Quote(v.String())))
|
||||||
|
|
||||||
|
case reflect.Interface:
|
||||||
|
// The only time we should get here is for nil interfaces due to
|
||||||
|
// unpackValue calls.
|
||||||
|
if v.IsNil() {
|
||||||
|
d.w.Write(nilAngleBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Do nothing. We should never get here since pointers have already
|
||||||
|
// been handled above.
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
// nil maps should be indicated as different than empty maps
|
||||||
|
if v.IsNil() {
|
||||||
|
d.w.Write(nilAngleBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
d.w.Write(openBraceNewlineBytes)
|
||||||
|
d.depth++
|
||||||
|
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(maxNewlineBytes)
|
||||||
|
} else {
|
||||||
|
numEntries := v.Len()
|
||||||
|
keys := v.MapKeys()
|
||||||
|
if d.cs.SortKeys {
|
||||||
|
sortValues(keys, d.cs)
|
||||||
|
}
|
||||||
|
for i, key := range keys {
|
||||||
|
d.dump(d.unpackValue(key))
|
||||||
|
d.w.Write(colonSpaceBytes)
|
||||||
|
d.ignoreNextIndent = true
|
||||||
|
d.dump(d.unpackValue(v.MapIndex(key)))
|
||||||
|
if i < (numEntries - 1) {
|
||||||
|
d.w.Write(commaNewlineBytes)
|
||||||
|
} else {
|
||||||
|
d.w.Write(newlineBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.depth--
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(closeBraceBytes)
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
d.w.Write(openBraceNewlineBytes)
|
||||||
|
d.depth++
|
||||||
|
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(maxNewlineBytes)
|
||||||
|
} else {
|
||||||
|
vt := v.Type()
|
||||||
|
numFields := v.NumField()
|
||||||
|
for i := 0; i < numFields; i++ {
|
||||||
|
d.indent()
|
||||||
|
vtf := vt.Field(i)
|
||||||
|
d.w.Write([]byte(vtf.Name))
|
||||||
|
d.w.Write(colonSpaceBytes)
|
||||||
|
d.ignoreNextIndent = true
|
||||||
|
d.dump(d.unpackValue(v.Field(i)))
|
||||||
|
if i < (numFields - 1) {
|
||||||
|
d.w.Write(commaNewlineBytes)
|
||||||
|
} else {
|
||||||
|
d.w.Write(newlineBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.depth--
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(closeBraceBytes)
|
||||||
|
|
||||||
|
case reflect.Uintptr:
|
||||||
|
printHexPtr(d.w, uintptr(v.Uint()))
|
||||||
|
|
||||||
|
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||||
|
printHexPtr(d.w, v.Pointer())
|
||||||
|
|
||||||
|
// There were not any other types at the time this code was written, but
|
||||||
|
// fall back to letting the default fmt package handle it in case any new
|
||||||
|
// types are added.
|
||||||
|
default:
|
||||||
|
if v.CanInterface() {
|
||||||
|
fmt.Fprintf(d.w, "%v", v.Interface())
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(d.w, "%v", v.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fdump is a helper function to consolidate the logic from the various public
|
||||||
|
// methods which take varying writers and config states.
|
||||||
|
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
||||||
|
for _, arg := range a {
|
||||||
|
if arg == nil {
|
||||||
|
w.Write(interfaceBytes)
|
||||||
|
w.Write(spaceBytes)
|
||||||
|
w.Write(nilAngleBytes)
|
||||||
|
w.Write(newlineBytes)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
d := dumpState{w: w, cs: cs}
|
||||||
|
d.pointers = make(map[uintptr]int)
|
||||||
|
d.dump(reflect.ValueOf(arg))
|
||||||
|
d.w.Write(newlineBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||||
|
// exactly the same as Dump.
|
||||||
|
func Fdump(w io.Writer, a ...interface{}) {
|
||||||
|
fdump(&Config, w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||||
|
// as Dump.
|
||||||
|
func Sdump(a ...interface{}) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fdump(&Config, &buf, a...)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dump displays the passed parameters to standard out with newlines, customizable
|
||||||
|
indentation, and additional debug information such as complete types and all
|
||||||
|
pointer addresses used to indirect to the final value. It provides the
|
||||||
|
following features over the built-in printing facilities provided by the fmt
|
||||||
|
package:
|
||||||
|
|
||||||
|
* Pointers are dereferenced and followed
|
||||||
|
* Circular data structures are detected and handled properly
|
||||||
|
* Custom Stringer/error interfaces are optionally invoked, including
|
||||||
|
on unexported types
|
||||||
|
* Custom types which only implement the Stringer/error interfaces via
|
||||||
|
a pointer receiver are optionally invoked when passing non-pointer
|
||||||
|
variables
|
||||||
|
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||||
|
includes offsets, byte values in hex, and ASCII output
|
||||||
|
|
||||||
|
The configuration options are controlled by an exported package global,
|
||||||
|
spew.Config. See ConfigState for options documentation.
|
||||||
|
|
||||||
|
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||||
|
get the formatted result as a string.
|
||||||
|
*/
|
||||||
|
func Dump(a ...interface{}) {
|
||||||
|
fdump(&Config, os.Stdout, a...)
|
||||||
|
}
|
||||||
419
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
Normal file
419
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// supportedFlags is a list of all the character flags supported by fmt package.
|
||||||
|
const supportedFlags = "0-+# "
|
||||||
|
|
||||||
|
// formatState implements the fmt.Formatter interface and contains information
|
||||||
|
// about the state of a formatting operation. The NewFormatter function can
|
||||||
|
// be used to get a new Formatter which can be used directly as arguments
|
||||||
|
// in standard fmt package printing calls.
|
||||||
|
type formatState struct {
|
||||||
|
value interface{}
|
||||||
|
fs fmt.State
|
||||||
|
depth int
|
||||||
|
pointers map[uintptr]int
|
||||||
|
ignoreNextType bool
|
||||||
|
cs *ConfigState
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildDefaultFormat recreates the original format string without precision
|
||||||
|
// and width information to pass in to fmt.Sprintf in the case of an
|
||||||
|
// unrecognized type. Unless new types are added to the language, this
|
||||||
|
// function won't ever be called.
|
||||||
|
func (f *formatState) buildDefaultFormat() (format string) {
|
||||||
|
buf := bytes.NewBuffer(percentBytes)
|
||||||
|
|
||||||
|
for _, flag := range supportedFlags {
|
||||||
|
if f.fs.Flag(int(flag)) {
|
||||||
|
buf.WriteRune(flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune('v')
|
||||||
|
|
||||||
|
format = buf.String()
|
||||||
|
return format
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructOrigFormat recreates the original format string including precision
|
||||||
|
// and width information to pass along to the standard fmt package. This allows
|
||||||
|
// automatic deferral of all format strings this package doesn't support.
|
||||||
|
func (f *formatState) constructOrigFormat(verb rune) (format string) {
|
||||||
|
buf := bytes.NewBuffer(percentBytes)
|
||||||
|
|
||||||
|
for _, flag := range supportedFlags {
|
||||||
|
if f.fs.Flag(int(flag)) {
|
||||||
|
buf.WriteRune(flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if width, ok := f.fs.Width(); ok {
|
||||||
|
buf.WriteString(strconv.Itoa(width))
|
||||||
|
}
|
||||||
|
|
||||||
|
if precision, ok := f.fs.Precision(); ok {
|
||||||
|
buf.Write(precisionBytes)
|
||||||
|
buf.WriteString(strconv.Itoa(precision))
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune(verb)
|
||||||
|
|
||||||
|
format = buf.String()
|
||||||
|
return format
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpackValue returns values inside of non-nil interfaces when possible and
|
||||||
|
// ensures that types for values which have been unpacked from an interface
|
||||||
|
// are displayed when the show types flag is also set.
|
||||||
|
// This is useful for data types like structs, arrays, slices, and maps which
|
||||||
|
// can contain varying types packed inside an interface.
|
||||||
|
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
|
||||||
|
if v.Kind() == reflect.Interface {
|
||||||
|
f.ignoreNextType = false
|
||||||
|
if !v.IsNil() {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
||||||
|
func (f *formatState) formatPtr(v reflect.Value) {
|
||||||
|
// Display nil if top level pointer is nil.
|
||||||
|
showTypes := f.fs.Flag('#')
|
||||||
|
if v.IsNil() && (!showTypes || f.ignoreNextType) {
|
||||||
|
f.fs.Write(nilAngleBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove pointers at or below the current depth from map used to detect
|
||||||
|
// circular refs.
|
||||||
|
for k, depth := range f.pointers {
|
||||||
|
if depth >= f.depth {
|
||||||
|
delete(f.pointers, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep list of all dereferenced pointers to possibly show later.
|
||||||
|
pointerChain := make([]uintptr, 0)
|
||||||
|
|
||||||
|
// Figure out how many levels of indirection there are by derferencing
|
||||||
|
// pointers and unpacking interfaces down the chain while detecting circular
|
||||||
|
// references.
|
||||||
|
nilFound := false
|
||||||
|
cycleFound := false
|
||||||
|
indirects := 0
|
||||||
|
ve := v
|
||||||
|
for ve.Kind() == reflect.Ptr {
|
||||||
|
if ve.IsNil() {
|
||||||
|
nilFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
indirects++
|
||||||
|
addr := ve.Pointer()
|
||||||
|
pointerChain = append(pointerChain, addr)
|
||||||
|
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
||||||
|
cycleFound = true
|
||||||
|
indirects--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
f.pointers[addr] = f.depth
|
||||||
|
|
||||||
|
ve = ve.Elem()
|
||||||
|
if ve.Kind() == reflect.Interface {
|
||||||
|
if ve.IsNil() {
|
||||||
|
nilFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ve = ve.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display type or indirection level depending on flags.
|
||||||
|
if showTypes && !f.ignoreNextType {
|
||||||
|
f.fs.Write(openParenBytes)
|
||||||
|
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||||
|
f.fs.Write([]byte(ve.Type().String()))
|
||||||
|
f.fs.Write(closeParenBytes)
|
||||||
|
} else {
|
||||||
|
if nilFound || cycleFound {
|
||||||
|
indirects += strings.Count(ve.Type().String(), "*")
|
||||||
|
}
|
||||||
|
f.fs.Write(openAngleBytes)
|
||||||
|
f.fs.Write([]byte(strings.Repeat("*", indirects)))
|
||||||
|
f.fs.Write(closeAngleBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display pointer information depending on flags.
|
||||||
|
if f.fs.Flag('+') && (len(pointerChain) > 0) {
|
||||||
|
f.fs.Write(openParenBytes)
|
||||||
|
for i, addr := range pointerChain {
|
||||||
|
if i > 0 {
|
||||||
|
f.fs.Write(pointerChainBytes)
|
||||||
|
}
|
||||||
|
printHexPtr(f.fs, addr)
|
||||||
|
}
|
||||||
|
f.fs.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display dereferenced value.
|
||||||
|
switch {
|
||||||
|
case nilFound:
|
||||||
|
f.fs.Write(nilAngleBytes)
|
||||||
|
|
||||||
|
case cycleFound:
|
||||||
|
f.fs.Write(circularShortBytes)
|
||||||
|
|
||||||
|
default:
|
||||||
|
f.ignoreNextType = true
|
||||||
|
f.format(ve)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// format is the main workhorse for providing the Formatter interface. It
|
||||||
|
// uses the passed reflect value to figure out what kind of object we are
|
||||||
|
// dealing with and formats it appropriately. It is a recursive function,
|
||||||
|
// however circular data structures are detected and handled properly.
|
||||||
|
func (f *formatState) format(v reflect.Value) {
|
||||||
|
// Handle invalid reflect values immediately.
|
||||||
|
kind := v.Kind()
|
||||||
|
if kind == reflect.Invalid {
|
||||||
|
f.fs.Write(invalidAngleBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle pointers specially.
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
f.formatPtr(v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print type information unless already handled elsewhere.
|
||||||
|
if !f.ignoreNextType && f.fs.Flag('#') {
|
||||||
|
f.fs.Write(openParenBytes)
|
||||||
|
f.fs.Write([]byte(v.Type().String()))
|
||||||
|
f.fs.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
f.ignoreNextType = false
|
||||||
|
|
||||||
|
// Call Stringer/error interfaces if they exist and the handle methods
|
||||||
|
// flag is enabled.
|
||||||
|
if !f.cs.DisableMethods {
|
||||||
|
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||||
|
if handled := handleMethods(f.cs, f.fs, v); handled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch kind {
|
||||||
|
case reflect.Invalid:
|
||||||
|
// Do nothing. We should never get here since invalid has already
|
||||||
|
// been handled above.
|
||||||
|
|
||||||
|
case reflect.Bool:
|
||||||
|
printBool(f.fs, v.Bool())
|
||||||
|
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
printInt(f.fs, v.Int(), 10)
|
||||||
|
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||||
|
printUint(f.fs, v.Uint(), 10)
|
||||||
|
|
||||||
|
case reflect.Float32:
|
||||||
|
printFloat(f.fs, v.Float(), 32)
|
||||||
|
|
||||||
|
case reflect.Float64:
|
||||||
|
printFloat(f.fs, v.Float(), 64)
|
||||||
|
|
||||||
|
case reflect.Complex64:
|
||||||
|
printComplex(f.fs, v.Complex(), 32)
|
||||||
|
|
||||||
|
case reflect.Complex128:
|
||||||
|
printComplex(f.fs, v.Complex(), 64)
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
if v.IsNil() {
|
||||||
|
f.fs.Write(nilAngleBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case reflect.Array:
|
||||||
|
f.fs.Write(openBracketBytes)
|
||||||
|
f.depth++
|
||||||
|
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||||
|
f.fs.Write(maxShortBytes)
|
||||||
|
} else {
|
||||||
|
numEntries := v.Len()
|
||||||
|
for i := 0; i < numEntries; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
f.fs.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
f.ignoreNextType = true
|
||||||
|
f.format(f.unpackValue(v.Index(i)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.depth--
|
||||||
|
f.fs.Write(closeBracketBytes)
|
||||||
|
|
||||||
|
case reflect.String:
|
||||||
|
f.fs.Write([]byte(v.String()))
|
||||||
|
|
||||||
|
case reflect.Interface:
|
||||||
|
// The only time we should get here is for nil interfaces due to
|
||||||
|
// unpackValue calls.
|
||||||
|
if v.IsNil() {
|
||||||
|
f.fs.Write(nilAngleBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Do nothing. We should never get here since pointers have already
|
||||||
|
// been handled above.
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
// nil maps should be indicated as different than empty maps
|
||||||
|
if v.IsNil() {
|
||||||
|
f.fs.Write(nilAngleBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
f.fs.Write(openMapBytes)
|
||||||
|
f.depth++
|
||||||
|
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||||
|
f.fs.Write(maxShortBytes)
|
||||||
|
} else {
|
||||||
|
keys := v.MapKeys()
|
||||||
|
if f.cs.SortKeys {
|
||||||
|
sortValues(keys, f.cs)
|
||||||
|
}
|
||||||
|
for i, key := range keys {
|
||||||
|
if i > 0 {
|
||||||
|
f.fs.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
f.ignoreNextType = true
|
||||||
|
f.format(f.unpackValue(key))
|
||||||
|
f.fs.Write(colonBytes)
|
||||||
|
f.ignoreNextType = true
|
||||||
|
f.format(f.unpackValue(v.MapIndex(key)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.depth--
|
||||||
|
f.fs.Write(closeMapBytes)
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
numFields := v.NumField()
|
||||||
|
f.fs.Write(openBraceBytes)
|
||||||
|
f.depth++
|
||||||
|
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||||
|
f.fs.Write(maxShortBytes)
|
||||||
|
} else {
|
||||||
|
vt := v.Type()
|
||||||
|
for i := 0; i < numFields; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
f.fs.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
vtf := vt.Field(i)
|
||||||
|
if f.fs.Flag('+') || f.fs.Flag('#') {
|
||||||
|
f.fs.Write([]byte(vtf.Name))
|
||||||
|
f.fs.Write(colonBytes)
|
||||||
|
}
|
||||||
|
f.format(f.unpackValue(v.Field(i)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.depth--
|
||||||
|
f.fs.Write(closeBraceBytes)
|
||||||
|
|
||||||
|
case reflect.Uintptr:
|
||||||
|
printHexPtr(f.fs, uintptr(v.Uint()))
|
||||||
|
|
||||||
|
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||||
|
printHexPtr(f.fs, v.Pointer())
|
||||||
|
|
||||||
|
// There were not any other types at the time this code was written, but
|
||||||
|
// fall back to letting the default fmt package handle it if any get added.
|
||||||
|
default:
|
||||||
|
format := f.buildDefaultFormat()
|
||||||
|
if v.CanInterface() {
|
||||||
|
fmt.Fprintf(f.fs, format, v.Interface())
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f.fs, format, v.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
|
||||||
|
// details.
|
||||||
|
func (f *formatState) Format(fs fmt.State, verb rune) {
|
||||||
|
f.fs = fs
|
||||||
|
|
||||||
|
// Use standard formatting for verbs that are not v.
|
||||||
|
if verb != 'v' {
|
||||||
|
format := f.constructOrigFormat(verb)
|
||||||
|
fmt.Fprintf(fs, format, f.value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.value == nil {
|
||||||
|
if fs.Flag('#') {
|
||||||
|
fs.Write(interfaceBytes)
|
||||||
|
}
|
||||||
|
fs.Write(nilAngleBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.format(reflect.ValueOf(f.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFormatter is a helper function to consolidate the logic from the various
|
||||||
|
// public methods which take varying config states.
|
||||||
|
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
|
||||||
|
fs := &formatState{value: v, cs: cs}
|
||||||
|
fs.pointers = make(map[uintptr]int)
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||||
|
interface. As a result, it integrates cleanly with standard fmt package
|
||||||
|
printing functions. The formatter is useful for inline printing of smaller data
|
||||||
|
types similar to the standard %v format specifier.
|
||||||
|
|
||||||
|
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||||
|
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||||
|
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||||
|
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||||
|
the width and precision arguments (however they will still work on the format
|
||||||
|
specifiers not handled by the custom formatter).
|
||||||
|
|
||||||
|
Typically this function shouldn't be called directly. It is much easier to make
|
||||||
|
use of the custom formatter by calling one of the convenience functions such as
|
||||||
|
Printf, Println, or Fprintf.
|
||||||
|
*/
|
||||||
|
func NewFormatter(v interface{}) fmt.Formatter {
|
||||||
|
return newFormatter(&Config, v)
|
||||||
|
}
|
||||||
148
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
Normal file
148
tests/metrics/cmd/checkmetrics/vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the formatted string as a value that satisfies error. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Errorf(format string, a ...interface{}) (err error) {
|
||||||
|
return fmt.Errorf(format, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprint(w, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintf(w, format, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintln(w, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Print(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Print(convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Printf(format string, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Printf(format, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Println(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Println(convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Sprint(a ...interface{}) string {
|
||||||
|
return fmt.Sprint(convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Sprintf(format string, a ...interface{}) string {
|
||||||
|
return fmt.Sprintf(format, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||||
|
// were passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Sprintln(a ...interface{}) string {
|
||||||
|
return fmt.Sprintln(convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||||
|
// length with each argument converted to a default spew Formatter interface.
|
||||||
|
func convertArgs(args []interface{}) (formatters []interface{}) {
|
||||||
|
formatters = make([]interface{}, len(args))
|
||||||
|
for index, arg := range args {
|
||||||
|
formatters[index] = NewFormatter(arg)
|
||||||
|
}
|
||||||
|
return formatters
|
||||||
|
}
|
||||||
16
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/.travis.yml
generated
vendored
Normal file
16
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
language: go
|
||||||
|
sudo: false
|
||||||
|
go:
|
||||||
|
- 1.13.x
|
||||||
|
- tip
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get -t -v ./...
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go generate
|
||||||
|
- git diff --cached --exit-code
|
||||||
|
- ./go.test.sh
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
21
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/LICENSE
generated
vendored
Normal file
21
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Yasuhiro Matsumoto
|
||||||
|
|
||||||
|
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.
|
||||||
27
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/README.md
generated
vendored
Normal file
27
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/README.md
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
go-runewidth
|
||||||
|
============
|
||||||
|
|
||||||
|
[](https://travis-ci.org/mattn/go-runewidth)
|
||||||
|
[](https://codecov.io/gh/mattn/go-runewidth)
|
||||||
|
[](http://godoc.org/github.com/mattn/go-runewidth)
|
||||||
|
[](https://goreportcard.com/report/github.com/mattn/go-runewidth)
|
||||||
|
|
||||||
|
Provides functions to get fixed width of the character or string.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
```go
|
||||||
|
runewidth.StringWidth("つのだ☆HIRO") == 12
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Author
|
||||||
|
------
|
||||||
|
|
||||||
|
Yasuhiro Matsumoto
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
under the MIT License: http://mattn.mit-license.org/2013
|
||||||
12
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/go.test.sh
generated
vendored
Normal file
12
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/go.test.sh
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
echo "" > coverage.txt
|
||||||
|
|
||||||
|
for d in $(go list ./... | grep -v vendor); do
|
||||||
|
go test -race -coverprofile=profile.out -covermode=atomic "$d"
|
||||||
|
if [ -f profile.out ]; then
|
||||||
|
cat profile.out >> coverage.txt
|
||||||
|
rm profile.out
|
||||||
|
fi
|
||||||
|
done
|
||||||
257
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth.go
generated
vendored
Normal file
257
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
package runewidth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run script/generate.go
|
||||||
|
|
||||||
|
var (
|
||||||
|
// EastAsianWidth will be set true if the current locale is CJK
|
||||||
|
EastAsianWidth bool
|
||||||
|
|
||||||
|
// ZeroWidthJoiner is flag to set to use UTR#51 ZWJ
|
||||||
|
ZeroWidthJoiner bool
|
||||||
|
|
||||||
|
// DefaultCondition is a condition in current locale
|
||||||
|
DefaultCondition = &Condition{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
handleEnv()
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleEnv() {
|
||||||
|
env := os.Getenv("RUNEWIDTH_EASTASIAN")
|
||||||
|
if env == "" {
|
||||||
|
EastAsianWidth = IsEastAsian()
|
||||||
|
} else {
|
||||||
|
EastAsianWidth = env == "1"
|
||||||
|
}
|
||||||
|
// update DefaultCondition
|
||||||
|
DefaultCondition.EastAsianWidth = EastAsianWidth
|
||||||
|
DefaultCondition.ZeroWidthJoiner = ZeroWidthJoiner
|
||||||
|
}
|
||||||
|
|
||||||
|
type interval struct {
|
||||||
|
first rune
|
||||||
|
last rune
|
||||||
|
}
|
||||||
|
|
||||||
|
type table []interval
|
||||||
|
|
||||||
|
func inTables(r rune, ts ...table) bool {
|
||||||
|
for _, t := range ts {
|
||||||
|
if inTable(r, t) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func inTable(r rune, t table) bool {
|
||||||
|
if r < t[0].first {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
bot := 0
|
||||||
|
top := len(t) - 1
|
||||||
|
for top >= bot {
|
||||||
|
mid := (bot + top) >> 1
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case t[mid].last < r:
|
||||||
|
bot = mid + 1
|
||||||
|
case t[mid].first > r:
|
||||||
|
top = mid - 1
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var private = table{
|
||||||
|
{0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD},
|
||||||
|
}
|
||||||
|
|
||||||
|
var nonprint = table{
|
||||||
|
{0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD},
|
||||||
|
{0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F},
|
||||||
|
{0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF},
|
||||||
|
{0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Condition have flag EastAsianWidth whether the current locale is CJK or not.
|
||||||
|
type Condition struct {
|
||||||
|
EastAsianWidth bool
|
||||||
|
ZeroWidthJoiner bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCondition return new instance of Condition which is current locale.
|
||||||
|
func NewCondition() *Condition {
|
||||||
|
return &Condition{
|
||||||
|
EastAsianWidth: EastAsianWidth,
|
||||||
|
ZeroWidthJoiner: ZeroWidthJoiner,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RuneWidth returns the number of cells in r.
|
||||||
|
// See http://www.unicode.org/reports/tr11/
|
||||||
|
func (c *Condition) RuneWidth(r rune) int {
|
||||||
|
switch {
|
||||||
|
case r < 0 || r > 0x10FFFF || inTables(r, nonprint, combining, notassigned):
|
||||||
|
return 0
|
||||||
|
case (c.EastAsianWidth && IsAmbiguousWidth(r)) || inTables(r, doublewidth):
|
||||||
|
return 2
|
||||||
|
default:
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Condition) stringWidth(s string) (width int) {
|
||||||
|
for _, r := range []rune(s) {
|
||||||
|
width += c.RuneWidth(r)
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Condition) stringWidthZeroJoiner(s string) (width int) {
|
||||||
|
r1, r2 := rune(0), rune(0)
|
||||||
|
for _, r := range []rune(s) {
|
||||||
|
if r == 0xFE0E || r == 0xFE0F {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
w := c.RuneWidth(r)
|
||||||
|
if r2 == 0x200D && inTables(r, emoji) && inTables(r1, emoji) {
|
||||||
|
if width < w {
|
||||||
|
width = w
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
width += w
|
||||||
|
}
|
||||||
|
r1, r2 = r2, r
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringWidth return width as you can see
|
||||||
|
func (c *Condition) StringWidth(s string) (width int) {
|
||||||
|
if c.ZeroWidthJoiner {
|
||||||
|
return c.stringWidthZeroJoiner(s)
|
||||||
|
}
|
||||||
|
return c.stringWidth(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate return string truncated with w cells
|
||||||
|
func (c *Condition) Truncate(s string, w int, tail string) string {
|
||||||
|
if c.StringWidth(s) <= w {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
r := []rune(s)
|
||||||
|
tw := c.StringWidth(tail)
|
||||||
|
w -= tw
|
||||||
|
width := 0
|
||||||
|
i := 0
|
||||||
|
for ; i < len(r); i++ {
|
||||||
|
cw := c.RuneWidth(r[i])
|
||||||
|
if width+cw > w {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
width += cw
|
||||||
|
}
|
||||||
|
return string(r[0:i]) + tail
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap return string wrapped with w cells
|
||||||
|
func (c *Condition) Wrap(s string, w int) string {
|
||||||
|
width := 0
|
||||||
|
out := ""
|
||||||
|
for _, r := range []rune(s) {
|
||||||
|
cw := RuneWidth(r)
|
||||||
|
if r == '\n' {
|
||||||
|
out += string(r)
|
||||||
|
width = 0
|
||||||
|
continue
|
||||||
|
} else if width+cw > w {
|
||||||
|
out += "\n"
|
||||||
|
width = 0
|
||||||
|
out += string(r)
|
||||||
|
width += cw
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out += string(r)
|
||||||
|
width += cw
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillLeft return string filled in left by spaces in w cells
|
||||||
|
func (c *Condition) FillLeft(s string, w int) string {
|
||||||
|
width := c.StringWidth(s)
|
||||||
|
count := w - width
|
||||||
|
if count > 0 {
|
||||||
|
b := make([]byte, count)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = ' '
|
||||||
|
}
|
||||||
|
return string(b) + s
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillRight return string filled in left by spaces in w cells
|
||||||
|
func (c *Condition) FillRight(s string, w int) string {
|
||||||
|
width := c.StringWidth(s)
|
||||||
|
count := w - width
|
||||||
|
if count > 0 {
|
||||||
|
b := make([]byte, count)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = ' '
|
||||||
|
}
|
||||||
|
return s + string(b)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// RuneWidth returns the number of cells in r.
|
||||||
|
// See http://www.unicode.org/reports/tr11/
|
||||||
|
func RuneWidth(r rune) int {
|
||||||
|
return DefaultCondition.RuneWidth(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAmbiguousWidth returns whether is ambiguous width or not.
|
||||||
|
func IsAmbiguousWidth(r rune) bool {
|
||||||
|
return inTables(r, private, ambiguous)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNeutralWidth returns whether is neutral width or not.
|
||||||
|
func IsNeutralWidth(r rune) bool {
|
||||||
|
return inTable(r, neutral)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringWidth return width as you can see
|
||||||
|
func StringWidth(s string) (width int) {
|
||||||
|
return DefaultCondition.StringWidth(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate return string truncated with w cells
|
||||||
|
func Truncate(s string, w int, tail string) string {
|
||||||
|
return DefaultCondition.Truncate(s, w, tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap return string wrapped with w cells
|
||||||
|
func Wrap(s string, w int) string {
|
||||||
|
return DefaultCondition.Wrap(s, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillLeft return string filled in left by spaces in w cells
|
||||||
|
func FillLeft(s string, w int) string {
|
||||||
|
return DefaultCondition.FillLeft(s, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillRight return string filled in left by spaces in w cells
|
||||||
|
func FillRight(s string, w int) string {
|
||||||
|
return DefaultCondition.FillRight(s, w)
|
||||||
|
}
|
||||||
8
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go
generated
vendored
Normal file
8
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// +build appengine
|
||||||
|
|
||||||
|
package runewidth
|
||||||
|
|
||||||
|
// IsEastAsian return true if the current locale is CJK
|
||||||
|
func IsEastAsian() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
9
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth_js.go
generated
vendored
Normal file
9
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth_js.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// +build js
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package runewidth
|
||||||
|
|
||||||
|
func IsEastAsian() bool {
|
||||||
|
// TODO: Implement this for the web. Detect east asian in a compatible way, and return true.
|
||||||
|
return false
|
||||||
|
}
|
||||||
82
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth_posix.go
generated
vendored
Normal file
82
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth_posix.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// +build !windows
|
||||||
|
// +build !js
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package runewidth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`)
|
||||||
|
|
||||||
|
var mblenTable = map[string]int{
|
||||||
|
"utf-8": 6,
|
||||||
|
"utf8": 6,
|
||||||
|
"jis": 8,
|
||||||
|
"eucjp": 3,
|
||||||
|
"euckr": 2,
|
||||||
|
"euccn": 2,
|
||||||
|
"sjis": 2,
|
||||||
|
"cp932": 2,
|
||||||
|
"cp51932": 2,
|
||||||
|
"cp936": 2,
|
||||||
|
"cp949": 2,
|
||||||
|
"cp950": 2,
|
||||||
|
"big5": 2,
|
||||||
|
"gbk": 2,
|
||||||
|
"gb2312": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEastAsian(locale string) bool {
|
||||||
|
charset := strings.ToLower(locale)
|
||||||
|
r := reLoc.FindStringSubmatch(locale)
|
||||||
|
if len(r) == 2 {
|
||||||
|
charset = strings.ToLower(r[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(charset, "@cjk_narrow") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for pos, b := range []byte(charset) {
|
||||||
|
if b == '@' {
|
||||||
|
charset = charset[:pos]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
max := 1
|
||||||
|
if m, ok := mblenTable[charset]; ok {
|
||||||
|
max = m
|
||||||
|
}
|
||||||
|
if max > 1 && (charset[0] != 'u' ||
|
||||||
|
strings.HasPrefix(locale, "ja") ||
|
||||||
|
strings.HasPrefix(locale, "ko") ||
|
||||||
|
strings.HasPrefix(locale, "zh")) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEastAsian return true if the current locale is CJK
|
||||||
|
func IsEastAsian() bool {
|
||||||
|
locale := os.Getenv("LC_ALL")
|
||||||
|
if locale == "" {
|
||||||
|
locale = os.Getenv("LC_CTYPE")
|
||||||
|
}
|
||||||
|
if locale == "" {
|
||||||
|
locale = os.Getenv("LANG")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore C locale
|
||||||
|
if locale == "POSIX" || locale == "C" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEastAsian(locale)
|
||||||
|
}
|
||||||
437
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth_table.go
generated
vendored
Normal file
437
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth_table.go
generated
vendored
Normal file
@@ -0,0 +1,437 @@
|
|||||||
|
// Code generated by script/generate.go. DO NOT EDIT.
|
||||||
|
|
||||||
|
package runewidth
|
||||||
|
|
||||||
|
var combining = table{
|
||||||
|
{0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3},
|
||||||
|
{0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0D00, 0x0D01},
|
||||||
|
{0x135D, 0x135F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1AC0},
|
||||||
|
{0x1B6B, 0x1B73}, {0x1DC0, 0x1DF9}, {0x1DFB, 0x1DFF},
|
||||||
|
{0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF},
|
||||||
|
{0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D},
|
||||||
|
{0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1},
|
||||||
|
{0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A},
|
||||||
|
{0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x11300, 0x11301},
|
||||||
|
{0x1133B, 0x1133C}, {0x11366, 0x1136C}, {0x11370, 0x11374},
|
||||||
|
{0x16AF0, 0x16AF4}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172},
|
||||||
|
{0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD},
|
||||||
|
{0x1D242, 0x1D244}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018},
|
||||||
|
{0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A},
|
||||||
|
{0x1E8D0, 0x1E8D6},
|
||||||
|
}
|
||||||
|
|
||||||
|
var doublewidth = table{
|
||||||
|
{0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A},
|
||||||
|
{0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3},
|
||||||
|
{0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653},
|
||||||
|
{0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1},
|
||||||
|
{0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5},
|
||||||
|
{0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA},
|
||||||
|
{0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA},
|
||||||
|
{0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B},
|
||||||
|
{0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E},
|
||||||
|
{0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797},
|
||||||
|
{0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C},
|
||||||
|
{0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99},
|
||||||
|
{0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB},
|
||||||
|
{0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF},
|
||||||
|
{0x3105, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31E3},
|
||||||
|
{0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x4DBF},
|
||||||
|
{0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C},
|
||||||
|
{0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19},
|
||||||
|
{0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B},
|
||||||
|
{0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4},
|
||||||
|
{0x16FF0, 0x16FF1}, {0x17000, 0x187F7}, {0x18800, 0x18CD5},
|
||||||
|
{0x18D00, 0x18D08}, {0x1B000, 0x1B11E}, {0x1B150, 0x1B152},
|
||||||
|
{0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004},
|
||||||
|
{0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A},
|
||||||
|
{0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248},
|
||||||
|
{0x1F250, 0x1F251}, {0x1F260, 0x1F265}, {0x1F300, 0x1F320},
|
||||||
|
{0x1F32D, 0x1F335}, {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393},
|
||||||
|
{0x1F3A0, 0x1F3CA}, {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0},
|
||||||
|
{0x1F3F4, 0x1F3F4}, {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440},
|
||||||
|
{0x1F442, 0x1F4FC}, {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E},
|
||||||
|
{0x1F550, 0x1F567}, {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596},
|
||||||
|
{0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5},
|
||||||
|
{0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D7},
|
||||||
|
{0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB},
|
||||||
|
{0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F978},
|
||||||
|
{0x1F97A, 0x1F9CB}, {0x1F9CD, 0x1F9FF}, {0x1FA70, 0x1FA74},
|
||||||
|
{0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA86}, {0x1FA90, 0x1FAA8},
|
||||||
|
{0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6},
|
||||||
|
{0x20000, 0x2FFFD}, {0x30000, 0x3FFFD},
|
||||||
|
}
|
||||||
|
|
||||||
|
var ambiguous = table{
|
||||||
|
{0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8},
|
||||||
|
{0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4},
|
||||||
|
{0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6},
|
||||||
|
{0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1},
|
||||||
|
{0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED},
|
||||||
|
{0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA},
|
||||||
|
{0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101},
|
||||||
|
{0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B},
|
||||||
|
{0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133},
|
||||||
|
{0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144},
|
||||||
|
{0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153},
|
||||||
|
{0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE},
|
||||||
|
{0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4},
|
||||||
|
{0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA},
|
||||||
|
{0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261},
|
||||||
|
{0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB},
|
||||||
|
{0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB},
|
||||||
|
{0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F},
|
||||||
|
{0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1},
|
||||||
|
{0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F},
|
||||||
|
{0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016},
|
||||||
|
{0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022},
|
||||||
|
{0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033},
|
||||||
|
{0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E},
|
||||||
|
{0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084},
|
||||||
|
{0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105},
|
||||||
|
{0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116},
|
||||||
|
{0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B},
|
||||||
|
{0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B},
|
||||||
|
{0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199},
|
||||||
|
{0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4},
|
||||||
|
{0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203},
|
||||||
|
{0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F},
|
||||||
|
{0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A},
|
||||||
|
{0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225},
|
||||||
|
{0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237},
|
||||||
|
{0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C},
|
||||||
|
{0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267},
|
||||||
|
{0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283},
|
||||||
|
{0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299},
|
||||||
|
{0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312},
|
||||||
|
{0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573},
|
||||||
|
{0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1},
|
||||||
|
{0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7},
|
||||||
|
{0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8},
|
||||||
|
{0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5},
|
||||||
|
{0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609},
|
||||||
|
{0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E},
|
||||||
|
{0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661},
|
||||||
|
{0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D},
|
||||||
|
{0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF},
|
||||||
|
{0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1},
|
||||||
|
{0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1},
|
||||||
|
{0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC},
|
||||||
|
{0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F},
|
||||||
|
{0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF},
|
||||||
|
{0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A},
|
||||||
|
{0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D},
|
||||||
|
{0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF},
|
||||||
|
{0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD},
|
||||||
|
}
|
||||||
|
var notassigned = table{
|
||||||
|
{0x27E6, 0x27ED}, {0x2985, 0x2986},
|
||||||
|
}
|
||||||
|
|
||||||
|
var neutral = table{
|
||||||
|
{0x0000, 0x001F}, {0x007F, 0x00A0}, {0x00A9, 0x00A9},
|
||||||
|
{0x00AB, 0x00AB}, {0x00B5, 0x00B5}, {0x00BB, 0x00BB},
|
||||||
|
{0x00C0, 0x00C5}, {0x00C7, 0x00CF}, {0x00D1, 0x00D6},
|
||||||
|
{0x00D9, 0x00DD}, {0x00E2, 0x00E5}, {0x00E7, 0x00E7},
|
||||||
|
{0x00EB, 0x00EB}, {0x00EE, 0x00EF}, {0x00F1, 0x00F1},
|
||||||
|
{0x00F4, 0x00F6}, {0x00FB, 0x00FB}, {0x00FD, 0x00FD},
|
||||||
|
{0x00FF, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112},
|
||||||
|
{0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A},
|
||||||
|
{0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E},
|
||||||
|
{0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C},
|
||||||
|
{0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A},
|
||||||
|
{0x016C, 0x01CD}, {0x01CF, 0x01CF}, {0x01D1, 0x01D1},
|
||||||
|
{0x01D3, 0x01D3}, {0x01D5, 0x01D5}, {0x01D7, 0x01D7},
|
||||||
|
{0x01D9, 0x01D9}, {0x01DB, 0x01DB}, {0x01DD, 0x0250},
|
||||||
|
{0x0252, 0x0260}, {0x0262, 0x02C3}, {0x02C5, 0x02C6},
|
||||||
|
{0x02C8, 0x02C8}, {0x02CC, 0x02CC}, {0x02CE, 0x02CF},
|
||||||
|
{0x02D1, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE},
|
||||||
|
{0x02E0, 0x02FF}, {0x0370, 0x0377}, {0x037A, 0x037F},
|
||||||
|
{0x0384, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x0390},
|
||||||
|
{0x03AA, 0x03B0}, {0x03C2, 0x03C2}, {0x03CA, 0x0400},
|
||||||
|
{0x0402, 0x040F}, {0x0450, 0x0450}, {0x0452, 0x052F},
|
||||||
|
{0x0531, 0x0556}, {0x0559, 0x058A}, {0x058D, 0x058F},
|
||||||
|
{0x0591, 0x05C7}, {0x05D0, 0x05EA}, {0x05EF, 0x05F4},
|
||||||
|
{0x0600, 0x061C}, {0x061E, 0x070D}, {0x070F, 0x074A},
|
||||||
|
{0x074D, 0x07B1}, {0x07C0, 0x07FA}, {0x07FD, 0x082D},
|
||||||
|
{0x0830, 0x083E}, {0x0840, 0x085B}, {0x085E, 0x085E},
|
||||||
|
{0x0860, 0x086A}, {0x08A0, 0x08B4}, {0x08B6, 0x08C7},
|
||||||
|
{0x08D3, 0x0983}, {0x0985, 0x098C}, {0x098F, 0x0990},
|
||||||
|
{0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B2, 0x09B2},
|
||||||
|
{0x09B6, 0x09B9}, {0x09BC, 0x09C4}, {0x09C7, 0x09C8},
|
||||||
|
{0x09CB, 0x09CE}, {0x09D7, 0x09D7}, {0x09DC, 0x09DD},
|
||||||
|
{0x09DF, 0x09E3}, {0x09E6, 0x09FE}, {0x0A01, 0x0A03},
|
||||||
|
{0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28},
|
||||||
|
{0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, {0x0A35, 0x0A36},
|
||||||
|
{0x0A38, 0x0A39}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42},
|
||||||
|
{0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51},
|
||||||
|
{0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A76},
|
||||||
|
{0x0A81, 0x0A83}, {0x0A85, 0x0A8D}, {0x0A8F, 0x0A91},
|
||||||
|
{0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3},
|
||||||
|
{0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9},
|
||||||
|
{0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3},
|
||||||
|
{0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF}, {0x0B01, 0x0B03},
|
||||||
|
{0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28},
|
||||||
|
{0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B35, 0x0B39},
|
||||||
|
{0x0B3C, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D},
|
||||||
|
{0x0B55, 0x0B57}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63},
|
||||||
|
{0x0B66, 0x0B77}, {0x0B82, 0x0B83}, {0x0B85, 0x0B8A},
|
||||||
|
{0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A},
|
||||||
|
{0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4},
|
||||||
|
{0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2},
|
||||||
|
{0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0},
|
||||||
|
{0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C},
|
||||||
|
{0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C39},
|
||||||
|
{0x0C3D, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D},
|
||||||
|
{0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C63},
|
||||||
|
{0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90},
|
||||||
|
{0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9},
|
||||||
|
{0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD},
|
||||||
|
{0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE3},
|
||||||
|
{0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, {0x0D00, 0x0D0C},
|
||||||
|
{0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, {0x0D46, 0x0D48},
|
||||||
|
{0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, {0x0D66, 0x0D7F},
|
||||||
|
{0x0D81, 0x0D83}, {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1},
|
||||||
|
{0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6},
|
||||||
|
{0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6},
|
||||||
|
{0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4},
|
||||||
|
{0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82},
|
||||||
|
{0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3},
|
||||||
|
{0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4},
|
||||||
|
{0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9},
|
||||||
|
{0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C},
|
||||||
|
{0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC},
|
||||||
|
{0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7},
|
||||||
|
{0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248},
|
||||||
|
{0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258},
|
||||||
|
{0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D},
|
||||||
|
{0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE},
|
||||||
|
{0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6},
|
||||||
|
{0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A},
|
||||||
|
{0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5},
|
||||||
|
{0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8},
|
||||||
|
{0x1700, 0x170C}, {0x170E, 0x1714}, {0x1720, 0x1736},
|
||||||
|
{0x1740, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770},
|
||||||
|
{0x1772, 0x1773}, {0x1780, 0x17DD}, {0x17E0, 0x17E9},
|
||||||
|
{0x17F0, 0x17F9}, {0x1800, 0x180E}, {0x1810, 0x1819},
|
||||||
|
{0x1820, 0x1878}, {0x1880, 0x18AA}, {0x18B0, 0x18F5},
|
||||||
|
{0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B},
|
||||||
|
{0x1940, 0x1940}, {0x1944, 0x196D}, {0x1970, 0x1974},
|
||||||
|
{0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA},
|
||||||
|
{0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C},
|
||||||
|
{0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD},
|
||||||
|
{0x1AB0, 0x1AC0}, {0x1B00, 0x1B4B}, {0x1B50, 0x1B7C},
|
||||||
|
{0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49},
|
||||||
|
{0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7},
|
||||||
|
{0x1CD0, 0x1CFA}, {0x1D00, 0x1DF9}, {0x1DFB, 0x1F15},
|
||||||
|
{0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D},
|
||||||
|
{0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B},
|
||||||
|
{0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4},
|
||||||
|
{0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB},
|
||||||
|
{0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE},
|
||||||
|
{0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017},
|
||||||
|
{0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023},
|
||||||
|
{0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034},
|
||||||
|
{0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064},
|
||||||
|
{0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080},
|
||||||
|
{0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8},
|
||||||
|
{0x20AA, 0x20AB}, {0x20AD, 0x20BF}, {0x20D0, 0x20F0},
|
||||||
|
{0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108},
|
||||||
|
{0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120},
|
||||||
|
{0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152},
|
||||||
|
{0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F},
|
||||||
|
{0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7},
|
||||||
|
{0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6},
|
||||||
|
{0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206},
|
||||||
|
{0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210},
|
||||||
|
{0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C},
|
||||||
|
{0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226},
|
||||||
|
{0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B},
|
||||||
|
{0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251},
|
||||||
|
{0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269},
|
||||||
|
{0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285},
|
||||||
|
{0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4},
|
||||||
|
{0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319},
|
||||||
|
{0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF},
|
||||||
|
{0x23F1, 0x23F2}, {0x23F4, 0x2426}, {0x2440, 0x244A},
|
||||||
|
{0x24EA, 0x24EA}, {0x254C, 0x254F}, {0x2574, 0x257F},
|
||||||
|
{0x2590, 0x2591}, {0x2596, 0x259F}, {0x25A2, 0x25A2},
|
||||||
|
{0x25AA, 0x25B1}, {0x25B4, 0x25B5}, {0x25B8, 0x25BB},
|
||||||
|
{0x25BE, 0x25BF}, {0x25C2, 0x25C5}, {0x25C9, 0x25CA},
|
||||||
|
{0x25CC, 0x25CD}, {0x25D2, 0x25E1}, {0x25E6, 0x25EE},
|
||||||
|
{0x25F0, 0x25FC}, {0x25FF, 0x2604}, {0x2607, 0x2608},
|
||||||
|
{0x260A, 0x260D}, {0x2610, 0x2613}, {0x2616, 0x261B},
|
||||||
|
{0x261D, 0x261D}, {0x261F, 0x263F}, {0x2641, 0x2641},
|
||||||
|
{0x2643, 0x2647}, {0x2654, 0x265F}, {0x2662, 0x2662},
|
||||||
|
{0x2666, 0x2666}, {0x266B, 0x266B}, {0x266E, 0x266E},
|
||||||
|
{0x2670, 0x267E}, {0x2680, 0x2692}, {0x2694, 0x269D},
|
||||||
|
{0x26A0, 0x26A0}, {0x26A2, 0x26A9}, {0x26AC, 0x26BC},
|
||||||
|
{0x26C0, 0x26C3}, {0x26E2, 0x26E2}, {0x26E4, 0x26E7},
|
||||||
|
{0x2700, 0x2704}, {0x2706, 0x2709}, {0x270C, 0x2727},
|
||||||
|
{0x2729, 0x273C}, {0x273E, 0x274B}, {0x274D, 0x274D},
|
||||||
|
{0x274F, 0x2752}, {0x2756, 0x2756}, {0x2758, 0x2775},
|
||||||
|
{0x2780, 0x2794}, {0x2798, 0x27AF}, {0x27B1, 0x27BE},
|
||||||
|
{0x27C0, 0x27E5}, {0x27EE, 0x2984}, {0x2987, 0x2B1A},
|
||||||
|
{0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, {0x2B5A, 0x2B73},
|
||||||
|
{0x2B76, 0x2B95}, {0x2B97, 0x2C2E}, {0x2C30, 0x2C5E},
|
||||||
|
{0x2C60, 0x2CF3}, {0x2CF9, 0x2D25}, {0x2D27, 0x2D27},
|
||||||
|
{0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D70},
|
||||||
|
{0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE},
|
||||||
|
{0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6},
|
||||||
|
{0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE},
|
||||||
|
{0x2DE0, 0x2E52}, {0x303F, 0x303F}, {0x4DC0, 0x4DFF},
|
||||||
|
{0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7BF},
|
||||||
|
{0xA7C2, 0xA7CA}, {0xA7F5, 0xA82C}, {0xA830, 0xA839},
|
||||||
|
{0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9},
|
||||||
|
{0xA8E0, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD},
|
||||||
|
{0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36},
|
||||||
|
{0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2},
|
||||||
|
{0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E},
|
||||||
|
{0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E},
|
||||||
|
{0xAB30, 0xAB6B}, {0xAB70, 0xABED}, {0xABF0, 0xABF9},
|
||||||
|
{0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF},
|
||||||
|
{0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36},
|
||||||
|
{0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41},
|
||||||
|
{0xFB43, 0xFB44}, {0xFB46, 0xFBC1}, {0xFBD3, 0xFD3F},
|
||||||
|
{0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, {0xFDF0, 0xFDFD},
|
||||||
|
{0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC},
|
||||||
|
{0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B},
|
||||||
|
{0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D},
|
||||||
|
{0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA},
|
||||||
|
{0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E},
|
||||||
|
{0x10190, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD},
|
||||||
|
{0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB},
|
||||||
|
{0x10300, 0x10323}, {0x1032D, 0x1034A}, {0x10350, 0x1037A},
|
||||||
|
{0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5},
|
||||||
|
{0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3},
|
||||||
|
{0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563},
|
||||||
|
{0x1056F, 0x1056F}, {0x10600, 0x10736}, {0x10740, 0x10755},
|
||||||
|
{0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808},
|
||||||
|
{0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C},
|
||||||
|
{0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF},
|
||||||
|
{0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x1091B},
|
||||||
|
{0x1091F, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x109B7},
|
||||||
|
{0x109BC, 0x109CF}, {0x109D2, 0x10A03}, {0x10A05, 0x10A06},
|
||||||
|
{0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35},
|
||||||
|
{0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, {0x10A50, 0x10A58},
|
||||||
|
{0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6},
|
||||||
|
{0x10B00, 0x10B35}, {0x10B39, 0x10B55}, {0x10B58, 0x10B72},
|
||||||
|
{0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF},
|
||||||
|
{0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2},
|
||||||
|
{0x10CFA, 0x10D27}, {0x10D30, 0x10D39}, {0x10E60, 0x10E7E},
|
||||||
|
{0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD}, {0x10EB0, 0x10EB1},
|
||||||
|
{0x10F00, 0x10F27}, {0x10F30, 0x10F59}, {0x10FB0, 0x10FCB},
|
||||||
|
{0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x1106F},
|
||||||
|
{0x1107F, 0x110C1}, {0x110CD, 0x110CD}, {0x110D0, 0x110E8},
|
||||||
|
{0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11147},
|
||||||
|
{0x11150, 0x11176}, {0x11180, 0x111DF}, {0x111E1, 0x111F4},
|
||||||
|
{0x11200, 0x11211}, {0x11213, 0x1123E}, {0x11280, 0x11286},
|
||||||
|
{0x11288, 0x11288}, {0x1128A, 0x1128D}, {0x1128F, 0x1129D},
|
||||||
|
{0x1129F, 0x112A9}, {0x112B0, 0x112EA}, {0x112F0, 0x112F9},
|
||||||
|
{0x11300, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310},
|
||||||
|
{0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333},
|
||||||
|
{0x11335, 0x11339}, {0x1133B, 0x11344}, {0x11347, 0x11348},
|
||||||
|
{0x1134B, 0x1134D}, {0x11350, 0x11350}, {0x11357, 0x11357},
|
||||||
|
{0x1135D, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374},
|
||||||
|
{0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7},
|
||||||
|
{0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD},
|
||||||
|
{0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C},
|
||||||
|
{0x11680, 0x116B8}, {0x116C0, 0x116C9}, {0x11700, 0x1171A},
|
||||||
|
{0x1171D, 0x1172B}, {0x11730, 0x1173F}, {0x11800, 0x1183B},
|
||||||
|
{0x118A0, 0x118F2}, {0x118FF, 0x11906}, {0x11909, 0x11909},
|
||||||
|
{0x1190C, 0x11913}, {0x11915, 0x11916}, {0x11918, 0x11935},
|
||||||
|
{0x11937, 0x11938}, {0x1193B, 0x11946}, {0x11950, 0x11959},
|
||||||
|
{0x119A0, 0x119A7}, {0x119AA, 0x119D7}, {0x119DA, 0x119E4},
|
||||||
|
{0x11A00, 0x11A47}, {0x11A50, 0x11AA2}, {0x11AC0, 0x11AF8},
|
||||||
|
{0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45},
|
||||||
|
{0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7},
|
||||||
|
{0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09},
|
||||||
|
{0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D},
|
||||||
|
{0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, {0x11D60, 0x11D65},
|
||||||
|
{0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, {0x11D90, 0x11D91},
|
||||||
|
{0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8},
|
||||||
|
{0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399},
|
||||||
|
{0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543},
|
||||||
|
{0x13000, 0x1342E}, {0x13430, 0x13438}, {0x14400, 0x14646},
|
||||||
|
{0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69},
|
||||||
|
{0x16A6E, 0x16A6F}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5},
|
||||||
|
{0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61},
|
||||||
|
{0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A},
|
||||||
|
{0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F},
|
||||||
|
{0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88},
|
||||||
|
{0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1D000, 0x1D0F5},
|
||||||
|
{0x1D100, 0x1D126}, {0x1D129, 0x1D1E8}, {0x1D200, 0x1D245},
|
||||||
|
{0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356}, {0x1D360, 0x1D378},
|
||||||
|
{0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F},
|
||||||
|
{0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC},
|
||||||
|
{0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3},
|
||||||
|
{0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514},
|
||||||
|
{0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E},
|
||||||
|
{0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550},
|
||||||
|
{0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B},
|
||||||
|
{0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006},
|
||||||
|
{0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024},
|
||||||
|
{0x1E026, 0x1E02A}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D},
|
||||||
|
{0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E2C0, 0x1E2F9},
|
||||||
|
{0x1E2FF, 0x1E2FF}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6},
|
||||||
|
{0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F},
|
||||||
|
{0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03},
|
||||||
|
{0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24},
|
||||||
|
{0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37},
|
||||||
|
{0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42},
|
||||||
|
{0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B},
|
||||||
|
{0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54},
|
||||||
|
{0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B},
|
||||||
|
{0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62},
|
||||||
|
{0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72},
|
||||||
|
{0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E},
|
||||||
|
{0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3},
|
||||||
|
{0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1},
|
||||||
|
{0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093},
|
||||||
|
{0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE},
|
||||||
|
{0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F},
|
||||||
|
{0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF},
|
||||||
|
{0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D},
|
||||||
|
{0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF},
|
||||||
|
{0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F},
|
||||||
|
{0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A},
|
||||||
|
{0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594},
|
||||||
|
{0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F},
|
||||||
|
{0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4},
|
||||||
|
{0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773},
|
||||||
|
{0x1F780, 0x1F7D8}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847},
|
||||||
|
{0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD},
|
||||||
|
{0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B},
|
||||||
|
{0x1F946, 0x1F946}, {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D},
|
||||||
|
{0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9},
|
||||||
|
{0xE0001, 0xE0001}, {0xE0020, 0xE007F},
|
||||||
|
}
|
||||||
|
|
||||||
|
var emoji = table{
|
||||||
|
{0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122},
|
||||||
|
{0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA},
|
||||||
|
{0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388},
|
||||||
|
{0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA},
|
||||||
|
{0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6},
|
||||||
|
{0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605},
|
||||||
|
{0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705},
|
||||||
|
{0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716},
|
||||||
|
{0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728},
|
||||||
|
{0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747},
|
||||||
|
{0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755},
|
||||||
|
{0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797},
|
||||||
|
{0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF},
|
||||||
|
{0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C},
|
||||||
|
{0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030},
|
||||||
|
{0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299},
|
||||||
|
{0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F},
|
||||||
|
{0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E},
|
||||||
|
{0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F},
|
||||||
|
{0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A},
|
||||||
|
{0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D},
|
||||||
|
{0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F},
|
||||||
|
{0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F},
|
||||||
|
{0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF},
|
||||||
|
{0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FAFF},
|
||||||
|
{0x1FC00, 0x1FFFD},
|
||||||
|
}
|
||||||
28
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth_windows.go
generated
vendored
Normal file
28
tests/metrics/cmd/checkmetrics/vendor/github.com/mattn/go-runewidth/runewidth_windows.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// +build windows
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package runewidth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32 = syscall.NewLazyDLL("kernel32")
|
||||||
|
procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP")
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsEastAsian return true if the current locale is CJK
|
||||||
|
func IsEastAsian() bool {
|
||||||
|
r1, _, _ := procGetConsoleOutputCP.Call()
|
||||||
|
if r1 == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch int(r1) {
|
||||||
|
case 932, 51932, 936, 949, 950:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
7
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/.gitignore
generated
vendored
Normal file
7
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
coverage.out
|
||||||
|
coverage.txt
|
||||||
|
release-notes.txt
|
||||||
|
.directory
|
||||||
|
.chglog
|
||||||
|
.vscode
|
||||||
|
.DS_Store
|
||||||
534
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/CHANGELOG.md
generated
vendored
Normal file
534
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,534 @@
|
|||||||
|
<a name="unreleased"></a>
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.7.1"></a>
|
||||||
|
## [v0.7.1] - 2023-05-11
|
||||||
|
### Add
|
||||||
|
- Add describe functions ([#77](https://github.com/montanaflynn/stats/issues/77))
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update .gitignore
|
||||||
|
- Update README.md, LICENSE and DOCUMENTATION.md files
|
||||||
|
- Update github action go workflow to run on push
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.7.0"></a>
|
||||||
|
## [v0.7.0] - 2023-01-08
|
||||||
|
### Add
|
||||||
|
- Add geometric distribution functions ([#75](https://github.com/montanaflynn/stats/issues/75))
|
||||||
|
- Add GitHub action go workflow
|
||||||
|
|
||||||
|
### Remove
|
||||||
|
- Remove travis CI config
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update changelog with v0.7.0 changes
|
||||||
|
- Update changelog with v0.7.0 changes
|
||||||
|
- Update github action go workflow
|
||||||
|
- Update geometric distribution tests
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.6.6"></a>
|
||||||
|
## [v0.6.6] - 2021-04-26
|
||||||
|
### Add
|
||||||
|
- Add support for string and io.Reader in LoadRawData (pr [#68](https://github.com/montanaflynn/stats/issues/68))
|
||||||
|
- Add latest versions of Go to test against
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update changelog with v0.6.6 changes
|
||||||
|
|
||||||
|
### Use
|
||||||
|
- Use math.Sqrt in StandardDeviation (PR [#64](https://github.com/montanaflynn/stats/issues/64))
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.6.5"></a>
|
||||||
|
## [v0.6.5] - 2021-02-21
|
||||||
|
### Add
|
||||||
|
- Add Float64Data.Quartiles documentation
|
||||||
|
- Add Quartiles method to Float64Data type (issue [#60](https://github.com/montanaflynn/stats/issues/60))
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
- Fix make release changelog command and add changelog history
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update changelog with v0.6.5 changes
|
||||||
|
- Update changelog with v0.6.4 changes
|
||||||
|
- Update README.md links to CHANGELOG.md and DOCUMENTATION.md
|
||||||
|
- Update README.md and Makefile with new release commands
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.6.4"></a>
|
||||||
|
## [v0.6.4] - 2021-01-13
|
||||||
|
### Fix
|
||||||
|
- Fix failing tests due to precision errors on arm64 ([#58](https://github.com/montanaflynn/stats/issues/58))
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update changelog with v0.6.4 changes
|
||||||
|
- Update examples directory to include a README.md used for synopsis
|
||||||
|
- Update go.mod to include go version where modules are enabled by default
|
||||||
|
- Update changelog with v0.6.3 changes
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.6.3"></a>
|
||||||
|
## [v0.6.3] - 2020-02-18
|
||||||
|
### Add
|
||||||
|
- Add creating and committing changelog to Makefile release directive
|
||||||
|
- Add release-notes.txt and .chglog directory to .gitignore
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update exported tests to use import for better example documentation
|
||||||
|
- Update documentation using godoc2md
|
||||||
|
- Update changelog with v0.6.2 release
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.6.2"></a>
|
||||||
|
## [v0.6.2] - 2020-02-18
|
||||||
|
### Fix
|
||||||
|
- Fix linting errcheck warnings in go benchmarks
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update Makefile release directive to use correct release name
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.6.1"></a>
|
||||||
|
## [v0.6.1] - 2020-02-18
|
||||||
|
### Add
|
||||||
|
- Add StableSample function signature to readme
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
- Fix linting warnings for normal distribution functions formatting and tests
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update documentation links and rename DOC.md to DOCUMENTATION.md
|
||||||
|
- Update README with link to pkg.go.dev reference and release section
|
||||||
|
- Update Makefile with new changelog, docs, and release directives
|
||||||
|
- Update DOC.md links to GitHub source code
|
||||||
|
- Update doc.go comment and add DOC.md package reference file
|
||||||
|
- Update changelog using git-chglog
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.6.0"></a>
|
||||||
|
## [v0.6.0] - 2020-02-17
|
||||||
|
### Add
|
||||||
|
- Add Normal Distribution Functions ([#56](https://github.com/montanaflynn/stats/issues/56))
|
||||||
|
- Add previous versions of Go to travis CI config
|
||||||
|
- Add check for distinct values in Mode function ([#51](https://github.com/montanaflynn/stats/issues/51))
|
||||||
|
- Add StableSample function ([#48](https://github.com/montanaflynn/stats/issues/48))
|
||||||
|
- Add doc.go file to show description and usage on godoc.org
|
||||||
|
- Add comments to new error and legacy error variables
|
||||||
|
- Add ExampleRound function to tests
|
||||||
|
- Add go.mod file for module support
|
||||||
|
- Add Sigmoid, SoftMax and Entropy methods and tests
|
||||||
|
- Add Entropy documentation, example and benchmarks
|
||||||
|
- Add Entropy function ([#44](https://github.com/montanaflynn/stats/issues/44))
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
- Fix percentile when only one element ([#47](https://github.com/montanaflynn/stats/issues/47))
|
||||||
|
- Fix AutoCorrelation name in comments and remove unneeded Sprintf
|
||||||
|
|
||||||
|
### Improve
|
||||||
|
- Improve documentation section with command comments
|
||||||
|
|
||||||
|
### Remove
|
||||||
|
- Remove very old versions of Go in travis CI config
|
||||||
|
- Remove boolean comparison to get rid of gometalinter warning
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update license dates
|
||||||
|
- Update Distance functions signatures to use Float64Data
|
||||||
|
- Update Sigmoid examples
|
||||||
|
- Update error names with backward compatibility
|
||||||
|
|
||||||
|
### Use
|
||||||
|
- Use relative link to examples/main.go
|
||||||
|
- Use a single var block for exported errors
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.5.0"></a>
|
||||||
|
## [v0.5.0] - 2019-01-16
|
||||||
|
### Add
|
||||||
|
- Add Sigmoid and Softmax functions
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
- Fix syntax highlighting and add CumulativeSum func
|
||||||
|
|
||||||
|
|
||||||
|
<a name="v0.4.0"></a>
|
||||||
|
## [v0.4.0] - 2019-01-14
|
||||||
|
### Add
|
||||||
|
- Add goreport badge and documentation section to README.md
|
||||||
|
- Add Examples to test files
|
||||||
|
- Add AutoCorrelation and nist tests
|
||||||
|
- Add String method to statsErr type
|
||||||
|
- Add Y coordinate error for ExponentialRegression
|
||||||
|
- Add syntax highlighting ([#43](https://github.com/montanaflynn/stats/issues/43))
|
||||||
|
- Add CumulativeSum ([#40](https://github.com/montanaflynn/stats/issues/40))
|
||||||
|
- Add more tests and rename distance files
|
||||||
|
- Add coverage and benchmarks to azure pipeline
|
||||||
|
- Add go tests to azure pipeline
|
||||||
|
|
||||||
|
### Change
|
||||||
|
- Change travis tip alias to master
|
||||||
|
- Change codecov to coveralls for code coverage
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
- Fix a few lint warnings
|
||||||
|
- Fix example error
|
||||||
|
|
||||||
|
### Improve
|
||||||
|
- Improve test coverage of distance functions
|
||||||
|
|
||||||
|
### Only
|
||||||
|
- Only run travis on stable and tip versions
|
||||||
|
- Only check code coverage on tip
|
||||||
|
|
||||||
|
### Remove
|
||||||
|
- Remove azure CI pipeline
|
||||||
|
- Remove unnecessary type conversions
|
||||||
|
|
||||||
|
### Return
|
||||||
|
- Return EmptyInputErr instead of EmptyInput
|
||||||
|
|
||||||
|
### Set
|
||||||
|
- Set up CI with Azure Pipelines
|
||||||
|
|
||||||
|
|
||||||
|
<a name="0.3.0"></a>
|
||||||
|
## [0.3.0] - 2017-12-02
|
||||||
|
### Add
|
||||||
|
- Add Chebyshev, Manhattan, Euclidean and Minkowski distance functions ([#35](https://github.com/montanaflynn/stats/issues/35))
|
||||||
|
- Add function for computing chebyshev distance. ([#34](https://github.com/montanaflynn/stats/issues/34))
|
||||||
|
- Add support for time.Duration
|
||||||
|
- Add LoadRawData to docs and examples
|
||||||
|
- Add unit test for edge case that wasn't covered
|
||||||
|
- Add unit tests for edge cases that weren't covered
|
||||||
|
- Add pearson alias delegating to correlation
|
||||||
|
- Add CovariancePopulation to Float64Data
|
||||||
|
- Add pearson product-moment correlation coefficient
|
||||||
|
- Add population covariance
|
||||||
|
- Add random slice benchmarks
|
||||||
|
- Add all applicable functions as methods to Float64Data type
|
||||||
|
- Add MIT license badge
|
||||||
|
- Add link to examples/methods.go
|
||||||
|
- Add Protips for usage and documentation sections
|
||||||
|
- Add tests for rounding up
|
||||||
|
- Add webdoc target and remove linting from test target
|
||||||
|
- Add example usage and consolidate contributing information
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Added MedianAbsoluteDeviation
|
||||||
|
|
||||||
|
### Annotation
|
||||||
|
- Annotation spelling error
|
||||||
|
|
||||||
|
### Auto
|
||||||
|
- auto commit
|
||||||
|
- auto commit
|
||||||
|
|
||||||
|
### Calculate
|
||||||
|
- Calculate correlation with sdev and covp
|
||||||
|
|
||||||
|
### Clean
|
||||||
|
- Clean up README.md and add info for offline docs
|
||||||
|
|
||||||
|
### Consolidated
|
||||||
|
- Consolidated all error values.
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
- Fix Percentile logic
|
||||||
|
- Fix InterQuartileRange method test
|
||||||
|
- Fix zero percent bug and add test
|
||||||
|
- Fix usage example output typos
|
||||||
|
|
||||||
|
### Improve
|
||||||
|
- Improve bounds checking in Percentile
|
||||||
|
- Improve error log messaging
|
||||||
|
|
||||||
|
### Imput
|
||||||
|
- Imput -> Input
|
||||||
|
|
||||||
|
### Include
|
||||||
|
- Include alternative way to set Float64Data in example
|
||||||
|
|
||||||
|
### Make
|
||||||
|
- Make various changes to README.md
|
||||||
|
|
||||||
|
### Merge
|
||||||
|
- Merge branch 'master' of github.com:montanaflynn/stats
|
||||||
|
- Merge master
|
||||||
|
|
||||||
|
### Mode
|
||||||
|
- Mode calculation fix and tests
|
||||||
|
|
||||||
|
### Realized
|
||||||
|
- Realized the obvious efficiency gains of ignoring the unique numbers at the beginning of the slice. Benchmark joy ensued.
|
||||||
|
|
||||||
|
### Refactor
|
||||||
|
- Refactor testing of Round()
|
||||||
|
- Refactor setting Coordinate y field using Exp in place of Pow
|
||||||
|
- Refactor Makefile and add docs target
|
||||||
|
|
||||||
|
### Remove
|
||||||
|
- Remove deep links to types and functions
|
||||||
|
|
||||||
|
### Rename
|
||||||
|
- Rename file from types to data
|
||||||
|
|
||||||
|
### Retrieve
|
||||||
|
- Retrieve InterQuartileRange for the Float64Data.
|
||||||
|
|
||||||
|
### Split
|
||||||
|
- Split up stats.go into separate files
|
||||||
|
|
||||||
|
### Support
|
||||||
|
- Support more types on LoadRawData() ([#36](https://github.com/montanaflynn/stats/issues/36))
|
||||||
|
|
||||||
|
### Switch
|
||||||
|
- Switch default and check targets
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update Readme
|
||||||
|
- Update example methods and some text
|
||||||
|
- Update README and include Float64Data type method examples
|
||||||
|
|
||||||
|
### Pull Requests
|
||||||
|
- Merge pull request [#32](https://github.com/montanaflynn/stats/issues/32) from a-robinson/percentile
|
||||||
|
- Merge pull request [#30](https://github.com/montanaflynn/stats/issues/30) from montanaflynn/fix-test
|
||||||
|
- Merge pull request [#29](https://github.com/montanaflynn/stats/issues/29) from edupsousa/master
|
||||||
|
- Merge pull request [#27](https://github.com/montanaflynn/stats/issues/27) from andrey-yantsen/fix-percentile-out-of-bounds
|
||||||
|
- Merge pull request [#25](https://github.com/montanaflynn/stats/issues/25) from kazhuravlev/patch-1
|
||||||
|
- Merge pull request [#22](https://github.com/montanaflynn/stats/issues/22) from JanBerktold/time-duration
|
||||||
|
- Merge pull request [#24](https://github.com/montanaflynn/stats/issues/24) from alouche/master
|
||||||
|
- Merge pull request [#21](https://github.com/montanaflynn/stats/issues/21) from brydavis/master
|
||||||
|
- Merge pull request [#19](https://github.com/montanaflynn/stats/issues/19) from ginodeis/mode-bug
|
||||||
|
- Merge pull request [#17](https://github.com/montanaflynn/stats/issues/17) from Kunde21/master
|
||||||
|
- Merge pull request [#3](https://github.com/montanaflynn/stats/issues/3) from montanaflynn/master
|
||||||
|
- Merge pull request [#2](https://github.com/montanaflynn/stats/issues/2) from montanaflynn/master
|
||||||
|
- Merge pull request [#13](https://github.com/montanaflynn/stats/issues/13) from toashd/pearson
|
||||||
|
- Merge pull request [#12](https://github.com/montanaflynn/stats/issues/12) from alixaxel/MAD
|
||||||
|
- Merge pull request [#1](https://github.com/montanaflynn/stats/issues/1) from montanaflynn/master
|
||||||
|
- Merge pull request [#11](https://github.com/montanaflynn/stats/issues/11) from Kunde21/modeMemReduce
|
||||||
|
- Merge pull request [#10](https://github.com/montanaflynn/stats/issues/10) from Kunde21/ModeRewrite
|
||||||
|
|
||||||
|
|
||||||
|
<a name="0.2.0"></a>
|
||||||
|
## [0.2.0] - 2015-10-14
|
||||||
|
### Add
|
||||||
|
- Add Makefile with gometalinter, testing, benchmarking and coverage report targets
|
||||||
|
- Add comments describing functions and structs
|
||||||
|
- Add Correlation func
|
||||||
|
- Add Covariance func
|
||||||
|
- Add tests for new function shortcuts
|
||||||
|
- Add StandardDeviation function as a shortcut to StandardDeviationPopulation
|
||||||
|
- Add Float64Data and Series types
|
||||||
|
|
||||||
|
### Change
|
||||||
|
- Change Sample to return a standard []float64 type
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
- Fix broken link to Makefile
|
||||||
|
- Fix broken link and simplify code coverage reporting command
|
||||||
|
- Fix go vet warning about printf type placeholder
|
||||||
|
- Fix failing codecov test coverage reporting
|
||||||
|
- Fix link to CHANGELOG.md
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed typographical error, changed accomdate to accommodate in README.
|
||||||
|
|
||||||
|
### Include
|
||||||
|
- Include Variance and StandardDeviation shortcuts
|
||||||
|
|
||||||
|
### Pass
|
||||||
|
- Pass gometalinter
|
||||||
|
|
||||||
|
### Refactor
|
||||||
|
- Refactor Variance function to be the same as population variance
|
||||||
|
|
||||||
|
### Release
|
||||||
|
- Release version 0.2.0
|
||||||
|
|
||||||
|
### Remove
|
||||||
|
- Remove unneeded do packages and update cover URL
|
||||||
|
- Remove sudo from pip install
|
||||||
|
|
||||||
|
### Reorder
|
||||||
|
- Reorder functions and sections
|
||||||
|
|
||||||
|
### Revert
|
||||||
|
- Revert to legacy containers to preserve go1.1 testing
|
||||||
|
|
||||||
|
### Switch
|
||||||
|
- Switch from legacy to container-based CI infrastructure
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update contributing instructions and mention Makefile
|
||||||
|
|
||||||
|
### Pull Requests
|
||||||
|
- Merge pull request [#5](https://github.com/montanaflynn/stats/issues/5) from orthographic-pedant/spell_check/accommodate
|
||||||
|
|
||||||
|
|
||||||
|
<a name="0.1.0"></a>
|
||||||
|
## [0.1.0] - 2015-08-19
|
||||||
|
### Add
|
||||||
|
- Add CONTRIBUTING.md
|
||||||
|
|
||||||
|
### Rename
|
||||||
|
- Rename functions while preserving backwards compatibility
|
||||||
|
|
||||||
|
|
||||||
|
<a name="0.0.9"></a>
|
||||||
|
## 0.0.9 - 2015-08-18
|
||||||
|
### Add
|
||||||
|
- Add HarmonicMean func
|
||||||
|
- Add GeometricMean func
|
||||||
|
- Add .gitignore to avoid commiting test coverage report
|
||||||
|
- Add Outliers stuct and QuantileOutliers func
|
||||||
|
- Add Interquartile Range, Midhinge and Trimean examples
|
||||||
|
- Add Trimean
|
||||||
|
- Add Midhinge
|
||||||
|
- Add Inter Quartile Range
|
||||||
|
- Add a unit test to check for an empty slice error
|
||||||
|
- Add Quantiles struct and Quantile func
|
||||||
|
- Add more tests and fix a typo
|
||||||
|
- Add Golang 1.5 to build tests
|
||||||
|
- Add a standard MIT license file
|
||||||
|
- Add basic benchmarking
|
||||||
|
- Add regression models
|
||||||
|
- Add codecov token
|
||||||
|
- Add codecov
|
||||||
|
- Add check for slices with a single item
|
||||||
|
- Add coverage tests
|
||||||
|
- Add back previous Go versions to Travis CI
|
||||||
|
- Add Travis CI
|
||||||
|
- Add GoDoc badge
|
||||||
|
- Add Percentile and Float64ToInt functions
|
||||||
|
- Add another rounding test for whole numbers
|
||||||
|
- Add build status badge
|
||||||
|
- Add code coverage badge
|
||||||
|
- Add test for NaN, achieving 100% code coverage
|
||||||
|
- Add round function
|
||||||
|
- Add standard deviation function
|
||||||
|
- Add sum function
|
||||||
|
|
||||||
|
### Add
|
||||||
|
- add tests for sample
|
||||||
|
- add sample
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Added sample and population variance and deviation functions
|
||||||
|
- Added README
|
||||||
|
|
||||||
|
### Adjust
|
||||||
|
- Adjust API ordering
|
||||||
|
|
||||||
|
### Avoid
|
||||||
|
- Avoid unintended consequence of using sort
|
||||||
|
|
||||||
|
### Better
|
||||||
|
- Better performing min/max
|
||||||
|
- Better description
|
||||||
|
|
||||||
|
### Change
|
||||||
|
- Change package path to potentially fix a bug in earlier versions of Go
|
||||||
|
|
||||||
|
### Clean
|
||||||
|
- Clean up README and add some more information
|
||||||
|
- Clean up test error
|
||||||
|
|
||||||
|
### Consistent
|
||||||
|
- Consistent empty slice error messages
|
||||||
|
- Consistent var naming
|
||||||
|
- Consistent func declaration
|
||||||
|
|
||||||
|
### Convert
|
||||||
|
- Convert ints to floats
|
||||||
|
|
||||||
|
### Duplicate
|
||||||
|
- Duplicate packages for all versions
|
||||||
|
|
||||||
|
### Export
|
||||||
|
- Export Coordinate struct fields
|
||||||
|
|
||||||
|
### First
|
||||||
|
- First commit
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
- Fix copy pasta mistake testing the wrong function
|
||||||
|
- Fix error message
|
||||||
|
- Fix usage output and edit API doc section
|
||||||
|
- Fix testing edgecase where map was in wrong order
|
||||||
|
- Fix usage example
|
||||||
|
- Fix usage examples
|
||||||
|
|
||||||
|
### Include
|
||||||
|
- Include the Nearest Rank method of calculating percentiles
|
||||||
|
|
||||||
|
### More
|
||||||
|
- More commenting
|
||||||
|
|
||||||
|
### Move
|
||||||
|
- Move GoDoc link to top
|
||||||
|
|
||||||
|
### Redirect
|
||||||
|
- Redirect kills newer versions of Go
|
||||||
|
|
||||||
|
### Refactor
|
||||||
|
- Refactor code and error checking
|
||||||
|
|
||||||
|
### Remove
|
||||||
|
- Remove unnecassary typecasting in sum func
|
||||||
|
- Remove cover since it doesn't work for later versions of go
|
||||||
|
- Remove golint and gocoveralls
|
||||||
|
|
||||||
|
### Rename
|
||||||
|
- Rename StandardDev to StdDev
|
||||||
|
- Rename StandardDev to StdDev
|
||||||
|
|
||||||
|
### Return
|
||||||
|
- Return errors for all functions
|
||||||
|
|
||||||
|
### Run
|
||||||
|
- Run go fmt to clean up formatting
|
||||||
|
|
||||||
|
### Simplify
|
||||||
|
- Simplify min/max function
|
||||||
|
|
||||||
|
### Start
|
||||||
|
- Start with minimal tests
|
||||||
|
|
||||||
|
### Switch
|
||||||
|
- Switch wercker to travis and update todos
|
||||||
|
|
||||||
|
### Table
|
||||||
|
- table testing style
|
||||||
|
|
||||||
|
### Update
|
||||||
|
- Update README and move the example main.go into it's own file
|
||||||
|
- Update TODO list
|
||||||
|
- Update README
|
||||||
|
- Update usage examples and todos
|
||||||
|
|
||||||
|
### Use
|
||||||
|
- Use codecov the recommended way
|
||||||
|
- Use correct string formatting types
|
||||||
|
|
||||||
|
### Pull Requests
|
||||||
|
- Merge pull request [#4](https://github.com/montanaflynn/stats/issues/4) from saromanov/sample
|
||||||
|
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/montanaflynn/stats/compare/v0.7.1...HEAD
|
||||||
|
[v0.7.1]: https://github.com/montanaflynn/stats/compare/v0.7.0...v0.7.1
|
||||||
|
[v0.7.0]: https://github.com/montanaflynn/stats/compare/v0.6.6...v0.7.0
|
||||||
|
[v0.6.6]: https://github.com/montanaflynn/stats/compare/v0.6.5...v0.6.6
|
||||||
|
[v0.6.5]: https://github.com/montanaflynn/stats/compare/v0.6.4...v0.6.5
|
||||||
|
[v0.6.4]: https://github.com/montanaflynn/stats/compare/v0.6.3...v0.6.4
|
||||||
|
[v0.6.3]: https://github.com/montanaflynn/stats/compare/v0.6.2...v0.6.3
|
||||||
|
[v0.6.2]: https://github.com/montanaflynn/stats/compare/v0.6.1...v0.6.2
|
||||||
|
[v0.6.1]: https://github.com/montanaflynn/stats/compare/v0.6.0...v0.6.1
|
||||||
|
[v0.6.0]: https://github.com/montanaflynn/stats/compare/v0.5.0...v0.6.0
|
||||||
|
[v0.5.0]: https://github.com/montanaflynn/stats/compare/v0.4.0...v0.5.0
|
||||||
|
[v0.4.0]: https://github.com/montanaflynn/stats/compare/0.3.0...v0.4.0
|
||||||
|
[0.3.0]: https://github.com/montanaflynn/stats/compare/0.2.0...0.3.0
|
||||||
|
[0.2.0]: https://github.com/montanaflynn/stats/compare/0.1.0...0.2.0
|
||||||
|
[0.1.0]: https://github.com/montanaflynn/stats/compare/0.0.9...0.1.0
|
||||||
1271
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/DOCUMENTATION.md
generated
vendored
Normal file
1271
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/DOCUMENTATION.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
21
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/LICENSE
generated
vendored
Normal file
21
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014-2023 Montana Flynn (https://montanaflynn.com)
|
||||||
|
|
||||||
|
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.
|
||||||
34
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/Makefile
generated
vendored
Normal file
34
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/Makefile
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
default: test lint
|
||||||
|
|
||||||
|
format:
|
||||||
|
go fmt .
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -race
|
||||||
|
|
||||||
|
check: format test
|
||||||
|
|
||||||
|
benchmark:
|
||||||
|
go test -bench=. -benchmem
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
go test -coverprofile=coverage.out
|
||||||
|
go tool cover -html="coverage.out"
|
||||||
|
|
||||||
|
lint: format
|
||||||
|
golangci-lint run .
|
||||||
|
|
||||||
|
docs:
|
||||||
|
godoc2md github.com/montanaflynn/stats | sed -e s#src/target/##g > DOCUMENTATION.md
|
||||||
|
|
||||||
|
release:
|
||||||
|
git-chglog --output CHANGELOG.md --next-tag ${TAG}
|
||||||
|
git add CHANGELOG.md
|
||||||
|
git commit -m "Update changelog with ${TAG} changes"
|
||||||
|
git tag ${TAG}
|
||||||
|
git-chglog $(TAG) | tail -n +4 | gsed '1s/^/$(TAG)\n/gm' > release-notes.txt
|
||||||
|
git push origin master ${TAG}
|
||||||
|
hub release create --copy -F release-notes.txt ${TAG}
|
||||||
|
|
||||||
237
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/README.md
generated
vendored
Normal file
237
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/README.md
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
# Stats - Golang Statistics Package
|
||||||
|
|
||||||
|
[![][action-svg]][action-url] [![][codecov-svg]][codecov-url] [![][goreport-svg]][goreport-url] [![][godoc-svg]][godoc-url] [![][pkggodev-svg]][pkggodev-url] [![][license-svg]][license-url]
|
||||||
|
|
||||||
|
A well tested and comprehensive Golang statistics library / package / module with no dependencies.
|
||||||
|
|
||||||
|
If you have any suggestions, problems or bug reports please [create an issue](https://github.com/montanaflynn/stats/issues) and I'll do my best to accommodate you. In addition simply starring the repo would show your support for the project and be very much appreciated!
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/montanaflynn/stats
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
All the functions can be seen in [examples/main.go](examples/main.go) but here's a little taste:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// start with some source data to use
|
||||||
|
data := []float64{1.0, 2.1, 3.2, 4.823, 4.1, 5.8}
|
||||||
|
|
||||||
|
// you could also use different types like this
|
||||||
|
// data := stats.LoadRawData([]int{1, 2, 3, 4, 5})
|
||||||
|
// data := stats.LoadRawData([]interface{}{1.1, "2", 3})
|
||||||
|
// etc...
|
||||||
|
|
||||||
|
median, _ := stats.Median(data)
|
||||||
|
fmt.Println(median) // 3.65
|
||||||
|
|
||||||
|
roundedMedian, _ := stats.Round(median, 0)
|
||||||
|
fmt.Println(roundedMedian) // 4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
The entire API documentation is available on [GoDoc.org](http://godoc.org/github.com/montanaflynn/stats) or [pkg.go.dev](https://pkg.go.dev/github.com/montanaflynn/stats).
|
||||||
|
|
||||||
|
You can also view docs offline with the following commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Command line
|
||||||
|
godoc . # show all exported apis
|
||||||
|
godoc . Median # show a single function
|
||||||
|
godoc -ex . Round # show function with example
|
||||||
|
godoc . Float64Data # show the type and methods
|
||||||
|
|
||||||
|
# Local website
|
||||||
|
godoc -http=:4444 # start the godoc server on port 4444
|
||||||
|
open http://localhost:4444/pkg/github.com/montanaflynn/stats/
|
||||||
|
```
|
||||||
|
|
||||||
|
The exported API is as follows:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var (
|
||||||
|
ErrEmptyInput = statsError{"Input must not be empty."}
|
||||||
|
ErrNaN = statsError{"Not a number."}
|
||||||
|
ErrNegative = statsError{"Must not contain negative values."}
|
||||||
|
ErrZero = statsError{"Must not contain zero values."}
|
||||||
|
ErrBounds = statsError{"Input is outside of range."}
|
||||||
|
ErrSize = statsError{"Must be the same length."}
|
||||||
|
ErrInfValue = statsError{"Value is infinite."}
|
||||||
|
ErrYCoord = statsError{"Y Value must be greater than zero."}
|
||||||
|
)
|
||||||
|
|
||||||
|
func Round(input float64, places int) (rounded float64, err error) {}
|
||||||
|
|
||||||
|
type Float64Data []float64
|
||||||
|
|
||||||
|
func LoadRawData(raw interface{}) (f Float64Data) {}
|
||||||
|
|
||||||
|
func AutoCorrelation(data Float64Data, lags int) (float64, error) {}
|
||||||
|
func ChebyshevDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {}
|
||||||
|
func Correlation(data1, data2 Float64Data) (float64, error) {}
|
||||||
|
func Covariance(data1, data2 Float64Data) (float64, error) {}
|
||||||
|
func CovariancePopulation(data1, data2 Float64Data) (float64, error) {}
|
||||||
|
func CumulativeSum(input Float64Data) ([]float64, error) {}
|
||||||
|
func Describe(input Float64Data, allowNaN bool, percentiles *[]float64) (*Description, error) {}
|
||||||
|
func DescribePercentileFunc(input Float64Data, allowNaN bool, percentiles *[]float64, percentileFunc func(Float64Data, float64) (float64, error)) (*Description, error) {}
|
||||||
|
func Entropy(input Float64Data) (float64, error) {}
|
||||||
|
func EuclideanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {}
|
||||||
|
func GeometricMean(input Float64Data) (float64, error) {}
|
||||||
|
func HarmonicMean(input Float64Data) (float64, error) {}
|
||||||
|
func InterQuartileRange(input Float64Data) (float64, error) {}
|
||||||
|
func ManhattanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {}
|
||||||
|
func Max(input Float64Data) (max float64, err error) {}
|
||||||
|
func Mean(input Float64Data) (float64, error) {}
|
||||||
|
func Median(input Float64Data) (median float64, err error) {}
|
||||||
|
func MedianAbsoluteDeviation(input Float64Data) (mad float64, err error) {}
|
||||||
|
func MedianAbsoluteDeviationPopulation(input Float64Data) (mad float64, err error) {}
|
||||||
|
func Midhinge(input Float64Data) (float64, error) {}
|
||||||
|
func Min(input Float64Data) (min float64, err error) {}
|
||||||
|
func MinkowskiDistance(dataPointX, dataPointY Float64Data, lambda float64) (distance float64, err error) {}
|
||||||
|
func Mode(input Float64Data) (mode []float64, err error) {}
|
||||||
|
func NormBoxMullerRvs(loc float64, scale float64, size int) []float64 {}
|
||||||
|
func NormCdf(x float64, loc float64, scale float64) float64 {}
|
||||||
|
func NormEntropy(loc float64, scale float64) float64 {}
|
||||||
|
func NormFit(data []float64) [2]float64{}
|
||||||
|
func NormInterval(alpha float64, loc float64, scale float64 ) [2]float64 {}
|
||||||
|
func NormIsf(p float64, loc float64, scale float64) (x float64) {}
|
||||||
|
func NormLogCdf(x float64, loc float64, scale float64) float64 {}
|
||||||
|
func NormLogPdf(x float64, loc float64, scale float64) float64 {}
|
||||||
|
func NormLogSf(x float64, loc float64, scale float64) float64 {}
|
||||||
|
func NormMean(loc float64, scale float64) float64 {}
|
||||||
|
func NormMedian(loc float64, scale float64) float64 {}
|
||||||
|
func NormMoment(n int, loc float64, scale float64) float64 {}
|
||||||
|
func NormPdf(x float64, loc float64, scale float64) float64 {}
|
||||||
|
func NormPpf(p float64, loc float64, scale float64) (x float64) {}
|
||||||
|
func NormPpfRvs(loc float64, scale float64, size int) []float64 {}
|
||||||
|
func NormSf(x float64, loc float64, scale float64) float64 {}
|
||||||
|
func NormStats(loc float64, scale float64, moments string) []float64 {}
|
||||||
|
func NormStd(loc float64, scale float64) float64 {}
|
||||||
|
func NormVar(loc float64, scale float64) float64 {}
|
||||||
|
func Pearson(data1, data2 Float64Data) (float64, error) {}
|
||||||
|
func Percentile(input Float64Data, percent float64) (percentile float64, err error) {}
|
||||||
|
func PercentileNearestRank(input Float64Data, percent float64) (percentile float64, err error) {}
|
||||||
|
func PopulationVariance(input Float64Data) (pvar float64, err error) {}
|
||||||
|
func Sample(input Float64Data, takenum int, replacement bool) ([]float64, error) {}
|
||||||
|
func SampleVariance(input Float64Data) (svar float64, err error) {}
|
||||||
|
func Sigmoid(input Float64Data) ([]float64, error) {}
|
||||||
|
func SoftMax(input Float64Data) ([]float64, error) {}
|
||||||
|
func StableSample(input Float64Data, takenum int) ([]float64, error) {}
|
||||||
|
func StandardDeviation(input Float64Data) (sdev float64, err error) {}
|
||||||
|
func StandardDeviationPopulation(input Float64Data) (sdev float64, err error) {}
|
||||||
|
func StandardDeviationSample(input Float64Data) (sdev float64, err error) {}
|
||||||
|
func StdDevP(input Float64Data) (sdev float64, err error) {}
|
||||||
|
func StdDevS(input Float64Data) (sdev float64, err error) {}
|
||||||
|
func Sum(input Float64Data) (sum float64, err error) {}
|
||||||
|
func Trimean(input Float64Data) (float64, error) {}
|
||||||
|
func VarP(input Float64Data) (sdev float64, err error) {}
|
||||||
|
func VarS(input Float64Data) (sdev float64, err error) {}
|
||||||
|
func Variance(input Float64Data) (sdev float64, err error) {}
|
||||||
|
func ProbGeom(a int, b int, p float64) (prob float64, err error) {}
|
||||||
|
func ExpGeom(p float64) (exp float64, err error) {}
|
||||||
|
func VarGeom(p float64) (exp float64, err error) {}
|
||||||
|
|
||||||
|
type Coordinate struct {
|
||||||
|
X, Y float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Series []Coordinate
|
||||||
|
|
||||||
|
func ExponentialRegression(s Series) (regressions Series, err error) {}
|
||||||
|
func LinearRegression(s Series) (regressions Series, err error) {}
|
||||||
|
func LogarithmicRegression(s Series) (regressions Series, err error) {}
|
||||||
|
|
||||||
|
type Outliers struct {
|
||||||
|
Mild Float64Data
|
||||||
|
Extreme Float64Data
|
||||||
|
}
|
||||||
|
|
||||||
|
type Quartiles struct {
|
||||||
|
Q1 float64
|
||||||
|
Q2 float64
|
||||||
|
Q3 float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func Quartile(input Float64Data) (Quartiles, error) {}
|
||||||
|
func QuartileOutliers(input Float64Data) (Outliers, error) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Pull request are always welcome no matter how big or small. I've included a [Makefile](https://github.com/montanaflynn/stats/blob/master/Makefile) that has a lot of helper targets for common actions such as linting, testing, code coverage reporting and more.
|
||||||
|
|
||||||
|
1. Fork the repo and clone your fork
|
||||||
|
2. Create new branch (`git checkout -b some-thing`)
|
||||||
|
3. Make the desired changes
|
||||||
|
4. Ensure tests pass (`go test -cover` or `make test`)
|
||||||
|
5. Run lint and fix problems (`go vet .` or `make lint`)
|
||||||
|
6. Commit changes (`git commit -am 'Did something'`)
|
||||||
|
7. Push branch (`git push origin some-thing`)
|
||||||
|
8. Submit pull request
|
||||||
|
|
||||||
|
To make things as seamless as possible please also consider the following steps:
|
||||||
|
|
||||||
|
- Update `examples/main.go` with a simple example of the new feature
|
||||||
|
- Update `README.md` documentation section with any new exported API
|
||||||
|
- Keep 100% code coverage (you can check with `make coverage`)
|
||||||
|
- Squash commits into single units of work with `git rebase -i new-feature`
|
||||||
|
|
||||||
|
## Releasing
|
||||||
|
|
||||||
|
This is not required by contributors and mostly here as a reminder to myself as the maintainer of this repo. To release a new version we should update the [CHANGELOG.md](/CHANGELOG.md) and [DOCUMENTATION.md](/DOCUMENTATION.md).
|
||||||
|
|
||||||
|
First install the tools used to generate the markdown files and release:
|
||||||
|
|
||||||
|
```
|
||||||
|
go install github.com/davecheney/godoc2md@latest
|
||||||
|
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||||
|
brew tap git-chglog/git-chglog
|
||||||
|
brew install gnu-sed hub git-chglog
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can run these `make` directives:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Generate DOCUMENTATION.md
|
||||||
|
make docs
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we can create a [CHANGELOG.md](/CHANGELOG.md) a new git tag and a github release:
|
||||||
|
|
||||||
|
```
|
||||||
|
make release TAG=v0.x.x
|
||||||
|
```
|
||||||
|
|
||||||
|
To authenticate `hub` for the release you will need to create a personal access token and use it as the password when it's requested.
|
||||||
|
|
||||||
|
## MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2014-2023 Montana Flynn (https://montanaflynn.com)
|
||||||
|
|
||||||
|
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 AUTHORpublicS 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.
|
||||||
|
|
||||||
|
[action-url]: https://github.com/montanaflynn/stats/actions
|
||||||
|
[action-svg]: https://img.shields.io/github/actions/workflow/status/montanaflynn/stats/go.yml
|
||||||
|
|
||||||
|
[codecov-url]: https://app.codecov.io/gh/montanaflynn/stats
|
||||||
|
[codecov-svg]: https://img.shields.io/codecov/c/github/montanaflynn/stats?token=wnw8dActnH
|
||||||
|
|
||||||
|
[goreport-url]: https://goreportcard.com/report/github.com/montanaflynn/stats
|
||||||
|
[goreport-svg]: https://goreportcard.com/badge/github.com/montanaflynn/stats
|
||||||
|
|
||||||
|
[godoc-url]: https://godoc.org/github.com/montanaflynn/stats
|
||||||
|
[godoc-svg]: https://godoc.org/github.com/montanaflynn/stats?status.svg
|
||||||
|
|
||||||
|
[pkggodev-url]: https://pkg.go.dev/github.com/montanaflynn/stats
|
||||||
|
[pkggodev-svg]: https://gistcdn.githack.com/montanaflynn/b02f1d78d8c0de8435895d7e7cd0d473/raw/17f2a5a69f1323ecd42c00e0683655da96d9ecc8/badge.svg
|
||||||
|
|
||||||
|
[license-url]: https://github.com/montanaflynn/stats/blob/master/LICENSE
|
||||||
|
[license-svg]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||||
60
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/correlation.go
generated
vendored
Normal file
60
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/correlation.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Correlation describes the degree of relationship between two sets of data
|
||||||
|
func Correlation(data1, data2 Float64Data) (float64, error) {
|
||||||
|
|
||||||
|
l1 := data1.Len()
|
||||||
|
l2 := data2.Len()
|
||||||
|
|
||||||
|
if l1 == 0 || l2 == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if l1 != l2 {
|
||||||
|
return math.NaN(), SizeErr
|
||||||
|
}
|
||||||
|
|
||||||
|
sdev1, _ := StandardDeviationPopulation(data1)
|
||||||
|
sdev2, _ := StandardDeviationPopulation(data2)
|
||||||
|
|
||||||
|
if sdev1 == 0 || sdev2 == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
covp, _ := CovariancePopulation(data1, data2)
|
||||||
|
return covp / (sdev1 * sdev2), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pearson calculates the Pearson product-moment correlation coefficient between two variables
|
||||||
|
func Pearson(data1, data2 Float64Data) (float64, error) {
|
||||||
|
return Correlation(data1, data2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoCorrelation is the correlation of a signal with a delayed copy of itself as a function of delay
|
||||||
|
func AutoCorrelation(data Float64Data, lags int) (float64, error) {
|
||||||
|
if len(data) < 1 {
|
||||||
|
return 0, EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
mean, _ := Mean(data)
|
||||||
|
|
||||||
|
var result, q float64
|
||||||
|
|
||||||
|
for i := 0; i < lags; i++ {
|
||||||
|
v := (data[0] - mean) * (data[0] - mean)
|
||||||
|
for i := 1; i < len(data); i++ {
|
||||||
|
delta0 := data[i-1] - mean
|
||||||
|
delta1 := data[i] - mean
|
||||||
|
q += (delta0*delta1 - q) / float64(i+1)
|
||||||
|
v += (delta1*delta1 - v) / float64(i+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = q / v
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
21
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/cumulative_sum.go
generated
vendored
Normal file
21
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/cumulative_sum.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
// CumulativeSum calculates the cumulative sum of the input slice
|
||||||
|
func CumulativeSum(input Float64Data) ([]float64, error) {
|
||||||
|
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return Float64Data{}, EmptyInput
|
||||||
|
}
|
||||||
|
|
||||||
|
cumSum := make([]float64, input.Len())
|
||||||
|
|
||||||
|
for i, val := range input {
|
||||||
|
if i == 0 {
|
||||||
|
cumSum[i] = val
|
||||||
|
} else {
|
||||||
|
cumSum[i] = cumSum[i-1] + val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cumSum, nil
|
||||||
|
}
|
||||||
169
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/data.go
generated
vendored
Normal file
169
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/data.go
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
// Float64Data is a named type for []float64 with helper methods
|
||||||
|
type Float64Data []float64
|
||||||
|
|
||||||
|
// Get item in slice
|
||||||
|
func (f Float64Data) Get(i int) float64 { return f[i] }
|
||||||
|
|
||||||
|
// Len returns length of slice
|
||||||
|
func (f Float64Data) Len() int { return len(f) }
|
||||||
|
|
||||||
|
// Less returns if one number is less than another
|
||||||
|
func (f Float64Data) Less(i, j int) bool { return f[i] < f[j] }
|
||||||
|
|
||||||
|
// Swap switches out two numbers in slice
|
||||||
|
func (f Float64Data) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
||||||
|
|
||||||
|
// Min returns the minimum number in the data
|
||||||
|
func (f Float64Data) Min() (float64, error) { return Min(f) }
|
||||||
|
|
||||||
|
// Max returns the maximum number in the data
|
||||||
|
func (f Float64Data) Max() (float64, error) { return Max(f) }
|
||||||
|
|
||||||
|
// Sum returns the total of all the numbers in the data
|
||||||
|
func (f Float64Data) Sum() (float64, error) { return Sum(f) }
|
||||||
|
|
||||||
|
// CumulativeSum returns the cumulative sum of the data
|
||||||
|
func (f Float64Data) CumulativeSum() ([]float64, error) { return CumulativeSum(f) }
|
||||||
|
|
||||||
|
// Mean returns the mean of the data
|
||||||
|
func (f Float64Data) Mean() (float64, error) { return Mean(f) }
|
||||||
|
|
||||||
|
// Median returns the median of the data
|
||||||
|
func (f Float64Data) Median() (float64, error) { return Median(f) }
|
||||||
|
|
||||||
|
// Mode returns the mode of the data
|
||||||
|
func (f Float64Data) Mode() ([]float64, error) { return Mode(f) }
|
||||||
|
|
||||||
|
// GeometricMean returns the median of the data
|
||||||
|
func (f Float64Data) GeometricMean() (float64, error) { return GeometricMean(f) }
|
||||||
|
|
||||||
|
// HarmonicMean returns the mode of the data
|
||||||
|
func (f Float64Data) HarmonicMean() (float64, error) { return HarmonicMean(f) }
|
||||||
|
|
||||||
|
// MedianAbsoluteDeviation the median of the absolute deviations from the dataset median
|
||||||
|
func (f Float64Data) MedianAbsoluteDeviation() (float64, error) {
|
||||||
|
return MedianAbsoluteDeviation(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MedianAbsoluteDeviationPopulation finds the median of the absolute deviations from the population median
|
||||||
|
func (f Float64Data) MedianAbsoluteDeviationPopulation() (float64, error) {
|
||||||
|
return MedianAbsoluteDeviationPopulation(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardDeviation the amount of variation in the dataset
|
||||||
|
func (f Float64Data) StandardDeviation() (float64, error) {
|
||||||
|
return StandardDeviation(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardDeviationPopulation finds the amount of variation from the population
|
||||||
|
func (f Float64Data) StandardDeviationPopulation() (float64, error) {
|
||||||
|
return StandardDeviationPopulation(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardDeviationSample finds the amount of variation from a sample
|
||||||
|
func (f Float64Data) StandardDeviationSample() (float64, error) {
|
||||||
|
return StandardDeviationSample(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuartileOutliers finds the mild and extreme outliers
|
||||||
|
func (f Float64Data) QuartileOutliers() (Outliers, error) {
|
||||||
|
return QuartileOutliers(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Percentile finds the relative standing in a slice of floats
|
||||||
|
func (f Float64Data) Percentile(p float64) (float64, error) {
|
||||||
|
return Percentile(f, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PercentileNearestRank finds the relative standing using the Nearest Rank method
|
||||||
|
func (f Float64Data) PercentileNearestRank(p float64) (float64, error) {
|
||||||
|
return PercentileNearestRank(f, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correlation describes the degree of relationship between two sets of data
|
||||||
|
func (f Float64Data) Correlation(d Float64Data) (float64, error) {
|
||||||
|
return Correlation(f, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoCorrelation is the correlation of a signal with a delayed copy of itself as a function of delay
|
||||||
|
func (f Float64Data) AutoCorrelation(lags int) (float64, error) {
|
||||||
|
return AutoCorrelation(f, lags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pearson calculates the Pearson product-moment correlation coefficient between two variables.
|
||||||
|
func (f Float64Data) Pearson(d Float64Data) (float64, error) {
|
||||||
|
return Pearson(f, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quartile returns the three quartile points from a slice of data
|
||||||
|
func (f Float64Data) Quartile(d Float64Data) (Quartiles, error) {
|
||||||
|
return Quartile(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterQuartileRange finds the range between Q1 and Q3
|
||||||
|
func (f Float64Data) InterQuartileRange() (float64, error) {
|
||||||
|
return InterQuartileRange(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Midhinge finds the average of the first and third quartiles
|
||||||
|
func (f Float64Data) Midhinge(d Float64Data) (float64, error) {
|
||||||
|
return Midhinge(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trimean finds the average of the median and the midhinge
|
||||||
|
func (f Float64Data) Trimean(d Float64Data) (float64, error) {
|
||||||
|
return Trimean(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample returns sample from input with replacement or without
|
||||||
|
func (f Float64Data) Sample(n int, r bool) ([]float64, error) {
|
||||||
|
return Sample(f, n, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variance the amount of variation in the dataset
|
||||||
|
func (f Float64Data) Variance() (float64, error) {
|
||||||
|
return Variance(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopulationVariance finds the amount of variance within a population
|
||||||
|
func (f Float64Data) PopulationVariance() (float64, error) {
|
||||||
|
return PopulationVariance(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SampleVariance finds the amount of variance within a sample
|
||||||
|
func (f Float64Data) SampleVariance() (float64, error) {
|
||||||
|
return SampleVariance(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Covariance is a measure of how much two sets of data change
|
||||||
|
func (f Float64Data) Covariance(d Float64Data) (float64, error) {
|
||||||
|
return Covariance(f, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CovariancePopulation computes covariance for entire population between two variables
|
||||||
|
func (f Float64Data) CovariancePopulation(d Float64Data) (float64, error) {
|
||||||
|
return CovariancePopulation(f, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sigmoid returns the input values along the sigmoid or s-shaped curve
|
||||||
|
func (f Float64Data) Sigmoid() ([]float64, error) {
|
||||||
|
return Sigmoid(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SoftMax returns the input values in the range of 0 to 1
|
||||||
|
// with sum of all the probabilities being equal to one.
|
||||||
|
func (f Float64Data) SoftMax() ([]float64, error) {
|
||||||
|
return SoftMax(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entropy provides calculation of the entropy
|
||||||
|
func (f Float64Data) Entropy() (float64, error) {
|
||||||
|
return Entropy(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quartiles returns the three quartile points from instance of Float64Data
|
||||||
|
func (f Float64Data) Quartiles() (Quartiles, error) {
|
||||||
|
return Quartile(f)
|
||||||
|
}
|
||||||
81
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/describe.go
generated
vendored
Normal file
81
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/describe.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Holds information about the dataset provided to Describe
|
||||||
|
type Description struct {
|
||||||
|
Count int
|
||||||
|
Mean float64
|
||||||
|
Std float64
|
||||||
|
Max float64
|
||||||
|
Min float64
|
||||||
|
DescriptionPercentiles []descriptionPercentile
|
||||||
|
AllowedNaN bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specifies percentiles to be computed
|
||||||
|
type descriptionPercentile struct {
|
||||||
|
Percentile float64
|
||||||
|
Value float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe generates descriptive statistics about a provided dataset, similar to python's pandas.describe()
|
||||||
|
func Describe(input Float64Data, allowNaN bool, percentiles *[]float64) (*Description, error) {
|
||||||
|
return DescribePercentileFunc(input, allowNaN, percentiles, Percentile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe generates descriptive statistics about a provided dataset, similar to python's pandas.describe()
|
||||||
|
// Takes in a function to use for percentile calculation
|
||||||
|
func DescribePercentileFunc(input Float64Data, allowNaN bool, percentiles *[]float64, percentileFunc func(Float64Data, float64) (float64, error)) (*Description, error) {
|
||||||
|
var description Description
|
||||||
|
description.AllowedNaN = allowNaN
|
||||||
|
description.Count = input.Len()
|
||||||
|
|
||||||
|
if description.Count == 0 && !allowNaN {
|
||||||
|
return &description, ErrEmptyInput
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disregard error, since it cannot be thrown if Count is > 0 and allowNaN is false, else NaN is accepted
|
||||||
|
description.Std, _ = StandardDeviation(input)
|
||||||
|
description.Max, _ = Max(input)
|
||||||
|
description.Min, _ = Min(input)
|
||||||
|
description.Mean, _ = Mean(input)
|
||||||
|
|
||||||
|
if percentiles != nil {
|
||||||
|
for _, percentile := range *percentiles {
|
||||||
|
if value, err := percentileFunc(input, percentile); err == nil || allowNaN {
|
||||||
|
description.DescriptionPercentiles = append(description.DescriptionPercentiles, descriptionPercentile{Percentile: percentile, Value: value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &description, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Represents the Description instance in a string format with specified number of decimals
|
||||||
|
|
||||||
|
count 3
|
||||||
|
mean 2.00
|
||||||
|
std 0.82
|
||||||
|
max 3.00
|
||||||
|
min 1.00
|
||||||
|
25.00% NaN
|
||||||
|
50.00% 1.50
|
||||||
|
75.00% 2.50
|
||||||
|
NaN OK true
|
||||||
|
*/
|
||||||
|
func (d *Description) String(decimals int) string {
|
||||||
|
var str string
|
||||||
|
|
||||||
|
str += fmt.Sprintf("count\t%d\n", d.Count)
|
||||||
|
str += fmt.Sprintf("mean\t%.*f\n", decimals, d.Mean)
|
||||||
|
str += fmt.Sprintf("std\t%.*f\n", decimals, d.Std)
|
||||||
|
str += fmt.Sprintf("max\t%.*f\n", decimals, d.Max)
|
||||||
|
str += fmt.Sprintf("min\t%.*f\n", decimals, d.Min)
|
||||||
|
for _, percentile := range d.DescriptionPercentiles {
|
||||||
|
str += fmt.Sprintf("%.2f%%\t%.*f\n", percentile.Percentile, decimals, percentile.Value)
|
||||||
|
}
|
||||||
|
str += fmt.Sprintf("NaN OK\t%t", d.AllowedNaN)
|
||||||
|
return str
|
||||||
|
}
|
||||||
57
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/deviation.go
generated
vendored
Normal file
57
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/deviation.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// MedianAbsoluteDeviation finds the median of the absolute deviations from the dataset median
|
||||||
|
func MedianAbsoluteDeviation(input Float64Data) (mad float64, err error) {
|
||||||
|
return MedianAbsoluteDeviationPopulation(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MedianAbsoluteDeviationPopulation finds the median of the absolute deviations from the population median
|
||||||
|
func MedianAbsoluteDeviationPopulation(input Float64Data) (mad float64, err error) {
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
i := copyslice(input)
|
||||||
|
m, _ := Median(i)
|
||||||
|
|
||||||
|
for key, value := range i {
|
||||||
|
i[key] = math.Abs(value - m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Median(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardDeviation the amount of variation in the dataset
|
||||||
|
func StandardDeviation(input Float64Data) (sdev float64, err error) {
|
||||||
|
return StandardDeviationPopulation(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardDeviationPopulation finds the amount of variation from the population
|
||||||
|
func StandardDeviationPopulation(input Float64Data) (sdev float64, err error) {
|
||||||
|
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the population variance
|
||||||
|
vp, _ := PopulationVariance(input)
|
||||||
|
|
||||||
|
// Return the population standard deviation
|
||||||
|
return math.Sqrt(vp), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardDeviationSample finds the amount of variation from a sample
|
||||||
|
func StandardDeviationSample(input Float64Data) (sdev float64, err error) {
|
||||||
|
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the sample variance
|
||||||
|
vs, _ := SampleVariance(input)
|
||||||
|
|
||||||
|
// Return the sample standard deviation
|
||||||
|
return math.Sqrt(vs), nil
|
||||||
|
}
|
||||||
91
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/distances.go
generated
vendored
Normal file
91
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/distances.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate data for distance calculation
|
||||||
|
func validateData(dataPointX, dataPointY Float64Data) error {
|
||||||
|
if len(dataPointX) == 0 || len(dataPointY) == 0 {
|
||||||
|
return EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dataPointX) != len(dataPointY) {
|
||||||
|
return SizeErr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChebyshevDistance computes the Chebyshev distance between two data sets
|
||||||
|
func ChebyshevDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {
|
||||||
|
err = validateData(dataPointX, dataPointY)
|
||||||
|
if err != nil {
|
||||||
|
return math.NaN(), err
|
||||||
|
}
|
||||||
|
var tempDistance float64
|
||||||
|
for i := 0; i < len(dataPointY); i++ {
|
||||||
|
tempDistance = math.Abs(dataPointX[i] - dataPointY[i])
|
||||||
|
if distance < tempDistance {
|
||||||
|
distance = tempDistance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return distance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EuclideanDistance computes the Euclidean distance between two data sets
|
||||||
|
func EuclideanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {
|
||||||
|
|
||||||
|
err = validateData(dataPointX, dataPointY)
|
||||||
|
if err != nil {
|
||||||
|
return math.NaN(), err
|
||||||
|
}
|
||||||
|
distance = 0
|
||||||
|
for i := 0; i < len(dataPointX); i++ {
|
||||||
|
distance = distance + ((dataPointX[i] - dataPointY[i]) * (dataPointX[i] - dataPointY[i]))
|
||||||
|
}
|
||||||
|
return math.Sqrt(distance), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ManhattanDistance computes the Manhattan distance between two data sets
|
||||||
|
func ManhattanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {
|
||||||
|
err = validateData(dataPointX, dataPointY)
|
||||||
|
if err != nil {
|
||||||
|
return math.NaN(), err
|
||||||
|
}
|
||||||
|
distance = 0
|
||||||
|
for i := 0; i < len(dataPointX); i++ {
|
||||||
|
distance = distance + math.Abs(dataPointX[i]-dataPointY[i])
|
||||||
|
}
|
||||||
|
return distance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinkowskiDistance computes the Minkowski distance between two data sets
|
||||||
|
//
|
||||||
|
// Arguments:
|
||||||
|
//
|
||||||
|
// dataPointX: First set of data points
|
||||||
|
// dataPointY: Second set of data points. Length of both data
|
||||||
|
// sets must be equal.
|
||||||
|
// lambda: aka p or city blocks; With lambda = 1
|
||||||
|
// returned distance is manhattan distance and
|
||||||
|
// lambda = 2; it is euclidean distance. Lambda
|
||||||
|
// reaching to infinite - distance would be chebysev
|
||||||
|
// distance.
|
||||||
|
//
|
||||||
|
// Return:
|
||||||
|
//
|
||||||
|
// Distance or error
|
||||||
|
func MinkowskiDistance(dataPointX, dataPointY Float64Data, lambda float64) (distance float64, err error) {
|
||||||
|
err = validateData(dataPointX, dataPointY)
|
||||||
|
if err != nil {
|
||||||
|
return math.NaN(), err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(dataPointY); i++ {
|
||||||
|
distance = distance + math.Pow(math.Abs(dataPointX[i]-dataPointY[i]), lambda)
|
||||||
|
}
|
||||||
|
distance = math.Pow(distance, 1/lambda)
|
||||||
|
if math.IsInf(distance, 1) {
|
||||||
|
return math.NaN(), InfValue
|
||||||
|
}
|
||||||
|
return distance, nil
|
||||||
|
}
|
||||||
23
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/doc.go
generated
vendored
Normal file
23
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/doc.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
Package stats is a well tested and comprehensive
|
||||||
|
statistics library package with no dependencies.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
|
||||||
|
// start with some source data to use
|
||||||
|
data := []float64{1.0, 2.1, 3.2, 4.823, 4.1, 5.8}
|
||||||
|
|
||||||
|
// you could also use different types like this
|
||||||
|
// data := stats.LoadRawData([]int{1, 2, 3, 4, 5})
|
||||||
|
// data := stats.LoadRawData([]interface{}{1.1, "2", 3})
|
||||||
|
// etc...
|
||||||
|
|
||||||
|
median, _ := stats.Median(data)
|
||||||
|
fmt.Println(median) // 3.65
|
||||||
|
|
||||||
|
roundedMedian, _ := stats.Round(median, 0)
|
||||||
|
fmt.Println(roundedMedian) // 4
|
||||||
|
|
||||||
|
MIT License Copyright (c) 2014-2020 Montana Flynn (https://montanaflynn.com)
|
||||||
|
*/
|
||||||
|
package stats
|
||||||
31
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/entropy.go
generated
vendored
Normal file
31
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/entropy.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Entropy provides calculation of the entropy
|
||||||
|
func Entropy(input Float64Data) (float64, error) {
|
||||||
|
input, err := normalize(input)
|
||||||
|
if err != nil {
|
||||||
|
return math.NaN(), err
|
||||||
|
}
|
||||||
|
var result float64
|
||||||
|
for i := 0; i < input.Len(); i++ {
|
||||||
|
v := input.Get(i)
|
||||||
|
if v == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result += (v * math.Log(v))
|
||||||
|
}
|
||||||
|
return -result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize(input Float64Data) (Float64Data, error) {
|
||||||
|
sum, err := input.Sum()
|
||||||
|
if err != nil {
|
||||||
|
return Float64Data{}, err
|
||||||
|
}
|
||||||
|
for i := 0; i < input.Len(); i++ {
|
||||||
|
input[i] = input[i] / sum
|
||||||
|
}
|
||||||
|
return input, nil
|
||||||
|
}
|
||||||
35
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/errors.go
generated
vendored
Normal file
35
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/errors.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
type statsError struct {
|
||||||
|
err string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s statsError) Error() string {
|
||||||
|
return s.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s statsError) String() string {
|
||||||
|
return s.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are the package-wide error values.
|
||||||
|
// All error identification should use these values.
|
||||||
|
// https://github.com/golang/go/wiki/Errors#naming
|
||||||
|
var (
|
||||||
|
// ErrEmptyInput Input must not be empty
|
||||||
|
ErrEmptyInput = statsError{"Input must not be empty."}
|
||||||
|
// ErrNaN Not a number
|
||||||
|
ErrNaN = statsError{"Not a number."}
|
||||||
|
// ErrNegative Must not contain negative values
|
||||||
|
ErrNegative = statsError{"Must not contain negative values."}
|
||||||
|
// ErrZero Must not contain zero values
|
||||||
|
ErrZero = statsError{"Must not contain zero values."}
|
||||||
|
// ErrBounds Input is outside of range
|
||||||
|
ErrBounds = statsError{"Input is outside of range."}
|
||||||
|
// ErrSize Must be the same length
|
||||||
|
ErrSize = statsError{"Must be the same length."}
|
||||||
|
// ErrInfValue Value is infinite
|
||||||
|
ErrInfValue = statsError{"Value is infinite."}
|
||||||
|
// ErrYCoord Y Value must be greater than zero
|
||||||
|
ErrYCoord = statsError{"Y Value must be greater than zero."}
|
||||||
|
)
|
||||||
42
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/geometric_distribution.go
generated
vendored
Normal file
42
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/geometric_distribution.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProbGeom generates the probability for a geometric random variable
|
||||||
|
// with parameter p to achieve success in the interval of [a, b] trials
|
||||||
|
// See https://en.wikipedia.org/wiki/Geometric_distribution for more information
|
||||||
|
func ProbGeom(a int, b int, p float64) (prob float64, err error) {
|
||||||
|
if (a > b) || (a < 1) {
|
||||||
|
return math.NaN(), ErrBounds
|
||||||
|
}
|
||||||
|
|
||||||
|
prob = 0
|
||||||
|
q := 1 - p // probability of failure
|
||||||
|
|
||||||
|
for k := a + 1; k <= b; k++ {
|
||||||
|
prob = prob + p*math.Pow(q, float64(k-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
return prob, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProbGeom generates the expectation or average number of trials
|
||||||
|
// for a geometric random variable with parameter p
|
||||||
|
func ExpGeom(p float64) (exp float64, err error) {
|
||||||
|
if (p > 1) || (p < 0) {
|
||||||
|
return math.NaN(), ErrNegative
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1 / p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProbGeom generates the variance for number for a
|
||||||
|
// geometric random variable with parameter p
|
||||||
|
func VarGeom(p float64) (exp float64, err error) {
|
||||||
|
if (p > 1) || (p < 0) {
|
||||||
|
return math.NaN(), ErrNegative
|
||||||
|
}
|
||||||
|
return (1 - p) / math.Pow(p, 2), nil
|
||||||
|
}
|
||||||
49
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/legacy.go
generated
vendored
Normal file
49
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/legacy.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
// VarP is a shortcut to PopulationVariance
|
||||||
|
func VarP(input Float64Data) (sdev float64, err error) {
|
||||||
|
return PopulationVariance(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarS is a shortcut to SampleVariance
|
||||||
|
func VarS(input Float64Data) (sdev float64, err error) {
|
||||||
|
return SampleVariance(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdDevP is a shortcut to StandardDeviationPopulation
|
||||||
|
func StdDevP(input Float64Data) (sdev float64, err error) {
|
||||||
|
return StandardDeviationPopulation(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdDevS is a shortcut to StandardDeviationSample
|
||||||
|
func StdDevS(input Float64Data) (sdev float64, err error) {
|
||||||
|
return StandardDeviationSample(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinReg is a shortcut to LinearRegression
|
||||||
|
func LinReg(s []Coordinate) (regressions []Coordinate, err error) {
|
||||||
|
return LinearRegression(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpReg is a shortcut to ExponentialRegression
|
||||||
|
func ExpReg(s []Coordinate) (regressions []Coordinate, err error) {
|
||||||
|
return ExponentialRegression(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogReg is a shortcut to LogarithmicRegression
|
||||||
|
func LogReg(s []Coordinate) (regressions []Coordinate, err error) {
|
||||||
|
return LogarithmicRegression(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy error names that didn't start with Err
|
||||||
|
var (
|
||||||
|
EmptyInputErr = ErrEmptyInput
|
||||||
|
NaNErr = ErrNaN
|
||||||
|
NegativeErr = ErrNegative
|
||||||
|
ZeroErr = ErrZero
|
||||||
|
BoundsErr = ErrBounds
|
||||||
|
SizeErr = ErrSize
|
||||||
|
InfValue = ErrInfValue
|
||||||
|
YCoordErr = ErrYCoord
|
||||||
|
EmptyInput = ErrEmptyInput
|
||||||
|
)
|
||||||
199
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/load.go
generated
vendored
Normal file
199
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/load.go
generated
vendored
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadRawData parses and converts a slice of mixed data types to floats
|
||||||
|
func LoadRawData(raw interface{}) (f Float64Data) {
|
||||||
|
var r []interface{}
|
||||||
|
var s Float64Data
|
||||||
|
|
||||||
|
switch t := raw.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
r = t
|
||||||
|
case []uint:
|
||||||
|
for _, v := range t {
|
||||||
|
s = append(s, float64(v))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []uint8:
|
||||||
|
for _, v := range t {
|
||||||
|
s = append(s, float64(v))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []uint16:
|
||||||
|
for _, v := range t {
|
||||||
|
s = append(s, float64(v))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []uint32:
|
||||||
|
for _, v := range t {
|
||||||
|
s = append(s, float64(v))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []uint64:
|
||||||
|
for _, v := range t {
|
||||||
|
s = append(s, float64(v))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []bool:
|
||||||
|
for _, v := range t {
|
||||||
|
if v {
|
||||||
|
s = append(s, 1.0)
|
||||||
|
} else {
|
||||||
|
s = append(s, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []float64:
|
||||||
|
return Float64Data(t)
|
||||||
|
case []int:
|
||||||
|
for _, v := range t {
|
||||||
|
s = append(s, float64(v))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []int8:
|
||||||
|
for _, v := range t {
|
||||||
|
s = append(s, float64(v))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []int16:
|
||||||
|
for _, v := range t {
|
||||||
|
s = append(s, float64(v))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []int32:
|
||||||
|
for _, v := range t {
|
||||||
|
s = append(s, float64(v))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []int64:
|
||||||
|
for _, v := range t {
|
||||||
|
s = append(s, float64(v))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case []string:
|
||||||
|
for _, v := range t {
|
||||||
|
r = append(r, v)
|
||||||
|
}
|
||||||
|
case []time.Duration:
|
||||||
|
for _, v := range t {
|
||||||
|
r = append(r, v)
|
||||||
|
}
|
||||||
|
case map[int]int:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, float64(t[i]))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]int8:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, float64(t[i]))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]int16:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, float64(t[i]))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]int32:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, float64(t[i]))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]int64:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, float64(t[i]))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]string:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
r = append(r, t[i])
|
||||||
|
}
|
||||||
|
case map[int]uint:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, float64(t[i]))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]uint8:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, float64(t[i]))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]uint16:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, float64(t[i]))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]uint32:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, float64(t[i]))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]uint64:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, float64(t[i]))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]bool:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
if t[i] {
|
||||||
|
s = append(s, 1.0)
|
||||||
|
} else {
|
||||||
|
s = append(s, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]float64:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
s = append(s, t[i])
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case map[int]time.Duration:
|
||||||
|
for i := 0; i < len(t); i++ {
|
||||||
|
r = append(r, t[i])
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
for _, v := range strings.Fields(t) {
|
||||||
|
r = append(r, v)
|
||||||
|
}
|
||||||
|
case io.Reader:
|
||||||
|
scanner := bufio.NewScanner(t)
|
||||||
|
for scanner.Scan() {
|
||||||
|
l := scanner.Text()
|
||||||
|
for _, v := range strings.Fields(l) {
|
||||||
|
r = append(r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range r {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case int:
|
||||||
|
a := float64(t)
|
||||||
|
f = append(f, a)
|
||||||
|
case uint:
|
||||||
|
f = append(f, float64(t))
|
||||||
|
case float64:
|
||||||
|
f = append(f, t)
|
||||||
|
case string:
|
||||||
|
fl, err := strconv.ParseFloat(t, 64)
|
||||||
|
if err == nil {
|
||||||
|
f = append(f, fl)
|
||||||
|
}
|
||||||
|
case bool:
|
||||||
|
if t {
|
||||||
|
f = append(f, 1.0)
|
||||||
|
} else {
|
||||||
|
f = append(f, 0.0)
|
||||||
|
}
|
||||||
|
case time.Duration:
|
||||||
|
f = append(f, float64(t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
26
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/max.go
generated
vendored
Normal file
26
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/max.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Max finds the highest number in a slice
|
||||||
|
func Max(input Float64Data) (max float64, err error) {
|
||||||
|
|
||||||
|
// Return an error if there are no numbers
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first value as the starting point
|
||||||
|
max = input.Get(0)
|
||||||
|
|
||||||
|
// Loop and replace higher values
|
||||||
|
for i := 1; i < input.Len(); i++ {
|
||||||
|
if input.Get(i) > max {
|
||||||
|
max = input.Get(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return max, nil
|
||||||
|
}
|
||||||
60
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/mean.go
generated
vendored
Normal file
60
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/mean.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Mean gets the average of a slice of numbers
|
||||||
|
func Mean(input Float64Data) (float64, error) {
|
||||||
|
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
sum, _ := input.Sum()
|
||||||
|
|
||||||
|
return sum / float64(input.Len()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GeometricMean gets the geometric mean for a slice of numbers
|
||||||
|
func GeometricMean(input Float64Data) (float64, error) {
|
||||||
|
|
||||||
|
l := input.Len()
|
||||||
|
if l == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the product of all the numbers
|
||||||
|
var p float64
|
||||||
|
for _, n := range input {
|
||||||
|
if p == 0 {
|
||||||
|
p = n
|
||||||
|
} else {
|
||||||
|
p *= n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the geometric mean
|
||||||
|
return math.Pow(p, 1/float64(l)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HarmonicMean gets the harmonic mean for a slice of numbers
|
||||||
|
func HarmonicMean(input Float64Data) (float64, error) {
|
||||||
|
|
||||||
|
l := input.Len()
|
||||||
|
if l == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the sum of all the numbers reciprocals and return an
|
||||||
|
// error for values that cannot be included in harmonic mean
|
||||||
|
var p float64
|
||||||
|
for _, n := range input {
|
||||||
|
if n < 0 {
|
||||||
|
return math.NaN(), NegativeErr
|
||||||
|
} else if n == 0 {
|
||||||
|
return math.NaN(), ZeroErr
|
||||||
|
}
|
||||||
|
p += (1 / n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return float64(l) / p, nil
|
||||||
|
}
|
||||||
25
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/median.go
generated
vendored
Normal file
25
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/median.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Median gets the median number in a slice of numbers
|
||||||
|
func Median(input Float64Data) (median float64, err error) {
|
||||||
|
|
||||||
|
// Start by sorting a copy of the slice
|
||||||
|
c := sortedCopy(input)
|
||||||
|
|
||||||
|
// No math is needed if there are no numbers
|
||||||
|
// For even numbers we add the two middle numbers
|
||||||
|
// and divide by two using the mean function above
|
||||||
|
// For odd numbers we just use the middle number
|
||||||
|
l := len(c)
|
||||||
|
if l == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
} else if l%2 == 0 {
|
||||||
|
median, _ = Mean(c[l/2-1 : l/2+1])
|
||||||
|
} else {
|
||||||
|
median = c[l/2]
|
||||||
|
}
|
||||||
|
|
||||||
|
return median, nil
|
||||||
|
}
|
||||||
26
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/min.go
generated
vendored
Normal file
26
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/min.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Min finds the lowest number in a set of data
|
||||||
|
func Min(input Float64Data) (min float64, err error) {
|
||||||
|
|
||||||
|
// Get the count of numbers in the slice
|
||||||
|
l := input.Len()
|
||||||
|
|
||||||
|
// Return an error if there are no numbers
|
||||||
|
if l == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first value as the starting point
|
||||||
|
min = input.Get(0)
|
||||||
|
|
||||||
|
// Iterate until done checking for a lower value
|
||||||
|
for i := 1; i < l; i++ {
|
||||||
|
if input.Get(i) < min {
|
||||||
|
min = input.Get(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min, nil
|
||||||
|
}
|
||||||
47
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/mode.go
generated
vendored
Normal file
47
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/mode.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
// Mode gets the mode [most frequent value(s)] of a slice of float64s
|
||||||
|
func Mode(input Float64Data) (mode []float64, err error) {
|
||||||
|
// Return the input if there's only one number
|
||||||
|
l := input.Len()
|
||||||
|
if l == 1 {
|
||||||
|
return input, nil
|
||||||
|
} else if l == 0 {
|
||||||
|
return nil, EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
c := sortedCopyDif(input)
|
||||||
|
// Traverse sorted array,
|
||||||
|
// tracking the longest repeating sequence
|
||||||
|
mode = make([]float64, 5)
|
||||||
|
cnt, maxCnt := 1, 1
|
||||||
|
for i := 1; i < l; i++ {
|
||||||
|
switch {
|
||||||
|
case c[i] == c[i-1]:
|
||||||
|
cnt++
|
||||||
|
case cnt == maxCnt && maxCnt != 1:
|
||||||
|
mode = append(mode, c[i-1])
|
||||||
|
cnt = 1
|
||||||
|
case cnt > maxCnt:
|
||||||
|
mode = append(mode[:0], c[i-1])
|
||||||
|
maxCnt, cnt = cnt, 1
|
||||||
|
default:
|
||||||
|
cnt = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case cnt == maxCnt:
|
||||||
|
mode = append(mode, c[l-1])
|
||||||
|
case cnt > maxCnt:
|
||||||
|
mode = append(mode[:0], c[l-1])
|
||||||
|
maxCnt = cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since length must be greater than 1,
|
||||||
|
// check for slices of distinct values
|
||||||
|
if maxCnt == 1 || len(mode)*maxCnt == l && maxCnt != l {
|
||||||
|
return Float64Data{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return mode, nil
|
||||||
|
}
|
||||||
254
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/norm.go
generated
vendored
Normal file
254
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/norm.go
generated
vendored
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NormPpfRvs generates random variates using the Point Percentile Function.
|
||||||
|
// For more information please visit: https://demonstrations.wolfram.com/TheMethodOfInverseTransforms/
|
||||||
|
func NormPpfRvs(loc float64, scale float64, size int) []float64 {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
var toReturn []float64
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
toReturn = append(toReturn, NormPpf(rand.Float64(), loc, scale))
|
||||||
|
}
|
||||||
|
return toReturn
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormBoxMullerRvs generates random variates using the Box–Muller transform.
|
||||||
|
// For more information please visit: http://mathworld.wolfram.com/Box-MullerTransformation.html
|
||||||
|
func NormBoxMullerRvs(loc float64, scale float64, size int) []float64 {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
var toReturn []float64
|
||||||
|
for i := 0; i < int(float64(size/2)+float64(size%2)); i++ {
|
||||||
|
// u1 and u2 are uniformly distributed random numbers between 0 and 1.
|
||||||
|
u1 := rand.Float64()
|
||||||
|
u2 := rand.Float64()
|
||||||
|
// x1 and x2 are normally distributed random numbers.
|
||||||
|
x1 := loc + (scale * (math.Sqrt(-2*math.Log(u1)) * math.Cos(2*math.Pi*u2)))
|
||||||
|
toReturn = append(toReturn, x1)
|
||||||
|
if (i+1)*2 <= size {
|
||||||
|
x2 := loc + (scale * (math.Sqrt(-2*math.Log(u1)) * math.Sin(2*math.Pi*u2)))
|
||||||
|
toReturn = append(toReturn, x2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toReturn
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormPdf is the probability density function.
|
||||||
|
func NormPdf(x float64, loc float64, scale float64) float64 {
|
||||||
|
return (math.Pow(math.E, -(math.Pow(x-loc, 2))/(2*math.Pow(scale, 2)))) / (scale * math.Sqrt(2*math.Pi))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormLogPdf is the log of the probability density function.
|
||||||
|
func NormLogPdf(x float64, loc float64, scale float64) float64 {
|
||||||
|
return math.Log((math.Pow(math.E, -(math.Pow(x-loc, 2))/(2*math.Pow(scale, 2)))) / (scale * math.Sqrt(2*math.Pi)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormCdf is the cumulative distribution function.
|
||||||
|
func NormCdf(x float64, loc float64, scale float64) float64 {
|
||||||
|
return 0.5 * (1 + math.Erf((x-loc)/(scale*math.Sqrt(2))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormLogCdf is the log of the cumulative distribution function.
|
||||||
|
func NormLogCdf(x float64, loc float64, scale float64) float64 {
|
||||||
|
return math.Log(0.5 * (1 + math.Erf((x-loc)/(scale*math.Sqrt(2)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormSf is the survival function (also defined as 1 - cdf, but sf is sometimes more accurate).
|
||||||
|
func NormSf(x float64, loc float64, scale float64) float64 {
|
||||||
|
return 1 - 0.5*(1+math.Erf((x-loc)/(scale*math.Sqrt(2))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormLogSf is the log of the survival function.
|
||||||
|
func NormLogSf(x float64, loc float64, scale float64) float64 {
|
||||||
|
return math.Log(1 - 0.5*(1+math.Erf((x-loc)/(scale*math.Sqrt(2)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormPpf is the point percentile function.
|
||||||
|
// This is based on Peter John Acklam's inverse normal CDF.
|
||||||
|
// algorithm: http://home.online.no/~pjacklam/notes/invnorm/ (no longer visible).
|
||||||
|
// For more information please visit: https://stackedboxes.org/2017/05/01/acklams-normal-quantile-function/
|
||||||
|
func NormPpf(p float64, loc float64, scale float64) (x float64) {
|
||||||
|
const (
|
||||||
|
a1 = -3.969683028665376e+01
|
||||||
|
a2 = 2.209460984245205e+02
|
||||||
|
a3 = -2.759285104469687e+02
|
||||||
|
a4 = 1.383577518672690e+02
|
||||||
|
a5 = -3.066479806614716e+01
|
||||||
|
a6 = 2.506628277459239e+00
|
||||||
|
|
||||||
|
b1 = -5.447609879822406e+01
|
||||||
|
b2 = 1.615858368580409e+02
|
||||||
|
b3 = -1.556989798598866e+02
|
||||||
|
b4 = 6.680131188771972e+01
|
||||||
|
b5 = -1.328068155288572e+01
|
||||||
|
|
||||||
|
c1 = -7.784894002430293e-03
|
||||||
|
c2 = -3.223964580411365e-01
|
||||||
|
c3 = -2.400758277161838e+00
|
||||||
|
c4 = -2.549732539343734e+00
|
||||||
|
c5 = 4.374664141464968e+00
|
||||||
|
c6 = 2.938163982698783e+00
|
||||||
|
|
||||||
|
d1 = 7.784695709041462e-03
|
||||||
|
d2 = 3.224671290700398e-01
|
||||||
|
d3 = 2.445134137142996e+00
|
||||||
|
d4 = 3.754408661907416e+00
|
||||||
|
|
||||||
|
plow = 0.02425
|
||||||
|
phigh = 1 - plow
|
||||||
|
)
|
||||||
|
|
||||||
|
if p < 0 || p > 1 {
|
||||||
|
return math.NaN()
|
||||||
|
} else if p == 0 {
|
||||||
|
return -math.Inf(0)
|
||||||
|
} else if p == 1 {
|
||||||
|
return math.Inf(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p < plow {
|
||||||
|
q := math.Sqrt(-2 * math.Log(p))
|
||||||
|
x = (((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q + c6) /
|
||||||
|
((((d1*q+d2)*q+d3)*q+d4)*q + 1)
|
||||||
|
} else if phigh < p {
|
||||||
|
q := math.Sqrt(-2 * math.Log(1-p))
|
||||||
|
x = -(((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q + c6) /
|
||||||
|
((((d1*q+d2)*q+d3)*q+d4)*q + 1)
|
||||||
|
} else {
|
||||||
|
q := p - 0.5
|
||||||
|
r := q * q
|
||||||
|
x = (((((a1*r+a2)*r+a3)*r+a4)*r+a5)*r + a6) * q /
|
||||||
|
(((((b1*r+b2)*r+b3)*r+b4)*r+b5)*r + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
e := 0.5*math.Erfc(-x/math.Sqrt2) - p
|
||||||
|
u := e * math.Sqrt(2*math.Pi) * math.Exp(x*x/2)
|
||||||
|
x = x - u/(1+x*u/2)
|
||||||
|
|
||||||
|
return x*scale + loc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormIsf is the inverse survival function (inverse of sf).
|
||||||
|
func NormIsf(p float64, loc float64, scale float64) (x float64) {
|
||||||
|
if -NormPpf(p, loc, scale) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return -NormPpf(p, loc, scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormMoment approximates the non-central (raw) moment of order n.
|
||||||
|
// For more information please visit: https://math.stackexchange.com/questions/1945448/methods-for-finding-raw-moments-of-the-normal-distribution
|
||||||
|
func NormMoment(n int, loc float64, scale float64) float64 {
|
||||||
|
toReturn := 0.0
|
||||||
|
for i := 0; i < n+1; i++ {
|
||||||
|
if (n-i)%2 == 0 {
|
||||||
|
toReturn += float64(Ncr(n, i)) * (math.Pow(loc, float64(i))) * (math.Pow(scale, float64(n-i))) *
|
||||||
|
(float64(factorial(n-i)) / ((math.Pow(2.0, float64((n-i)/2))) *
|
||||||
|
float64(factorial((n-i)/2))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toReturn
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormStats returns the mean, variance, skew, and/or kurtosis.
|
||||||
|
// Mean(‘m’), variance(‘v’), skew(‘s’), and/or kurtosis(‘k’).
|
||||||
|
// Takes string containing any of 'mvsk'.
|
||||||
|
// Returns array of m v s k in that order.
|
||||||
|
func NormStats(loc float64, scale float64, moments string) []float64 {
|
||||||
|
var toReturn []float64
|
||||||
|
if strings.ContainsAny(moments, "m") {
|
||||||
|
toReturn = append(toReturn, loc)
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(moments, "v") {
|
||||||
|
toReturn = append(toReturn, math.Pow(scale, 2))
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(moments, "s") {
|
||||||
|
toReturn = append(toReturn, 0.0)
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(moments, "k") {
|
||||||
|
toReturn = append(toReturn, 0.0)
|
||||||
|
}
|
||||||
|
return toReturn
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormEntropy is the differential entropy of the RV.
|
||||||
|
func NormEntropy(loc float64, scale float64) float64 {
|
||||||
|
return math.Log(scale * math.Sqrt(2*math.Pi*math.E))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormFit returns the maximum likelihood estimators for the Normal Distribution.
|
||||||
|
// Takes array of float64 values.
|
||||||
|
// Returns array of Mean followed by Standard Deviation.
|
||||||
|
func NormFit(data []float64) [2]float64 {
|
||||||
|
sum := 0.00
|
||||||
|
for i := 0; i < len(data); i++ {
|
||||||
|
sum += data[i]
|
||||||
|
}
|
||||||
|
mean := sum / float64(len(data))
|
||||||
|
stdNumerator := 0.00
|
||||||
|
for i := 0; i < len(data); i++ {
|
||||||
|
stdNumerator += math.Pow(data[i]-mean, 2)
|
||||||
|
}
|
||||||
|
return [2]float64{mean, math.Sqrt((stdNumerator) / (float64(len(data))))}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormMedian is the median of the distribution.
|
||||||
|
func NormMedian(loc float64, scale float64) float64 {
|
||||||
|
return loc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormMean is the mean/expected value of the distribution.
|
||||||
|
func NormMean(loc float64, scale float64) float64 {
|
||||||
|
return loc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormVar is the variance of the distribution.
|
||||||
|
func NormVar(loc float64, scale float64) float64 {
|
||||||
|
return math.Pow(scale, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormStd is the standard deviation of the distribution.
|
||||||
|
func NormStd(loc float64, scale float64) float64 {
|
||||||
|
return scale
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormInterval finds endpoints of the range that contains alpha percent of the distribution.
|
||||||
|
func NormInterval(alpha float64, loc float64, scale float64) [2]float64 {
|
||||||
|
q1 := (1.0 - alpha) / 2
|
||||||
|
q2 := (1.0 + alpha) / 2
|
||||||
|
a := NormPpf(q1, loc, scale)
|
||||||
|
b := NormPpf(q2, loc, scale)
|
||||||
|
return [2]float64{a, b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// factorial is the naive factorial algorithm.
|
||||||
|
func factorial(x int) int {
|
||||||
|
if x == 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return x * factorial(x-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ncr is an N choose R algorithm.
|
||||||
|
// Aaron Cannon's algorithm.
|
||||||
|
func Ncr(n, r int) int {
|
||||||
|
if n <= 1 || r == 0 || n == r {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if newR := n - r; newR < r {
|
||||||
|
r = newR
|
||||||
|
}
|
||||||
|
if r == 1 {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
ret := int(n - r + 1)
|
||||||
|
for i, j := ret+1, int(2); j <= r; i, j = i+1, j+1 {
|
||||||
|
ret = ret * i / j
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
44
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/outlier.go
generated
vendored
Normal file
44
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/outlier.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
// Outliers holds mild and extreme outliers found in data
|
||||||
|
type Outliers struct {
|
||||||
|
Mild Float64Data
|
||||||
|
Extreme Float64Data
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuartileOutliers finds the mild and extreme outliers
|
||||||
|
func QuartileOutliers(input Float64Data) (Outliers, error) {
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return Outliers{}, EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start by sorting a copy of the slice
|
||||||
|
copy := sortedCopy(input)
|
||||||
|
|
||||||
|
// Calculate the quartiles and interquartile range
|
||||||
|
qs, _ := Quartile(copy)
|
||||||
|
iqr, _ := InterQuartileRange(copy)
|
||||||
|
|
||||||
|
// Calculate the lower and upper inner and outer fences
|
||||||
|
lif := qs.Q1 - (1.5 * iqr)
|
||||||
|
uif := qs.Q3 + (1.5 * iqr)
|
||||||
|
lof := qs.Q1 - (3 * iqr)
|
||||||
|
uof := qs.Q3 + (3 * iqr)
|
||||||
|
|
||||||
|
// Find the data points that are outside of the
|
||||||
|
// inner and upper fences and add them to mild
|
||||||
|
// and extreme outlier slices
|
||||||
|
var mild Float64Data
|
||||||
|
var extreme Float64Data
|
||||||
|
for _, v := range copy {
|
||||||
|
|
||||||
|
if v < lof || v > uof {
|
||||||
|
extreme = append(extreme, v)
|
||||||
|
} else if v < lif || v > uif {
|
||||||
|
mild = append(mild, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap them into our struct
|
||||||
|
return Outliers{mild, extreme}, nil
|
||||||
|
}
|
||||||
86
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/percentile.go
generated
vendored
Normal file
86
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/percentile.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Percentile finds the relative standing in a slice of floats
|
||||||
|
func Percentile(input Float64Data, percent float64) (percentile float64, err error) {
|
||||||
|
length := input.Len()
|
||||||
|
if length == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if length == 1 {
|
||||||
|
return input[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if percent <= 0 || percent > 100 {
|
||||||
|
return math.NaN(), BoundsErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start by sorting a copy of the slice
|
||||||
|
c := sortedCopy(input)
|
||||||
|
|
||||||
|
// Multiply percent by length of input
|
||||||
|
index := (percent / 100) * float64(len(c))
|
||||||
|
|
||||||
|
// Check if the index is a whole number
|
||||||
|
if index == float64(int64(index)) {
|
||||||
|
|
||||||
|
// Convert float to int
|
||||||
|
i := int(index)
|
||||||
|
|
||||||
|
// Find the value at the index
|
||||||
|
percentile = c[i-1]
|
||||||
|
|
||||||
|
} else if index > 1 {
|
||||||
|
|
||||||
|
// Convert float to int via truncation
|
||||||
|
i := int(index)
|
||||||
|
|
||||||
|
// Find the average of the index and following values
|
||||||
|
percentile, _ = Mean(Float64Data{c[i-1], c[i]})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return math.NaN(), BoundsErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return percentile, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// PercentileNearestRank finds the relative standing in a slice of floats using the Nearest Rank method
|
||||||
|
func PercentileNearestRank(input Float64Data, percent float64) (percentile float64, err error) {
|
||||||
|
|
||||||
|
// Find the length of items in the slice
|
||||||
|
il := input.Len()
|
||||||
|
|
||||||
|
// Return an error for empty slices
|
||||||
|
if il == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return error for less than 0 or greater than 100 percentages
|
||||||
|
if percent < 0 || percent > 100 {
|
||||||
|
return math.NaN(), BoundsErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start by sorting a copy of the slice
|
||||||
|
c := sortedCopy(input)
|
||||||
|
|
||||||
|
// Return the last item
|
||||||
|
if percent == 100.0 {
|
||||||
|
return c[il-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find ordinal ranking
|
||||||
|
or := int(math.Ceil(float64(il) * percent / 100))
|
||||||
|
|
||||||
|
// Return the item that is in the place of the ordinal rank
|
||||||
|
if or == 0 {
|
||||||
|
return c[0], nil
|
||||||
|
}
|
||||||
|
return c[or-1], nil
|
||||||
|
|
||||||
|
}
|
||||||
74
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/quartile.go
generated
vendored
Normal file
74
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/quartile.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Quartiles holds the three quartile points
|
||||||
|
type Quartiles struct {
|
||||||
|
Q1 float64
|
||||||
|
Q2 float64
|
||||||
|
Q3 float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quartile returns the three quartile points from a slice of data
|
||||||
|
func Quartile(input Float64Data) (Quartiles, error) {
|
||||||
|
|
||||||
|
il := input.Len()
|
||||||
|
if il == 0 {
|
||||||
|
return Quartiles{}, EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start by sorting a copy of the slice
|
||||||
|
copy := sortedCopy(input)
|
||||||
|
|
||||||
|
// Find the cutoff places depeding on if
|
||||||
|
// the input slice length is even or odd
|
||||||
|
var c1 int
|
||||||
|
var c2 int
|
||||||
|
if il%2 == 0 {
|
||||||
|
c1 = il / 2
|
||||||
|
c2 = il / 2
|
||||||
|
} else {
|
||||||
|
c1 = (il - 1) / 2
|
||||||
|
c2 = c1 + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the Medians with the cutoff points
|
||||||
|
Q1, _ := Median(copy[:c1])
|
||||||
|
Q2, _ := Median(copy)
|
||||||
|
Q3, _ := Median(copy[c2:])
|
||||||
|
|
||||||
|
return Quartiles{Q1, Q2, Q3}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterQuartileRange finds the range between Q1 and Q3
|
||||||
|
func InterQuartileRange(input Float64Data) (float64, error) {
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
qs, _ := Quartile(input)
|
||||||
|
iqr := qs.Q3 - qs.Q1
|
||||||
|
return iqr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Midhinge finds the average of the first and third quartiles
|
||||||
|
func Midhinge(input Float64Data) (float64, error) {
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
qs, _ := Quartile(input)
|
||||||
|
mh := (qs.Q1 + qs.Q3) / 2
|
||||||
|
return mh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trimean finds the average of the median and the midhinge
|
||||||
|
func Trimean(input Float64Data) (float64, error) {
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
c := sortedCopy(input)
|
||||||
|
q, _ := Quartile(c)
|
||||||
|
|
||||||
|
return (q.Q1 + (q.Q2 * 2) + q.Q3) / 4, nil
|
||||||
|
}
|
||||||
183
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/ranksum.go
generated
vendored
Normal file
183
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/ranksum.go
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
// import "math"
|
||||||
|
//
|
||||||
|
// // WilcoxonRankSum tests the null hypothesis that two sets
|
||||||
|
// // of data are drawn from the same distribution. It does
|
||||||
|
// // not handle ties between measurements in x and y.
|
||||||
|
// //
|
||||||
|
// // Parameters:
|
||||||
|
// // data1 Float64Data: First set of data points.
|
||||||
|
// // data2 Float64Data: Second set of data points.
|
||||||
|
// // Length of both data samples must be equal.
|
||||||
|
// //
|
||||||
|
// // Return:
|
||||||
|
// // statistic float64: The test statistic under the
|
||||||
|
// // large-sample approximation that the
|
||||||
|
// // rank sum statistic is normally distributed.
|
||||||
|
// // pvalue float64: The two-sided p-value of the test
|
||||||
|
// // err error: Any error from the input data parameters
|
||||||
|
// //
|
||||||
|
// // https://en.wikipedia.org/wiki/Wilcoxon_rank-sum_test
|
||||||
|
// func WilcoxonRankSum(data1, data2 Float64Data) (float64, float64, error) {
|
||||||
|
//
|
||||||
|
// l1 := data1.Len()
|
||||||
|
// l2 := data2.Len()
|
||||||
|
//
|
||||||
|
// if l1 == 0 || l2 == 0 {
|
||||||
|
// return math.NaN(), math.NaN(), EmptyInputErr
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if l1 != l2 {
|
||||||
|
// return math.NaN(), math.NaN(), SizeErr
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// alldata := Float64Data{}
|
||||||
|
// alldata = append(alldata, data1...)
|
||||||
|
// alldata = append(alldata, data2...)
|
||||||
|
//
|
||||||
|
// // ranked :=
|
||||||
|
//
|
||||||
|
// return 0.0, 0.0, nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // x, y = map(np.asarray, (x, y))
|
||||||
|
// // n1 = len(x)
|
||||||
|
// // n2 = len(y)
|
||||||
|
// // alldata = np.concatenate((x, y))
|
||||||
|
// // ranked = rankdata(alldata)
|
||||||
|
// // x = ranked[:n1]
|
||||||
|
// // s = np.sum(x, axis=0)
|
||||||
|
// // expected = n1 * (n1+n2+1) / 2.0
|
||||||
|
// // z = (s - expected) / np.sqrt(n1*n2*(n1+n2+1)/12.0)
|
||||||
|
// // prob = 2 * distributions.norm.sf(abs(z))
|
||||||
|
// //
|
||||||
|
// // return RanksumsResult(z, prob)
|
||||||
|
//
|
||||||
|
// // def rankdata(a, method='average'):
|
||||||
|
// // """
|
||||||
|
// // Assign ranks to data, dealing with ties appropriately.
|
||||||
|
// // Ranks begin at 1. The `method` argument controls how ranks are assigned
|
||||||
|
// // to equal values. See [1]_ for further discussion of ranking methods.
|
||||||
|
// // Parameters
|
||||||
|
// // ----------
|
||||||
|
// // a : array_like
|
||||||
|
// // The array of values to be ranked. The array is first flattened.
|
||||||
|
// // method : str, optional
|
||||||
|
// // The method used to assign ranks to tied elements.
|
||||||
|
// // The options are 'average', 'min', 'max', 'dense' and 'ordinal'.
|
||||||
|
// // 'average':
|
||||||
|
// // The average of the ranks that would have been assigned to
|
||||||
|
// // all the tied values is assigned to each value.
|
||||||
|
// // 'min':
|
||||||
|
// // The minimum of the ranks that would have been assigned to all
|
||||||
|
// // the tied values is assigned to each value. (This is also
|
||||||
|
// // referred to as "competition" ranking.)
|
||||||
|
// // 'max':
|
||||||
|
// // The maximum of the ranks that would have been assigned to all
|
||||||
|
// // the tied values is assigned to each value.
|
||||||
|
// // 'dense':
|
||||||
|
// // Like 'min', but the rank of the next highest element is assigned
|
||||||
|
// // the rank immediately after those assigned to the tied elements.
|
||||||
|
// // 'ordinal':
|
||||||
|
// // All values are given a distinct rank, corresponding to the order
|
||||||
|
// // that the values occur in `a`.
|
||||||
|
// // The default is 'average'.
|
||||||
|
// // Returns
|
||||||
|
// // -------
|
||||||
|
// // ranks : ndarray
|
||||||
|
// // An array of length equal to the size of `a`, containing rank
|
||||||
|
// // scores.
|
||||||
|
// // References
|
||||||
|
// // ----------
|
||||||
|
// // .. [1] "Ranking", https://en.wikipedia.org/wiki/Ranking
|
||||||
|
// // Examples
|
||||||
|
// // --------
|
||||||
|
// // >>> from scipy.stats import rankdata
|
||||||
|
// // >>> rankdata([0, 2, 3, 2])
|
||||||
|
// // array([ 1. , 2.5, 4. , 2.5])
|
||||||
|
// // """
|
||||||
|
// //
|
||||||
|
// // arr = np.ravel(np.asarray(a))
|
||||||
|
// // algo = 'quicksort'
|
||||||
|
// // sorter = np.argsort(arr, kind=algo)
|
||||||
|
// //
|
||||||
|
// // inv = np.empty(sorter.size, dtype=np.intp)
|
||||||
|
// // inv[sorter] = np.arange(sorter.size, dtype=np.intp)
|
||||||
|
// //
|
||||||
|
// //
|
||||||
|
// // arr = arr[sorter]
|
||||||
|
// // obs = np.r_[True, arr[1:] != arr[:-1]]
|
||||||
|
// // dense = obs.cumsum()[inv]
|
||||||
|
// //
|
||||||
|
// //
|
||||||
|
// // # cumulative counts of each unique value
|
||||||
|
// // count = np.r_[np.nonzero(obs)[0], len(obs)]
|
||||||
|
// //
|
||||||
|
// // # average method
|
||||||
|
// // return .5 * (count[dense] + count[dense - 1] + 1)
|
||||||
|
//
|
||||||
|
// type rankable interface {
|
||||||
|
// Len() int
|
||||||
|
// RankEqual(int, int) bool
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func StandardRank(d rankable) []float64 {
|
||||||
|
// r := make([]float64, d.Len())
|
||||||
|
// var k int
|
||||||
|
// for i := range r {
|
||||||
|
// if i == 0 || !d.RankEqual(i, i-1) {
|
||||||
|
// k = i + 1
|
||||||
|
// }
|
||||||
|
// r[i] = float64(k)
|
||||||
|
// }
|
||||||
|
// return r
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func ModifiedRank(d rankable) []float64 {
|
||||||
|
// r := make([]float64, d.Len())
|
||||||
|
// for i := range r {
|
||||||
|
// k := i + 1
|
||||||
|
// for j := i + 1; j < len(r) && d.RankEqual(i, j); j++ {
|
||||||
|
// k = j + 1
|
||||||
|
// }
|
||||||
|
// r[i] = float64(k)
|
||||||
|
// }
|
||||||
|
// return r
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func DenseRank(d rankable) []float64 {
|
||||||
|
// r := make([]float64, d.Len())
|
||||||
|
// var k int
|
||||||
|
// for i := range r {
|
||||||
|
// if i == 0 || !d.RankEqual(i, i-1) {
|
||||||
|
// k++
|
||||||
|
// }
|
||||||
|
// r[i] = float64(k)
|
||||||
|
// }
|
||||||
|
// return r
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func OrdinalRank(d rankable) []float64 {
|
||||||
|
// r := make([]float64, d.Len())
|
||||||
|
// for i := range r {
|
||||||
|
// r[i] = float64(i + 1)
|
||||||
|
// }
|
||||||
|
// return r
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func FractionalRank(d rankable) []float64 {
|
||||||
|
// r := make([]float64, d.Len())
|
||||||
|
// for i := 0; i < len(r); {
|
||||||
|
// var j int
|
||||||
|
// f := float64(i + 1)
|
||||||
|
// for j = i + 1; j < len(r) && d.RankEqual(i, j); j++ {
|
||||||
|
// f += float64(j + 1)
|
||||||
|
// }
|
||||||
|
// f /= float64(j - i)
|
||||||
|
// for ; i < j; i++ {
|
||||||
|
// r[i] = f
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return r
|
||||||
|
// }
|
||||||
113
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/regression.go
generated
vendored
Normal file
113
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/regression.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Series is a container for a series of data
|
||||||
|
type Series []Coordinate
|
||||||
|
|
||||||
|
// Coordinate holds the data in a series
|
||||||
|
type Coordinate struct {
|
||||||
|
X, Y float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinearRegression finds the least squares linear regression on data series
|
||||||
|
func LinearRegression(s Series) (regressions Series, err error) {
|
||||||
|
|
||||||
|
if len(s) == 0 {
|
||||||
|
return nil, EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placeholder for the math to be done
|
||||||
|
var sum [5]float64
|
||||||
|
|
||||||
|
// Loop over data keeping index in place
|
||||||
|
i := 0
|
||||||
|
for ; i < len(s); i++ {
|
||||||
|
sum[0] += s[i].X
|
||||||
|
sum[1] += s[i].Y
|
||||||
|
sum[2] += s[i].X * s[i].X
|
||||||
|
sum[3] += s[i].X * s[i].Y
|
||||||
|
sum[4] += s[i].Y * s[i].Y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find gradient and intercept
|
||||||
|
f := float64(i)
|
||||||
|
gradient := (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0])
|
||||||
|
intercept := (sum[1] / f) - (gradient * sum[0] / f)
|
||||||
|
|
||||||
|
// Create the new regression series
|
||||||
|
for j := 0; j < len(s); j++ {
|
||||||
|
regressions = append(regressions, Coordinate{
|
||||||
|
X: s[j].X,
|
||||||
|
Y: s[j].X*gradient + intercept,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return regressions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExponentialRegression returns an exponential regression on data series
|
||||||
|
func ExponentialRegression(s Series) (regressions Series, err error) {
|
||||||
|
|
||||||
|
if len(s) == 0 {
|
||||||
|
return nil, EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum [6]float64
|
||||||
|
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i].Y < 0 {
|
||||||
|
return nil, YCoordErr
|
||||||
|
}
|
||||||
|
sum[0] += s[i].X
|
||||||
|
sum[1] += s[i].Y
|
||||||
|
sum[2] += s[i].X * s[i].X * s[i].Y
|
||||||
|
sum[3] += s[i].Y * math.Log(s[i].Y)
|
||||||
|
sum[4] += s[i].X * s[i].Y * math.Log(s[i].Y)
|
||||||
|
sum[5] += s[i].X * s[i].Y
|
||||||
|
}
|
||||||
|
|
||||||
|
denominator := (sum[1]*sum[2] - sum[5]*sum[5])
|
||||||
|
a := math.Pow(math.E, (sum[2]*sum[3]-sum[5]*sum[4])/denominator)
|
||||||
|
b := (sum[1]*sum[4] - sum[5]*sum[3]) / denominator
|
||||||
|
|
||||||
|
for j := 0; j < len(s); j++ {
|
||||||
|
regressions = append(regressions, Coordinate{
|
||||||
|
X: s[j].X,
|
||||||
|
Y: a * math.Exp(b*s[j].X),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return regressions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogarithmicRegression returns an logarithmic regression on data series
|
||||||
|
func LogarithmicRegression(s Series) (regressions Series, err error) {
|
||||||
|
|
||||||
|
if len(s) == 0 {
|
||||||
|
return nil, EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum [4]float64
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for ; i < len(s); i++ {
|
||||||
|
sum[0] += math.Log(s[i].X)
|
||||||
|
sum[1] += s[i].Y * math.Log(s[i].X)
|
||||||
|
sum[2] += s[i].Y
|
||||||
|
sum[3] += math.Pow(math.Log(s[i].X), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := float64(i)
|
||||||
|
a := (f*sum[1] - sum[2]*sum[0]) / (f*sum[3] - sum[0]*sum[0])
|
||||||
|
b := (sum[2] - a*sum[0]) / f
|
||||||
|
|
||||||
|
for j := 0; j < len(s); j++ {
|
||||||
|
regressions = append(regressions, Coordinate{
|
||||||
|
X: s[j].X,
|
||||||
|
Y: b + a*math.Log(s[j].X),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return regressions, nil
|
||||||
|
}
|
||||||
38
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/round.go
generated
vendored
Normal file
38
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/round.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Round a float to a specific decimal place or precision
|
||||||
|
func Round(input float64, places int) (rounded float64, err error) {
|
||||||
|
|
||||||
|
// If the float is not a number
|
||||||
|
if math.IsNaN(input) {
|
||||||
|
return math.NaN(), NaNErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find out the actual sign and correct the input for later
|
||||||
|
sign := 1.0
|
||||||
|
if input < 0 {
|
||||||
|
sign = -1
|
||||||
|
input *= -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the places arg to get the amount of precision wanted
|
||||||
|
precision := math.Pow(10, float64(places))
|
||||||
|
|
||||||
|
// Find the decimal place we are looking to round
|
||||||
|
digit := input * precision
|
||||||
|
|
||||||
|
// Get the actual decimal number as a fraction to be compared
|
||||||
|
_, decimal := math.Modf(digit)
|
||||||
|
|
||||||
|
// If the decimal is less than .5 we round down otherwise up
|
||||||
|
if decimal >= 0.5 {
|
||||||
|
rounded = math.Ceil(digit)
|
||||||
|
} else {
|
||||||
|
rounded = math.Floor(digit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally we do the math to actually create a rounded number
|
||||||
|
return rounded / precision * sign, nil
|
||||||
|
}
|
||||||
76
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/sample.go
generated
vendored
Normal file
76
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/sample.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sample returns sample from input with replacement or without
|
||||||
|
func Sample(input Float64Data, takenum int, replacement bool) ([]float64, error) {
|
||||||
|
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return nil, EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
length := input.Len()
|
||||||
|
if replacement {
|
||||||
|
|
||||||
|
result := Float64Data{}
|
||||||
|
rand.Seed(unixnano())
|
||||||
|
|
||||||
|
// In every step, randomly take the num for
|
||||||
|
for i := 0; i < takenum; i++ {
|
||||||
|
idx := rand.Intn(length)
|
||||||
|
result = append(result, input[idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
|
||||||
|
} else if !replacement && takenum <= length {
|
||||||
|
|
||||||
|
rand.Seed(unixnano())
|
||||||
|
|
||||||
|
// Get permutation of number of indexies
|
||||||
|
perm := rand.Perm(length)
|
||||||
|
result := Float64Data{}
|
||||||
|
|
||||||
|
// Get element of input by permutated index
|
||||||
|
for _, idx := range perm[0:takenum] {
|
||||||
|
result = append(result, input[idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, BoundsErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// StableSample like stable sort, it returns samples from input while keeps the order of original data.
|
||||||
|
func StableSample(input Float64Data, takenum int) ([]float64, error) {
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return nil, EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
length := input.Len()
|
||||||
|
|
||||||
|
if takenum <= length {
|
||||||
|
|
||||||
|
rand.Seed(unixnano())
|
||||||
|
|
||||||
|
perm := rand.Perm(length)
|
||||||
|
perm = perm[0:takenum]
|
||||||
|
// Sort perm before applying
|
||||||
|
sort.Ints(perm)
|
||||||
|
result := Float64Data{}
|
||||||
|
|
||||||
|
for _, idx := range perm {
|
||||||
|
result = append(result, input[idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, BoundsErr
|
||||||
|
}
|
||||||
18
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/sigmoid.go
generated
vendored
Normal file
18
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/sigmoid.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Sigmoid returns the input values in the range of -1 to 1
|
||||||
|
// along the sigmoid or s-shaped curve, commonly used in
|
||||||
|
// machine learning while training neural networks as an
|
||||||
|
// activation function.
|
||||||
|
func Sigmoid(input Float64Data) ([]float64, error) {
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return Float64Data{}, EmptyInput
|
||||||
|
}
|
||||||
|
s := make([]float64, len(input))
|
||||||
|
for i, v := range input {
|
||||||
|
s[i] = 1 / (1 + math.Exp(-v))
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
25
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/softmax.go
generated
vendored
Normal file
25
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/softmax.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// SoftMax returns the input values in the range of 0 to 1
|
||||||
|
// with sum of all the probabilities being equal to one. It
|
||||||
|
// is commonly used in machine learning neural networks.
|
||||||
|
func SoftMax(input Float64Data) ([]float64, error) {
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return Float64Data{}, EmptyInput
|
||||||
|
}
|
||||||
|
|
||||||
|
s := 0.0
|
||||||
|
c, _ := Max(input)
|
||||||
|
for _, e := range input {
|
||||||
|
s += math.Exp(e - c)
|
||||||
|
}
|
||||||
|
|
||||||
|
sm := make([]float64, len(input))
|
||||||
|
for i, v := range input {
|
||||||
|
sm[i] = math.Exp(v-c) / s
|
||||||
|
}
|
||||||
|
|
||||||
|
return sm, nil
|
||||||
|
}
|
||||||
18
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/sum.go
generated
vendored
Normal file
18
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/sum.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Sum adds all the numbers of a slice together
|
||||||
|
func Sum(input Float64Data) (sum float64, err error) {
|
||||||
|
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add em up
|
||||||
|
for _, n := range input {
|
||||||
|
sum += n
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum, nil
|
||||||
|
}
|
||||||
43
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/util.go
generated
vendored
Normal file
43
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/util.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// float64ToInt rounds a float64 to an int
|
||||||
|
func float64ToInt(input float64) (output int) {
|
||||||
|
r, _ := Round(input, 0)
|
||||||
|
return int(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unixnano returns nanoseconds from UTC epoch
|
||||||
|
func unixnano() int64 {
|
||||||
|
return time.Now().UTC().UnixNano()
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyslice copies a slice of float64s
|
||||||
|
func copyslice(input Float64Data) Float64Data {
|
||||||
|
s := make(Float64Data, input.Len())
|
||||||
|
copy(s, input)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortedCopy returns a sorted copy of float64s
|
||||||
|
func sortedCopy(input Float64Data) (copy Float64Data) {
|
||||||
|
copy = copyslice(input)
|
||||||
|
sort.Float64s(copy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortedCopyDif returns a sorted copy of float64s
|
||||||
|
// only if the original data isn't sorted.
|
||||||
|
// Only use this if returned slice won't be manipulated!
|
||||||
|
func sortedCopyDif(input Float64Data) (copy Float64Data) {
|
||||||
|
if sort.Float64sAreSorted(input) {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
copy = copyslice(input)
|
||||||
|
sort.Float64s(copy)
|
||||||
|
return
|
||||||
|
}
|
||||||
105
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/variance.go
generated
vendored
Normal file
105
tests/metrics/cmd/checkmetrics/vendor/github.com/montanaflynn/stats/variance.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// _variance finds the variance for both population and sample data
|
||||||
|
func _variance(input Float64Data, sample int) (variance float64, err error) {
|
||||||
|
|
||||||
|
if input.Len() == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum the square of the mean subtracted from each number
|
||||||
|
m, _ := Mean(input)
|
||||||
|
|
||||||
|
for _, n := range input {
|
||||||
|
variance += (n - m) * (n - m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When getting the mean of the squared differences
|
||||||
|
// "sample" will allow us to know if it's a sample
|
||||||
|
// or population and wether to subtract by one or not
|
||||||
|
return variance / float64((input.Len() - (1 * sample))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variance the amount of variation in the dataset
|
||||||
|
func Variance(input Float64Data) (sdev float64, err error) {
|
||||||
|
return PopulationVariance(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopulationVariance finds the amount of variance within a population
|
||||||
|
func PopulationVariance(input Float64Data) (pvar float64, err error) {
|
||||||
|
|
||||||
|
v, err := _variance(input, 0)
|
||||||
|
if err != nil {
|
||||||
|
return math.NaN(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SampleVariance finds the amount of variance within a sample
|
||||||
|
func SampleVariance(input Float64Data) (svar float64, err error) {
|
||||||
|
|
||||||
|
v, err := _variance(input, 1)
|
||||||
|
if err != nil {
|
||||||
|
return math.NaN(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Covariance is a measure of how much two sets of data change
|
||||||
|
func Covariance(data1, data2 Float64Data) (float64, error) {
|
||||||
|
|
||||||
|
l1 := data1.Len()
|
||||||
|
l2 := data2.Len()
|
||||||
|
|
||||||
|
if l1 == 0 || l2 == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if l1 != l2 {
|
||||||
|
return math.NaN(), SizeErr
|
||||||
|
}
|
||||||
|
|
||||||
|
m1, _ := Mean(data1)
|
||||||
|
m2, _ := Mean(data2)
|
||||||
|
|
||||||
|
// Calculate sum of squares
|
||||||
|
var ss float64
|
||||||
|
for i := 0; i < l1; i++ {
|
||||||
|
delta1 := (data1.Get(i) - m1)
|
||||||
|
delta2 := (data2.Get(i) - m2)
|
||||||
|
ss += (delta1*delta2 - ss) / float64(i+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss * float64(l1) / float64(l1-1), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CovariancePopulation computes covariance for entire population between two variables.
|
||||||
|
func CovariancePopulation(data1, data2 Float64Data) (float64, error) {
|
||||||
|
|
||||||
|
l1 := data1.Len()
|
||||||
|
l2 := data2.Len()
|
||||||
|
|
||||||
|
if l1 == 0 || l2 == 0 {
|
||||||
|
return math.NaN(), EmptyInputErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if l1 != l2 {
|
||||||
|
return math.NaN(), SizeErr
|
||||||
|
}
|
||||||
|
|
||||||
|
m1, _ := Mean(data1)
|
||||||
|
m2, _ := Mean(data2)
|
||||||
|
|
||||||
|
var s float64
|
||||||
|
for i := 0; i < l1; i++ {
|
||||||
|
delta1 := (data1.Get(i) - m1)
|
||||||
|
delta2 := (data2.Get(i) - m2)
|
||||||
|
s += delta1 * delta2
|
||||||
|
}
|
||||||
|
|
||||||
|
return s / float64(l1), nil
|
||||||
|
}
|
||||||
15
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/.gitignore
generated
vendored
Normal file
15
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### Go template
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, build with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
22
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/.travis.yml
generated
vendored
Normal file
22
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
language: go
|
||||||
|
arch:
|
||||||
|
- ppc64le
|
||||||
|
- amd64
|
||||||
|
go:
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
|
- 1.6
|
||||||
|
- 1.7
|
||||||
|
- 1.8
|
||||||
|
- 1.9
|
||||||
|
- "1.10"
|
||||||
|
- tip
|
||||||
|
jobs:
|
||||||
|
exclude :
|
||||||
|
- arch : ppc64le
|
||||||
|
go :
|
||||||
|
- 1.3
|
||||||
|
- arch : ppc64le
|
||||||
|
go :
|
||||||
|
- 1.4
|
||||||
19
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/LICENSE.md
generated
vendored
Normal file
19
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (C) 2014 by Oleku Konko
|
||||||
|
|
||||||
|
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.
|
||||||
431
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/README.md
generated
vendored
Normal file
431
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/README.md
generated
vendored
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
ASCII Table Writer
|
||||||
|
=========
|
||||||
|
|
||||||
|
[](https://travis-ci.org/olekukonko/tablewriter)
|
||||||
|
[](https://sourcegraph.com/github.com/olekukonko/tablewriter)
|
||||||
|
[](https://godoc.org/github.com/olekukonko/tablewriter)
|
||||||
|
|
||||||
|
Generate ASCII table on the fly ... Installation is simple as
|
||||||
|
|
||||||
|
go get github.com/olekukonko/tablewriter
|
||||||
|
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
- Automatic Padding
|
||||||
|
- Support Multiple Lines
|
||||||
|
- Supports Alignment
|
||||||
|
- Support Custom Separators
|
||||||
|
- Automatic Alignment of numbers & percentage
|
||||||
|
- Write directly to http , file etc via `io.Writer`
|
||||||
|
- Read directly from CSV file
|
||||||
|
- Optional row line via `SetRowLine`
|
||||||
|
- Normalise table header
|
||||||
|
- Make CSV Headers optional
|
||||||
|
- Enable or disable table border
|
||||||
|
- Set custom footer support
|
||||||
|
- Optional identical cells merging
|
||||||
|
- Set custom caption
|
||||||
|
- Optional reflowing of paragraphs in multi-line cells.
|
||||||
|
|
||||||
|
#### Example 1 - Basic
|
||||||
|
```go
|
||||||
|
data := [][]string{
|
||||||
|
[]string{"A", "The Good", "500"},
|
||||||
|
[]string{"B", "The Very very Bad Man", "288"},
|
||||||
|
[]string{"C", "The Ugly", "120"},
|
||||||
|
[]string{"D", "The Gopher", "800"},
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"Name", "Sign", "Rating"})
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
table.Append(v)
|
||||||
|
}
|
||||||
|
table.Render() // Send output
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Output 1
|
||||||
|
```
|
||||||
|
+------+-----------------------+--------+
|
||||||
|
| NAME | SIGN | RATING |
|
||||||
|
+------+-----------------------+--------+
|
||||||
|
| A | The Good | 500 |
|
||||||
|
| B | The Very very Bad Man | 288 |
|
||||||
|
| C | The Ugly | 120 |
|
||||||
|
| D | The Gopher | 800 |
|
||||||
|
+------+-----------------------+--------+
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 2 - Without Border / Footer / Bulk Append
|
||||||
|
```go
|
||||||
|
data := [][]string{
|
||||||
|
[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
|
||||||
|
[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
|
||||||
|
[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
|
||||||
|
[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
|
||||||
|
table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
|
||||||
|
table.SetBorder(false) // Set Border to false
|
||||||
|
table.AppendBulk(data) // Add Bulk Data
|
||||||
|
table.Render()
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Output 2
|
||||||
|
```
|
||||||
|
|
||||||
|
DATE | DESCRIPTION | CV2 | AMOUNT
|
||||||
|
-----------+--------------------------+-------+----------
|
||||||
|
1/1/2014 | Domain name | 2233 | $10.98
|
||||||
|
1/1/2014 | January Hosting | 2233 | $54.95
|
||||||
|
1/4/2014 | February Hosting | 2233 | $51.00
|
||||||
|
1/4/2014 | February Extra Bandwidth | 2233 | $30.00
|
||||||
|
-----------+--------------------------+-------+----------
|
||||||
|
TOTAL | $146 93
|
||||||
|
--------+----------
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Example 3 - CSV
|
||||||
|
```go
|
||||||
|
table, _ := tablewriter.NewCSV(os.Stdout, "testdata/test_info.csv", true)
|
||||||
|
table.SetAlignment(tablewriter.ALIGN_LEFT) // Set Alignment
|
||||||
|
table.Render()
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Output 3
|
||||||
|
```
|
||||||
|
+----------+--------------+------+-----+---------+----------------+
|
||||||
|
| FIELD | TYPE | NULL | KEY | DEFAULT | EXTRA |
|
||||||
|
+----------+--------------+------+-----+---------+----------------+
|
||||||
|
| user_id | smallint(5) | NO | PRI | NULL | auto_increment |
|
||||||
|
| username | varchar(10) | NO | | NULL | |
|
||||||
|
| password | varchar(100) | NO | | NULL | |
|
||||||
|
+----------+--------------+------+-----+---------+----------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 4 - Custom Separator
|
||||||
|
```go
|
||||||
|
table, _ := tablewriter.NewCSV(os.Stdout, "testdata/test.csv", true)
|
||||||
|
table.SetRowLine(true) // Enable row line
|
||||||
|
|
||||||
|
// Change table lines
|
||||||
|
table.SetCenterSeparator("*")
|
||||||
|
table.SetColumnSeparator("╪")
|
||||||
|
table.SetRowSeparator("-")
|
||||||
|
|
||||||
|
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||||
|
table.Render()
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Output 4
|
||||||
|
```
|
||||||
|
*------------*-----------*---------*
|
||||||
|
╪ FIRST NAME ╪ LAST NAME ╪ SSN ╪
|
||||||
|
*------------*-----------*---------*
|
||||||
|
╪ John ╪ Barry ╪ 123456 ╪
|
||||||
|
*------------*-----------*---------*
|
||||||
|
╪ Kathy ╪ Smith ╪ 687987 ╪
|
||||||
|
*------------*-----------*---------*
|
||||||
|
╪ Bob ╪ McCornick ╪ 3979870 ╪
|
||||||
|
*------------*-----------*---------*
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 5 - Markdown Format
|
||||||
|
```go
|
||||||
|
data := [][]string{
|
||||||
|
[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
|
||||||
|
[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
|
||||||
|
[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
|
||||||
|
[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
|
||||||
|
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
|
||||||
|
table.SetCenterSeparator("|")
|
||||||
|
table.AppendBulk(data) // Add Bulk Data
|
||||||
|
table.Render()
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Output 5
|
||||||
|
```
|
||||||
|
| DATE | DESCRIPTION | CV2 | AMOUNT |
|
||||||
|
|----------|--------------------------|------|--------|
|
||||||
|
| 1/1/2014 | Domain name | 2233 | $10.98 |
|
||||||
|
| 1/1/2014 | January Hosting | 2233 | $54.95 |
|
||||||
|
| 1/4/2014 | February Hosting | 2233 | $51.00 |
|
||||||
|
| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 |
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 6 - Identical cells merging
|
||||||
|
```go
|
||||||
|
data := [][]string{
|
||||||
|
[]string{"1/1/2014", "Domain name", "1234", "$10.98"},
|
||||||
|
[]string{"1/1/2014", "January Hosting", "2345", "$54.95"},
|
||||||
|
[]string{"1/4/2014", "February Hosting", "3456", "$51.00"},
|
||||||
|
[]string{"1/4/2014", "February Extra Bandwidth", "4567", "$30.00"},
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
|
||||||
|
table.SetFooter([]string{"", "", "Total", "$146.93"})
|
||||||
|
table.SetAutoMergeCells(true)
|
||||||
|
table.SetRowLine(true)
|
||||||
|
table.AppendBulk(data)
|
||||||
|
table.Render()
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Output 6
|
||||||
|
```
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
| DATE | DESCRIPTION | CV2 | AMOUNT |
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
| 1/1/2014 | Domain name | 1234 | $10.98 |
|
||||||
|
+ +--------------------------+-------+---------+
|
||||||
|
| | January Hosting | 2345 | $54.95 |
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
| 1/4/2014 | February Hosting | 3456 | $51.00 |
|
||||||
|
+ +--------------------------+-------+---------+
|
||||||
|
| | February Extra Bandwidth | 4567 | $30.00 |
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
| TOTAL | $146 93 |
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 7 - Identical cells merging (specify the column index to merge)
|
||||||
|
```go
|
||||||
|
data := [][]string{
|
||||||
|
[]string{"1/1/2014", "Domain name", "1234", "$10.98"},
|
||||||
|
[]string{"1/1/2014", "January Hosting", "1234", "$10.98"},
|
||||||
|
[]string{"1/4/2014", "February Hosting", "3456", "$51.00"},
|
||||||
|
[]string{"1/4/2014", "February Extra Bandwidth", "4567", "$30.00"},
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
|
||||||
|
table.SetFooter([]string{"", "", "Total", "$146.93"})
|
||||||
|
table.SetAutoMergeCellsByColumnIndex([]int{2, 3})
|
||||||
|
table.SetRowLine(true)
|
||||||
|
table.AppendBulk(data)
|
||||||
|
table.Render()
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Output 7
|
||||||
|
```
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
| DATE | DESCRIPTION | CV2 | AMOUNT |
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
| 1/1/2014 | Domain name | 1234 | $10.98 |
|
||||||
|
+----------+--------------------------+ + +
|
||||||
|
| 1/1/2014 | January Hosting | | |
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
| 1/4/2014 | February Hosting | 3456 | $51.00 |
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
| 1/4/2014 | February Extra Bandwidth | 4567 | $30.00 |
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
| TOTAL | $146.93 |
|
||||||
|
+----------+--------------------------+-------+---------+
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Table with color
|
||||||
|
```go
|
||||||
|
data := [][]string{
|
||||||
|
[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
|
||||||
|
[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
|
||||||
|
[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
|
||||||
|
[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
|
||||||
|
table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
|
||||||
|
table.SetBorder(false) // Set Border to false
|
||||||
|
|
||||||
|
table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
|
||||||
|
tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
|
||||||
|
tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor},
|
||||||
|
tablewriter.Colors{tablewriter.BgCyanColor, tablewriter.FgWhiteColor})
|
||||||
|
|
||||||
|
table.SetColumnColor(tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
|
||||||
|
tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiRedColor},
|
||||||
|
tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
|
||||||
|
tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor})
|
||||||
|
|
||||||
|
table.SetFooterColor(tablewriter.Colors{}, tablewriter.Colors{},
|
||||||
|
tablewriter.Colors{tablewriter.Bold},
|
||||||
|
tablewriter.Colors{tablewriter.FgHiRedColor})
|
||||||
|
|
||||||
|
table.AppendBulk(data)
|
||||||
|
table.Render()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Table with color Output
|
||||||
|

|
||||||
|
|
||||||
|
#### Example - 8 Table Cells with Color
|
||||||
|
|
||||||
|
Individual Cell Colors from `func Rich` take precedence over Column Colors
|
||||||
|
|
||||||
|
```go
|
||||||
|
data := [][]string{
|
||||||
|
[]string{"Test1Merge", "HelloCol2 - 1", "HelloCol3 - 1", "HelloCol4 - 1"},
|
||||||
|
[]string{"Test1Merge", "HelloCol2 - 2", "HelloCol3 - 2", "HelloCol4 - 2"},
|
||||||
|
[]string{"Test1Merge", "HelloCol2 - 3", "HelloCol3 - 3", "HelloCol4 - 3"},
|
||||||
|
[]string{"Test2Merge", "HelloCol2 - 4", "HelloCol3 - 4", "HelloCol4 - 4"},
|
||||||
|
[]string{"Test2Merge", "HelloCol2 - 5", "HelloCol3 - 5", "HelloCol4 - 5"},
|
||||||
|
[]string{"Test2Merge", "HelloCol2 - 6", "HelloCol3 - 6", "HelloCol4 - 6"},
|
||||||
|
[]string{"Test2Merge", "HelloCol2 - 7", "HelloCol3 - 7", "HelloCol4 - 7"},
|
||||||
|
[]string{"Test3Merge", "HelloCol2 - 8", "HelloCol3 - 8", "HelloCol4 - 8"},
|
||||||
|
[]string{"Test3Merge", "HelloCol2 - 9", "HelloCol3 - 9", "HelloCol4 - 9"},
|
||||||
|
[]string{"Test3Merge", "HelloCol2 - 10", "HelloCol3 -10", "HelloCol4 - 10"},
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"Col1", "Col2", "Col3", "Col4"})
|
||||||
|
table.SetFooter([]string{"", "", "Footer3", "Footer4"})
|
||||||
|
table.SetBorder(false)
|
||||||
|
|
||||||
|
table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
|
||||||
|
tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
|
||||||
|
tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor},
|
||||||
|
tablewriter.Colors{tablewriter.BgCyanColor, tablewriter.FgWhiteColor})
|
||||||
|
|
||||||
|
table.SetColumnColor(tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
|
||||||
|
tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiRedColor},
|
||||||
|
tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
|
||||||
|
tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor})
|
||||||
|
|
||||||
|
table.SetFooterColor(tablewriter.Colors{}, tablewriter.Colors{},
|
||||||
|
tablewriter.Colors{tablewriter.Bold},
|
||||||
|
tablewriter.Colors{tablewriter.FgHiRedColor})
|
||||||
|
|
||||||
|
colorData1 := []string{"TestCOLOR1Merge", "HelloCol2 - COLOR1", "HelloCol3 - COLOR1", "HelloCol4 - COLOR1"}
|
||||||
|
colorData2 := []string{"TestCOLOR2Merge", "HelloCol2 - COLOR2", "HelloCol3 - COLOR2", "HelloCol4 - COLOR2"}
|
||||||
|
|
||||||
|
for i, row := range data {
|
||||||
|
if i == 4 {
|
||||||
|
table.Rich(colorData1, []tablewriter.Colors{tablewriter.Colors{}, tablewriter.Colors{tablewriter.Normal, tablewriter.FgCyanColor}, tablewriter.Colors{tablewriter.Bold, tablewriter.FgWhiteColor}, tablewriter.Colors{}})
|
||||||
|
table.Rich(colorData2, []tablewriter.Colors{tablewriter.Colors{tablewriter.Normal, tablewriter.FgMagentaColor}, tablewriter.Colors{}, tablewriter.Colors{tablewriter.Bold, tablewriter.BgRedColor}, tablewriter.Colors{tablewriter.FgHiGreenColor, tablewriter.Italic, tablewriter.BgHiCyanColor}})
|
||||||
|
}
|
||||||
|
table.Append(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
table.SetAutoMergeCells(true)
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Table cells with color Output
|
||||||
|

|
||||||
|
|
||||||
|
#### Example 9 - Set table caption
|
||||||
|
```go
|
||||||
|
data := [][]string{
|
||||||
|
[]string{"A", "The Good", "500"},
|
||||||
|
[]string{"B", "The Very very Bad Man", "288"},
|
||||||
|
[]string{"C", "The Ugly", "120"},
|
||||||
|
[]string{"D", "The Gopher", "800"},
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"Name", "Sign", "Rating"})
|
||||||
|
table.SetCaption(true, "Movie ratings.")
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
table.Append(v)
|
||||||
|
}
|
||||||
|
table.Render() // Send output
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Caption text will wrap with total width of rendered table.
|
||||||
|
|
||||||
|
##### Output 9
|
||||||
|
```
|
||||||
|
+------+-----------------------+--------+
|
||||||
|
| NAME | SIGN | RATING |
|
||||||
|
+------+-----------------------+--------+
|
||||||
|
| A | The Good | 500 |
|
||||||
|
| B | The Very very Bad Man | 288 |
|
||||||
|
| C | The Ugly | 120 |
|
||||||
|
| D | The Gopher | 800 |
|
||||||
|
+------+-----------------------+--------+
|
||||||
|
Movie ratings.
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 10 - Set NoWhiteSpace and TablePadding option
|
||||||
|
```go
|
||||||
|
data := [][]string{
|
||||||
|
{"node1.example.com", "Ready", "compute", "1.11"},
|
||||||
|
{"node2.example.com", "Ready", "compute", "1.11"},
|
||||||
|
{"node3.example.com", "Ready", "compute", "1.11"},
|
||||||
|
{"node4.example.com", "NotReady", "compute", "1.11"},
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"Name", "Status", "Role", "Version"})
|
||||||
|
table.SetAutoWrapText(false)
|
||||||
|
table.SetAutoFormatHeaders(true)
|
||||||
|
table.SetHeaderAlignment(ALIGN_LEFT)
|
||||||
|
table.SetAlignment(ALIGN_LEFT)
|
||||||
|
table.SetCenterSeparator("")
|
||||||
|
table.SetColumnSeparator("")
|
||||||
|
table.SetRowSeparator("")
|
||||||
|
table.SetHeaderLine(false)
|
||||||
|
table.SetBorder(false)
|
||||||
|
table.SetTablePadding("\t") // pad with tabs
|
||||||
|
table.SetNoWhiteSpace(true)
|
||||||
|
table.AppendBulk(data) // Add Bulk Data
|
||||||
|
table.Render()
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Output 10
|
||||||
|
```
|
||||||
|
NAME STATUS ROLE VERSION
|
||||||
|
node1.example.com Ready compute 1.11
|
||||||
|
node2.example.com Ready compute 1.11
|
||||||
|
node3.example.com Ready compute 1.11
|
||||||
|
node4.example.com NotReady compute 1.11
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Render table into a string
|
||||||
|
|
||||||
|
Instead of rendering the table to `io.Stdout` you can also render it into a string. Go 1.10 introduced the `strings.Builder` type which implements the `io.Writer` interface and can therefore be used for this task. Example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/olekukonko/tablewriter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
tableString := &strings.Builder{}
|
||||||
|
table := tablewriter.NewWriter(tableString)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Code to fill the table
|
||||||
|
*/
|
||||||
|
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
fmt.Println(tableString.String())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### TODO
|
||||||
|
- ~~Import Directly from CSV~~ - `done`
|
||||||
|
- ~~Support for `SetFooter`~~ - `done`
|
||||||
|
- ~~Support for `SetBorder`~~ - `done`
|
||||||
|
- ~~Support table with uneven rows~~ - `done`
|
||||||
|
- ~~Support custom alignment~~
|
||||||
|
- General Improvement & Optimisation
|
||||||
|
- `NewHTML` Parse table from HTML
|
||||||
52
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/csv.go
generated
vendored
Normal file
52
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/csv.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2014 Oleku Konko All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This module is a Table Writer API for the Go Programming Language.
|
||||||
|
// The protocols were written in pure Go and works on windows and unix systems
|
||||||
|
|
||||||
|
package tablewriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start A new table by importing from a CSV file
|
||||||
|
// Takes io.Writer and csv File name
|
||||||
|
func NewCSV(writer io.Writer, fileName string, hasHeader bool) (*Table, error) {
|
||||||
|
file, err := os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return &Table{}, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
csvReader := csv.NewReader(file)
|
||||||
|
t, err := NewCSVReader(writer, csvReader, hasHeader)
|
||||||
|
return t, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a New Table Writer with csv.Reader
|
||||||
|
// This enables customisation such as reader.Comma = ';'
|
||||||
|
// See http://golang.org/src/pkg/encoding/csv/reader.go?s=3213:3671#L94
|
||||||
|
func NewCSVReader(writer io.Writer, csvReader *csv.Reader, hasHeader bool) (*Table, error) {
|
||||||
|
t := NewWriter(writer)
|
||||||
|
if hasHeader {
|
||||||
|
// Read the first row
|
||||||
|
headers, err := csvReader.Read()
|
||||||
|
if err != nil {
|
||||||
|
return &Table{}, err
|
||||||
|
}
|
||||||
|
t.SetHeader(headers)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
record, err := csvReader.Read()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return &Table{}, err
|
||||||
|
}
|
||||||
|
t.Append(record)
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
967
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/table.go
generated
vendored
Normal file
967
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/table.go
generated
vendored
Normal file
@@ -0,0 +1,967 @@
|
|||||||
|
// Copyright 2014 Oleku Konko All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This module is a Table Writer API for the Go Programming Language.
|
||||||
|
// The protocols were written in pure Go and works on windows and unix systems
|
||||||
|
|
||||||
|
// Create & Generate text based table
|
||||||
|
package tablewriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MAX_ROW_WIDTH = 30
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CENTER = "+"
|
||||||
|
ROW = "-"
|
||||||
|
COLUMN = "|"
|
||||||
|
SPACE = " "
|
||||||
|
NEWLINE = "\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ALIGN_DEFAULT = iota
|
||||||
|
ALIGN_CENTER
|
||||||
|
ALIGN_RIGHT
|
||||||
|
ALIGN_LEFT
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
decimal = regexp.MustCompile(`^-?(?:\d{1,3}(?:,\d{3})*|\d+)(?:\.\d+)?$`)
|
||||||
|
percent = regexp.MustCompile(`^-?\d+\.?\d*$%$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Border struct {
|
||||||
|
Left bool
|
||||||
|
Right bool
|
||||||
|
Top bool
|
||||||
|
Bottom bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Table struct {
|
||||||
|
out io.Writer
|
||||||
|
rows [][]string
|
||||||
|
lines [][][]string
|
||||||
|
cs map[int]int
|
||||||
|
rs map[int]int
|
||||||
|
headers [][]string
|
||||||
|
footers [][]string
|
||||||
|
caption bool
|
||||||
|
captionText string
|
||||||
|
autoFmt bool
|
||||||
|
autoWrap bool
|
||||||
|
reflowText bool
|
||||||
|
mW int
|
||||||
|
pCenter string
|
||||||
|
pRow string
|
||||||
|
pColumn string
|
||||||
|
tColumn int
|
||||||
|
tRow int
|
||||||
|
hAlign int
|
||||||
|
fAlign int
|
||||||
|
align int
|
||||||
|
newLine string
|
||||||
|
rowLine bool
|
||||||
|
autoMergeCells bool
|
||||||
|
columnsToAutoMergeCells map[int]bool
|
||||||
|
noWhiteSpace bool
|
||||||
|
tablePadding string
|
||||||
|
hdrLine bool
|
||||||
|
borders Border
|
||||||
|
colSize int
|
||||||
|
headerParams []string
|
||||||
|
columnsParams []string
|
||||||
|
footerParams []string
|
||||||
|
columnsAlign []int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start New Table
|
||||||
|
// Take io.Writer Directly
|
||||||
|
func NewWriter(writer io.Writer) *Table {
|
||||||
|
t := &Table{
|
||||||
|
out: writer,
|
||||||
|
rows: [][]string{},
|
||||||
|
lines: [][][]string{},
|
||||||
|
cs: make(map[int]int),
|
||||||
|
rs: make(map[int]int),
|
||||||
|
headers: [][]string{},
|
||||||
|
footers: [][]string{},
|
||||||
|
caption: false,
|
||||||
|
captionText: "Table caption.",
|
||||||
|
autoFmt: true,
|
||||||
|
autoWrap: true,
|
||||||
|
reflowText: true,
|
||||||
|
mW: MAX_ROW_WIDTH,
|
||||||
|
pCenter: CENTER,
|
||||||
|
pRow: ROW,
|
||||||
|
pColumn: COLUMN,
|
||||||
|
tColumn: -1,
|
||||||
|
tRow: -1,
|
||||||
|
hAlign: ALIGN_DEFAULT,
|
||||||
|
fAlign: ALIGN_DEFAULT,
|
||||||
|
align: ALIGN_DEFAULT,
|
||||||
|
newLine: NEWLINE,
|
||||||
|
rowLine: false,
|
||||||
|
hdrLine: true,
|
||||||
|
borders: Border{Left: true, Right: true, Bottom: true, Top: true},
|
||||||
|
colSize: -1,
|
||||||
|
headerParams: []string{},
|
||||||
|
columnsParams: []string{},
|
||||||
|
footerParams: []string{},
|
||||||
|
columnsAlign: []int{}}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render table output
|
||||||
|
func (t *Table) Render() {
|
||||||
|
if t.borders.Top {
|
||||||
|
t.printLine(true)
|
||||||
|
}
|
||||||
|
t.printHeading()
|
||||||
|
if t.autoMergeCells {
|
||||||
|
t.printRowsMergeCells()
|
||||||
|
} else {
|
||||||
|
t.printRows()
|
||||||
|
}
|
||||||
|
if !t.rowLine && t.borders.Bottom {
|
||||||
|
t.printLine(true)
|
||||||
|
}
|
||||||
|
t.printFooter()
|
||||||
|
|
||||||
|
if t.caption {
|
||||||
|
t.printCaption()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
headerRowIdx = -1
|
||||||
|
footerRowIdx = -2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set table header
|
||||||
|
func (t *Table) SetHeader(keys []string) {
|
||||||
|
t.colSize = len(keys)
|
||||||
|
for i, v := range keys {
|
||||||
|
lines := t.parseDimension(v, i, headerRowIdx)
|
||||||
|
t.headers = append(t.headers, lines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set table Footer
|
||||||
|
func (t *Table) SetFooter(keys []string) {
|
||||||
|
//t.colSize = len(keys)
|
||||||
|
for i, v := range keys {
|
||||||
|
lines := t.parseDimension(v, i, footerRowIdx)
|
||||||
|
t.footers = append(t.footers, lines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set table Caption
|
||||||
|
func (t *Table) SetCaption(caption bool, captionText ...string) {
|
||||||
|
t.caption = caption
|
||||||
|
if len(captionText) == 1 {
|
||||||
|
t.captionText = captionText[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn header autoformatting on/off. Default is on (true).
|
||||||
|
func (t *Table) SetAutoFormatHeaders(auto bool) {
|
||||||
|
t.autoFmt = auto
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn automatic multiline text adjustment on/off. Default is on (true).
|
||||||
|
func (t *Table) SetAutoWrapText(auto bool) {
|
||||||
|
t.autoWrap = auto
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn automatic reflowing of multiline text when rewrapping. Default is on (true).
|
||||||
|
func (t *Table) SetReflowDuringAutoWrap(auto bool) {
|
||||||
|
t.reflowText = auto
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the Default column width
|
||||||
|
func (t *Table) SetColWidth(width int) {
|
||||||
|
t.mW = width
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the minimal width for a column
|
||||||
|
func (t *Table) SetColMinWidth(column int, width int) {
|
||||||
|
t.cs[column] = width
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the Column Separator
|
||||||
|
func (t *Table) SetColumnSeparator(sep string) {
|
||||||
|
t.pColumn = sep
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the Row Separator
|
||||||
|
func (t *Table) SetRowSeparator(sep string) {
|
||||||
|
t.pRow = sep
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the center Separator
|
||||||
|
func (t *Table) SetCenterSeparator(sep string) {
|
||||||
|
t.pCenter = sep
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Header Alignment
|
||||||
|
func (t *Table) SetHeaderAlignment(hAlign int) {
|
||||||
|
t.hAlign = hAlign
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Footer Alignment
|
||||||
|
func (t *Table) SetFooterAlignment(fAlign int) {
|
||||||
|
t.fAlign = fAlign
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Table Alignment
|
||||||
|
func (t *Table) SetAlignment(align int) {
|
||||||
|
t.align = align
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set No White Space
|
||||||
|
func (t *Table) SetNoWhiteSpace(allow bool) {
|
||||||
|
t.noWhiteSpace = allow
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Table Padding
|
||||||
|
func (t *Table) SetTablePadding(padding string) {
|
||||||
|
t.tablePadding = padding
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) SetColumnAlignment(keys []int) {
|
||||||
|
for _, v := range keys {
|
||||||
|
switch v {
|
||||||
|
case ALIGN_CENTER:
|
||||||
|
break
|
||||||
|
case ALIGN_LEFT:
|
||||||
|
break
|
||||||
|
case ALIGN_RIGHT:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
v = ALIGN_DEFAULT
|
||||||
|
}
|
||||||
|
t.columnsAlign = append(t.columnsAlign, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set New Line
|
||||||
|
func (t *Table) SetNewLine(nl string) {
|
||||||
|
t.newLine = nl
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Header Line
|
||||||
|
// This would enable / disable a line after the header
|
||||||
|
func (t *Table) SetHeaderLine(line bool) {
|
||||||
|
t.hdrLine = line
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Row Line
|
||||||
|
// This would enable / disable a line on each row of the table
|
||||||
|
func (t *Table) SetRowLine(line bool) {
|
||||||
|
t.rowLine = line
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Auto Merge Cells
|
||||||
|
// This would enable / disable the merge of cells with identical values
|
||||||
|
func (t *Table) SetAutoMergeCells(auto bool) {
|
||||||
|
t.autoMergeCells = auto
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Auto Merge Cells By Column Index
|
||||||
|
// This would enable / disable the merge of cells with identical values for specific columns
|
||||||
|
// If cols is empty, it is the same as `SetAutoMergeCells(true)`.
|
||||||
|
func (t *Table) SetAutoMergeCellsByColumnIndex(cols []int) {
|
||||||
|
t.autoMergeCells = true
|
||||||
|
|
||||||
|
if len(cols) > 0 {
|
||||||
|
m := make(map[int]bool)
|
||||||
|
for _, col := range cols {
|
||||||
|
m[col] = true
|
||||||
|
}
|
||||||
|
t.columnsToAutoMergeCells = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Table Border
|
||||||
|
// This would enable / disable line around the table
|
||||||
|
func (t *Table) SetBorder(border bool) {
|
||||||
|
t.SetBorders(Border{border, border, border, border})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) SetBorders(border Border) {
|
||||||
|
t.borders = border
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append row to table
|
||||||
|
func (t *Table) Append(row []string) {
|
||||||
|
rowSize := len(t.headers)
|
||||||
|
if rowSize > t.colSize {
|
||||||
|
t.colSize = rowSize
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(t.lines)
|
||||||
|
line := [][]string{}
|
||||||
|
for i, v := range row {
|
||||||
|
|
||||||
|
// Detect string width
|
||||||
|
// Detect String height
|
||||||
|
// Break strings into words
|
||||||
|
out := t.parseDimension(v, i, n)
|
||||||
|
|
||||||
|
// Append broken words
|
||||||
|
line = append(line, out)
|
||||||
|
}
|
||||||
|
t.lines = append(t.lines, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append row to table with color attributes
|
||||||
|
func (t *Table) Rich(row []string, colors []Colors) {
|
||||||
|
rowSize := len(t.headers)
|
||||||
|
if rowSize > t.colSize {
|
||||||
|
t.colSize = rowSize
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(t.lines)
|
||||||
|
line := [][]string{}
|
||||||
|
for i, v := range row {
|
||||||
|
|
||||||
|
// Detect string width
|
||||||
|
// Detect String height
|
||||||
|
// Break strings into words
|
||||||
|
out := t.parseDimension(v, i, n)
|
||||||
|
|
||||||
|
if len(colors) > i {
|
||||||
|
color := colors[i]
|
||||||
|
out[0] = format(out[0], color)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append broken words
|
||||||
|
line = append(line, out)
|
||||||
|
}
|
||||||
|
t.lines = append(t.lines, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow Support for Bulk Append
|
||||||
|
// Eliminates repeated for loops
|
||||||
|
func (t *Table) AppendBulk(rows [][]string) {
|
||||||
|
for _, row := range rows {
|
||||||
|
t.Append(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumLines to get the number of lines
|
||||||
|
func (t *Table) NumLines() int {
|
||||||
|
return len(t.lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear rows
|
||||||
|
func (t *Table) ClearRows() {
|
||||||
|
t.lines = [][][]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear footer
|
||||||
|
func (t *Table) ClearFooter() {
|
||||||
|
t.footers = [][]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Center based on position and border.
|
||||||
|
func (t *Table) center(i int) string {
|
||||||
|
if i == -1 && !t.borders.Left {
|
||||||
|
return t.pRow
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == len(t.cs)-1 && !t.borders.Right {
|
||||||
|
return t.pRow
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.pCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print line based on row width
|
||||||
|
func (t *Table) printLine(nl bool) {
|
||||||
|
fmt.Fprint(t.out, t.center(-1))
|
||||||
|
for i := 0; i < len(t.cs); i++ {
|
||||||
|
v := t.cs[i]
|
||||||
|
fmt.Fprintf(t.out, "%s%s%s%s",
|
||||||
|
t.pRow,
|
||||||
|
strings.Repeat(string(t.pRow), v),
|
||||||
|
t.pRow,
|
||||||
|
t.center(i))
|
||||||
|
}
|
||||||
|
if nl {
|
||||||
|
fmt.Fprint(t.out, t.newLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print line based on row width with our without cell separator
|
||||||
|
func (t *Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) {
|
||||||
|
fmt.Fprint(t.out, t.pCenter)
|
||||||
|
for i := 0; i < len(t.cs); i++ {
|
||||||
|
v := t.cs[i]
|
||||||
|
if i > len(displayCellSeparator) || displayCellSeparator[i] {
|
||||||
|
// Display the cell separator
|
||||||
|
fmt.Fprintf(t.out, "%s%s%s%s",
|
||||||
|
t.pRow,
|
||||||
|
strings.Repeat(string(t.pRow), v),
|
||||||
|
t.pRow,
|
||||||
|
t.pCenter)
|
||||||
|
} else {
|
||||||
|
// Don't display the cell separator for this cell
|
||||||
|
fmt.Fprintf(t.out, "%s%s",
|
||||||
|
strings.Repeat(" ", v+2),
|
||||||
|
t.pCenter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nl {
|
||||||
|
fmt.Fprint(t.out, t.newLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the PadRight function if align is left, PadLeft if align is right,
|
||||||
|
// and Pad by default
|
||||||
|
func pad(align int) func(string, string, int) string {
|
||||||
|
padFunc := Pad
|
||||||
|
switch align {
|
||||||
|
case ALIGN_LEFT:
|
||||||
|
padFunc = PadRight
|
||||||
|
case ALIGN_RIGHT:
|
||||||
|
padFunc = PadLeft
|
||||||
|
}
|
||||||
|
return padFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print heading information
|
||||||
|
func (t *Table) printHeading() {
|
||||||
|
// Check if headers is available
|
||||||
|
if len(t.headers) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify last column
|
||||||
|
end := len(t.cs) - 1
|
||||||
|
|
||||||
|
// Get pad function
|
||||||
|
padFunc := pad(t.hAlign)
|
||||||
|
|
||||||
|
// Checking for ANSI escape sequences for header
|
||||||
|
is_esc_seq := false
|
||||||
|
if len(t.headerParams) > 0 {
|
||||||
|
is_esc_seq = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum height.
|
||||||
|
max := t.rs[headerRowIdx]
|
||||||
|
|
||||||
|
// Print Heading
|
||||||
|
for x := 0; x < max; x++ {
|
||||||
|
// Check if border is set
|
||||||
|
// Replace with space if not set
|
||||||
|
if !t.noWhiteSpace {
|
||||||
|
fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := 0; y <= end; y++ {
|
||||||
|
v := t.cs[y]
|
||||||
|
h := ""
|
||||||
|
|
||||||
|
if y < len(t.headers) && x < len(t.headers[y]) {
|
||||||
|
h = t.headers[y][x]
|
||||||
|
}
|
||||||
|
if t.autoFmt {
|
||||||
|
h = Title(h)
|
||||||
|
}
|
||||||
|
pad := ConditionString((y == end && !t.borders.Left), SPACE, t.pColumn)
|
||||||
|
if t.noWhiteSpace {
|
||||||
|
pad = ConditionString((y == end && !t.borders.Left), SPACE, t.tablePadding)
|
||||||
|
}
|
||||||
|
if is_esc_seq {
|
||||||
|
if !t.noWhiteSpace {
|
||||||
|
fmt.Fprintf(t.out, " %s %s",
|
||||||
|
format(padFunc(h, SPACE, v),
|
||||||
|
t.headerParams[y]), pad)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(t.out, "%s %s",
|
||||||
|
format(padFunc(h, SPACE, v),
|
||||||
|
t.headerParams[y]), pad)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !t.noWhiteSpace {
|
||||||
|
fmt.Fprintf(t.out, " %s %s",
|
||||||
|
padFunc(h, SPACE, v),
|
||||||
|
pad)
|
||||||
|
} else {
|
||||||
|
// the spaces between breaks the kube formatting
|
||||||
|
fmt.Fprintf(t.out, "%s%s",
|
||||||
|
padFunc(h, SPACE, v),
|
||||||
|
pad)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Next line
|
||||||
|
fmt.Fprint(t.out, t.newLine)
|
||||||
|
}
|
||||||
|
if t.hdrLine {
|
||||||
|
t.printLine(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print heading information
|
||||||
|
func (t *Table) printFooter() {
|
||||||
|
// Check if headers is available
|
||||||
|
if len(t.footers) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only print line if border is not set
|
||||||
|
if !t.borders.Bottom {
|
||||||
|
t.printLine(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify last column
|
||||||
|
end := len(t.cs) - 1
|
||||||
|
|
||||||
|
// Get pad function
|
||||||
|
padFunc := pad(t.fAlign)
|
||||||
|
|
||||||
|
// Checking for ANSI escape sequences for header
|
||||||
|
is_esc_seq := false
|
||||||
|
if len(t.footerParams) > 0 {
|
||||||
|
is_esc_seq = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum height.
|
||||||
|
max := t.rs[footerRowIdx]
|
||||||
|
|
||||||
|
// Print Footer
|
||||||
|
erasePad := make([]bool, len(t.footers))
|
||||||
|
for x := 0; x < max; x++ {
|
||||||
|
// Check if border is set
|
||||||
|
// Replace with space if not set
|
||||||
|
fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
|
||||||
|
|
||||||
|
for y := 0; y <= end; y++ {
|
||||||
|
v := t.cs[y]
|
||||||
|
f := ""
|
||||||
|
if y < len(t.footers) && x < len(t.footers[y]) {
|
||||||
|
f = t.footers[y][x]
|
||||||
|
}
|
||||||
|
if t.autoFmt {
|
||||||
|
f = Title(f)
|
||||||
|
}
|
||||||
|
pad := ConditionString((y == end && !t.borders.Top), SPACE, t.pColumn)
|
||||||
|
|
||||||
|
if erasePad[y] || (x == 0 && len(f) == 0) {
|
||||||
|
pad = SPACE
|
||||||
|
erasePad[y] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_esc_seq {
|
||||||
|
fmt.Fprintf(t.out, " %s %s",
|
||||||
|
format(padFunc(f, SPACE, v),
|
||||||
|
t.footerParams[y]), pad)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(t.out, " %s %s",
|
||||||
|
padFunc(f, SPACE, v),
|
||||||
|
pad)
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Fprintf(t.out, " %s %s",
|
||||||
|
// padFunc(f, SPACE, v),
|
||||||
|
// pad)
|
||||||
|
}
|
||||||
|
// Next line
|
||||||
|
fmt.Fprint(t.out, t.newLine)
|
||||||
|
//t.printLine(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPrinted := false
|
||||||
|
|
||||||
|
for i := 0; i <= end; i++ {
|
||||||
|
v := t.cs[i]
|
||||||
|
pad := t.pRow
|
||||||
|
center := t.pCenter
|
||||||
|
length := len(t.footers[i][0])
|
||||||
|
|
||||||
|
if length > 0 {
|
||||||
|
hasPrinted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set center to be space if length is 0
|
||||||
|
if length == 0 && !t.borders.Right {
|
||||||
|
center = SPACE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print first junction
|
||||||
|
if i == 0 {
|
||||||
|
if length > 0 && !t.borders.Left {
|
||||||
|
center = t.pRow
|
||||||
|
}
|
||||||
|
fmt.Fprint(t.out, center)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad With space of length is 0
|
||||||
|
if length == 0 {
|
||||||
|
pad = SPACE
|
||||||
|
}
|
||||||
|
// Ignore left space as it has printed before
|
||||||
|
if hasPrinted || t.borders.Left {
|
||||||
|
pad = t.pRow
|
||||||
|
center = t.pCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change Center end position
|
||||||
|
if center != SPACE {
|
||||||
|
if i == end && !t.borders.Right {
|
||||||
|
center = t.pRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change Center start position
|
||||||
|
if center == SPACE {
|
||||||
|
if i < end && len(t.footers[i+1][0]) != 0 {
|
||||||
|
if !t.borders.Left {
|
||||||
|
center = t.pRow
|
||||||
|
} else {
|
||||||
|
center = t.pCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the footer
|
||||||
|
fmt.Fprintf(t.out, "%s%s%s%s",
|
||||||
|
pad,
|
||||||
|
strings.Repeat(string(pad), v),
|
||||||
|
pad,
|
||||||
|
center)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(t.out, t.newLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print caption text
|
||||||
|
func (t Table) printCaption() {
|
||||||
|
width := t.getTableWidth()
|
||||||
|
paragraph, _ := WrapString(t.captionText, width)
|
||||||
|
for linecount := 0; linecount < len(paragraph); linecount++ {
|
||||||
|
fmt.Fprintln(t.out, paragraph[linecount])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the total number of characters in a row
|
||||||
|
func (t Table) getTableWidth() int {
|
||||||
|
var chars int
|
||||||
|
for _, v := range t.cs {
|
||||||
|
chars += v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add chars, spaces, seperators to calculate the total width of the table.
|
||||||
|
// ncols := t.colSize
|
||||||
|
// spaces := ncols * 2
|
||||||
|
// seps := ncols + 1
|
||||||
|
|
||||||
|
return (chars + (3 * t.colSize) + 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Table) printRows() {
|
||||||
|
for i, lines := range t.lines {
|
||||||
|
t.printRow(lines, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) fillAlignment(num int) {
|
||||||
|
if len(t.columnsAlign) < num {
|
||||||
|
t.columnsAlign = make([]int, num)
|
||||||
|
for i := range t.columnsAlign {
|
||||||
|
t.columnsAlign[i] = t.align
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print Row Information
|
||||||
|
// Adjust column alignment based on type
|
||||||
|
|
||||||
|
func (t *Table) printRow(columns [][]string, rowIdx int) {
|
||||||
|
// Get Maximum Height
|
||||||
|
max := t.rs[rowIdx]
|
||||||
|
total := len(columns)
|
||||||
|
|
||||||
|
// TODO Fix uneven col size
|
||||||
|
// if total < t.colSize {
|
||||||
|
// for n := t.colSize - total; n < t.colSize ; n++ {
|
||||||
|
// columns = append(columns, []string{SPACE})
|
||||||
|
// t.cs[n] = t.mW
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Pad Each Height
|
||||||
|
pads := []int{}
|
||||||
|
|
||||||
|
// Checking for ANSI escape sequences for columns
|
||||||
|
is_esc_seq := false
|
||||||
|
if len(t.columnsParams) > 0 {
|
||||||
|
is_esc_seq = true
|
||||||
|
}
|
||||||
|
t.fillAlignment(total)
|
||||||
|
|
||||||
|
for i, line := range columns {
|
||||||
|
length := len(line)
|
||||||
|
pad := max - length
|
||||||
|
pads = append(pads, pad)
|
||||||
|
for n := 0; n < pad; n++ {
|
||||||
|
columns[i] = append(columns[i], " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//fmt.Println(max, "\n")
|
||||||
|
for x := 0; x < max; x++ {
|
||||||
|
for y := 0; y < total; y++ {
|
||||||
|
|
||||||
|
// Check if border is set
|
||||||
|
if !t.noWhiteSpace {
|
||||||
|
fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
|
||||||
|
fmt.Fprintf(t.out, SPACE)
|
||||||
|
}
|
||||||
|
|
||||||
|
str := columns[y][x]
|
||||||
|
|
||||||
|
// Embedding escape sequence with column value
|
||||||
|
if is_esc_seq {
|
||||||
|
str = format(str, t.columnsParams[y])
|
||||||
|
}
|
||||||
|
|
||||||
|
// This would print alignment
|
||||||
|
// Default alignment would use multiple configuration
|
||||||
|
switch t.columnsAlign[y] {
|
||||||
|
case ALIGN_CENTER: //
|
||||||
|
fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
|
||||||
|
case ALIGN_RIGHT:
|
||||||
|
fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
|
||||||
|
case ALIGN_LEFT:
|
||||||
|
fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
|
||||||
|
default:
|
||||||
|
if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
|
||||||
|
fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
|
||||||
|
|
||||||
|
// TODO Custom alignment per column
|
||||||
|
//if max == 1 || pads[y] > 0 {
|
||||||
|
// fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
|
||||||
|
//} else {
|
||||||
|
// fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !t.noWhiteSpace {
|
||||||
|
fmt.Fprintf(t.out, SPACE)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(t.out, t.tablePadding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if border is set
|
||||||
|
// Replace with space if not set
|
||||||
|
if !t.noWhiteSpace {
|
||||||
|
fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
|
||||||
|
}
|
||||||
|
fmt.Fprint(t.out, t.newLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.rowLine {
|
||||||
|
t.printLine(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the rows of the table and merge the cells that are identical
|
||||||
|
func (t *Table) printRowsMergeCells() {
|
||||||
|
var previousLine []string
|
||||||
|
var displayCellBorder []bool
|
||||||
|
var tmpWriter bytes.Buffer
|
||||||
|
for i, lines := range t.lines {
|
||||||
|
// We store the display of the current line in a tmp writer, as we need to know which border needs to be print above
|
||||||
|
previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine)
|
||||||
|
if i > 0 { //We don't need to print borders above first line
|
||||||
|
if t.rowLine {
|
||||||
|
t.printLineOptionalCellSeparators(true, displayCellBorder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpWriter.WriteTo(t.out)
|
||||||
|
}
|
||||||
|
//Print the end of the table
|
||||||
|
if t.rowLine {
|
||||||
|
t.printLine(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print Row Information to a writer and merge identical cells.
|
||||||
|
// Adjust column alignment based on type
|
||||||
|
|
||||||
|
func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, rowIdx int, previousLine []string) ([]string, []bool) {
|
||||||
|
// Get Maximum Height
|
||||||
|
max := t.rs[rowIdx]
|
||||||
|
total := len(columns)
|
||||||
|
|
||||||
|
// Pad Each Height
|
||||||
|
pads := []int{}
|
||||||
|
|
||||||
|
// Checking for ANSI escape sequences for columns
|
||||||
|
is_esc_seq := false
|
||||||
|
if len(t.columnsParams) > 0 {
|
||||||
|
is_esc_seq = true
|
||||||
|
}
|
||||||
|
for i, line := range columns {
|
||||||
|
length := len(line)
|
||||||
|
pad := max - length
|
||||||
|
pads = append(pads, pad)
|
||||||
|
for n := 0; n < pad; n++ {
|
||||||
|
columns[i] = append(columns[i], " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayCellBorder []bool
|
||||||
|
t.fillAlignment(total)
|
||||||
|
for x := 0; x < max; x++ {
|
||||||
|
for y := 0; y < total; y++ {
|
||||||
|
|
||||||
|
// Check if border is set
|
||||||
|
fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
|
||||||
|
|
||||||
|
fmt.Fprintf(writer, SPACE)
|
||||||
|
|
||||||
|
str := columns[y][x]
|
||||||
|
|
||||||
|
// Embedding escape sequence with column value
|
||||||
|
if is_esc_seq {
|
||||||
|
str = format(str, t.columnsParams[y])
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.autoMergeCells {
|
||||||
|
var mergeCell bool
|
||||||
|
if t.columnsToAutoMergeCells != nil {
|
||||||
|
// Check to see if the column index is in columnsToAutoMergeCells.
|
||||||
|
if t.columnsToAutoMergeCells[y] {
|
||||||
|
mergeCell = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// columnsToAutoMergeCells was not set.
|
||||||
|
mergeCell = true
|
||||||
|
}
|
||||||
|
//Store the full line to merge mutli-lines cells
|
||||||
|
fullLine := strings.TrimRight(strings.Join(columns[y], " "), " ")
|
||||||
|
if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" && mergeCell {
|
||||||
|
// If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty.
|
||||||
|
displayCellBorder = append(displayCellBorder, false)
|
||||||
|
str = ""
|
||||||
|
} else {
|
||||||
|
// First line or different content, keep the content and print the cell border
|
||||||
|
displayCellBorder = append(displayCellBorder, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This would print alignment
|
||||||
|
// Default alignment would use multiple configuration
|
||||||
|
switch t.columnsAlign[y] {
|
||||||
|
case ALIGN_CENTER: //
|
||||||
|
fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y]))
|
||||||
|
case ALIGN_RIGHT:
|
||||||
|
fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
|
||||||
|
case ALIGN_LEFT:
|
||||||
|
fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
|
||||||
|
default:
|
||||||
|
if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
|
||||||
|
fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(writer, SPACE)
|
||||||
|
}
|
||||||
|
// Check if border is set
|
||||||
|
// Replace with space if not set
|
||||||
|
fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE))
|
||||||
|
fmt.Fprint(writer, t.newLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
//The new previous line is the current one
|
||||||
|
previousLine = make([]string, total)
|
||||||
|
for y := 0; y < total; y++ {
|
||||||
|
previousLine[y] = strings.TrimRight(strings.Join(columns[y], " "), " ") //Store the full line for multi-lines cells
|
||||||
|
}
|
||||||
|
//Returns the newly added line and wether or not a border should be displayed above.
|
||||||
|
return previousLine, displayCellBorder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) parseDimension(str string, colKey, rowKey int) []string {
|
||||||
|
var (
|
||||||
|
raw []string
|
||||||
|
maxWidth int
|
||||||
|
)
|
||||||
|
|
||||||
|
raw = getLines(str)
|
||||||
|
maxWidth = 0
|
||||||
|
for _, line := range raw {
|
||||||
|
if w := DisplayWidth(line); w > maxWidth {
|
||||||
|
maxWidth = w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If wrapping, ensure that all paragraphs in the cell fit in the
|
||||||
|
// specified width.
|
||||||
|
if t.autoWrap {
|
||||||
|
// If there's a maximum allowed width for wrapping, use that.
|
||||||
|
if maxWidth > t.mW {
|
||||||
|
maxWidth = t.mW
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the process of doing so, we need to recompute maxWidth. This
|
||||||
|
// is because perhaps a word in the cell is longer than the
|
||||||
|
// allowed maximum width in t.mW.
|
||||||
|
newMaxWidth := maxWidth
|
||||||
|
newRaw := make([]string, 0, len(raw))
|
||||||
|
|
||||||
|
if t.reflowText {
|
||||||
|
// Make a single paragraph of everything.
|
||||||
|
raw = []string{strings.Join(raw, " ")}
|
||||||
|
}
|
||||||
|
for i, para := range raw {
|
||||||
|
paraLines, _ := WrapString(para, maxWidth)
|
||||||
|
for _, line := range paraLines {
|
||||||
|
if w := DisplayWidth(line); w > newMaxWidth {
|
||||||
|
newMaxWidth = w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
newRaw = append(newRaw, " ")
|
||||||
|
}
|
||||||
|
newRaw = append(newRaw, paraLines...)
|
||||||
|
}
|
||||||
|
raw = newRaw
|
||||||
|
maxWidth = newMaxWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the new known maximum width.
|
||||||
|
v, ok := t.cs[colKey]
|
||||||
|
if !ok || v < maxWidth || v == 0 {
|
||||||
|
t.cs[colKey] = maxWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember the number of lines for the row printer.
|
||||||
|
h := len(raw)
|
||||||
|
v, ok = t.rs[rowKey]
|
||||||
|
|
||||||
|
if !ok || v < h || v == 0 {
|
||||||
|
t.rs[rowKey] = h
|
||||||
|
}
|
||||||
|
//fmt.Printf("Raw %+v %d\n", raw, len(raw))
|
||||||
|
return raw
|
||||||
|
}
|
||||||
136
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/table_with_color.go
generated
vendored
Normal file
136
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/table_with_color.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
package tablewriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ESC = "\033"
|
||||||
|
const SEP = ";"
|
||||||
|
|
||||||
|
const (
|
||||||
|
BgBlackColor int = iota + 40
|
||||||
|
BgRedColor
|
||||||
|
BgGreenColor
|
||||||
|
BgYellowColor
|
||||||
|
BgBlueColor
|
||||||
|
BgMagentaColor
|
||||||
|
BgCyanColor
|
||||||
|
BgWhiteColor
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FgBlackColor int = iota + 30
|
||||||
|
FgRedColor
|
||||||
|
FgGreenColor
|
||||||
|
FgYellowColor
|
||||||
|
FgBlueColor
|
||||||
|
FgMagentaColor
|
||||||
|
FgCyanColor
|
||||||
|
FgWhiteColor
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BgHiBlackColor int = iota + 100
|
||||||
|
BgHiRedColor
|
||||||
|
BgHiGreenColor
|
||||||
|
BgHiYellowColor
|
||||||
|
BgHiBlueColor
|
||||||
|
BgHiMagentaColor
|
||||||
|
BgHiCyanColor
|
||||||
|
BgHiWhiteColor
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FgHiBlackColor int = iota + 90
|
||||||
|
FgHiRedColor
|
||||||
|
FgHiGreenColor
|
||||||
|
FgHiYellowColor
|
||||||
|
FgHiBlueColor
|
||||||
|
FgHiMagentaColor
|
||||||
|
FgHiCyanColor
|
||||||
|
FgHiWhiteColor
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Normal = 0
|
||||||
|
Bold = 1
|
||||||
|
UnderlineSingle = 4
|
||||||
|
Italic
|
||||||
|
)
|
||||||
|
|
||||||
|
type Colors []int
|
||||||
|
|
||||||
|
func startFormat(seq string) string {
|
||||||
|
return fmt.Sprintf("%s[%sm", ESC, seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopFormat() string {
|
||||||
|
return fmt.Sprintf("%s[%dm", ESC, Normal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Making the SGR (Select Graphic Rendition) sequence.
|
||||||
|
func makeSequence(codes []int) string {
|
||||||
|
codesInString := []string{}
|
||||||
|
for _, code := range codes {
|
||||||
|
codesInString = append(codesInString, strconv.Itoa(code))
|
||||||
|
}
|
||||||
|
return strings.Join(codesInString, SEP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding ANSI escape sequences before and after string
|
||||||
|
func format(s string, codes interface{}) string {
|
||||||
|
var seq string
|
||||||
|
|
||||||
|
switch v := codes.(type) {
|
||||||
|
|
||||||
|
case string:
|
||||||
|
seq = v
|
||||||
|
case []int:
|
||||||
|
seq = makeSequence(v)
|
||||||
|
case Colors:
|
||||||
|
seq = makeSequence(v)
|
||||||
|
default:
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(seq) == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return startFormat(seq) + s + stopFormat()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding header colors (ANSI codes)
|
||||||
|
func (t *Table) SetHeaderColor(colors ...Colors) {
|
||||||
|
if t.colSize != len(colors) {
|
||||||
|
panic("Number of header colors must be equal to number of headers.")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(colors); i++ {
|
||||||
|
t.headerParams = append(t.headerParams, makeSequence(colors[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding column colors (ANSI codes)
|
||||||
|
func (t *Table) SetColumnColor(colors ...Colors) {
|
||||||
|
if t.colSize != len(colors) {
|
||||||
|
panic("Number of column colors must be equal to number of headers.")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(colors); i++ {
|
||||||
|
t.columnsParams = append(t.columnsParams, makeSequence(colors[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding column colors (ANSI codes)
|
||||||
|
func (t *Table) SetFooterColor(colors ...Colors) {
|
||||||
|
if len(t.footers) != len(colors) {
|
||||||
|
panic("Number of footer colors must be equal to number of footer.")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(colors); i++ {
|
||||||
|
t.footerParams = append(t.footerParams, makeSequence(colors[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Color(colors ...int) []int {
|
||||||
|
return colors
|
||||||
|
}
|
||||||
93
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/util.go
generated
vendored
Normal file
93
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/util.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2014 Oleku Konko All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This module is a Table Writer API for the Go Programming Language.
|
||||||
|
// The protocols were written in pure Go and works on windows and unix systems
|
||||||
|
|
||||||
|
package tablewriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ansi = regexp.MustCompile("\033\\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]")
|
||||||
|
|
||||||
|
func DisplayWidth(str string) int {
|
||||||
|
return runewidth.StringWidth(ansi.ReplaceAllLiteralString(str, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple Condition for string
|
||||||
|
// Returns value based on condition
|
||||||
|
func ConditionString(cond bool, valid, inValid string) string {
|
||||||
|
if cond {
|
||||||
|
return valid
|
||||||
|
}
|
||||||
|
return inValid
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNumOrSpace(r rune) bool {
|
||||||
|
return ('0' <= r && r <= '9') || r == ' '
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format Table Header
|
||||||
|
// Replace _ , . and spaces
|
||||||
|
func Title(name string) string {
|
||||||
|
origLen := len(name)
|
||||||
|
rs := []rune(name)
|
||||||
|
for i, r := range rs {
|
||||||
|
switch r {
|
||||||
|
case '_':
|
||||||
|
rs[i] = ' '
|
||||||
|
case '.':
|
||||||
|
// ignore floating number 0.0
|
||||||
|
if (i != 0 && !isNumOrSpace(rs[i-1])) || (i != len(rs)-1 && !isNumOrSpace(rs[i+1])) {
|
||||||
|
rs[i] = ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name = string(rs)
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
if len(name) == 0 && origLen > 0 {
|
||||||
|
// Keep at least one character. This is important to preserve
|
||||||
|
// empty lines in multi-line headers/footers.
|
||||||
|
name = " "
|
||||||
|
}
|
||||||
|
return strings.ToUpper(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad String
|
||||||
|
// Attempts to place string in the center
|
||||||
|
func Pad(s, pad string, width int) string {
|
||||||
|
gap := width - DisplayWidth(s)
|
||||||
|
if gap > 0 {
|
||||||
|
gapLeft := int(math.Ceil(float64(gap / 2)))
|
||||||
|
gapRight := gap - gapLeft
|
||||||
|
return strings.Repeat(string(pad), gapLeft) + s + strings.Repeat(string(pad), gapRight)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad String Right position
|
||||||
|
// This would place string at the left side of the screen
|
||||||
|
func PadRight(s, pad string, width int) string {
|
||||||
|
gap := width - DisplayWidth(s)
|
||||||
|
if gap > 0 {
|
||||||
|
return s + strings.Repeat(string(pad), gap)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad String Left position
|
||||||
|
// This would place string at the right side of the screen
|
||||||
|
func PadLeft(s, pad string, width int) string {
|
||||||
|
gap := width - DisplayWidth(s)
|
||||||
|
if gap > 0 {
|
||||||
|
return strings.Repeat(string(pad), gap) + s
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
99
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/wrap.go
generated
vendored
Normal file
99
tests/metrics/cmd/checkmetrics/vendor/github.com/olekukonko/tablewriter/wrap.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// Copyright 2014 Oleku Konko All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This module is a Table Writer API for the Go Programming Language.
|
||||||
|
// The protocols were written in pure Go and works on windows and unix systems
|
||||||
|
|
||||||
|
package tablewriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nl = "\n"
|
||||||
|
sp = " "
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultPenalty = 1e5
|
||||||
|
|
||||||
|
// Wrap wraps s into a paragraph of lines of length lim, with minimal
|
||||||
|
// raggedness.
|
||||||
|
func WrapString(s string, lim int) ([]string, int) {
|
||||||
|
words := strings.Split(strings.Replace(s, nl, sp, -1), sp)
|
||||||
|
var lines []string
|
||||||
|
max := 0
|
||||||
|
for _, v := range words {
|
||||||
|
max = runewidth.StringWidth(v)
|
||||||
|
if max > lim {
|
||||||
|
lim = max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
|
||||||
|
lines = append(lines, strings.Join(line, sp))
|
||||||
|
}
|
||||||
|
return lines, lim
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapWords is the low-level line-breaking algorithm, useful if you need more
|
||||||
|
// control over the details of the text wrapping process. For most uses,
|
||||||
|
// WrapString will be sufficient and more convenient.
|
||||||
|
//
|
||||||
|
// WrapWords splits a list of words into lines with minimal "raggedness",
|
||||||
|
// treating each rune as one unit, accounting for spc units between adjacent
|
||||||
|
// words on each line, and attempting to limit lines to lim units. Raggedness
|
||||||
|
// is the total error over all lines, where error is the square of the
|
||||||
|
// difference of the length of the line and lim. Too-long lines (which only
|
||||||
|
// happen when a single word is longer than lim units) have pen penalty units
|
||||||
|
// added to the error.
|
||||||
|
func WrapWords(words []string, spc, lim, pen int) [][]string {
|
||||||
|
n := len(words)
|
||||||
|
|
||||||
|
length := make([][]int, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
length[i] = make([]int, n)
|
||||||
|
length[i][i] = runewidth.StringWidth(words[i])
|
||||||
|
for j := i + 1; j < n; j++ {
|
||||||
|
length[i][j] = length[i][j-1] + spc + runewidth.StringWidth(words[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nbrk := make([]int, n)
|
||||||
|
cost := make([]int, n)
|
||||||
|
for i := range cost {
|
||||||
|
cost[i] = math.MaxInt32
|
||||||
|
}
|
||||||
|
for i := n - 1; i >= 0; i-- {
|
||||||
|
if length[i][n-1] <= lim {
|
||||||
|
cost[i] = 0
|
||||||
|
nbrk[i] = n
|
||||||
|
} else {
|
||||||
|
for j := i + 1; j < n; j++ {
|
||||||
|
d := lim - length[i][j-1]
|
||||||
|
c := d*d + cost[j]
|
||||||
|
if length[i][j-1] > lim {
|
||||||
|
c += pen // too-long lines get a worse penalty
|
||||||
|
}
|
||||||
|
if c < cost[i] {
|
||||||
|
cost[i] = c
|
||||||
|
nbrk[i] = j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var lines [][]string
|
||||||
|
i := 0
|
||||||
|
for i < n {
|
||||||
|
lines = append(lines, words[i:nbrk[i]])
|
||||||
|
i = nbrk[i]
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLines decomposes a multiline string into a slice of strings.
|
||||||
|
func getLines(s string) []string {
|
||||||
|
return strings.Split(s, nl)
|
||||||
|
}
|
||||||
27
tests/metrics/cmd/checkmetrics/vendor/github.com/pmezard/go-difflib/LICENSE
generated
vendored
Normal file
27
tests/metrics/cmd/checkmetrics/vendor/github.com/pmezard/go-difflib/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2013, Patrick Mezard
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
The names of its contributors may not be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
772
tests/metrics/cmd/checkmetrics/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
generated
vendored
Normal file
772
tests/metrics/cmd/checkmetrics/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
generated
vendored
Normal file
@@ -0,0 +1,772 @@
|
|||||||
|
// Package difflib is a partial port of Python difflib module.
|
||||||
|
//
|
||||||
|
// It provides tools to compare sequences of strings and generate textual diffs.
|
||||||
|
//
|
||||||
|
// The following class and functions have been ported:
|
||||||
|
//
|
||||||
|
// - SequenceMatcher
|
||||||
|
//
|
||||||
|
// - unified_diff
|
||||||
|
//
|
||||||
|
// - context_diff
|
||||||
|
//
|
||||||
|
// Getting unified diffs was the main goal of the port. Keep in mind this code
|
||||||
|
// is mostly suitable to output text differences in a human friendly way, there
|
||||||
|
// are no guarantees generated diffs are consumable by patch(1).
|
||||||
|
package difflib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func max(a, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateRatio(matches, length int) float64 {
|
||||||
|
if length > 0 {
|
||||||
|
return 2.0 * float64(matches) / float64(length)
|
||||||
|
}
|
||||||
|
return 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Match struct {
|
||||||
|
A int
|
||||||
|
B int
|
||||||
|
Size int
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpCode struct {
|
||||||
|
Tag byte
|
||||||
|
I1 int
|
||||||
|
I2 int
|
||||||
|
J1 int
|
||||||
|
J2 int
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceMatcher compares sequence of strings. The basic
|
||||||
|
// algorithm predates, and is a little fancier than, an algorithm
|
||||||
|
// published in the late 1980's by Ratcliff and Obershelp under the
|
||||||
|
// hyperbolic name "gestalt pattern matching". The basic idea is to find
|
||||||
|
// the longest contiguous matching subsequence that contains no "junk"
|
||||||
|
// elements (R-O doesn't address junk). The same idea is then applied
|
||||||
|
// recursively to the pieces of the sequences to the left and to the right
|
||||||
|
// of the matching subsequence. This does not yield minimal edit
|
||||||
|
// sequences, but does tend to yield matches that "look right" to people.
|
||||||
|
//
|
||||||
|
// SequenceMatcher tries to compute a "human-friendly diff" between two
|
||||||
|
// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
|
||||||
|
// longest *contiguous* & junk-free matching subsequence. That's what
|
||||||
|
// catches peoples' eyes. The Windows(tm) windiff has another interesting
|
||||||
|
// notion, pairing up elements that appear uniquely in each sequence.
|
||||||
|
// That, and the method here, appear to yield more intuitive difference
|
||||||
|
// reports than does diff. This method appears to be the least vulnerable
|
||||||
|
// to synching up on blocks of "junk lines", though (like blank lines in
|
||||||
|
// ordinary text files, or maybe "<P>" lines in HTML files). That may be
|
||||||
|
// because this is the only method of the 3 that has a *concept* of
|
||||||
|
// "junk" <wink>.
|
||||||
|
//
|
||||||
|
// Timing: Basic R-O is cubic time worst case and quadratic time expected
|
||||||
|
// case. SequenceMatcher is quadratic time for the worst case and has
|
||||||
|
// expected-case behavior dependent in a complicated way on how many
|
||||||
|
// elements the sequences have in common; best case time is linear.
|
||||||
|
type SequenceMatcher struct {
|
||||||
|
a []string
|
||||||
|
b []string
|
||||||
|
b2j map[string][]int
|
||||||
|
IsJunk func(string) bool
|
||||||
|
autoJunk bool
|
||||||
|
bJunk map[string]struct{}
|
||||||
|
matchingBlocks []Match
|
||||||
|
fullBCount map[string]int
|
||||||
|
bPopular map[string]struct{}
|
||||||
|
opCodes []OpCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMatcher(a, b []string) *SequenceMatcher {
|
||||||
|
m := SequenceMatcher{autoJunk: true}
|
||||||
|
m.SetSeqs(a, b)
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMatcherWithJunk(a, b []string, autoJunk bool,
|
||||||
|
isJunk func(string) bool) *SequenceMatcher {
|
||||||
|
|
||||||
|
m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
|
||||||
|
m.SetSeqs(a, b)
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set two sequences to be compared.
|
||||||
|
func (m *SequenceMatcher) SetSeqs(a, b []string) {
|
||||||
|
m.SetSeq1(a)
|
||||||
|
m.SetSeq2(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the first sequence to be compared. The second sequence to be compared is
|
||||||
|
// not changed.
|
||||||
|
//
|
||||||
|
// SequenceMatcher computes and caches detailed information about the second
|
||||||
|
// sequence, so if you want to compare one sequence S against many sequences,
|
||||||
|
// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
|
||||||
|
// sequences.
|
||||||
|
//
|
||||||
|
// See also SetSeqs() and SetSeq2().
|
||||||
|
func (m *SequenceMatcher) SetSeq1(a []string) {
|
||||||
|
if &a == &m.a {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.a = a
|
||||||
|
m.matchingBlocks = nil
|
||||||
|
m.opCodes = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the second sequence to be compared. The first sequence to be compared is
|
||||||
|
// not changed.
|
||||||
|
func (m *SequenceMatcher) SetSeq2(b []string) {
|
||||||
|
if &b == &m.b {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.b = b
|
||||||
|
m.matchingBlocks = nil
|
||||||
|
m.opCodes = nil
|
||||||
|
m.fullBCount = nil
|
||||||
|
m.chainB()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SequenceMatcher) chainB() {
|
||||||
|
// Populate line -> index mapping
|
||||||
|
b2j := map[string][]int{}
|
||||||
|
for i, s := range m.b {
|
||||||
|
indices := b2j[s]
|
||||||
|
indices = append(indices, i)
|
||||||
|
b2j[s] = indices
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge junk elements
|
||||||
|
m.bJunk = map[string]struct{}{}
|
||||||
|
if m.IsJunk != nil {
|
||||||
|
junk := m.bJunk
|
||||||
|
for s, _ := range b2j {
|
||||||
|
if m.IsJunk(s) {
|
||||||
|
junk[s] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for s, _ := range junk {
|
||||||
|
delete(b2j, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge remaining popular elements
|
||||||
|
popular := map[string]struct{}{}
|
||||||
|
n := len(m.b)
|
||||||
|
if m.autoJunk && n >= 200 {
|
||||||
|
ntest := n/100 + 1
|
||||||
|
for s, indices := range b2j {
|
||||||
|
if len(indices) > ntest {
|
||||||
|
popular[s] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for s, _ := range popular {
|
||||||
|
delete(b2j, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.bPopular = popular
|
||||||
|
m.b2j = b2j
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SequenceMatcher) isBJunk(s string) bool {
|
||||||
|
_, ok := m.bJunk[s]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find longest matching block in a[alo:ahi] and b[blo:bhi].
|
||||||
|
//
|
||||||
|
// If IsJunk is not defined:
|
||||||
|
//
|
||||||
|
// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
|
||||||
|
// alo <= i <= i+k <= ahi
|
||||||
|
// blo <= j <= j+k <= bhi
|
||||||
|
// and for all (i',j',k') meeting those conditions,
|
||||||
|
// k >= k'
|
||||||
|
// i <= i'
|
||||||
|
// and if i == i', j <= j'
|
||||||
|
//
|
||||||
|
// In other words, of all maximal matching blocks, return one that
|
||||||
|
// starts earliest in a, and of all those maximal matching blocks that
|
||||||
|
// start earliest in a, return the one that starts earliest in b.
|
||||||
|
//
|
||||||
|
// If IsJunk is defined, first the longest matching block is
|
||||||
|
// determined as above, but with the additional restriction that no
|
||||||
|
// junk element appears in the block. Then that block is extended as
|
||||||
|
// far as possible by matching (only) junk elements on both sides. So
|
||||||
|
// the resulting block never matches on junk except as identical junk
|
||||||
|
// happens to be adjacent to an "interesting" match.
|
||||||
|
//
|
||||||
|
// If no blocks match, return (alo, blo, 0).
|
||||||
|
func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
|
||||||
|
// CAUTION: stripping common prefix or suffix would be incorrect.
|
||||||
|
// E.g.,
|
||||||
|
// ab
|
||||||
|
// acab
|
||||||
|
// Longest matching block is "ab", but if common prefix is
|
||||||
|
// stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
|
||||||
|
// strip, so ends up claiming that ab is changed to acab by
|
||||||
|
// inserting "ca" in the middle. That's minimal but unintuitive:
|
||||||
|
// "it's obvious" that someone inserted "ac" at the front.
|
||||||
|
// Windiff ends up at the same place as diff, but by pairing up
|
||||||
|
// the unique 'b's and then matching the first two 'a's.
|
||||||
|
besti, bestj, bestsize := alo, blo, 0
|
||||||
|
|
||||||
|
// find longest junk-free match
|
||||||
|
// during an iteration of the loop, j2len[j] = length of longest
|
||||||
|
// junk-free match ending with a[i-1] and b[j]
|
||||||
|
j2len := map[int]int{}
|
||||||
|
for i := alo; i != ahi; i++ {
|
||||||
|
// look at all instances of a[i] in b; note that because
|
||||||
|
// b2j has no junk keys, the loop is skipped if a[i] is junk
|
||||||
|
newj2len := map[int]int{}
|
||||||
|
for _, j := range m.b2j[m.a[i]] {
|
||||||
|
// a[i] matches b[j]
|
||||||
|
if j < blo {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if j >= bhi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
k := j2len[j-1] + 1
|
||||||
|
newj2len[j] = k
|
||||||
|
if k > bestsize {
|
||||||
|
besti, bestj, bestsize = i-k+1, j-k+1, k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j2len = newj2len
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the best by non-junk elements on each end. In particular,
|
||||||
|
// "popular" non-junk elements aren't in b2j, which greatly speeds
|
||||||
|
// the inner loop above, but also means "the best" match so far
|
||||||
|
// doesn't contain any junk *or* popular non-junk elements.
|
||||||
|
for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
|
||||||
|
m.a[besti-1] == m.b[bestj-1] {
|
||||||
|
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
|
||||||
|
}
|
||||||
|
for besti+bestsize < ahi && bestj+bestsize < bhi &&
|
||||||
|
!m.isBJunk(m.b[bestj+bestsize]) &&
|
||||||
|
m.a[besti+bestsize] == m.b[bestj+bestsize] {
|
||||||
|
bestsize += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we have a wholly interesting match (albeit possibly
|
||||||
|
// empty!), we may as well suck up the matching junk on each
|
||||||
|
// side of it too. Can't think of a good reason not to, and it
|
||||||
|
// saves post-processing the (possibly considerable) expense of
|
||||||
|
// figuring out what to do with it. In the case of an empty
|
||||||
|
// interesting match, this is clearly the right thing to do,
|
||||||
|
// because no other kind of match is possible in the regions.
|
||||||
|
for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
|
||||||
|
m.a[besti-1] == m.b[bestj-1] {
|
||||||
|
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
|
||||||
|
}
|
||||||
|
for besti+bestsize < ahi && bestj+bestsize < bhi &&
|
||||||
|
m.isBJunk(m.b[bestj+bestsize]) &&
|
||||||
|
m.a[besti+bestsize] == m.b[bestj+bestsize] {
|
||||||
|
bestsize += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return Match{A: besti, B: bestj, Size: bestsize}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return list of triples describing matching subsequences.
|
||||||
|
//
|
||||||
|
// Each triple is of the form (i, j, n), and means that
|
||||||
|
// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
|
||||||
|
// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
|
||||||
|
// adjacent triples in the list, and the second is not the last triple in the
|
||||||
|
// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
|
||||||
|
// adjacent equal blocks.
|
||||||
|
//
|
||||||
|
// The last triple is a dummy, (len(a), len(b), 0), and is the only
|
||||||
|
// triple with n==0.
|
||||||
|
func (m *SequenceMatcher) GetMatchingBlocks() []Match {
|
||||||
|
if m.matchingBlocks != nil {
|
||||||
|
return m.matchingBlocks
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
|
||||||
|
matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
|
||||||
|
match := m.findLongestMatch(alo, ahi, blo, bhi)
|
||||||
|
i, j, k := match.A, match.B, match.Size
|
||||||
|
if match.Size > 0 {
|
||||||
|
if alo < i && blo < j {
|
||||||
|
matched = matchBlocks(alo, i, blo, j, matched)
|
||||||
|
}
|
||||||
|
matched = append(matched, match)
|
||||||
|
if i+k < ahi && j+k < bhi {
|
||||||
|
matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matched
|
||||||
|
}
|
||||||
|
matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
|
||||||
|
|
||||||
|
// It's possible that we have adjacent equal blocks in the
|
||||||
|
// matching_blocks list now.
|
||||||
|
nonAdjacent := []Match{}
|
||||||
|
i1, j1, k1 := 0, 0, 0
|
||||||
|
for _, b := range matched {
|
||||||
|
// Is this block adjacent to i1, j1, k1?
|
||||||
|
i2, j2, k2 := b.A, b.B, b.Size
|
||||||
|
if i1+k1 == i2 && j1+k1 == j2 {
|
||||||
|
// Yes, so collapse them -- this just increases the length of
|
||||||
|
// the first block by the length of the second, and the first
|
||||||
|
// block so lengthened remains the block to compare against.
|
||||||
|
k1 += k2
|
||||||
|
} else {
|
||||||
|
// Not adjacent. Remember the first block (k1==0 means it's
|
||||||
|
// the dummy we started with), and make the second block the
|
||||||
|
// new block to compare against.
|
||||||
|
if k1 > 0 {
|
||||||
|
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
|
||||||
|
}
|
||||||
|
i1, j1, k1 = i2, j2, k2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if k1 > 0 {
|
||||||
|
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
|
||||||
|
}
|
||||||
|
|
||||||
|
nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
|
||||||
|
m.matchingBlocks = nonAdjacent
|
||||||
|
return m.matchingBlocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return list of 5-tuples describing how to turn a into b.
|
||||||
|
//
|
||||||
|
// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
|
||||||
|
// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
|
||||||
|
// tuple preceding it, and likewise for j1 == the previous j2.
|
||||||
|
//
|
||||||
|
// The tags are characters, with these meanings:
|
||||||
|
//
|
||||||
|
// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
|
||||||
|
//
|
||||||
|
// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
|
||||||
|
//
|
||||||
|
// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
|
||||||
|
//
|
||||||
|
// 'e' (equal): a[i1:i2] == b[j1:j2]
|
||||||
|
func (m *SequenceMatcher) GetOpCodes() []OpCode {
|
||||||
|
if m.opCodes != nil {
|
||||||
|
return m.opCodes
|
||||||
|
}
|
||||||
|
i, j := 0, 0
|
||||||
|
matching := m.GetMatchingBlocks()
|
||||||
|
opCodes := make([]OpCode, 0, len(matching))
|
||||||
|
for _, m := range matching {
|
||||||
|
// invariant: we've pumped out correct diffs to change
|
||||||
|
// a[:i] into b[:j], and the next matching block is
|
||||||
|
// a[ai:ai+size] == b[bj:bj+size]. So we need to pump
|
||||||
|
// out a diff to change a[i:ai] into b[j:bj], pump out
|
||||||
|
// the matching block, and move (i,j) beyond the match
|
||||||
|
ai, bj, size := m.A, m.B, m.Size
|
||||||
|
tag := byte(0)
|
||||||
|
if i < ai && j < bj {
|
||||||
|
tag = 'r'
|
||||||
|
} else if i < ai {
|
||||||
|
tag = 'd'
|
||||||
|
} else if j < bj {
|
||||||
|
tag = 'i'
|
||||||
|
}
|
||||||
|
if tag > 0 {
|
||||||
|
opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
|
||||||
|
}
|
||||||
|
i, j = ai+size, bj+size
|
||||||
|
// the list of matching blocks is terminated by a
|
||||||
|
// sentinel with size 0
|
||||||
|
if size > 0 {
|
||||||
|
opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.opCodes = opCodes
|
||||||
|
return m.opCodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Isolate change clusters by eliminating ranges with no changes.
|
||||||
|
//
|
||||||
|
// Return a generator of groups with up to n lines of context.
|
||||||
|
// Each group is in the same format as returned by GetOpCodes().
|
||||||
|
func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
|
||||||
|
if n < 0 {
|
||||||
|
n = 3
|
||||||
|
}
|
||||||
|
codes := m.GetOpCodes()
|
||||||
|
if len(codes) == 0 {
|
||||||
|
codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
|
||||||
|
}
|
||||||
|
// Fixup leading and trailing groups if they show no changes.
|
||||||
|
if codes[0].Tag == 'e' {
|
||||||
|
c := codes[0]
|
||||||
|
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||||
|
codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
|
||||||
|
}
|
||||||
|
if codes[len(codes)-1].Tag == 'e' {
|
||||||
|
c := codes[len(codes)-1]
|
||||||
|
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||||
|
codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
|
||||||
|
}
|
||||||
|
nn := n + n
|
||||||
|
groups := [][]OpCode{}
|
||||||
|
group := []OpCode{}
|
||||||
|
for _, c := range codes {
|
||||||
|
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||||
|
// End the current group and start a new one whenever
|
||||||
|
// there is a large range with no changes.
|
||||||
|
if c.Tag == 'e' && i2-i1 > nn {
|
||||||
|
group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
|
||||||
|
j1, min(j2, j1+n)})
|
||||||
|
groups = append(groups, group)
|
||||||
|
group = []OpCode{}
|
||||||
|
i1, j1 = max(i1, i2-n), max(j1, j2-n)
|
||||||
|
}
|
||||||
|
group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
|
||||||
|
}
|
||||||
|
if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
|
||||||
|
groups = append(groups, group)
|
||||||
|
}
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a measure of the sequences' similarity (float in [0,1]).
|
||||||
|
//
|
||||||
|
// Where T is the total number of elements in both sequences, and
|
||||||
|
// M is the number of matches, this is 2.0*M / T.
|
||||||
|
// Note that this is 1 if the sequences are identical, and 0 if
|
||||||
|
// they have nothing in common.
|
||||||
|
//
|
||||||
|
// .Ratio() is expensive to compute if you haven't already computed
|
||||||
|
// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
|
||||||
|
// want to try .QuickRatio() or .RealQuickRation() first to get an
|
||||||
|
// upper bound.
|
||||||
|
func (m *SequenceMatcher) Ratio() float64 {
|
||||||
|
matches := 0
|
||||||
|
for _, m := range m.GetMatchingBlocks() {
|
||||||
|
matches += m.Size
|
||||||
|
}
|
||||||
|
return calculateRatio(matches, len(m.a)+len(m.b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an upper bound on ratio() relatively quickly.
|
||||||
|
//
|
||||||
|
// This isn't defined beyond that it is an upper bound on .Ratio(), and
|
||||||
|
// is faster to compute.
|
||||||
|
func (m *SequenceMatcher) QuickRatio() float64 {
|
||||||
|
// viewing a and b as multisets, set matches to the cardinality
|
||||||
|
// of their intersection; this counts the number of matches
|
||||||
|
// without regard to order, so is clearly an upper bound
|
||||||
|
if m.fullBCount == nil {
|
||||||
|
m.fullBCount = map[string]int{}
|
||||||
|
for _, s := range m.b {
|
||||||
|
m.fullBCount[s] = m.fullBCount[s] + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// avail[x] is the number of times x appears in 'b' less the
|
||||||
|
// number of times we've seen it in 'a' so far ... kinda
|
||||||
|
avail := map[string]int{}
|
||||||
|
matches := 0
|
||||||
|
for _, s := range m.a {
|
||||||
|
n, ok := avail[s]
|
||||||
|
if !ok {
|
||||||
|
n = m.fullBCount[s]
|
||||||
|
}
|
||||||
|
avail[s] = n - 1
|
||||||
|
if n > 0 {
|
||||||
|
matches += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return calculateRatio(matches, len(m.a)+len(m.b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an upper bound on ratio() very quickly.
|
||||||
|
//
|
||||||
|
// This isn't defined beyond that it is an upper bound on .Ratio(), and
|
||||||
|
// is faster to compute than either .Ratio() or .QuickRatio().
|
||||||
|
func (m *SequenceMatcher) RealQuickRatio() float64 {
|
||||||
|
la, lb := len(m.a), len(m.b)
|
||||||
|
return calculateRatio(min(la, lb), la+lb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert range to the "ed" format
|
||||||
|
func formatRangeUnified(start, stop int) string {
|
||||||
|
// Per the diff spec at http://www.unix.org/single_unix_specification/
|
||||||
|
beginning := start + 1 // lines start numbering with one
|
||||||
|
length := stop - start
|
||||||
|
if length == 1 {
|
||||||
|
return fmt.Sprintf("%d", beginning)
|
||||||
|
}
|
||||||
|
if length == 0 {
|
||||||
|
beginning -= 1 // empty ranges begin at line just before the range
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d,%d", beginning, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unified diff parameters
|
||||||
|
type UnifiedDiff struct {
|
||||||
|
A []string // First sequence lines
|
||||||
|
FromFile string // First file name
|
||||||
|
FromDate string // First file time
|
||||||
|
B []string // Second sequence lines
|
||||||
|
ToFile string // Second file name
|
||||||
|
ToDate string // Second file time
|
||||||
|
Eol string // Headers end of line, defaults to LF
|
||||||
|
Context int // Number of context lines
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare two sequences of lines; generate the delta as a unified diff.
|
||||||
|
//
|
||||||
|
// Unified diffs are a compact way of showing line changes and a few
|
||||||
|
// lines of context. The number of context lines is set by 'n' which
|
||||||
|
// defaults to three.
|
||||||
|
//
|
||||||
|
// By default, the diff control lines (those with ---, +++, or @@) are
|
||||||
|
// created with a trailing newline. This is helpful so that inputs
|
||||||
|
// created from file.readlines() result in diffs that are suitable for
|
||||||
|
// file.writelines() since both the inputs and outputs have trailing
|
||||||
|
// newlines.
|
||||||
|
//
|
||||||
|
// For inputs that do not have trailing newlines, set the lineterm
|
||||||
|
// argument to "" so that the output will be uniformly newline free.
|
||||||
|
//
|
||||||
|
// The unidiff format normally has a header for filenames and modification
|
||||||
|
// times. Any or all of these may be specified using strings for
|
||||||
|
// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
|
||||||
|
// The modification times are normally expressed in the ISO 8601 format.
|
||||||
|
func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
|
||||||
|
buf := bufio.NewWriter(writer)
|
||||||
|
defer buf.Flush()
|
||||||
|
wf := func(format string, args ...interface{}) error {
|
||||||
|
_, err := buf.WriteString(fmt.Sprintf(format, args...))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ws := func(s string) error {
|
||||||
|
_, err := buf.WriteString(s)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(diff.Eol) == 0 {
|
||||||
|
diff.Eol = "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
started := false
|
||||||
|
m := NewMatcher(diff.A, diff.B)
|
||||||
|
for _, g := range m.GetGroupedOpCodes(diff.Context) {
|
||||||
|
if !started {
|
||||||
|
started = true
|
||||||
|
fromDate := ""
|
||||||
|
if len(diff.FromDate) > 0 {
|
||||||
|
fromDate = "\t" + diff.FromDate
|
||||||
|
}
|
||||||
|
toDate := ""
|
||||||
|
if len(diff.ToDate) > 0 {
|
||||||
|
toDate = "\t" + diff.ToDate
|
||||||
|
}
|
||||||
|
if diff.FromFile != "" || diff.ToFile != "" {
|
||||||
|
err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
first, last := g[0], g[len(g)-1]
|
||||||
|
range1 := formatRangeUnified(first.I1, last.I2)
|
||||||
|
range2 := formatRangeUnified(first.J1, last.J2)
|
||||||
|
if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, c := range g {
|
||||||
|
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||||
|
if c.Tag == 'e' {
|
||||||
|
for _, line := range diff.A[i1:i2] {
|
||||||
|
if err := ws(" " + line); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c.Tag == 'r' || c.Tag == 'd' {
|
||||||
|
for _, line := range diff.A[i1:i2] {
|
||||||
|
if err := ws("-" + line); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.Tag == 'r' || c.Tag == 'i' {
|
||||||
|
for _, line := range diff.B[j1:j2] {
|
||||||
|
if err := ws("+" + line); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like WriteUnifiedDiff but returns the diff a string.
|
||||||
|
func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
|
||||||
|
w := &bytes.Buffer{}
|
||||||
|
err := WriteUnifiedDiff(w, diff)
|
||||||
|
return string(w.Bytes()), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert range to the "ed" format.
|
||||||
|
func formatRangeContext(start, stop int) string {
|
||||||
|
// Per the diff spec at http://www.unix.org/single_unix_specification/
|
||||||
|
beginning := start + 1 // lines start numbering with one
|
||||||
|
length := stop - start
|
||||||
|
if length == 0 {
|
||||||
|
beginning -= 1 // empty ranges begin at line just before the range
|
||||||
|
}
|
||||||
|
if length <= 1 {
|
||||||
|
return fmt.Sprintf("%d", beginning)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContextDiff UnifiedDiff
|
||||||
|
|
||||||
|
// Compare two sequences of lines; generate the delta as a context diff.
|
||||||
|
//
|
||||||
|
// Context diffs are a compact way of showing line changes and a few
|
||||||
|
// lines of context. The number of context lines is set by diff.Context
|
||||||
|
// which defaults to three.
|
||||||
|
//
|
||||||
|
// By default, the diff control lines (those with *** or ---) are
|
||||||
|
// created with a trailing newline.
|
||||||
|
//
|
||||||
|
// For inputs that do not have trailing newlines, set the diff.Eol
|
||||||
|
// argument to "" so that the output will be uniformly newline free.
|
||||||
|
//
|
||||||
|
// The context diff format normally has a header for filenames and
|
||||||
|
// modification times. Any or all of these may be specified using
|
||||||
|
// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
|
||||||
|
// The modification times are normally expressed in the ISO 8601 format.
|
||||||
|
// If not specified, the strings default to blanks.
|
||||||
|
func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
|
||||||
|
buf := bufio.NewWriter(writer)
|
||||||
|
defer buf.Flush()
|
||||||
|
var diffErr error
|
||||||
|
wf := func(format string, args ...interface{}) {
|
||||||
|
_, err := buf.WriteString(fmt.Sprintf(format, args...))
|
||||||
|
if diffErr == nil && err != nil {
|
||||||
|
diffErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ws := func(s string) {
|
||||||
|
_, err := buf.WriteString(s)
|
||||||
|
if diffErr == nil && err != nil {
|
||||||
|
diffErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(diff.Eol) == 0 {
|
||||||
|
diff.Eol = "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := map[byte]string{
|
||||||
|
'i': "+ ",
|
||||||
|
'd': "- ",
|
||||||
|
'r': "! ",
|
||||||
|
'e': " ",
|
||||||
|
}
|
||||||
|
|
||||||
|
started := false
|
||||||
|
m := NewMatcher(diff.A, diff.B)
|
||||||
|
for _, g := range m.GetGroupedOpCodes(diff.Context) {
|
||||||
|
if !started {
|
||||||
|
started = true
|
||||||
|
fromDate := ""
|
||||||
|
if len(diff.FromDate) > 0 {
|
||||||
|
fromDate = "\t" + diff.FromDate
|
||||||
|
}
|
||||||
|
toDate := ""
|
||||||
|
if len(diff.ToDate) > 0 {
|
||||||
|
toDate = "\t" + diff.ToDate
|
||||||
|
}
|
||||||
|
if diff.FromFile != "" || diff.ToFile != "" {
|
||||||
|
wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
||||||
|
wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
first, last := g[0], g[len(g)-1]
|
||||||
|
ws("***************" + diff.Eol)
|
||||||
|
|
||||||
|
range1 := formatRangeContext(first.I1, last.I2)
|
||||||
|
wf("*** %s ****%s", range1, diff.Eol)
|
||||||
|
for _, c := range g {
|
||||||
|
if c.Tag == 'r' || c.Tag == 'd' {
|
||||||
|
for _, cc := range g {
|
||||||
|
if cc.Tag == 'i' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, line := range diff.A[cc.I1:cc.I2] {
|
||||||
|
ws(prefix[cc.Tag] + line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
range2 := formatRangeContext(first.J1, last.J2)
|
||||||
|
wf("--- %s ----%s", range2, diff.Eol)
|
||||||
|
for _, c := range g {
|
||||||
|
if c.Tag == 'r' || c.Tag == 'i' {
|
||||||
|
for _, cc := range g {
|
||||||
|
if cc.Tag == 'd' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, line := range diff.B[cc.J1:cc.J2] {
|
||||||
|
ws(prefix[cc.Tag] + line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diffErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like WriteContextDiff but returns the diff a string.
|
||||||
|
func GetContextDiffString(diff ContextDiff) (string, error) {
|
||||||
|
w := &bytes.Buffer{}
|
||||||
|
err := WriteContextDiff(w, diff)
|
||||||
|
return string(w.Bytes()), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split a string on "\n" while preserving them. The output can be used
|
||||||
|
// as input for UnifiedDiff and ContextDiff structures.
|
||||||
|
func SplitLines(s string) []string {
|
||||||
|
lines := strings.SplitAfter(s, "\n")
|
||||||
|
lines[len(lines)-1] += "\n"
|
||||||
|
return lines
|
||||||
|
}
|
||||||
8
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/.gitignore
generated
vendored
Normal file
8
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
*.out
|
||||||
|
*.swp
|
||||||
|
*.8
|
||||||
|
*.6
|
||||||
|
_obj
|
||||||
|
_test*
|
||||||
|
markdown
|
||||||
|
tags
|
||||||
17
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/.travis.yml
generated
vendored
Normal file
17
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- "1.10.x"
|
||||||
|
- "1.11.x"
|
||||||
|
- tip
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
install:
|
||||||
|
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
|
||||||
|
script:
|
||||||
|
- go get -t -v ./...
|
||||||
|
- diff -u <(echo -n) <(gofmt -d -s .)
|
||||||
|
- go tool vet .
|
||||||
|
- go test -v ./...
|
||||||
29
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/LICENSE.txt
generated
vendored
Normal file
29
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
Blackfriday is distributed under the Simplified BSD License:
|
||||||
|
|
||||||
|
> Copyright © 2011 Russ Ross
|
||||||
|
> All rights reserved.
|
||||||
|
>
|
||||||
|
> Redistribution and use in source and binary forms, with or without
|
||||||
|
> modification, are permitted provided that the following conditions
|
||||||
|
> are met:
|
||||||
|
>
|
||||||
|
> 1. Redistributions of source code must retain the above copyright
|
||||||
|
> notice, this list of conditions and the following disclaimer.
|
||||||
|
>
|
||||||
|
> 2. Redistributions in binary form must reproduce the above
|
||||||
|
> copyright notice, this list of conditions and the following
|
||||||
|
> disclaimer in the documentation and/or other materials provided with
|
||||||
|
> the distribution.
|
||||||
|
>
|
||||||
|
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
> "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
> LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
> FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
> COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
> INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
> BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
> LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
> LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
> ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
> POSSIBILITY OF SUCH DAMAGE.
|
||||||
335
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/README.md
generated
vendored
Normal file
335
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/README.md
generated
vendored
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
Blackfriday
|
||||||
|
[![Build Status][BuildV2SVG]][BuildV2URL]
|
||||||
|
[![PkgGoDev][PkgGoDevV2SVG]][PkgGoDevV2URL]
|
||||||
|
===========
|
||||||
|
|
||||||
|
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
|
||||||
|
is paranoid about its input (so you can safely feed it user-supplied
|
||||||
|
data), it is fast, it supports common extensions (tables, smart
|
||||||
|
punctuation substitutions, etc.), and it is safe for all utf-8
|
||||||
|
(unicode) input.
|
||||||
|
|
||||||
|
HTML output is currently supported, along with Smartypants
|
||||||
|
extensions.
|
||||||
|
|
||||||
|
It started as a translation from C of [Sundown][3].
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Blackfriday is compatible with modern Go releases in module mode.
|
||||||
|
With Go installed:
|
||||||
|
|
||||||
|
go get github.com/russross/blackfriday/v2
|
||||||
|
|
||||||
|
will resolve and add the package to the current development module,
|
||||||
|
then build and install it. Alternatively, you can achieve the same
|
||||||
|
if you import it in a package:
|
||||||
|
|
||||||
|
import "github.com/russross/blackfriday/v2"
|
||||||
|
|
||||||
|
and `go get` without parameters.
|
||||||
|
|
||||||
|
Legacy GOPATH mode is unsupported.
|
||||||
|
|
||||||
|
|
||||||
|
Versions
|
||||||
|
--------
|
||||||
|
|
||||||
|
Currently maintained and recommended version of Blackfriday is `v2`. It's being
|
||||||
|
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
|
||||||
|
documentation is available at
|
||||||
|
https://pkg.go.dev/github.com/russross/blackfriday/v2.
|
||||||
|
|
||||||
|
It is `go get`-able in module mode at `github.com/russross/blackfriday/v2`.
|
||||||
|
|
||||||
|
Version 2 offers a number of improvements over v1:
|
||||||
|
|
||||||
|
* Cleaned up API
|
||||||
|
* A separate call to [`Parse`][4], which produces an abstract syntax tree for
|
||||||
|
the document
|
||||||
|
* Latest bug fixes
|
||||||
|
* Flexibility to easily add your own rendering extensions
|
||||||
|
|
||||||
|
Potential drawbacks:
|
||||||
|
|
||||||
|
* Our benchmarks show v2 to be slightly slower than v1. Currently in the
|
||||||
|
ballpark of around 15%.
|
||||||
|
* API breakage. If you can't afford modifying your code to adhere to the new API
|
||||||
|
and don't care too much about the new features, v2 is probably not for you.
|
||||||
|
* Several bug fixes are trailing behind and still need to be forward-ported to
|
||||||
|
v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
|
||||||
|
tracking.
|
||||||
|
|
||||||
|
If you are still interested in the legacy `v1`, you can import it from
|
||||||
|
`github.com/russross/blackfriday`. Documentation for the legacy v1 can be found
|
||||||
|
here: https://pkg.go.dev/github.com/russross/blackfriday.
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
For the most sensible markdown processing, it is as simple as getting your input
|
||||||
|
into a byte slice and calling:
|
||||||
|
|
||||||
|
```go
|
||||||
|
output := blackfriday.Run(input)
|
||||||
|
```
|
||||||
|
|
||||||
|
Your input will be parsed and the output rendered with a set of most popular
|
||||||
|
extensions enabled. If you want the most basic feature set, corresponding with
|
||||||
|
the bare Markdown specification, use:
|
||||||
|
|
||||||
|
```go
|
||||||
|
output := blackfriday.Run(input, blackfriday.WithNoExtensions())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sanitize untrusted content
|
||||||
|
|
||||||
|
Blackfriday itself does nothing to protect against malicious content. If you are
|
||||||
|
dealing with user-supplied markdown, we recommend running Blackfriday's output
|
||||||
|
through HTML sanitizer such as [Bluemonday][5].
|
||||||
|
|
||||||
|
Here's an example of simple usage of Blackfriday together with Bluemonday:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/microcosm-cc/bluemonday"
|
||||||
|
"github.com/russross/blackfriday/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
unsafe := blackfriday.Run(input)
|
||||||
|
html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom options
|
||||||
|
|
||||||
|
If you want to customize the set of options, use `blackfriday.WithExtensions`,
|
||||||
|
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.
|
||||||
|
|
||||||
|
### `blackfriday-tool`
|
||||||
|
|
||||||
|
You can also check out `blackfriday-tool` for a more complete example
|
||||||
|
of how to use it. Download and install it using:
|
||||||
|
|
||||||
|
go get github.com/russross/blackfriday-tool
|
||||||
|
|
||||||
|
This is a simple command-line tool that allows you to process a
|
||||||
|
markdown file using a standalone program. You can also browse the
|
||||||
|
source directly on github if you are just looking for some example
|
||||||
|
code:
|
||||||
|
|
||||||
|
* <https://github.com/russross/blackfriday-tool>
|
||||||
|
|
||||||
|
Note that if you have not already done so, installing
|
||||||
|
`blackfriday-tool` will be sufficient to download and install
|
||||||
|
blackfriday in addition to the tool itself. The tool binary will be
|
||||||
|
installed in `$GOPATH/bin`. This is a statically-linked binary that
|
||||||
|
can be copied to wherever you need it without worrying about
|
||||||
|
dependencies and library versions.
|
||||||
|
|
||||||
|
### Sanitized anchor names
|
||||||
|
|
||||||
|
Blackfriday includes an algorithm for creating sanitized anchor names
|
||||||
|
corresponding to a given input text. This algorithm is used to create
|
||||||
|
anchors for headings when `AutoHeadingIDs` extension is enabled. The
|
||||||
|
algorithm has a specification, so that other packages can create
|
||||||
|
compatible anchor names and links to those anchors.
|
||||||
|
|
||||||
|
The specification is located at https://pkg.go.dev/github.com/russross/blackfriday/v2#hdr-Sanitized_Anchor_Names.
|
||||||
|
|
||||||
|
[`SanitizedAnchorName`](https://pkg.go.dev/github.com/russross/blackfriday/v2#SanitizedAnchorName) exposes this functionality, and can be used to
|
||||||
|
create compatible links to the anchor names generated by blackfriday.
|
||||||
|
This algorithm is also implemented in a small standalone package at
|
||||||
|
[`github.com/shurcooL/sanitized_anchor_name`](https://pkg.go.dev/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients
|
||||||
|
that want a small package and don't need full functionality of blackfriday.
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
All features of Sundown are supported, including:
|
||||||
|
|
||||||
|
* **Compatibility**. The Markdown v1.0.3 test suite passes with
|
||||||
|
the `--tidy` option. Without `--tidy`, the differences are
|
||||||
|
mostly in whitespace and entity escaping, where blackfriday is
|
||||||
|
more consistent and cleaner.
|
||||||
|
|
||||||
|
* **Common extensions**, including table support, fenced code
|
||||||
|
blocks, autolinks, strikethroughs, non-strict emphasis, etc.
|
||||||
|
|
||||||
|
* **Safety**. Blackfriday is paranoid when parsing, making it safe
|
||||||
|
to feed untrusted user input without fear of bad things
|
||||||
|
happening. The test suite stress tests this and there are no
|
||||||
|
known inputs that make it crash. If you find one, please let me
|
||||||
|
know and send me the input that does it.
|
||||||
|
|
||||||
|
NOTE: "safety" in this context means *runtime safety only*. In order to
|
||||||
|
protect yourself against JavaScript injection in untrusted content, see
|
||||||
|
[this example](https://github.com/russross/blackfriday#sanitize-untrusted-content).
|
||||||
|
|
||||||
|
* **Fast processing**. It is fast enough to render on-demand in
|
||||||
|
most web applications without having to cache the output.
|
||||||
|
|
||||||
|
* **Thread safety**. You can run multiple parsers in different
|
||||||
|
goroutines without ill effect. There is no dependence on global
|
||||||
|
shared state.
|
||||||
|
|
||||||
|
* **Minimal dependencies**. Blackfriday only depends on standard
|
||||||
|
library packages in Go. The source code is pretty
|
||||||
|
self-contained, so it is easy to add to any project, including
|
||||||
|
Google App Engine projects.
|
||||||
|
|
||||||
|
* **Standards compliant**. Output successfully validates using the
|
||||||
|
W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional.
|
||||||
|
|
||||||
|
|
||||||
|
Extensions
|
||||||
|
----------
|
||||||
|
|
||||||
|
In addition to the standard markdown syntax, this package
|
||||||
|
implements the following extensions:
|
||||||
|
|
||||||
|
* **Intra-word emphasis supression**. The `_` character is
|
||||||
|
commonly used inside words when discussing code, so having
|
||||||
|
markdown interpret it as an emphasis command is usually the
|
||||||
|
wrong thing. Blackfriday lets you treat all emphasis markers as
|
||||||
|
normal characters when they occur inside a word.
|
||||||
|
|
||||||
|
* **Tables**. Tables can be created by drawing them in the input
|
||||||
|
using a simple syntax:
|
||||||
|
|
||||||
|
```
|
||||||
|
Name | Age
|
||||||
|
--------|------
|
||||||
|
Bob | 27
|
||||||
|
Alice | 23
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Fenced code blocks**. In addition to the normal 4-space
|
||||||
|
indentation to mark code blocks, you can explicitly mark them
|
||||||
|
and supply a language (to make syntax highlighting simple). Just
|
||||||
|
mark it like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func getTrue() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use 3 or more backticks to mark the beginning of the
|
||||||
|
block, and the same number to mark the end of the block.
|
||||||
|
|
||||||
|
To preserve classes of fenced code blocks while using the bluemonday
|
||||||
|
HTML sanitizer, use the following policy:
|
||||||
|
|
||||||
|
```go
|
||||||
|
p := bluemonday.UGCPolicy()
|
||||||
|
p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")
|
||||||
|
html := p.SanitizeBytes(unsafe)
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Definition lists**. A simple definition list is made of a single-line
|
||||||
|
term followed by a colon and the definition for that term.
|
||||||
|
|
||||||
|
Cat
|
||||||
|
: Fluffy animal everyone likes
|
||||||
|
|
||||||
|
Internet
|
||||||
|
: Vector of transmission for pictures of cats
|
||||||
|
|
||||||
|
Terms must be separated from the previous definition by a blank line.
|
||||||
|
|
||||||
|
* **Footnotes**. A marker in the text that will become a superscript number;
|
||||||
|
a footnote definition that will be placed in a list of footnotes at the
|
||||||
|
end of the document. A footnote looks like this:
|
||||||
|
|
||||||
|
This is a footnote.[^1]
|
||||||
|
|
||||||
|
[^1]: the footnote text.
|
||||||
|
|
||||||
|
* **Autolinking**. Blackfriday can find URLs that have not been
|
||||||
|
explicitly marked as links and turn them into links.
|
||||||
|
|
||||||
|
* **Strikethrough**. Use two tildes (`~~`) to mark text that
|
||||||
|
should be crossed out.
|
||||||
|
|
||||||
|
* **Hard line breaks**. With this extension enabled newlines in the input
|
||||||
|
translate into line breaks in the output. This extension is off by default.
|
||||||
|
|
||||||
|
* **Smart quotes**. Smartypants-style punctuation substitution is
|
||||||
|
supported, turning normal double- and single-quote marks into
|
||||||
|
curly quotes, etc.
|
||||||
|
|
||||||
|
* **LaTeX-style dash parsing** is an additional option, where `--`
|
||||||
|
is translated into `–`, and `---` is translated into
|
||||||
|
`—`. This differs from most smartypants processors, which
|
||||||
|
turn a single hyphen into an ndash and a double hyphen into an
|
||||||
|
mdash.
|
||||||
|
|
||||||
|
* **Smart fractions**, where anything that looks like a fraction
|
||||||
|
is translated into suitable HTML (instead of just a few special
|
||||||
|
cases like most smartypant processors). For example, `4/5`
|
||||||
|
becomes `<sup>4</sup>⁄<sub>5</sub>`, which renders as
|
||||||
|
<sup>4</sup>⁄<sub>5</sub>.
|
||||||
|
|
||||||
|
|
||||||
|
Other renderers
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Blackfriday is structured to allow alternative rendering engines. Here
|
||||||
|
are a few of note:
|
||||||
|
|
||||||
|
* [github_flavored_markdown](https://pkg.go.dev/github.com/shurcooL/github_flavored_markdown):
|
||||||
|
provides a GitHub Flavored Markdown renderer with fenced code block
|
||||||
|
highlighting, clickable heading anchor links.
|
||||||
|
|
||||||
|
It's not customizable, and its goal is to produce HTML output
|
||||||
|
equivalent to the [GitHub Markdown API endpoint](https://developer.github.com/v3/markdown/#render-a-markdown-document-in-raw-mode),
|
||||||
|
except the rendering is performed locally.
|
||||||
|
|
||||||
|
* [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
|
||||||
|
but for markdown.
|
||||||
|
|
||||||
|
* [LaTeX output](https://gitlab.com/ambrevar/blackfriday-latex):
|
||||||
|
renders output as LaTeX.
|
||||||
|
|
||||||
|
* [bfchroma](https://github.com/Depado/bfchroma/): provides convenience
|
||||||
|
integration with the [Chroma](https://github.com/alecthomas/chroma) code
|
||||||
|
highlighting library. bfchroma is only compatible with v2 of Blackfriday and
|
||||||
|
provides a drop-in renderer ready to use with Blackfriday, as well as
|
||||||
|
options and means for further customization.
|
||||||
|
|
||||||
|
* [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.
|
||||||
|
|
||||||
|
* [Blackfriday-Slack](https://github.com/karriereat/blackfriday-slack): converts markdown to slack message style
|
||||||
|
|
||||||
|
|
||||||
|
TODO
|
||||||
|
----
|
||||||
|
|
||||||
|
* More unit testing
|
||||||
|
* Improve Unicode support. It does not understand all Unicode
|
||||||
|
rules (about what constitutes a letter, a punctuation symbol,
|
||||||
|
etc.), so it may fail to detect word boundaries correctly in
|
||||||
|
some instances. It is safe on all UTF-8 input.
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
[Blackfriday is distributed under the Simplified BSD License](LICENSE.txt)
|
||||||
|
|
||||||
|
|
||||||
|
[1]: https://daringfireball.net/projects/markdown/ "Markdown"
|
||||||
|
[2]: https://golang.org/ "Go Language"
|
||||||
|
[3]: https://github.com/vmg/sundown "Sundown"
|
||||||
|
[4]: https://pkg.go.dev/github.com/russross/blackfriday/v2#Parse "Parse func"
|
||||||
|
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
|
||||||
|
|
||||||
|
[BuildV2SVG]: https://travis-ci.org/russross/blackfriday.svg?branch=v2
|
||||||
|
[BuildV2URL]: https://travis-ci.org/russross/blackfriday
|
||||||
|
[PkgGoDevV2SVG]: https://pkg.go.dev/badge/github.com/russross/blackfriday/v2
|
||||||
|
[PkgGoDevV2URL]: https://pkg.go.dev/github.com/russross/blackfriday/v2
|
||||||
1612
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/block.go
generated
vendored
Normal file
1612
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/block.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
46
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/doc.go
generated
vendored
Normal file
46
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/doc.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Package blackfriday is a markdown processor.
|
||||||
|
//
|
||||||
|
// It translates plain text with simple formatting rules into an AST, which can
|
||||||
|
// then be further processed to HTML (provided by Blackfriday itself) or other
|
||||||
|
// formats (provided by the community).
|
||||||
|
//
|
||||||
|
// The simplest way to invoke Blackfriday is to call the Run function. It will
|
||||||
|
// take a text input and produce a text output in HTML (or other format).
|
||||||
|
//
|
||||||
|
// A slightly more sophisticated way to use Blackfriday is to create a Markdown
|
||||||
|
// processor and to call Parse, which returns a syntax tree for the input
|
||||||
|
// document. You can leverage Blackfriday's parsing for content extraction from
|
||||||
|
// markdown documents. You can assign a custom renderer and set various options
|
||||||
|
// to the Markdown processor.
|
||||||
|
//
|
||||||
|
// If you're interested in calling Blackfriday from command line, see
|
||||||
|
// https://github.com/russross/blackfriday-tool.
|
||||||
|
//
|
||||||
|
// Sanitized Anchor Names
|
||||||
|
//
|
||||||
|
// Blackfriday includes an algorithm for creating sanitized anchor names
|
||||||
|
// corresponding to a given input text. This algorithm is used to create
|
||||||
|
// anchors for headings when AutoHeadingIDs extension is enabled. The
|
||||||
|
// algorithm is specified below, so that other packages can create
|
||||||
|
// compatible anchor names and links to those anchors.
|
||||||
|
//
|
||||||
|
// The algorithm iterates over the input text, interpreted as UTF-8,
|
||||||
|
// one Unicode code point (rune) at a time. All runes that are letters (category L)
|
||||||
|
// or numbers (category N) are considered valid characters. They are mapped to
|
||||||
|
// lower case, and included in the output. All other runes are considered
|
||||||
|
// invalid characters. Invalid characters that precede the first valid character,
|
||||||
|
// as well as invalid character that follow the last valid character
|
||||||
|
// are dropped completely. All other sequences of invalid characters
|
||||||
|
// between two valid characters are replaced with a single dash character '-'.
|
||||||
|
//
|
||||||
|
// SanitizedAnchorName exposes this functionality, and can be used to
|
||||||
|
// create compatible links to the anchor names generated by blackfriday.
|
||||||
|
// This algorithm is also implemented in a small standalone package at
|
||||||
|
// github.com/shurcooL/sanitized_anchor_name. It can be useful for clients
|
||||||
|
// that want a small package and don't need full functionality of blackfriday.
|
||||||
|
package blackfriday
|
||||||
|
|
||||||
|
// NOTE: Keep Sanitized Anchor Name algorithm in sync with package
|
||||||
|
// github.com/shurcooL/sanitized_anchor_name.
|
||||||
|
// Otherwise, users of sanitized_anchor_name will get anchor names
|
||||||
|
// that are incompatible with those generated by blackfriday.
|
||||||
2236
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/entities.go
generated
vendored
Normal file
2236
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/entities.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
70
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/esc.go
generated
vendored
Normal file
70
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/esc.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package blackfriday
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var htmlEscaper = [256][]byte{
|
||||||
|
'&': []byte("&"),
|
||||||
|
'<': []byte("<"),
|
||||||
|
'>': []byte(">"),
|
||||||
|
'"': []byte("""),
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeHTML(w io.Writer, s []byte) {
|
||||||
|
escapeEntities(w, s, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeAllHTML(w io.Writer, s []byte) {
|
||||||
|
escapeEntities(w, s, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeEntities(w io.Writer, s []byte, escapeValidEntities bool) {
|
||||||
|
var start, end int
|
||||||
|
for end < len(s) {
|
||||||
|
escSeq := htmlEscaper[s[end]]
|
||||||
|
if escSeq != nil {
|
||||||
|
isEntity, entityEnd := nodeIsEntity(s, end)
|
||||||
|
if isEntity && !escapeValidEntities {
|
||||||
|
w.Write(s[start : entityEnd+1])
|
||||||
|
start = entityEnd + 1
|
||||||
|
} else {
|
||||||
|
w.Write(s[start:end])
|
||||||
|
w.Write(escSeq)
|
||||||
|
start = end + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
if start < len(s) && end <= len(s) {
|
||||||
|
w.Write(s[start:end])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeIsEntity(s []byte, end int) (isEntity bool, endEntityPos int) {
|
||||||
|
isEntity = false
|
||||||
|
endEntityPos = end + 1
|
||||||
|
|
||||||
|
if s[end] == '&' {
|
||||||
|
for endEntityPos < len(s) {
|
||||||
|
if s[endEntityPos] == ';' {
|
||||||
|
if entities[string(s[end:endEntityPos+1])] {
|
||||||
|
isEntity = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isalnum(s[endEntityPos]) && s[endEntityPos] != '&' && s[endEntityPos] != '#' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
endEntityPos++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEntity, endEntityPos
|
||||||
|
}
|
||||||
|
|
||||||
|
func escLink(w io.Writer, text []byte) {
|
||||||
|
unesc := html.UnescapeString(string(text))
|
||||||
|
escapeHTML(w, []byte(unesc))
|
||||||
|
}
|
||||||
952
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/html.go
generated
vendored
Normal file
952
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/html.go
generated
vendored
Normal file
@@ -0,0 +1,952 @@
|
|||||||
|
//
|
||||||
|
// Blackfriday Markdown Processor
|
||||||
|
// Available at http://github.com/russross/blackfriday
|
||||||
|
//
|
||||||
|
// Copyright © 2011 Russ Ross <russ@russross.com>.
|
||||||
|
// Distributed under the Simplified BSD License.
|
||||||
|
// See README.md for details.
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// HTML rendering backend
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
package blackfriday
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTMLFlags control optional behavior of HTML renderer.
|
||||||
|
type HTMLFlags int
|
||||||
|
|
||||||
|
// HTML renderer configuration options.
|
||||||
|
const (
|
||||||
|
HTMLFlagsNone HTMLFlags = 0
|
||||||
|
SkipHTML HTMLFlags = 1 << iota // Skip preformatted HTML blocks
|
||||||
|
SkipImages // Skip embedded images
|
||||||
|
SkipLinks // Skip all links
|
||||||
|
Safelink // Only link to trusted protocols
|
||||||
|
NofollowLinks // Only link with rel="nofollow"
|
||||||
|
NoreferrerLinks // Only link with rel="noreferrer"
|
||||||
|
NoopenerLinks // Only link with rel="noopener"
|
||||||
|
HrefTargetBlank // Add a blank target
|
||||||
|
CompletePage // Generate a complete HTML page
|
||||||
|
UseXHTML // Generate XHTML output instead of HTML
|
||||||
|
FootnoteReturnLinks // Generate a link at the end of a footnote to return to the source
|
||||||
|
Smartypants // Enable smart punctuation substitutions
|
||||||
|
SmartypantsFractions // Enable smart fractions (with Smartypants)
|
||||||
|
SmartypantsDashes // Enable smart dashes (with Smartypants)
|
||||||
|
SmartypantsLatexDashes // Enable LaTeX-style dashes (with Smartypants)
|
||||||
|
SmartypantsAngledQuotes // Enable angled double quotes (with Smartypants) for double quotes rendering
|
||||||
|
SmartypantsQuotesNBSP // Enable « French guillemets » (with Smartypants)
|
||||||
|
TOC // Generate a table of contents
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
htmlTagRe = regexp.MustCompile("(?i)^" + htmlTag)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
htmlTag = "(?:" + openTag + "|" + closeTag + "|" + htmlComment + "|" +
|
||||||
|
processingInstruction + "|" + declaration + "|" + cdata + ")"
|
||||||
|
closeTag = "</" + tagName + "\\s*[>]"
|
||||||
|
openTag = "<" + tagName + attribute + "*" + "\\s*/?>"
|
||||||
|
attribute = "(?:" + "\\s+" + attributeName + attributeValueSpec + "?)"
|
||||||
|
attributeValue = "(?:" + unquotedValue + "|" + singleQuotedValue + "|" + doubleQuotedValue + ")"
|
||||||
|
attributeValueSpec = "(?:" + "\\s*=" + "\\s*" + attributeValue + ")"
|
||||||
|
attributeName = "[a-zA-Z_:][a-zA-Z0-9:._-]*"
|
||||||
|
cdata = "<!\\[CDATA\\[[\\s\\S]*?\\]\\]>"
|
||||||
|
declaration = "<![A-Z]+" + "\\s+[^>]*>"
|
||||||
|
doubleQuotedValue = "\"[^\"]*\""
|
||||||
|
htmlComment = "<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->"
|
||||||
|
processingInstruction = "[<][?].*?[?][>]"
|
||||||
|
singleQuotedValue = "'[^']*'"
|
||||||
|
tagName = "[A-Za-z][A-Za-z0-9-]*"
|
||||||
|
unquotedValue = "[^\"'=<>`\\x00-\\x20]+"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTMLRendererParameters is a collection of supplementary parameters tweaking
|
||||||
|
// the behavior of various parts of HTML renderer.
|
||||||
|
type HTMLRendererParameters struct {
|
||||||
|
// Prepend this text to each relative URL.
|
||||||
|
AbsolutePrefix string
|
||||||
|
// Add this text to each footnote anchor, to ensure uniqueness.
|
||||||
|
FootnoteAnchorPrefix string
|
||||||
|
// Show this text inside the <a> tag for a footnote return link, if the
|
||||||
|
// HTML_FOOTNOTE_RETURN_LINKS flag is enabled. If blank, the string
|
||||||
|
// <sup>[return]</sup> is used.
|
||||||
|
FootnoteReturnLinkContents string
|
||||||
|
// If set, add this text to the front of each Heading ID, to ensure
|
||||||
|
// uniqueness.
|
||||||
|
HeadingIDPrefix string
|
||||||
|
// If set, add this text to the back of each Heading ID, to ensure uniqueness.
|
||||||
|
HeadingIDSuffix string
|
||||||
|
// Increase heading levels: if the offset is 1, <h1> becomes <h2> etc.
|
||||||
|
// Negative offset is also valid.
|
||||||
|
// Resulting levels are clipped between 1 and 6.
|
||||||
|
HeadingLevelOffset int
|
||||||
|
|
||||||
|
Title string // Document title (used if CompletePage is set)
|
||||||
|
CSS string // Optional CSS file URL (used if CompletePage is set)
|
||||||
|
Icon string // Optional icon file URL (used if CompletePage is set)
|
||||||
|
|
||||||
|
Flags HTMLFlags // Flags allow customizing this renderer's behavior
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTMLRenderer is a type that implements the Renderer interface for HTML output.
|
||||||
|
//
|
||||||
|
// Do not create this directly, instead use the NewHTMLRenderer function.
|
||||||
|
type HTMLRenderer struct {
|
||||||
|
HTMLRendererParameters
|
||||||
|
|
||||||
|
closeTag string // how to end singleton tags: either " />" or ">"
|
||||||
|
|
||||||
|
// Track heading IDs to prevent ID collision in a single generation.
|
||||||
|
headingIDs map[string]int
|
||||||
|
|
||||||
|
lastOutputLen int
|
||||||
|
disableTags int
|
||||||
|
|
||||||
|
sr *SPRenderer
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
xhtmlClose = " />"
|
||||||
|
htmlClose = ">"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewHTMLRenderer creates and configures an HTMLRenderer object, which
|
||||||
|
// satisfies the Renderer interface.
|
||||||
|
func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer {
|
||||||
|
// configure the rendering engine
|
||||||
|
closeTag := htmlClose
|
||||||
|
if params.Flags&UseXHTML != 0 {
|
||||||
|
closeTag = xhtmlClose
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.FootnoteReturnLinkContents == "" {
|
||||||
|
// U+FE0E is VARIATION SELECTOR-15.
|
||||||
|
// It suppresses automatic emoji presentation of the preceding
|
||||||
|
// U+21A9 LEFTWARDS ARROW WITH HOOK on iOS and iPadOS.
|
||||||
|
params.FootnoteReturnLinkContents = "<span aria-label='Return'>↩\ufe0e</span>"
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HTMLRenderer{
|
||||||
|
HTMLRendererParameters: params,
|
||||||
|
|
||||||
|
closeTag: closeTag,
|
||||||
|
headingIDs: make(map[string]int),
|
||||||
|
|
||||||
|
sr: NewSmartypantsRenderer(params.Flags),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isHTMLTag(tag []byte, tagname string) bool {
|
||||||
|
found, _ := findHTMLTagPos(tag, tagname)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for a character, but ignore it when it's in any kind of quotes, it
|
||||||
|
// might be JavaScript
|
||||||
|
func skipUntilCharIgnoreQuotes(html []byte, start int, char byte) int {
|
||||||
|
inSingleQuote := false
|
||||||
|
inDoubleQuote := false
|
||||||
|
inGraveQuote := false
|
||||||
|
i := start
|
||||||
|
for i < len(html) {
|
||||||
|
switch {
|
||||||
|
case html[i] == char && !inSingleQuote && !inDoubleQuote && !inGraveQuote:
|
||||||
|
return i
|
||||||
|
case html[i] == '\'':
|
||||||
|
inSingleQuote = !inSingleQuote
|
||||||
|
case html[i] == '"':
|
||||||
|
inDoubleQuote = !inDoubleQuote
|
||||||
|
case html[i] == '`':
|
||||||
|
inGraveQuote = !inGraveQuote
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return start
|
||||||
|
}
|
||||||
|
|
||||||
|
func findHTMLTagPos(tag []byte, tagname string) (bool, int) {
|
||||||
|
i := 0
|
||||||
|
if i < len(tag) && tag[0] != '<' {
|
||||||
|
return false, -1
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
i = skipSpace(tag, i)
|
||||||
|
|
||||||
|
if i < len(tag) && tag[i] == '/' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
i = skipSpace(tag, i)
|
||||||
|
j := 0
|
||||||
|
for ; i < len(tag); i, j = i+1, j+1 {
|
||||||
|
if j >= len(tagname) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.ToLower(string(tag[i]))[0] != tagname[j] {
|
||||||
|
return false, -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == len(tag) {
|
||||||
|
return false, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
rightAngle := skipUntilCharIgnoreQuotes(tag, i, '>')
|
||||||
|
if rightAngle >= i {
|
||||||
|
return true, rightAngle
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipSpace(tag []byte, i int) int {
|
||||||
|
for i < len(tag) && isspace(tag[i]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func isRelativeLink(link []byte) (yes bool) {
|
||||||
|
// a tag begin with '#'
|
||||||
|
if link[0] == '#' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// link begin with '/' but not '//', the second maybe a protocol relative link
|
||||||
|
if len(link) >= 2 && link[0] == '/' && link[1] != '/' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// only the root '/'
|
||||||
|
if len(link) == 1 && link[0] == '/' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// current directory : begin with "./"
|
||||||
|
if bytes.HasPrefix(link, []byte("./")) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// parent directory : begin with "../"
|
||||||
|
if bytes.HasPrefix(link, []byte("../")) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) ensureUniqueHeadingID(id string) string {
|
||||||
|
for count, found := r.headingIDs[id]; found; count, found = r.headingIDs[id] {
|
||||||
|
tmp := fmt.Sprintf("%s-%d", id, count+1)
|
||||||
|
|
||||||
|
if _, tmpFound := r.headingIDs[tmp]; !tmpFound {
|
||||||
|
r.headingIDs[id] = count + 1
|
||||||
|
id = tmp
|
||||||
|
} else {
|
||||||
|
id = id + "-1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := r.headingIDs[id]; !found {
|
||||||
|
r.headingIDs[id] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) addAbsPrefix(link []byte) []byte {
|
||||||
|
if r.AbsolutePrefix != "" && isRelativeLink(link) && link[0] != '.' {
|
||||||
|
newDest := r.AbsolutePrefix
|
||||||
|
if link[0] != '/' {
|
||||||
|
newDest += "/"
|
||||||
|
}
|
||||||
|
newDest += string(link)
|
||||||
|
return []byte(newDest)
|
||||||
|
}
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendLinkAttrs(attrs []string, flags HTMLFlags, link []byte) []string {
|
||||||
|
if isRelativeLink(link) {
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
val := []string{}
|
||||||
|
if flags&NofollowLinks != 0 {
|
||||||
|
val = append(val, "nofollow")
|
||||||
|
}
|
||||||
|
if flags&NoreferrerLinks != 0 {
|
||||||
|
val = append(val, "noreferrer")
|
||||||
|
}
|
||||||
|
if flags&NoopenerLinks != 0 {
|
||||||
|
val = append(val, "noopener")
|
||||||
|
}
|
||||||
|
if flags&HrefTargetBlank != 0 {
|
||||||
|
attrs = append(attrs, "target=\"_blank\"")
|
||||||
|
}
|
||||||
|
if len(val) == 0 {
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
attr := fmt.Sprintf("rel=%q", strings.Join(val, " "))
|
||||||
|
return append(attrs, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMailto(link []byte) bool {
|
||||||
|
return bytes.HasPrefix(link, []byte("mailto:"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func needSkipLink(flags HTMLFlags, dest []byte) bool {
|
||||||
|
if flags&SkipLinks != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return flags&Safelink != 0 && !isSafeLink(dest) && !isMailto(dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSmartypantable(node *Node) bool {
|
||||||
|
pt := node.Parent.Type
|
||||||
|
return pt != Link && pt != CodeBlock && pt != Code
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendLanguageAttr(attrs []string, info []byte) []string {
|
||||||
|
if len(info) == 0 {
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
endOfLang := bytes.IndexAny(info, "\t ")
|
||||||
|
if endOfLang < 0 {
|
||||||
|
endOfLang = len(info)
|
||||||
|
}
|
||||||
|
return append(attrs, fmt.Sprintf("class=\"language-%s\"", info[:endOfLang]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) tag(w io.Writer, name []byte, attrs []string) {
|
||||||
|
w.Write(name)
|
||||||
|
if len(attrs) > 0 {
|
||||||
|
w.Write(spaceBytes)
|
||||||
|
w.Write([]byte(strings.Join(attrs, " ")))
|
||||||
|
}
|
||||||
|
w.Write(gtBytes)
|
||||||
|
r.lastOutputLen = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func footnoteRef(prefix string, node *Node) []byte {
|
||||||
|
urlFrag := prefix + string(slugify(node.Destination))
|
||||||
|
anchor := fmt.Sprintf(`<a href="#fn:%s">%d</a>`, urlFrag, node.NoteID)
|
||||||
|
return []byte(fmt.Sprintf(`<sup class="footnote-ref" id="fnref:%s">%s</sup>`, urlFrag, anchor))
|
||||||
|
}
|
||||||
|
|
||||||
|
func footnoteItem(prefix string, slug []byte) []byte {
|
||||||
|
return []byte(fmt.Sprintf(`<li id="fn:%s%s">`, prefix, slug))
|
||||||
|
}
|
||||||
|
|
||||||
|
func footnoteReturnLink(prefix, returnLink string, slug []byte) []byte {
|
||||||
|
const format = ` <a class="footnote-return" href="#fnref:%s%s">%s</a>`
|
||||||
|
return []byte(fmt.Sprintf(format, prefix, slug, returnLink))
|
||||||
|
}
|
||||||
|
|
||||||
|
func itemOpenCR(node *Node) bool {
|
||||||
|
if node.Prev == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ld := node.Parent.ListData
|
||||||
|
return !ld.Tight && ld.ListFlags&ListTypeDefinition == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipParagraphTags(node *Node) bool {
|
||||||
|
grandparent := node.Parent.Parent
|
||||||
|
if grandparent == nil || grandparent.Type != List {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
tightOrTerm := grandparent.Tight || node.Parent.ListFlags&ListTypeTerm != 0
|
||||||
|
return grandparent.Type == List && tightOrTerm
|
||||||
|
}
|
||||||
|
|
||||||
|
func cellAlignment(align CellAlignFlags) string {
|
||||||
|
switch align {
|
||||||
|
case TableAlignmentLeft:
|
||||||
|
return "left"
|
||||||
|
case TableAlignmentRight:
|
||||||
|
return "right"
|
||||||
|
case TableAlignmentCenter:
|
||||||
|
return "center"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) out(w io.Writer, text []byte) {
|
||||||
|
if r.disableTags > 0 {
|
||||||
|
w.Write(htmlTagRe.ReplaceAll(text, []byte{}))
|
||||||
|
} else {
|
||||||
|
w.Write(text)
|
||||||
|
}
|
||||||
|
r.lastOutputLen = len(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) cr(w io.Writer) {
|
||||||
|
if r.lastOutputLen > 0 {
|
||||||
|
r.out(w, nlBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
nlBytes = []byte{'\n'}
|
||||||
|
gtBytes = []byte{'>'}
|
||||||
|
spaceBytes = []byte{' '}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
brTag = []byte("<br>")
|
||||||
|
brXHTMLTag = []byte("<br />")
|
||||||
|
emTag = []byte("<em>")
|
||||||
|
emCloseTag = []byte("</em>")
|
||||||
|
strongTag = []byte("<strong>")
|
||||||
|
strongCloseTag = []byte("</strong>")
|
||||||
|
delTag = []byte("<del>")
|
||||||
|
delCloseTag = []byte("</del>")
|
||||||
|
ttTag = []byte("<tt>")
|
||||||
|
ttCloseTag = []byte("</tt>")
|
||||||
|
aTag = []byte("<a")
|
||||||
|
aCloseTag = []byte("</a>")
|
||||||
|
preTag = []byte("<pre>")
|
||||||
|
preCloseTag = []byte("</pre>")
|
||||||
|
codeTag = []byte("<code>")
|
||||||
|
codeCloseTag = []byte("</code>")
|
||||||
|
pTag = []byte("<p>")
|
||||||
|
pCloseTag = []byte("</p>")
|
||||||
|
blockquoteTag = []byte("<blockquote>")
|
||||||
|
blockquoteCloseTag = []byte("</blockquote>")
|
||||||
|
hrTag = []byte("<hr>")
|
||||||
|
hrXHTMLTag = []byte("<hr />")
|
||||||
|
ulTag = []byte("<ul>")
|
||||||
|
ulCloseTag = []byte("</ul>")
|
||||||
|
olTag = []byte("<ol>")
|
||||||
|
olCloseTag = []byte("</ol>")
|
||||||
|
dlTag = []byte("<dl>")
|
||||||
|
dlCloseTag = []byte("</dl>")
|
||||||
|
liTag = []byte("<li>")
|
||||||
|
liCloseTag = []byte("</li>")
|
||||||
|
ddTag = []byte("<dd>")
|
||||||
|
ddCloseTag = []byte("</dd>")
|
||||||
|
dtTag = []byte("<dt>")
|
||||||
|
dtCloseTag = []byte("</dt>")
|
||||||
|
tableTag = []byte("<table>")
|
||||||
|
tableCloseTag = []byte("</table>")
|
||||||
|
tdTag = []byte("<td")
|
||||||
|
tdCloseTag = []byte("</td>")
|
||||||
|
thTag = []byte("<th")
|
||||||
|
thCloseTag = []byte("</th>")
|
||||||
|
theadTag = []byte("<thead>")
|
||||||
|
theadCloseTag = []byte("</thead>")
|
||||||
|
tbodyTag = []byte("<tbody>")
|
||||||
|
tbodyCloseTag = []byte("</tbody>")
|
||||||
|
trTag = []byte("<tr>")
|
||||||
|
trCloseTag = []byte("</tr>")
|
||||||
|
h1Tag = []byte("<h1")
|
||||||
|
h1CloseTag = []byte("</h1>")
|
||||||
|
h2Tag = []byte("<h2")
|
||||||
|
h2CloseTag = []byte("</h2>")
|
||||||
|
h3Tag = []byte("<h3")
|
||||||
|
h3CloseTag = []byte("</h3>")
|
||||||
|
h4Tag = []byte("<h4")
|
||||||
|
h4CloseTag = []byte("</h4>")
|
||||||
|
h5Tag = []byte("<h5")
|
||||||
|
h5CloseTag = []byte("</h5>")
|
||||||
|
h6Tag = []byte("<h6")
|
||||||
|
h6CloseTag = []byte("</h6>")
|
||||||
|
|
||||||
|
footnotesDivBytes = []byte("\n<div class=\"footnotes\">\n\n")
|
||||||
|
footnotesCloseDivBytes = []byte("\n</div>\n")
|
||||||
|
)
|
||||||
|
|
||||||
|
func headingTagsFromLevel(level int) ([]byte, []byte) {
|
||||||
|
if level <= 1 {
|
||||||
|
return h1Tag, h1CloseTag
|
||||||
|
}
|
||||||
|
switch level {
|
||||||
|
case 2:
|
||||||
|
return h2Tag, h2CloseTag
|
||||||
|
case 3:
|
||||||
|
return h3Tag, h3CloseTag
|
||||||
|
case 4:
|
||||||
|
return h4Tag, h4CloseTag
|
||||||
|
case 5:
|
||||||
|
return h5Tag, h5CloseTag
|
||||||
|
}
|
||||||
|
return h6Tag, h6CloseTag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) outHRTag(w io.Writer) {
|
||||||
|
if r.Flags&UseXHTML == 0 {
|
||||||
|
r.out(w, hrTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, hrXHTMLTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderNode is a default renderer of a single node of a syntax tree. For
|
||||||
|
// block nodes it will be called twice: first time with entering=true, second
|
||||||
|
// time with entering=false, so that it could know when it's working on an open
|
||||||
|
// tag and when on close. It writes the result to w.
|
||||||
|
//
|
||||||
|
// The return value is a way to tell the calling walker to adjust its walk
|
||||||
|
// pattern: e.g. it can terminate the traversal by returning Terminate. Or it
|
||||||
|
// can ask the walker to skip a subtree of this node by returning SkipChildren.
|
||||||
|
// The typical behavior is to return GoToNext, which asks for the usual
|
||||||
|
// traversal to the next node.
|
||||||
|
func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkStatus {
|
||||||
|
attrs := []string{}
|
||||||
|
switch node.Type {
|
||||||
|
case Text:
|
||||||
|
if r.Flags&Smartypants != 0 {
|
||||||
|
var tmp bytes.Buffer
|
||||||
|
escapeHTML(&tmp, node.Literal)
|
||||||
|
r.sr.Process(w, tmp.Bytes())
|
||||||
|
} else {
|
||||||
|
if node.Parent.Type == Link {
|
||||||
|
escLink(w, node.Literal)
|
||||||
|
} else {
|
||||||
|
escapeHTML(w, node.Literal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Softbreak:
|
||||||
|
r.cr(w)
|
||||||
|
// TODO: make it configurable via out(renderer.softbreak)
|
||||||
|
case Hardbreak:
|
||||||
|
if r.Flags&UseXHTML == 0 {
|
||||||
|
r.out(w, brTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, brXHTMLTag)
|
||||||
|
}
|
||||||
|
r.cr(w)
|
||||||
|
case Emph:
|
||||||
|
if entering {
|
||||||
|
r.out(w, emTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, emCloseTag)
|
||||||
|
}
|
||||||
|
case Strong:
|
||||||
|
if entering {
|
||||||
|
r.out(w, strongTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, strongCloseTag)
|
||||||
|
}
|
||||||
|
case Del:
|
||||||
|
if entering {
|
||||||
|
r.out(w, delTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, delCloseTag)
|
||||||
|
}
|
||||||
|
case HTMLSpan:
|
||||||
|
if r.Flags&SkipHTML != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r.out(w, node.Literal)
|
||||||
|
case Link:
|
||||||
|
// mark it but don't link it if it is not a safe link: no smartypants
|
||||||
|
dest := node.LinkData.Destination
|
||||||
|
if needSkipLink(r.Flags, dest) {
|
||||||
|
if entering {
|
||||||
|
r.out(w, ttTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, ttCloseTag)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if entering {
|
||||||
|
dest = r.addAbsPrefix(dest)
|
||||||
|
var hrefBuf bytes.Buffer
|
||||||
|
hrefBuf.WriteString("href=\"")
|
||||||
|
escLink(&hrefBuf, dest)
|
||||||
|
hrefBuf.WriteByte('"')
|
||||||
|
attrs = append(attrs, hrefBuf.String())
|
||||||
|
if node.NoteID != 0 {
|
||||||
|
r.out(w, footnoteRef(r.FootnoteAnchorPrefix, node))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
attrs = appendLinkAttrs(attrs, r.Flags, dest)
|
||||||
|
if len(node.LinkData.Title) > 0 {
|
||||||
|
var titleBuff bytes.Buffer
|
||||||
|
titleBuff.WriteString("title=\"")
|
||||||
|
escapeHTML(&titleBuff, node.LinkData.Title)
|
||||||
|
titleBuff.WriteByte('"')
|
||||||
|
attrs = append(attrs, titleBuff.String())
|
||||||
|
}
|
||||||
|
r.tag(w, aTag, attrs)
|
||||||
|
} else {
|
||||||
|
if node.NoteID != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r.out(w, aCloseTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Image:
|
||||||
|
if r.Flags&SkipImages != 0 {
|
||||||
|
return SkipChildren
|
||||||
|
}
|
||||||
|
if entering {
|
||||||
|
dest := node.LinkData.Destination
|
||||||
|
dest = r.addAbsPrefix(dest)
|
||||||
|
if r.disableTags == 0 {
|
||||||
|
//if options.safe && potentiallyUnsafe(dest) {
|
||||||
|
//out(w, `<img src="" alt="`)
|
||||||
|
//} else {
|
||||||
|
r.out(w, []byte(`<img src="`))
|
||||||
|
escLink(w, dest)
|
||||||
|
r.out(w, []byte(`" alt="`))
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
r.disableTags++
|
||||||
|
} else {
|
||||||
|
r.disableTags--
|
||||||
|
if r.disableTags == 0 {
|
||||||
|
if node.LinkData.Title != nil {
|
||||||
|
r.out(w, []byte(`" title="`))
|
||||||
|
escapeHTML(w, node.LinkData.Title)
|
||||||
|
}
|
||||||
|
r.out(w, []byte(`" />`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code:
|
||||||
|
r.out(w, codeTag)
|
||||||
|
escapeAllHTML(w, node.Literal)
|
||||||
|
r.out(w, codeCloseTag)
|
||||||
|
case Document:
|
||||||
|
break
|
||||||
|
case Paragraph:
|
||||||
|
if skipParagraphTags(node) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if entering {
|
||||||
|
// TODO: untangle this clusterfuck about when the newlines need
|
||||||
|
// to be added and when not.
|
||||||
|
if node.Prev != nil {
|
||||||
|
switch node.Prev.Type {
|
||||||
|
case HTMLBlock, List, Paragraph, Heading, CodeBlock, BlockQuote, HorizontalRule:
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.Parent.Type == BlockQuote && node.Prev == nil {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
r.out(w, pTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, pCloseTag)
|
||||||
|
if !(node.Parent.Type == Item && node.Next == nil) {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case BlockQuote:
|
||||||
|
if entering {
|
||||||
|
r.cr(w)
|
||||||
|
r.out(w, blockquoteTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, blockquoteCloseTag)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
case HTMLBlock:
|
||||||
|
if r.Flags&SkipHTML != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r.cr(w)
|
||||||
|
r.out(w, node.Literal)
|
||||||
|
r.cr(w)
|
||||||
|
case Heading:
|
||||||
|
headingLevel := r.HTMLRendererParameters.HeadingLevelOffset + node.Level
|
||||||
|
openTag, closeTag := headingTagsFromLevel(headingLevel)
|
||||||
|
if entering {
|
||||||
|
if node.IsTitleblock {
|
||||||
|
attrs = append(attrs, `class="title"`)
|
||||||
|
}
|
||||||
|
if node.HeadingID != "" {
|
||||||
|
id := r.ensureUniqueHeadingID(node.HeadingID)
|
||||||
|
if r.HeadingIDPrefix != "" {
|
||||||
|
id = r.HeadingIDPrefix + id
|
||||||
|
}
|
||||||
|
if r.HeadingIDSuffix != "" {
|
||||||
|
id = id + r.HeadingIDSuffix
|
||||||
|
}
|
||||||
|
attrs = append(attrs, fmt.Sprintf(`id="%s"`, id))
|
||||||
|
}
|
||||||
|
r.cr(w)
|
||||||
|
r.tag(w, openTag, attrs)
|
||||||
|
} else {
|
||||||
|
r.out(w, closeTag)
|
||||||
|
if !(node.Parent.Type == Item && node.Next == nil) {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case HorizontalRule:
|
||||||
|
r.cr(w)
|
||||||
|
r.outHRTag(w)
|
||||||
|
r.cr(w)
|
||||||
|
case List:
|
||||||
|
openTag := ulTag
|
||||||
|
closeTag := ulCloseTag
|
||||||
|
if node.ListFlags&ListTypeOrdered != 0 {
|
||||||
|
openTag = olTag
|
||||||
|
closeTag = olCloseTag
|
||||||
|
}
|
||||||
|
if node.ListFlags&ListTypeDefinition != 0 {
|
||||||
|
openTag = dlTag
|
||||||
|
closeTag = dlCloseTag
|
||||||
|
}
|
||||||
|
if entering {
|
||||||
|
if node.IsFootnotesList {
|
||||||
|
r.out(w, footnotesDivBytes)
|
||||||
|
r.outHRTag(w)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
r.cr(w)
|
||||||
|
if node.Parent.Type == Item && node.Parent.Parent.Tight {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
r.tag(w, openTag[:len(openTag)-1], attrs)
|
||||||
|
r.cr(w)
|
||||||
|
} else {
|
||||||
|
r.out(w, closeTag)
|
||||||
|
//cr(w)
|
||||||
|
//if node.parent.Type != Item {
|
||||||
|
// cr(w)
|
||||||
|
//}
|
||||||
|
if node.Parent.Type == Item && node.Next != nil {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
if node.Parent.Type == Document || node.Parent.Type == BlockQuote {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
if node.IsFootnotesList {
|
||||||
|
r.out(w, footnotesCloseDivBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Item:
|
||||||
|
openTag := liTag
|
||||||
|
closeTag := liCloseTag
|
||||||
|
if node.ListFlags&ListTypeDefinition != 0 {
|
||||||
|
openTag = ddTag
|
||||||
|
closeTag = ddCloseTag
|
||||||
|
}
|
||||||
|
if node.ListFlags&ListTypeTerm != 0 {
|
||||||
|
openTag = dtTag
|
||||||
|
closeTag = dtCloseTag
|
||||||
|
}
|
||||||
|
if entering {
|
||||||
|
if itemOpenCR(node) {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
if node.ListData.RefLink != nil {
|
||||||
|
slug := slugify(node.ListData.RefLink)
|
||||||
|
r.out(w, footnoteItem(r.FootnoteAnchorPrefix, slug))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r.out(w, openTag)
|
||||||
|
} else {
|
||||||
|
if node.ListData.RefLink != nil {
|
||||||
|
slug := slugify(node.ListData.RefLink)
|
||||||
|
if r.Flags&FootnoteReturnLinks != 0 {
|
||||||
|
r.out(w, footnoteReturnLink(r.FootnoteAnchorPrefix, r.FootnoteReturnLinkContents, slug))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.out(w, closeTag)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
case CodeBlock:
|
||||||
|
attrs = appendLanguageAttr(attrs, node.Info)
|
||||||
|
r.cr(w)
|
||||||
|
r.out(w, preTag)
|
||||||
|
r.tag(w, codeTag[:len(codeTag)-1], attrs)
|
||||||
|
escapeAllHTML(w, node.Literal)
|
||||||
|
r.out(w, codeCloseTag)
|
||||||
|
r.out(w, preCloseTag)
|
||||||
|
if node.Parent.Type != Item {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
case Table:
|
||||||
|
if entering {
|
||||||
|
r.cr(w)
|
||||||
|
r.out(w, tableTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, tableCloseTag)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
case TableCell:
|
||||||
|
openTag := tdTag
|
||||||
|
closeTag := tdCloseTag
|
||||||
|
if node.IsHeader {
|
||||||
|
openTag = thTag
|
||||||
|
closeTag = thCloseTag
|
||||||
|
}
|
||||||
|
if entering {
|
||||||
|
align := cellAlignment(node.Align)
|
||||||
|
if align != "" {
|
||||||
|
attrs = append(attrs, fmt.Sprintf(`align="%s"`, align))
|
||||||
|
}
|
||||||
|
if node.Prev == nil {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
r.tag(w, openTag, attrs)
|
||||||
|
} else {
|
||||||
|
r.out(w, closeTag)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
case TableHead:
|
||||||
|
if entering {
|
||||||
|
r.cr(w)
|
||||||
|
r.out(w, theadTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, theadCloseTag)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
case TableBody:
|
||||||
|
if entering {
|
||||||
|
r.cr(w)
|
||||||
|
r.out(w, tbodyTag)
|
||||||
|
// XXX: this is to adhere to a rather silly test. Should fix test.
|
||||||
|
if node.FirstChild == nil {
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r.out(w, tbodyCloseTag)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
case TableRow:
|
||||||
|
if entering {
|
||||||
|
r.cr(w)
|
||||||
|
r.out(w, trTag)
|
||||||
|
} else {
|
||||||
|
r.out(w, trCloseTag)
|
||||||
|
r.cr(w)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("Unknown node type " + node.Type.String())
|
||||||
|
}
|
||||||
|
return GoToNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderHeader writes HTML document preamble and TOC if requested.
|
||||||
|
func (r *HTMLRenderer) RenderHeader(w io.Writer, ast *Node) {
|
||||||
|
r.writeDocumentHeader(w)
|
||||||
|
if r.Flags&TOC != 0 {
|
||||||
|
r.writeTOC(w, ast)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderFooter writes HTML document footer.
|
||||||
|
func (r *HTMLRenderer) RenderFooter(w io.Writer, ast *Node) {
|
||||||
|
if r.Flags&CompletePage == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
io.WriteString(w, "\n</body>\n</html>\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) writeDocumentHeader(w io.Writer) {
|
||||||
|
if r.Flags&CompletePage == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ending := ""
|
||||||
|
if r.Flags&UseXHTML != 0 {
|
||||||
|
io.WriteString(w, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ")
|
||||||
|
io.WriteString(w, "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n")
|
||||||
|
io.WriteString(w, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n")
|
||||||
|
ending = " /"
|
||||||
|
} else {
|
||||||
|
io.WriteString(w, "<!DOCTYPE html>\n")
|
||||||
|
io.WriteString(w, "<html>\n")
|
||||||
|
}
|
||||||
|
io.WriteString(w, "<head>\n")
|
||||||
|
io.WriteString(w, " <title>")
|
||||||
|
if r.Flags&Smartypants != 0 {
|
||||||
|
r.sr.Process(w, []byte(r.Title))
|
||||||
|
} else {
|
||||||
|
escapeHTML(w, []byte(r.Title))
|
||||||
|
}
|
||||||
|
io.WriteString(w, "</title>\n")
|
||||||
|
io.WriteString(w, " <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v")
|
||||||
|
io.WriteString(w, Version)
|
||||||
|
io.WriteString(w, "\"")
|
||||||
|
io.WriteString(w, ending)
|
||||||
|
io.WriteString(w, ">\n")
|
||||||
|
io.WriteString(w, " <meta charset=\"utf-8\"")
|
||||||
|
io.WriteString(w, ending)
|
||||||
|
io.WriteString(w, ">\n")
|
||||||
|
if r.CSS != "" {
|
||||||
|
io.WriteString(w, " <link rel=\"stylesheet\" type=\"text/css\" href=\"")
|
||||||
|
escapeHTML(w, []byte(r.CSS))
|
||||||
|
io.WriteString(w, "\"")
|
||||||
|
io.WriteString(w, ending)
|
||||||
|
io.WriteString(w, ">\n")
|
||||||
|
}
|
||||||
|
if r.Icon != "" {
|
||||||
|
io.WriteString(w, " <link rel=\"icon\" type=\"image/x-icon\" href=\"")
|
||||||
|
escapeHTML(w, []byte(r.Icon))
|
||||||
|
io.WriteString(w, "\"")
|
||||||
|
io.WriteString(w, ending)
|
||||||
|
io.WriteString(w, ">\n")
|
||||||
|
}
|
||||||
|
io.WriteString(w, "</head>\n")
|
||||||
|
io.WriteString(w, "<body>\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLRenderer) writeTOC(w io.Writer, ast *Node) {
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
|
||||||
|
inHeading := false
|
||||||
|
tocLevel := 0
|
||||||
|
headingCount := 0
|
||||||
|
|
||||||
|
ast.Walk(func(node *Node, entering bool) WalkStatus {
|
||||||
|
if node.Type == Heading && !node.HeadingData.IsTitleblock {
|
||||||
|
inHeading = entering
|
||||||
|
if entering {
|
||||||
|
node.HeadingID = fmt.Sprintf("toc_%d", headingCount)
|
||||||
|
if node.Level == tocLevel {
|
||||||
|
buf.WriteString("</li>\n\n<li>")
|
||||||
|
} else if node.Level < tocLevel {
|
||||||
|
for node.Level < tocLevel {
|
||||||
|
tocLevel--
|
||||||
|
buf.WriteString("</li>\n</ul>")
|
||||||
|
}
|
||||||
|
buf.WriteString("</li>\n\n<li>")
|
||||||
|
} else {
|
||||||
|
for node.Level > tocLevel {
|
||||||
|
tocLevel++
|
||||||
|
buf.WriteString("\n<ul>\n<li>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(&buf, `<a href="#toc_%d">`, headingCount)
|
||||||
|
headingCount++
|
||||||
|
} else {
|
||||||
|
buf.WriteString("</a>")
|
||||||
|
}
|
||||||
|
return GoToNext
|
||||||
|
}
|
||||||
|
|
||||||
|
if inHeading {
|
||||||
|
return r.RenderNode(&buf, node, entering)
|
||||||
|
}
|
||||||
|
|
||||||
|
return GoToNext
|
||||||
|
})
|
||||||
|
|
||||||
|
for ; tocLevel > 0; tocLevel-- {
|
||||||
|
buf.WriteString("</li>\n</ul>")
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
io.WriteString(w, "<nav>\n")
|
||||||
|
w.Write(buf.Bytes())
|
||||||
|
io.WriteString(w, "\n\n</nav>\n")
|
||||||
|
}
|
||||||
|
r.lastOutputLen = buf.Len()
|
||||||
|
}
|
||||||
1228
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/inline.go
generated
vendored
Normal file
1228
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/inline.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
950
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/markdown.go
generated
vendored
Normal file
950
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/markdown.go
generated
vendored
Normal file
@@ -0,0 +1,950 @@
|
|||||||
|
// Blackfriday Markdown Processor
|
||||||
|
// Available at http://github.com/russross/blackfriday
|
||||||
|
//
|
||||||
|
// Copyright © 2011 Russ Ross <russ@russross.com>.
|
||||||
|
// Distributed under the Simplified BSD License.
|
||||||
|
// See README.md for details.
|
||||||
|
|
||||||
|
package blackfriday
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Markdown parsing and processing
|
||||||
|
//
|
||||||
|
|
||||||
|
// Version string of the package. Appears in the rendered document when
|
||||||
|
// CompletePage flag is on.
|
||||||
|
const Version = "2.0"
|
||||||
|
|
||||||
|
// Extensions is a bitwise or'ed collection of enabled Blackfriday's
|
||||||
|
// extensions.
|
||||||
|
type Extensions int
|
||||||
|
|
||||||
|
// These are the supported markdown parsing extensions.
|
||||||
|
// OR these values together to select multiple extensions.
|
||||||
|
const (
|
||||||
|
NoExtensions Extensions = 0
|
||||||
|
NoIntraEmphasis Extensions = 1 << iota // Ignore emphasis markers inside words
|
||||||
|
Tables // Render tables
|
||||||
|
FencedCode // Render fenced code blocks
|
||||||
|
Autolink // Detect embedded URLs that are not explicitly marked
|
||||||
|
Strikethrough // Strikethrough text using ~~test~~
|
||||||
|
LaxHTMLBlocks // Loosen up HTML block parsing rules
|
||||||
|
SpaceHeadings // Be strict about prefix heading rules
|
||||||
|
HardLineBreak // Translate newlines into line breaks
|
||||||
|
TabSizeEight // Expand tabs to eight spaces instead of four
|
||||||
|
Footnotes // Pandoc-style footnotes
|
||||||
|
NoEmptyLineBeforeBlock // No need to insert an empty line to start a (code, quote, ordered list, unordered list) block
|
||||||
|
HeadingIDs // specify heading IDs with {#id}
|
||||||
|
Titleblock // Titleblock ala pandoc
|
||||||
|
AutoHeadingIDs // Create the heading ID from the text
|
||||||
|
BackslashLineBreak // Translate trailing backslashes into line breaks
|
||||||
|
DefinitionLists // Render definition lists
|
||||||
|
|
||||||
|
CommonHTMLFlags HTMLFlags = UseXHTML | Smartypants |
|
||||||
|
SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes
|
||||||
|
|
||||||
|
CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode |
|
||||||
|
Autolink | Strikethrough | SpaceHeadings | HeadingIDs |
|
||||||
|
BackslashLineBreak | DefinitionLists
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListType contains bitwise or'ed flags for list and list item objects.
|
||||||
|
type ListType int
|
||||||
|
|
||||||
|
// These are the possible flag values for the ListItem renderer.
|
||||||
|
// Multiple flag values may be ORed together.
|
||||||
|
// These are mostly of interest if you are writing a new output format.
|
||||||
|
const (
|
||||||
|
ListTypeOrdered ListType = 1 << iota
|
||||||
|
ListTypeDefinition
|
||||||
|
ListTypeTerm
|
||||||
|
|
||||||
|
ListItemContainsBlock
|
||||||
|
ListItemBeginningOfList // TODO: figure out if this is of any use now
|
||||||
|
ListItemEndOfList
|
||||||
|
)
|
||||||
|
|
||||||
|
// CellAlignFlags holds a type of alignment in a table cell.
|
||||||
|
type CellAlignFlags int
|
||||||
|
|
||||||
|
// These are the possible flag values for the table cell renderer.
|
||||||
|
// Only a single one of these values will be used; they are not ORed together.
|
||||||
|
// These are mostly of interest if you are writing a new output format.
|
||||||
|
const (
|
||||||
|
TableAlignmentLeft CellAlignFlags = 1 << iota
|
||||||
|
TableAlignmentRight
|
||||||
|
TableAlignmentCenter = (TableAlignmentLeft | TableAlignmentRight)
|
||||||
|
)
|
||||||
|
|
||||||
|
// The size of a tab stop.
|
||||||
|
const (
|
||||||
|
TabSizeDefault = 4
|
||||||
|
TabSizeDouble = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// blockTags is a set of tags that are recognized as HTML block tags.
|
||||||
|
// Any of these can be included in markdown text without special escaping.
|
||||||
|
var blockTags = map[string]struct{}{
|
||||||
|
"blockquote": {},
|
||||||
|
"del": {},
|
||||||
|
"div": {},
|
||||||
|
"dl": {},
|
||||||
|
"fieldset": {},
|
||||||
|
"form": {},
|
||||||
|
"h1": {},
|
||||||
|
"h2": {},
|
||||||
|
"h3": {},
|
||||||
|
"h4": {},
|
||||||
|
"h5": {},
|
||||||
|
"h6": {},
|
||||||
|
"iframe": {},
|
||||||
|
"ins": {},
|
||||||
|
"math": {},
|
||||||
|
"noscript": {},
|
||||||
|
"ol": {},
|
||||||
|
"pre": {},
|
||||||
|
"p": {},
|
||||||
|
"script": {},
|
||||||
|
"style": {},
|
||||||
|
"table": {},
|
||||||
|
"ul": {},
|
||||||
|
|
||||||
|
// HTML5
|
||||||
|
"address": {},
|
||||||
|
"article": {},
|
||||||
|
"aside": {},
|
||||||
|
"canvas": {},
|
||||||
|
"figcaption": {},
|
||||||
|
"figure": {},
|
||||||
|
"footer": {},
|
||||||
|
"header": {},
|
||||||
|
"hgroup": {},
|
||||||
|
"main": {},
|
||||||
|
"nav": {},
|
||||||
|
"output": {},
|
||||||
|
"progress": {},
|
||||||
|
"section": {},
|
||||||
|
"video": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renderer is the rendering interface. This is mostly of interest if you are
|
||||||
|
// implementing a new rendering format.
|
||||||
|
//
|
||||||
|
// Only an HTML implementation is provided in this repository, see the README
|
||||||
|
// for external implementations.
|
||||||
|
type Renderer interface {
|
||||||
|
// RenderNode is the main rendering method. It will be called once for
|
||||||
|
// every leaf node and twice for every non-leaf node (first with
|
||||||
|
// entering=true, then with entering=false). The method should write its
|
||||||
|
// rendition of the node to the supplied writer w.
|
||||||
|
RenderNode(w io.Writer, node *Node, entering bool) WalkStatus
|
||||||
|
|
||||||
|
// RenderHeader is a method that allows the renderer to produce some
|
||||||
|
// content preceding the main body of the output document. The header is
|
||||||
|
// understood in the broad sense here. For example, the default HTML
|
||||||
|
// renderer will write not only the HTML document preamble, but also the
|
||||||
|
// table of contents if it was requested.
|
||||||
|
//
|
||||||
|
// The method will be passed an entire document tree, in case a particular
|
||||||
|
// implementation needs to inspect it to produce output.
|
||||||
|
//
|
||||||
|
// The output should be written to the supplied writer w. If your
|
||||||
|
// implementation has no header to write, supply an empty implementation.
|
||||||
|
RenderHeader(w io.Writer, ast *Node)
|
||||||
|
|
||||||
|
// RenderFooter is a symmetric counterpart of RenderHeader.
|
||||||
|
RenderFooter(w io.Writer, ast *Node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback functions for inline parsing. One such function is defined
|
||||||
|
// for each character that triggers a response when parsing inline data.
|
||||||
|
type inlineParser func(p *Markdown, data []byte, offset int) (int, *Node)
|
||||||
|
|
||||||
|
// Markdown is a type that holds extensions and the runtime state used by
|
||||||
|
// Parse, and the renderer. You can not use it directly, construct it with New.
|
||||||
|
type Markdown struct {
|
||||||
|
renderer Renderer
|
||||||
|
referenceOverride ReferenceOverrideFunc
|
||||||
|
refs map[string]*reference
|
||||||
|
inlineCallback [256]inlineParser
|
||||||
|
extensions Extensions
|
||||||
|
nesting int
|
||||||
|
maxNesting int
|
||||||
|
insideLink bool
|
||||||
|
|
||||||
|
// Footnotes need to be ordered as well as available to quickly check for
|
||||||
|
// presence. If a ref is also a footnote, it's stored both in refs and here
|
||||||
|
// in notes. Slice is nil if footnotes not enabled.
|
||||||
|
notes []*reference
|
||||||
|
|
||||||
|
doc *Node
|
||||||
|
tip *Node // = doc
|
||||||
|
oldTip *Node
|
||||||
|
lastMatchedContainer *Node // = doc
|
||||||
|
allClosed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Markdown) getRef(refid string) (ref *reference, found bool) {
|
||||||
|
if p.referenceOverride != nil {
|
||||||
|
r, overridden := p.referenceOverride(refid)
|
||||||
|
if overridden {
|
||||||
|
if r == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return &reference{
|
||||||
|
link: []byte(r.Link),
|
||||||
|
title: []byte(r.Title),
|
||||||
|
noteID: 0,
|
||||||
|
hasBlock: false,
|
||||||
|
text: []byte(r.Text)}, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// refs are case insensitive
|
||||||
|
ref, found = p.refs[strings.ToLower(refid)]
|
||||||
|
return ref, found
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Markdown) finalize(block *Node) {
|
||||||
|
above := block.Parent
|
||||||
|
block.open = false
|
||||||
|
p.tip = above
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Markdown) addChild(node NodeType, offset uint32) *Node {
|
||||||
|
return p.addExistingChild(NewNode(node), offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Markdown) addExistingChild(node *Node, offset uint32) *Node {
|
||||||
|
for !p.tip.canContain(node.Type) {
|
||||||
|
p.finalize(p.tip)
|
||||||
|
}
|
||||||
|
p.tip.AppendChild(node)
|
||||||
|
p.tip = node
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Markdown) closeUnmatchedBlocks() {
|
||||||
|
if !p.allClosed {
|
||||||
|
for p.oldTip != p.lastMatchedContainer {
|
||||||
|
parent := p.oldTip.Parent
|
||||||
|
p.finalize(p.oldTip)
|
||||||
|
p.oldTip = parent
|
||||||
|
}
|
||||||
|
p.allClosed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Public interface
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
// Reference represents the details of a link.
|
||||||
|
// See the documentation in Options for more details on use-case.
|
||||||
|
type Reference struct {
|
||||||
|
// Link is usually the URL the reference points to.
|
||||||
|
Link string
|
||||||
|
// Title is the alternate text describing the link in more detail.
|
||||||
|
Title string
|
||||||
|
// Text is the optional text to override the ref with if the syntax used was
|
||||||
|
// [refid][]
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReferenceOverrideFunc is expected to be called with a reference string and
|
||||||
|
// return either a valid Reference type that the reference string maps to or
|
||||||
|
// nil. If overridden is false, the default reference logic will be executed.
|
||||||
|
// See the documentation in Options for more details on use-case.
|
||||||
|
type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool)
|
||||||
|
|
||||||
|
// New constructs a Markdown processor. You can use the same With* functions as
|
||||||
|
// for Run() to customize parser's behavior and the renderer.
|
||||||
|
func New(opts ...Option) *Markdown {
|
||||||
|
var p Markdown
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&p)
|
||||||
|
}
|
||||||
|
p.refs = make(map[string]*reference)
|
||||||
|
p.maxNesting = 16
|
||||||
|
p.insideLink = false
|
||||||
|
docNode := NewNode(Document)
|
||||||
|
p.doc = docNode
|
||||||
|
p.tip = docNode
|
||||||
|
p.oldTip = docNode
|
||||||
|
p.lastMatchedContainer = docNode
|
||||||
|
p.allClosed = true
|
||||||
|
// register inline parsers
|
||||||
|
p.inlineCallback[' '] = maybeLineBreak
|
||||||
|
p.inlineCallback['*'] = emphasis
|
||||||
|
p.inlineCallback['_'] = emphasis
|
||||||
|
if p.extensions&Strikethrough != 0 {
|
||||||
|
p.inlineCallback['~'] = emphasis
|
||||||
|
}
|
||||||
|
p.inlineCallback['`'] = codeSpan
|
||||||
|
p.inlineCallback['\n'] = lineBreak
|
||||||
|
p.inlineCallback['['] = link
|
||||||
|
p.inlineCallback['<'] = leftAngle
|
||||||
|
p.inlineCallback['\\'] = escape
|
||||||
|
p.inlineCallback['&'] = entity
|
||||||
|
p.inlineCallback['!'] = maybeImage
|
||||||
|
p.inlineCallback['^'] = maybeInlineFootnote
|
||||||
|
if p.extensions&Autolink != 0 {
|
||||||
|
p.inlineCallback['h'] = maybeAutoLink
|
||||||
|
p.inlineCallback['m'] = maybeAutoLink
|
||||||
|
p.inlineCallback['f'] = maybeAutoLink
|
||||||
|
p.inlineCallback['H'] = maybeAutoLink
|
||||||
|
p.inlineCallback['M'] = maybeAutoLink
|
||||||
|
p.inlineCallback['F'] = maybeAutoLink
|
||||||
|
}
|
||||||
|
if p.extensions&Footnotes != 0 {
|
||||||
|
p.notes = make([]*reference, 0)
|
||||||
|
}
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option customizes the Markdown processor's default behavior.
|
||||||
|
type Option func(*Markdown)
|
||||||
|
|
||||||
|
// WithRenderer allows you to override the default renderer.
|
||||||
|
func WithRenderer(r Renderer) Option {
|
||||||
|
return func(p *Markdown) {
|
||||||
|
p.renderer = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithExtensions allows you to pick some of the many extensions provided by
|
||||||
|
// Blackfriday. You can bitwise OR them.
|
||||||
|
func WithExtensions(e Extensions) Option {
|
||||||
|
return func(p *Markdown) {
|
||||||
|
p.extensions = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNoExtensions turns off all extensions and custom behavior.
|
||||||
|
func WithNoExtensions() Option {
|
||||||
|
return func(p *Markdown) {
|
||||||
|
p.extensions = NoExtensions
|
||||||
|
p.renderer = NewHTMLRenderer(HTMLRendererParameters{
|
||||||
|
Flags: HTMLFlagsNone,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRefOverride sets an optional function callback that is called every
|
||||||
|
// time a reference is resolved.
|
||||||
|
//
|
||||||
|
// In Markdown, the link reference syntax can be made to resolve a link to
|
||||||
|
// a reference instead of an inline URL, in one of the following ways:
|
||||||
|
//
|
||||||
|
// * [link text][refid]
|
||||||
|
// * [refid][]
|
||||||
|
//
|
||||||
|
// Usually, the refid is defined at the bottom of the Markdown document. If
|
||||||
|
// this override function is provided, the refid is passed to the override
|
||||||
|
// function first, before consulting the defined refids at the bottom. If
|
||||||
|
// the override function indicates an override did not occur, the refids at
|
||||||
|
// the bottom will be used to fill in the link details.
|
||||||
|
func WithRefOverride(o ReferenceOverrideFunc) Option {
|
||||||
|
return func(p *Markdown) {
|
||||||
|
p.referenceOverride = o
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run is the main entry point to Blackfriday. It parses and renders a
|
||||||
|
// block of markdown-encoded text.
|
||||||
|
//
|
||||||
|
// The simplest invocation of Run takes one argument, input:
|
||||||
|
// output := Run(input)
|
||||||
|
// This will parse the input with CommonExtensions enabled and render it with
|
||||||
|
// the default HTMLRenderer (with CommonHTMLFlags).
|
||||||
|
//
|
||||||
|
// Variadic arguments opts can customize the default behavior. Since Markdown
|
||||||
|
// type does not contain exported fields, you can not use it directly. Instead,
|
||||||
|
// use the With* functions. For example, this will call the most basic
|
||||||
|
// functionality, with no extensions:
|
||||||
|
// output := Run(input, WithNoExtensions())
|
||||||
|
//
|
||||||
|
// You can use any number of With* arguments, even contradicting ones. They
|
||||||
|
// will be applied in order of appearance and the latter will override the
|
||||||
|
// former:
|
||||||
|
// output := Run(input, WithNoExtensions(), WithExtensions(exts),
|
||||||
|
// WithRenderer(yourRenderer))
|
||||||
|
func Run(input []byte, opts ...Option) []byte {
|
||||||
|
r := NewHTMLRenderer(HTMLRendererParameters{
|
||||||
|
Flags: CommonHTMLFlags,
|
||||||
|
})
|
||||||
|
optList := []Option{WithRenderer(r), WithExtensions(CommonExtensions)}
|
||||||
|
optList = append(optList, opts...)
|
||||||
|
parser := New(optList...)
|
||||||
|
ast := parser.Parse(input)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
parser.renderer.RenderHeader(&buf, ast)
|
||||||
|
ast.Walk(func(node *Node, entering bool) WalkStatus {
|
||||||
|
return parser.renderer.RenderNode(&buf, node, entering)
|
||||||
|
})
|
||||||
|
parser.renderer.RenderFooter(&buf, ast)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse is an entry point to the parsing part of Blackfriday. It takes an
|
||||||
|
// input markdown document and produces a syntax tree for its contents. This
|
||||||
|
// tree can then be rendered with a default or custom renderer, or
|
||||||
|
// analyzed/transformed by the caller to whatever non-standard needs they have.
|
||||||
|
// The return value is the root node of the syntax tree.
|
||||||
|
func (p *Markdown) Parse(input []byte) *Node {
|
||||||
|
p.block(input)
|
||||||
|
// Walk the tree and finish up some of unfinished blocks
|
||||||
|
for p.tip != nil {
|
||||||
|
p.finalize(p.tip)
|
||||||
|
}
|
||||||
|
// Walk the tree again and process inline markdown in each block
|
||||||
|
p.doc.Walk(func(node *Node, entering bool) WalkStatus {
|
||||||
|
if node.Type == Paragraph || node.Type == Heading || node.Type == TableCell {
|
||||||
|
p.inline(node, node.content)
|
||||||
|
node.content = nil
|
||||||
|
}
|
||||||
|
return GoToNext
|
||||||
|
})
|
||||||
|
p.parseRefsToAST()
|
||||||
|
return p.doc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Markdown) parseRefsToAST() {
|
||||||
|
if p.extensions&Footnotes == 0 || len(p.notes) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.tip = p.doc
|
||||||
|
block := p.addBlock(List, nil)
|
||||||
|
block.IsFootnotesList = true
|
||||||
|
block.ListFlags = ListTypeOrdered
|
||||||
|
flags := ListItemBeginningOfList
|
||||||
|
// Note: this loop is intentionally explicit, not range-form. This is
|
||||||
|
// because the body of the loop will append nested footnotes to p.notes and
|
||||||
|
// we need to process those late additions. Range form would only walk over
|
||||||
|
// the fixed initial set.
|
||||||
|
for i := 0; i < len(p.notes); i++ {
|
||||||
|
ref := p.notes[i]
|
||||||
|
p.addExistingChild(ref.footnote, 0)
|
||||||
|
block := ref.footnote
|
||||||
|
block.ListFlags = flags | ListTypeOrdered
|
||||||
|
block.RefLink = ref.link
|
||||||
|
if ref.hasBlock {
|
||||||
|
flags |= ListItemContainsBlock
|
||||||
|
p.block(ref.title)
|
||||||
|
} else {
|
||||||
|
p.inline(block, ref.title)
|
||||||
|
}
|
||||||
|
flags &^= ListItemBeginningOfList | ListItemContainsBlock
|
||||||
|
}
|
||||||
|
above := block.Parent
|
||||||
|
finalizeList(block)
|
||||||
|
p.tip = above
|
||||||
|
block.Walk(func(node *Node, entering bool) WalkStatus {
|
||||||
|
if node.Type == Paragraph || node.Type == Heading {
|
||||||
|
p.inline(node, node.content)
|
||||||
|
node.content = nil
|
||||||
|
}
|
||||||
|
return GoToNext
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Link references
|
||||||
|
//
|
||||||
|
// This section implements support for references that (usually) appear
|
||||||
|
// as footnotes in a document, and can be referenced anywhere in the document.
|
||||||
|
// The basic format is:
|
||||||
|
//
|
||||||
|
// [1]: http://www.google.com/ "Google"
|
||||||
|
// [2]: http://www.github.com/ "Github"
|
||||||
|
//
|
||||||
|
// Anywhere in the document, the reference can be linked by referring to its
|
||||||
|
// label, i.e., 1 and 2 in this example, as in:
|
||||||
|
//
|
||||||
|
// This library is hosted on [Github][2], a git hosting site.
|
||||||
|
//
|
||||||
|
// Actual footnotes as specified in Pandoc and supported by some other Markdown
|
||||||
|
// libraries such as php-markdown are also taken care of. They look like this:
|
||||||
|
//
|
||||||
|
// This sentence needs a bit of further explanation.[^note]
|
||||||
|
//
|
||||||
|
// [^note]: This is the explanation.
|
||||||
|
//
|
||||||
|
// Footnotes should be placed at the end of the document in an ordered list.
|
||||||
|
// Finally, there are inline footnotes such as:
|
||||||
|
//
|
||||||
|
// Inline footnotes^[Also supported.] provide a quick inline explanation,
|
||||||
|
// but are rendered at the bottom of the document.
|
||||||
|
//
|
||||||
|
|
||||||
|
// reference holds all information necessary for a reference-style links or
|
||||||
|
// footnotes.
|
||||||
|
//
|
||||||
|
// Consider this markdown with reference-style links:
|
||||||
|
//
|
||||||
|
// [link][ref]
|
||||||
|
//
|
||||||
|
// [ref]: /url/ "tooltip title"
|
||||||
|
//
|
||||||
|
// It will be ultimately converted to this HTML:
|
||||||
|
//
|
||||||
|
// <p><a href=\"/url/\" title=\"title\">link</a></p>
|
||||||
|
//
|
||||||
|
// And a reference structure will be populated as follows:
|
||||||
|
//
|
||||||
|
// p.refs["ref"] = &reference{
|
||||||
|
// link: "/url/",
|
||||||
|
// title: "tooltip title",
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Alternatively, reference can contain information about a footnote. Consider
|
||||||
|
// this markdown:
|
||||||
|
//
|
||||||
|
// Text needing a footnote.[^a]
|
||||||
|
//
|
||||||
|
// [^a]: This is the note
|
||||||
|
//
|
||||||
|
// A reference structure will be populated as follows:
|
||||||
|
//
|
||||||
|
// p.refs["a"] = &reference{
|
||||||
|
// link: "a",
|
||||||
|
// title: "This is the note",
|
||||||
|
// noteID: <some positive int>,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// TODO: As you can see, it begs for splitting into two dedicated structures
|
||||||
|
// for refs and for footnotes.
|
||||||
|
type reference struct {
|
||||||
|
link []byte
|
||||||
|
title []byte
|
||||||
|
noteID int // 0 if not a footnote ref
|
||||||
|
hasBlock bool
|
||||||
|
footnote *Node // a link to the Item node within a list of footnotes
|
||||||
|
|
||||||
|
text []byte // only gets populated by refOverride feature with Reference.Text
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reference) String() string {
|
||||||
|
return fmt.Sprintf("{link: %q, title: %q, text: %q, noteID: %d, hasBlock: %v}",
|
||||||
|
r.link, r.title, r.text, r.noteID, r.hasBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether or not data starts with a reference link.
|
||||||
|
// If so, it is parsed and stored in the list of references
|
||||||
|
// (in the render struct).
|
||||||
|
// Returns the number of bytes to skip to move past it,
|
||||||
|
// or zero if the first line is not a reference.
|
||||||
|
func isReference(p *Markdown, data []byte, tabSize int) int {
|
||||||
|
// up to 3 optional leading spaces
|
||||||
|
if len(data) < 4 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
for i < 3 && data[i] == ' ' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
noteID := 0
|
||||||
|
|
||||||
|
// id part: anything but a newline between brackets
|
||||||
|
if data[i] != '[' {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
if p.extensions&Footnotes != 0 {
|
||||||
|
if i < len(data) && data[i] == '^' {
|
||||||
|
// we can set it to anything here because the proper noteIds will
|
||||||
|
// be assigned later during the second pass. It just has to be != 0
|
||||||
|
noteID = 1
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idOffset := i
|
||||||
|
for i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != ']' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i >= len(data) || data[i] != ']' {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
idEnd := i
|
||||||
|
// footnotes can have empty ID, like this: [^], but a reference can not be
|
||||||
|
// empty like this: []. Break early if it's not a footnote and there's no ID
|
||||||
|
if noteID == 0 && idOffset == idEnd {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// spacer: colon (space | tab)* newline? (space | tab)*
|
||||||
|
i++
|
||||||
|
if i >= len(data) || data[i] != ':' {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i < len(data) && (data[i] == '\n' || data[i] == '\r') {
|
||||||
|
i++
|
||||||
|
if i < len(data) && data[i] == '\n' && data[i-1] == '\r' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i >= len(data) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
linkOffset, linkEnd int
|
||||||
|
titleOffset, titleEnd int
|
||||||
|
lineEnd int
|
||||||
|
raw []byte
|
||||||
|
hasBlock bool
|
||||||
|
)
|
||||||
|
|
||||||
|
if p.extensions&Footnotes != 0 && noteID != 0 {
|
||||||
|
linkOffset, linkEnd, raw, hasBlock = scanFootnote(p, data, i, tabSize)
|
||||||
|
lineEnd = linkEnd
|
||||||
|
} else {
|
||||||
|
linkOffset, linkEnd, titleOffset, titleEnd, lineEnd = scanLinkRef(p, data, i)
|
||||||
|
}
|
||||||
|
if lineEnd == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// a valid ref has been found
|
||||||
|
|
||||||
|
ref := &reference{
|
||||||
|
noteID: noteID,
|
||||||
|
hasBlock: hasBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
if noteID > 0 {
|
||||||
|
// reusing the link field for the id since footnotes don't have links
|
||||||
|
ref.link = data[idOffset:idEnd]
|
||||||
|
// if footnote, it's not really a title, it's the contained text
|
||||||
|
ref.title = raw
|
||||||
|
} else {
|
||||||
|
ref.link = data[linkOffset:linkEnd]
|
||||||
|
ref.title = data[titleOffset:titleEnd]
|
||||||
|
}
|
||||||
|
|
||||||
|
// id matches are case-insensitive
|
||||||
|
id := string(bytes.ToLower(data[idOffset:idEnd]))
|
||||||
|
|
||||||
|
p.refs[id] = ref
|
||||||
|
|
||||||
|
return lineEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
func scanLinkRef(p *Markdown, data []byte, i int) (linkOffset, linkEnd, titleOffset, titleEnd, lineEnd int) {
|
||||||
|
// link: whitespace-free sequence, optionally between angle brackets
|
||||||
|
if data[i] == '<' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
linkOffset = i
|
||||||
|
for i < len(data) && data[i] != ' ' && data[i] != '\t' && data[i] != '\n' && data[i] != '\r' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
linkEnd = i
|
||||||
|
if data[linkOffset] == '<' && data[linkEnd-1] == '>' {
|
||||||
|
linkOffset++
|
||||||
|
linkEnd--
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional spacer: (space | tab)* (newline | '\'' | '"' | '(' )
|
||||||
|
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != '\'' && data[i] != '"' && data[i] != '(' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute end-of-line
|
||||||
|
if i >= len(data) || data[i] == '\r' || data[i] == '\n' {
|
||||||
|
lineEnd = i
|
||||||
|
}
|
||||||
|
if i+1 < len(data) && data[i] == '\r' && data[i+1] == '\n' {
|
||||||
|
lineEnd++
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional (space|tab)* spacer after a newline
|
||||||
|
if lineEnd > 0 {
|
||||||
|
i = lineEnd + 1
|
||||||
|
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional title: any non-newline sequence enclosed in '"() alone on its line
|
||||||
|
if i+1 < len(data) && (data[i] == '\'' || data[i] == '"' || data[i] == '(') {
|
||||||
|
i++
|
||||||
|
titleOffset = i
|
||||||
|
|
||||||
|
// look for EOL
|
||||||
|
for i < len(data) && data[i] != '\n' && data[i] != '\r' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i+1 < len(data) && data[i] == '\n' && data[i+1] == '\r' {
|
||||||
|
titleEnd = i + 1
|
||||||
|
} else {
|
||||||
|
titleEnd = i
|
||||||
|
}
|
||||||
|
|
||||||
|
// step back
|
||||||
|
i--
|
||||||
|
for i > titleOffset && (data[i] == ' ' || data[i] == '\t') {
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
if i > titleOffset && (data[i] == '\'' || data[i] == '"' || data[i] == ')') {
|
||||||
|
lineEnd = titleEnd
|
||||||
|
titleEnd = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first bit of this logic is the same as Parser.listItem, but the rest
|
||||||
|
// is much simpler. This function simply finds the entire block and shifts it
|
||||||
|
// over by one tab if it is indeed a block (just returns the line if it's not).
|
||||||
|
// blockEnd is the end of the section in the input buffer, and contents is the
|
||||||
|
// extracted text that was shifted over one tab. It will need to be rendered at
|
||||||
|
// the end of the document.
|
||||||
|
func scanFootnote(p *Markdown, data []byte, i, indentSize int) (blockStart, blockEnd int, contents []byte, hasBlock bool) {
|
||||||
|
if i == 0 || len(data) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip leading whitespace on first line
|
||||||
|
for i < len(data) && data[i] == ' ' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
blockStart = i
|
||||||
|
|
||||||
|
// find the end of the line
|
||||||
|
blockEnd = i
|
||||||
|
for i < len(data) && data[i-1] != '\n' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// get working buffer
|
||||||
|
var raw bytes.Buffer
|
||||||
|
|
||||||
|
// put the first line into the working buffer
|
||||||
|
raw.Write(data[blockEnd:i])
|
||||||
|
blockEnd = i
|
||||||
|
|
||||||
|
// process the following lines
|
||||||
|
containsBlankLine := false
|
||||||
|
|
||||||
|
gatherLines:
|
||||||
|
for blockEnd < len(data) {
|
||||||
|
i++
|
||||||
|
|
||||||
|
// find the end of this line
|
||||||
|
for i < len(data) && data[i-1] != '\n' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it is an empty line, guess that it is part of this item
|
||||||
|
// and move on to the next line
|
||||||
|
if p.isEmpty(data[blockEnd:i]) > 0 {
|
||||||
|
containsBlankLine = true
|
||||||
|
blockEnd = i
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 0
|
||||||
|
if n = isIndented(data[blockEnd:i], indentSize); n == 0 {
|
||||||
|
// this is the end of the block.
|
||||||
|
// we don't want to include this last line in the index.
|
||||||
|
break gatherLines
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there were blank lines before this one, insert a new one now
|
||||||
|
if containsBlankLine {
|
||||||
|
raw.WriteByte('\n')
|
||||||
|
containsBlankLine = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// get rid of that first tab, write to buffer
|
||||||
|
raw.Write(data[blockEnd+n : i])
|
||||||
|
hasBlock = true
|
||||||
|
|
||||||
|
blockEnd = i
|
||||||
|
}
|
||||||
|
|
||||||
|
if data[blockEnd-1] != '\n' {
|
||||||
|
raw.WriteByte('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
contents = raw.Bytes()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Miscellaneous helper functions
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
// Test if a character is a punctuation symbol.
|
||||||
|
// Taken from a private function in regexp in the stdlib.
|
||||||
|
func ispunct(c byte) bool {
|
||||||
|
for _, r := range []byte("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") {
|
||||||
|
if c == r {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if a character is a whitespace character.
|
||||||
|
func isspace(c byte) bool {
|
||||||
|
return ishorizontalspace(c) || isverticalspace(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if a character is a horizontal whitespace character.
|
||||||
|
func ishorizontalspace(c byte) bool {
|
||||||
|
return c == ' ' || c == '\t'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if a character is a vertical character.
|
||||||
|
func isverticalspace(c byte) bool {
|
||||||
|
return c == '\n' || c == '\r' || c == '\f' || c == '\v'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if a character is letter.
|
||||||
|
func isletter(c byte) bool {
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if a character is a letter or a digit.
|
||||||
|
// TODO: check when this is looking for ASCII alnum and when it should use unicode
|
||||||
|
func isalnum(c byte) bool {
|
||||||
|
return (c >= '0' && c <= '9') || isletter(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace tab characters with spaces, aligning to the next TAB_SIZE column.
|
||||||
|
// always ends output with a newline
|
||||||
|
func expandTabs(out *bytes.Buffer, line []byte, tabSize int) {
|
||||||
|
// first, check for common cases: no tabs, or only tabs at beginning of line
|
||||||
|
i, prefix := 0, 0
|
||||||
|
slowcase := false
|
||||||
|
for i = 0; i < len(line); i++ {
|
||||||
|
if line[i] == '\t' {
|
||||||
|
if prefix == i {
|
||||||
|
prefix++
|
||||||
|
} else {
|
||||||
|
slowcase = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to decode runes if all tabs are at the beginning of the line
|
||||||
|
if !slowcase {
|
||||||
|
for i = 0; i < prefix*tabSize; i++ {
|
||||||
|
out.WriteByte(' ')
|
||||||
|
}
|
||||||
|
out.Write(line[prefix:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// the slow case: we need to count runes to figure out how
|
||||||
|
// many spaces to insert for each tab
|
||||||
|
column := 0
|
||||||
|
i = 0
|
||||||
|
for i < len(line) {
|
||||||
|
start := i
|
||||||
|
for i < len(line) && line[i] != '\t' {
|
||||||
|
_, size := utf8.DecodeRune(line[i:])
|
||||||
|
i += size
|
||||||
|
column++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > start {
|
||||||
|
out.Write(line[start:i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= len(line) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
out.WriteByte(' ')
|
||||||
|
column++
|
||||||
|
if column%tabSize == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find if a line counts as indented or not.
|
||||||
|
// Returns number of characters the indent is (0 = not indented).
|
||||||
|
func isIndented(data []byte, indentSize int) int {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if data[0] == '\t' {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if len(data) < indentSize {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
for i := 0; i < indentSize; i++ {
|
||||||
|
if data[i] != ' ' {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indentSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a url-safe slug for fragments
|
||||||
|
func slugify(in []byte) []byte {
|
||||||
|
if len(in) == 0 {
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
out := make([]byte, 0, len(in))
|
||||||
|
sym := false
|
||||||
|
|
||||||
|
for _, ch := range in {
|
||||||
|
if isalnum(ch) {
|
||||||
|
sym = false
|
||||||
|
out = append(out, ch)
|
||||||
|
} else if sym {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
out = append(out, '-')
|
||||||
|
sym = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var a, b int
|
||||||
|
var ch byte
|
||||||
|
for a, ch = range out {
|
||||||
|
if ch != '-' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for b = len(out) - 1; b > 0; b-- {
|
||||||
|
if out[b] != '-' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out[a : b+1]
|
||||||
|
}
|
||||||
360
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/node.go
generated
vendored
Normal file
360
tests/metrics/cmd/checkmetrics/vendor/github.com/russross/blackfriday/v2/node.go
generated
vendored
Normal file
@@ -0,0 +1,360 @@
|
|||||||
|
package blackfriday
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeType specifies a type of a single node of a syntax tree. Usually one
|
||||||
|
// node (and its type) corresponds to a single markdown feature, e.g. emphasis
|
||||||
|
// or code block.
|
||||||
|
type NodeType int
|
||||||
|
|
||||||
|
// Constants for identifying different types of nodes. See NodeType.
|
||||||
|
const (
|
||||||
|
Document NodeType = iota
|
||||||
|
BlockQuote
|
||||||
|
List
|
||||||
|
Item
|
||||||
|
Paragraph
|
||||||
|
Heading
|
||||||
|
HorizontalRule
|
||||||
|
Emph
|
||||||
|
Strong
|
||||||
|
Del
|
||||||
|
Link
|
||||||
|
Image
|
||||||
|
Text
|
||||||
|
HTMLBlock
|
||||||
|
CodeBlock
|
||||||
|
Softbreak
|
||||||
|
Hardbreak
|
||||||
|
Code
|
||||||
|
HTMLSpan
|
||||||
|
Table
|
||||||
|
TableCell
|
||||||
|
TableHead
|
||||||
|
TableBody
|
||||||
|
TableRow
|
||||||
|
)
|
||||||
|
|
||||||
|
var nodeTypeNames = []string{
|
||||||
|
Document: "Document",
|
||||||
|
BlockQuote: "BlockQuote",
|
||||||
|
List: "List",
|
||||||
|
Item: "Item",
|
||||||
|
Paragraph: "Paragraph",
|
||||||
|
Heading: "Heading",
|
||||||
|
HorizontalRule: "HorizontalRule",
|
||||||
|
Emph: "Emph",
|
||||||
|
Strong: "Strong",
|
||||||
|
Del: "Del",
|
||||||
|
Link: "Link",
|
||||||
|
Image: "Image",
|
||||||
|
Text: "Text",
|
||||||
|
HTMLBlock: "HTMLBlock",
|
||||||
|
CodeBlock: "CodeBlock",
|
||||||
|
Softbreak: "Softbreak",
|
||||||
|
Hardbreak: "Hardbreak",
|
||||||
|
Code: "Code",
|
||||||
|
HTMLSpan: "HTMLSpan",
|
||||||
|
Table: "Table",
|
||||||
|
TableCell: "TableCell",
|
||||||
|
TableHead: "TableHead",
|
||||||
|
TableBody: "TableBody",
|
||||||
|
TableRow: "TableRow",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t NodeType) String() string {
|
||||||
|
return nodeTypeNames[t]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListData contains fields relevant to a List and Item node type.
|
||||||
|
type ListData struct {
|
||||||
|
ListFlags ListType
|
||||||
|
Tight bool // Skip <p>s around list item data if true
|
||||||
|
BulletChar byte // '*', '+' or '-' in bullet lists
|
||||||
|
Delimiter byte // '.' or ')' after the number in ordered lists
|
||||||
|
RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering
|
||||||
|
IsFootnotesList bool // This is a list of footnotes
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinkData contains fields relevant to a Link node type.
|
||||||
|
type LinkData struct {
|
||||||
|
Destination []byte // Destination is what goes into a href
|
||||||
|
Title []byte // Title is the tooltip thing that goes in a title attribute
|
||||||
|
NoteID int // NoteID contains a serial number of a footnote, zero if it's not a footnote
|
||||||
|
Footnote *Node // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil.
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeBlockData contains fields relevant to a CodeBlock node type.
|
||||||
|
type CodeBlockData struct {
|
||||||
|
IsFenced bool // Specifies whether it's a fenced code block or an indented one
|
||||||
|
Info []byte // This holds the info string
|
||||||
|
FenceChar byte
|
||||||
|
FenceLength int
|
||||||
|
FenceOffset int
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableCellData contains fields relevant to a TableCell node type.
|
||||||
|
type TableCellData struct {
|
||||||
|
IsHeader bool // This tells if it's under the header row
|
||||||
|
Align CellAlignFlags // This holds the value for align attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeadingData contains fields relevant to a Heading node type.
|
||||||
|
type HeadingData struct {
|
||||||
|
Level int // This holds the heading level number
|
||||||
|
HeadingID string // This might hold heading ID, if present
|
||||||
|
IsTitleblock bool // Specifies whether it's a title block
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node is a single element in the abstract syntax tree of the parsed document.
|
||||||
|
// It holds connections to the structurally neighboring nodes and, for certain
|
||||||
|
// types of nodes, additional information that might be needed when rendering.
|
||||||
|
type Node struct {
|
||||||
|
Type NodeType // Determines the type of the node
|
||||||
|
Parent *Node // Points to the parent
|
||||||
|
FirstChild *Node // Points to the first child, if any
|
||||||
|
LastChild *Node // Points to the last child, if any
|
||||||
|
Prev *Node // Previous sibling; nil if it's the first child
|
||||||
|
Next *Node // Next sibling; nil if it's the last child
|
||||||
|
|
||||||
|
Literal []byte // Text contents of the leaf nodes
|
||||||
|
|
||||||
|
HeadingData // Populated if Type is Heading
|
||||||
|
ListData // Populated if Type is List
|
||||||
|
CodeBlockData // Populated if Type is CodeBlock
|
||||||
|
LinkData // Populated if Type is Link
|
||||||
|
TableCellData // Populated if Type is TableCell
|
||||||
|
|
||||||
|
content []byte // Markdown content of the block nodes
|
||||||
|
open bool // Specifies an open block node that has not been finished to process yet
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNode allocates a node of a specified type.
|
||||||
|
func NewNode(typ NodeType) *Node {
|
||||||
|
return &Node{
|
||||||
|
Type: typ,
|
||||||
|
open: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) String() string {
|
||||||
|
ellipsis := ""
|
||||||
|
snippet := n.Literal
|
||||||
|
if len(snippet) > 16 {
|
||||||
|
snippet = snippet[:16]
|
||||||
|
ellipsis = "..."
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s: '%s%s'", n.Type, snippet, ellipsis)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlink removes node 'n' from the tree.
|
||||||
|
// It panics if the node is nil.
|
||||||
|
func (n *Node) Unlink() {
|
||||||
|
if n.Prev != nil {
|
||||||
|
n.Prev.Next = n.Next
|
||||||
|
} else if n.Parent != nil {
|
||||||
|
n.Parent.FirstChild = n.Next
|
||||||
|
}
|
||||||
|
if n.Next != nil {
|
||||||
|
n.Next.Prev = n.Prev
|
||||||
|
} else if n.Parent != nil {
|
||||||
|
n.Parent.LastChild = n.Prev
|
||||||
|
}
|
||||||
|
n.Parent = nil
|
||||||
|
n.Next = nil
|
||||||
|
n.Prev = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendChild adds a node 'child' as a child of 'n'.
|
||||||
|
// It panics if either node is nil.
|
||||||
|
func (n *Node) AppendChild(child *Node) {
|
||||||
|
child.Unlink()
|
||||||
|
child.Parent = n
|
||||||
|
if n.LastChild != nil {
|
||||||
|
n.LastChild.Next = child
|
||||||
|
child.Prev = n.LastChild
|
||||||
|
n.LastChild = child
|
||||||
|
} else {
|
||||||
|
n.FirstChild = child
|
||||||
|
n.LastChild = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertBefore inserts 'sibling' immediately before 'n'.
|
||||||
|
// It panics if either node is nil.
|
||||||
|
func (n *Node) InsertBefore(sibling *Node) {
|
||||||
|
sibling.Unlink()
|
||||||
|
sibling.Prev = n.Prev
|
||||||
|
if sibling.Prev != nil {
|
||||||
|
sibling.Prev.Next = sibling
|
||||||
|
}
|
||||||
|
sibling.Next = n
|
||||||
|
n.Prev = sibling
|
||||||
|
sibling.Parent = n.Parent
|
||||||
|
if sibling.Prev == nil {
|
||||||
|
sibling.Parent.FirstChild = sibling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer returns true if 'n' can contain children.
|
||||||
|
func (n *Node) IsContainer() bool {
|
||||||
|
switch n.Type {
|
||||||
|
case Document:
|
||||||
|
fallthrough
|
||||||
|
case BlockQuote:
|
||||||
|
fallthrough
|
||||||
|
case List:
|
||||||
|
fallthrough
|
||||||
|
case Item:
|
||||||
|
fallthrough
|
||||||
|
case Paragraph:
|
||||||
|
fallthrough
|
||||||
|
case Heading:
|
||||||
|
fallthrough
|
||||||
|
case Emph:
|
||||||
|
fallthrough
|
||||||
|
case Strong:
|
||||||
|
fallthrough
|
||||||
|
case Del:
|
||||||
|
fallthrough
|
||||||
|
case Link:
|
||||||
|
fallthrough
|
||||||
|
case Image:
|
||||||
|
fallthrough
|
||||||
|
case Table:
|
||||||
|
fallthrough
|
||||||
|
case TableHead:
|
||||||
|
fallthrough
|
||||||
|
case TableBody:
|
||||||
|
fallthrough
|
||||||
|
case TableRow:
|
||||||
|
fallthrough
|
||||||
|
case TableCell:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLeaf returns true if 'n' is a leaf node.
|
||||||
|
func (n *Node) IsLeaf() bool {
|
||||||
|
return !n.IsContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) canContain(t NodeType) bool {
|
||||||
|
if n.Type == List {
|
||||||
|
return t == Item
|
||||||
|
}
|
||||||
|
if n.Type == Document || n.Type == BlockQuote || n.Type == Item {
|
||||||
|
return t != Item
|
||||||
|
}
|
||||||
|
if n.Type == Table {
|
||||||
|
return t == TableHead || t == TableBody
|
||||||
|
}
|
||||||
|
if n.Type == TableHead || n.Type == TableBody {
|
||||||
|
return t == TableRow
|
||||||
|
}
|
||||||
|
if n.Type == TableRow {
|
||||||
|
return t == TableCell
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalkStatus allows NodeVisitor to have some control over the tree traversal.
|
||||||
|
// It is returned from NodeVisitor and different values allow Node.Walk to
|
||||||
|
// decide which node to go to next.
|
||||||
|
type WalkStatus int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GoToNext is the default traversal of every node.
|
||||||
|
GoToNext WalkStatus = iota
|
||||||
|
// SkipChildren tells walker to skip all children of current node.
|
||||||
|
SkipChildren
|
||||||
|
// Terminate tells walker to terminate the traversal.
|
||||||
|
Terminate
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeVisitor is a callback to be called when traversing the syntax tree.
|
||||||
|
// Called twice for every node: once with entering=true when the branch is
|
||||||
|
// first visited, then with entering=false after all the children are done.
|
||||||
|
type NodeVisitor func(node *Node, entering bool) WalkStatus
|
||||||
|
|
||||||
|
// Walk is a convenience method that instantiates a walker and starts a
|
||||||
|
// traversal of subtree rooted at n.
|
||||||
|
func (n *Node) Walk(visitor NodeVisitor) {
|
||||||
|
w := newNodeWalker(n)
|
||||||
|
for w.current != nil {
|
||||||
|
status := visitor(w.current, w.entering)
|
||||||
|
switch status {
|
||||||
|
case GoToNext:
|
||||||
|
w.next()
|
||||||
|
case SkipChildren:
|
||||||
|
w.entering = false
|
||||||
|
w.next()
|
||||||
|
case Terminate:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type nodeWalker struct {
|
||||||
|
current *Node
|
||||||
|
root *Node
|
||||||
|
entering bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNodeWalker(root *Node) *nodeWalker {
|
||||||
|
return &nodeWalker{
|
||||||
|
current: root,
|
||||||
|
root: root,
|
||||||
|
entering: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nw *nodeWalker) next() {
|
||||||
|
if (!nw.current.IsContainer() || !nw.entering) && nw.current == nw.root {
|
||||||
|
nw.current = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if nw.entering && nw.current.IsContainer() {
|
||||||
|
if nw.current.FirstChild != nil {
|
||||||
|
nw.current = nw.current.FirstChild
|
||||||
|
nw.entering = true
|
||||||
|
} else {
|
||||||
|
nw.entering = false
|
||||||
|
}
|
||||||
|
} else if nw.current.Next == nil {
|
||||||
|
nw.current = nw.current.Parent
|
||||||
|
nw.entering = false
|
||||||
|
} else {
|
||||||
|
nw.current = nw.current.Next
|
||||||
|
nw.entering = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dump(ast *Node) {
|
||||||
|
fmt.Println(dumpString(ast))
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpR(ast *Node, depth int) string {
|
||||||
|
if ast == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
indent := bytes.Repeat([]byte("\t"), depth)
|
||||||
|
content := ast.Literal
|
||||||
|
if content == nil {
|
||||||
|
content = ast.content
|
||||||
|
}
|
||||||
|
result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content)
|
||||||
|
for n := ast.FirstChild; n != nil; n = n.Next {
|
||||||
|
result += dumpR(n, depth+1)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpString(ast *Node) string {
|
||||||
|
return dumpR(ast, 0)
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user