commit
846cadd5fd
@ -1,48 +1,58 @@
|
||||
module github.com/jesseduffield/lazydocker
|
||||
|
||||
go 1.12
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/docker/docker v0.7.3-0.20190307005417-54dddadc7d5d
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/fatih/color v1.7.0
|
||||
github.com/go-errors/errors v1.0.1
|
||||
github.com/gogo/protobuf v1.3.1 // indirect
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
|
||||
github.com/google/go-cmp v0.3.0 // indirect
|
||||
github.com/gorilla/mux v1.7.3 // indirect
|
||||
github.com/imdario/mergo v0.3.8
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20200201013258-57fdcf23edc5
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9 // indirect
|
||||
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56
|
||||
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/samber/lo v1.20.0
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.1 // indirect
|
||||
github.com/google/go-cmp v0.3.0 // indirect
|
||||
github.com/gorilla/mux v1.7.3 // indirect
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.11 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.8 // indirect
|
||||
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
|
||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
||||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||
github.com/stretchr/testify v1.2.2
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 // indirect
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 // indirect
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
google.golang.org/grpc v1.22.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gotest.tools v2.2.0+incompatible // indirect
|
||||
)
|
||||
|
@ -1,9 +0,0 @@
|
||||
module github.com/Microsoft/go-winio
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/sirupsen/logrus v1.4.1
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
|
||||
)
|
@ -1,16 +0,0 @@
|
||||
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/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
@ -1,8 +0,0 @@
|
||||
module github.com/OpenPeeDeeP/xdg
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.1.1 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
)
|
@ -1,8 +0,0 @@
|
||||
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
@ -0,0 +1,6 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- tip
|
@ -0,0 +1,2 @@
|
||||
test:
|
||||
go test
|
@ -0,0 +1,93 @@
|
||||
# go-throttle [![GoDoc](https://godoc.org/github.com/boz/go-throttle?status.svg)](https://godoc.org/github.com/boz/go-throttle) [![Build Status](https://travis-ci.org/boz/go-throttle.svg?branch=master)](https://travis-ci.org/boz/go-throttle)
|
||||
|
||||
Package `throttle` provides functionality to limit the frequency with which code is called
|
||||
|
||||
Throttling is of the `Trigger()` method and depends on the parameters passed (`period`, `trailing`).
|
||||
|
||||
The `period` parameter defines how often the throttled code can run. A period of one second means
|
||||
that the throttled code will run at most once per second.
|
||||
|
||||
The `trailing` parameter defines what hapens if `Trigger()` is called after the throttled code has been
|
||||
started, but before the period is finished. If `trailing` is false then these triggers are ignored.
|
||||
If `trailing` is true then the throttled code is executed one more time at the beginning of the next period.
|
||||
|
||||
Example with `period = time.Second` and `trailing = false`:
|
||||
|
||||
Whole seconds after first trigger...|0|0|0|0|1|1|1|1|
|
||||
Trigger() gets called...............|X| |X| | |X| | |
|
||||
Throttled code gets called..........|X| | | | |X| | |
|
||||
|
||||
Note that the second `Trigger()` had no effect. The third `Trigger()` caused immediate execution of the
|
||||
throttled code.
|
||||
|
||||
Example with `period = time.Second` and `trailing = true`:
|
||||
|
||||
Whole seconds after first trigger...|0|0|0|0|1|1|1|1|
|
||||
Trigger() gets called...............|X| |X| | |X| | |
|
||||
Throttled code gets called..........|X| | | |X| | | |
|
||||
|
||||
Note that the second `Trigger()` causes the throttled code to get called once the first period is over.
|
||||
The third `Trigger()` will do the same.
|
||||
|
||||
## Usage
|
||||
|
||||
Throttling execution of a function:
|
||||
|
||||
```go
|
||||
throttle := throttle.ThrottleFunc(period, false, func() {
|
||||
fmt.Println("fun, throttled.")
|
||||
})
|
||||
|
||||
go func() {
|
||||
for i := 0; i < 5; i++ {
|
||||
throttle.Trigger()
|
||||
time.Sleep(period / 6)
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(2 * period)
|
||||
throttle.Stop()
|
||||
|
||||
// Output: fun, throttled.
|
||||
```
|
||||
|
||||
Throttling arbitrary code:
|
||||
|
||||
```go
|
||||
package cache
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/boz/go-throttle"
|
||||
)
|
||||
|
||||
type CacheRebuilder struct {
|
||||
throttle throttle.Throttle
|
||||
}
|
||||
|
||||
// Create a cache rebuilder which will rebuild the cache at most once every 5 minutes, regardless
|
||||
// of how often a rebuild is requested.
|
||||
func NewRebuilder() *CacheRebuilder {
|
||||
cr := &CacheRebuilder{NewThrottle(5*time.Minute, true)}
|
||||
|
||||
go func() {
|
||||
for cr.throttle.Next() {
|
||||
cr.doRebuild()
|
||||
}
|
||||
}()
|
||||
|
||||
return cr
|
||||
}
|
||||
|
||||
func (cr *CacheRebuilder) Stop() {
|
||||
cr.throttle.Stop()
|
||||
}
|
||||
|
||||
func (cr *CacheRebuilder) Rebuild() {
|
||||
cr.throttle.Trigger()
|
||||
}
|
||||
|
||||
func (cr *CacheRebuilder) doRebuild() {
|
||||
// actually rebuild the cache.
|
||||
}
|
||||
```
|
@ -0,0 +1,131 @@
|
||||
// Package throttle provides functionality to limit the frequency with which code is called
|
||||
//
|
||||
// Throttling is of the Trigger() method and depends on the parameters passed (period, trailing).
|
||||
//
|
||||
// The period parameter defines how often the throttled code can run. A period of one second means
|
||||
// that the throttled code will run at most once per second.
|
||||
//
|
||||
// The trailing parameter defines what hapens if Trigger() is called after the throttled code has been
|
||||
// started, but before the period is finished. If trailing is false then these triggers are ignored.
|
||||
// If trailing is true then the throttled code is executed one more time at the beginning of the next period.
|
||||
//
|
||||
// Example with period = time.Second and trailing = false:
|
||||
//
|
||||
// Whole seconds after first trigger...|0|0|0|0|1|1|1|1|
|
||||
// Trigger() gets called...............|X| |X| | |X| | |
|
||||
// Throttled code gets called..........|X| | | | |X| | |
|
||||
//
|
||||
// Note that the second trigger had no effect. The third Trigger() caused immediate execution of the
|
||||
// throttled code.
|
||||
//
|
||||
// Example with period = time.Second and trailing = true:
|
||||
//
|
||||
// Whole seconds after first trigger...|0|0|0|0|1|1|1|1|
|
||||
// Trigger() gets called...............|X| |X| | |X| | |
|
||||
// Throttled code gets called..........|X| | | |X| | | |
|
||||
//
|
||||
// Note that the second Trigger() causes the throttled code to get called once the first period is over.
|
||||
// The third Trigger() will do the same.
|
||||
package throttle
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ThrottleDriver is an interface for requesting execution of the throttled resource
|
||||
// and for stopping the throttler.
|
||||
type ThrottleDriver interface {
|
||||
// Trigger() requests execution of the throttled resource.
|
||||
Trigger()
|
||||
|
||||
// Stop() stops the throttler.
|
||||
Stop()
|
||||
}
|
||||
|
||||
// Throttle extends ThrottleDriver with Next(), which is used by the client to throttle its code.
|
||||
type Throttle interface {
|
||||
ThrottleDriver
|
||||
|
||||
// Next() returns true at most once per `period`. If false is returned the throttler has been stoped.
|
||||
Next() bool
|
||||
}
|
||||
|
||||
// NewThrottle returns a new Throttle. If trailing is true then a multiple Trigger() calls in one
|
||||
// period will cause a delayed Trigger() to be called in the next period.
|
||||
func NewThrottle(period time.Duration, trailing bool) Throttle {
|
||||
return newThrottler(period, trailing)
|
||||
}
|
||||
|
||||
// ThottleFunc executes f at most once every period. Stop() must eventually be called
|
||||
// on the return value to prevent a leaked go proc.
|
||||
func ThrottleFunc(period time.Duration, trailing bool, f func()) ThrottleDriver {
|
||||
throttler := newThrottler(period, trailing)
|
||||
go func() {
|
||||
for throttler.Next() {
|
||||
f()
|
||||
}
|
||||
}()
|
||||
return throttler
|
||||
}
|
||||
|
||||
type throttler struct {
|
||||
cond *sync.Cond
|
||||
period time.Duration
|
||||
trailing bool
|
||||
last time.Time
|
||||
waiting bool
|
||||
stop bool
|
||||
}
|
||||
|
||||
func newThrottler(period time.Duration, trailing bool) *throttler {
|
||||
return &throttler{
|
||||
period: period,
|
||||
trailing: trailing,
|
||||
cond: sync.NewCond(&sync.Mutex{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger signals an attempt to execute the throttled code.
|
||||
// If Trigger is called twice within the same period, Next() will be called once for that period
|
||||
// (and once for the next period if trailing is true).
|
||||
func (t *throttler) Trigger() {
|
||||
t.cond.L.Lock()
|
||||
defer t.cond.L.Unlock()
|
||||
|
||||
if !t.waiting && !t.stop {
|
||||
|
||||
delta := time.Now().Sub(t.last)
|
||||
|
||||
if delta > t.period {
|
||||
t.waiting = true
|
||||
t.cond.Broadcast()
|
||||
} else if t.trailing {
|
||||
t.waiting = true
|
||||
time.AfterFunc(t.period-delta, t.cond.Broadcast)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next() returns true at most once per period. While it returns true, the throttle is running.
|
||||
// When it returns false the throttle has been stopped.
|
||||
func (t *throttler) Next() bool {
|
||||
t.cond.L.Lock()
|
||||
defer t.cond.L.Unlock()
|
||||
for !t.waiting && !t.stop {
|
||||
t.cond.Wait()
|
||||
}
|
||||
if !t.stop {
|
||||
t.waiting = false
|
||||
t.last = time.Now()
|
||||
}
|
||||
return !t.stop
|
||||
}
|
||||
|
||||
// Stop the throttle
|
||||
func (t *throttler) Stop() {
|
||||
t.cond.L.Lock()
|
||||
defer t.cond.L.Unlock()
|
||||
t.stop = true
|
||||
t.cond.Broadcast()
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module github.com/integrii/flaggy
|
||||
|
||||
go 1.12
|
@ -1,3 +0,0 @@
|
||||
module github.com/jesseduffield/yaml
|
||||
|
||||
require gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
|
@ -1,2 +0,0 @@
|
||||
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=
|
@ -1 +0,0 @@
|
||||
module github.com/konsorten/go-windows-terminal-sequences
|
@ -1,3 +0,0 @@
|
||||
module github.com/mattn/go-colorable
|
||||
|
||||
require github.com/mattn/go-isatty v0.0.8
|
@ -1,4 +0,0 @@
|
||||
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
@ -1,5 +0,0 @@
|
||||
module github.com/mattn/go-isatty
|
||||
|
||||
go 1.12
|
||||
|
||||
require golang.org/x/sys v0.0.0-20191026070338-33540a1f6037
|
@ -1,2 +0,0 @@
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
@ -1,3 +0,0 @@
|
||||
module github.com/mattn/go-runewidth
|
||||
|
||||
go 1.9
|
@ -0,0 +1,38 @@
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/go
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=go
|
||||
|
||||
### Go ###
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
### Go Patch ###
|
||||
/vendor/
|
||||
/Godeps/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/go
|
||||
|
||||
cover.out
|
||||
cover.html
|
||||
.vscode
|
||||
|
||||
.idea/
|
@ -0,0 +1,7 @@
|
||||
language: go
|
||||
before_install:
|
||||
- go mod download
|
||||
- make tools
|
||||
go:
|
||||
- "1.18"
|
||||
script: make test
|
@ -0,0 +1,225 @@
|
||||
# Changelog
|
||||
|
||||
@samber: I sometimes forget to update this file. Ping me on [Twitter](https://twitter.com/samuelberthe) or open an issue in case of error. We need to keep a clear changelog for easier lib upgrade.
|
||||
|
||||
## 1.20.0 (2022-05-02)
|
||||
|
||||
Adding:
|
||||
|
||||
- lo.Synchronize
|
||||
- lo.SumBy
|
||||
|
||||
Change:
|
||||
- Removed generic type definition for lo.Try0: `lo.Try0[T]()` -> `lo.Try0()`
|
||||
|
||||
## 1.19.0 (2022-04-30)
|
||||
|
||||
Adding:
|
||||
|
||||
- lo.RepeatBy
|
||||
- lo.Subset
|
||||
- lo.Replace
|
||||
- lo.ReplaceAll
|
||||
- lo.Substring
|
||||
- lo.RuneLength
|
||||
|
||||
## 1.18.0 (2022-04-28)
|
||||
|
||||
Adding:
|
||||
|
||||
- lo.SomeBy
|
||||
- lo.EveryBy
|
||||
- lo.None
|
||||
- lo.NoneBy
|
||||
|
||||
## 1.17.0 (2022-04-27)
|
||||
|
||||
Adding:
|
||||
|
||||
- lo.Unpack2 -> lo.Unpack3
|
||||
- lo.Async0 -> lo.Async6
|
||||
|
||||
## 1.16.0 (2022-04-26)
|
||||
|
||||
Adding:
|
||||
|
||||
- lo.AttemptWithDelay
|
||||
|
||||
## 1.15.0 (2022-04-22)
|
||||
|
||||
Improvement:
|
||||
|
||||
- lo.Must: error or boolean value
|
||||
|
||||
## 1.14.0 (2022-04-21)
|
||||
|
||||
Adding:
|
||||
|
||||
- lo.Colaesce
|
||||
|
||||
## 1.13.0 (2022-04-14)
|
||||
|
||||
Adding:
|
||||
|
||||
- PickBy
|
||||
- PickByKeys
|
||||
- PickByValues
|
||||
- OmitBy
|
||||
- OmitByKeys
|
||||
- OmitByValues
|
||||
- Clamp
|
||||
- MapKeys
|
||||
- Invert
|
||||
- IfF + ElseIfF + ElseF
|
||||
- T0() + T1() + T2() + T3() + ...
|
||||
|
||||
## 1.12.0 (2022-04-12)
|
||||
|
||||
Adding:
|
||||
|
||||
- Must
|
||||
- Must{0-6}
|
||||
- FindOrElse
|
||||
- Async
|
||||
- MinBy
|
||||
- MaxBy
|
||||
- Count
|
||||
- CountBy
|
||||
- FindIndexOf
|
||||
- FindLastIndexOf
|
||||
- FilterMap
|
||||
|
||||
## 1.11.0 (2022-03-11)
|
||||
|
||||
Adding:
|
||||
|
||||
- Try
|
||||
- Try{0-6}
|
||||
- TryWitchValue
|
||||
- TryCatch
|
||||
- TryCatchWitchValue
|
||||
- Debounce
|
||||
- Reject
|
||||
|
||||
## 1.10.0 (2022-03-11)
|
||||
|
||||
Adding:
|
||||
|
||||
- Range
|
||||
- RangeFrom
|
||||
- RangeWithSteps
|
||||
|
||||
## 1.9.0 (2022-03-10)
|
||||
|
||||
Added
|
||||
|
||||
- Drop
|
||||
- DropRight
|
||||
- DropWhile
|
||||
- DropRightWhile
|
||||
|
||||
## 1.8.0 (2022-03-10)
|
||||
|
||||
Adding Union.
|
||||
|
||||
## 1.7.0 (2022-03-09)
|
||||
|
||||
Adding ContainBy
|
||||
|
||||
Adding MapValues
|
||||
|
||||
Adding FlatMap
|
||||
|
||||
## 1.6.0 (2022-03-07)
|
||||
|
||||
Fixed PartitionBy.
|
||||
|
||||
Adding Sample
|
||||
|
||||
Adding Samples
|
||||
|
||||
## 1.5.0 (2022-03-07)
|
||||
|
||||
Adding Times
|
||||
|
||||
Adding Attempt
|
||||
|
||||
Adding Repeat
|
||||
|
||||
## 1.4.0 (2022-03-07)
|
||||
|
||||
- adding tuple types (2->9)
|
||||
- adding Zip + Unzip
|
||||
- adding lo.PartitionBy + lop.PartitionBy
|
||||
- adding lop.GroupBy
|
||||
- fixing Nth
|
||||
|
||||
## 1.3.0 (2022-03-03)
|
||||
|
||||
Last and Nth return errors
|
||||
|
||||
## 1.2.0 (2022-03-03)
|
||||
|
||||
Adding `lop.Map` and `lop.ForEach`.
|
||||
|
||||
## 1.1.0 (2022-03-03)
|
||||
|
||||
Adding `i int` param to `lo.Map()`, `lo.Filter()`, `lo.ForEach()` and `lo.Reduce()` predicates.
|
||||
|
||||
## 1.0.0 (2022-03-02)
|
||||
|
||||
*Initial release*
|
||||
|
||||
Supported helpers for slices:
|
||||
|
||||
- Filter
|
||||
- Map
|
||||
- Reduce
|
||||
- ForEach
|
||||
- Uniq
|
||||
- UniqBy
|
||||
- GroupBy
|
||||
- Chunk
|
||||
- Flatten
|
||||
- Shuffle
|
||||
- Reverse
|
||||
- Fill
|
||||
- ToMap
|
||||
|
||||
Supported helpers for maps:
|
||||
|
||||
- Keys
|
||||
- Values
|
||||
- Entries
|
||||
- FromEntries
|
||||
- Assign (maps merge)
|
||||
|
||||
Supported intersection helpers:
|
||||
|
||||
- Contains
|
||||
- Every
|
||||
- Some
|
||||
- Intersect
|
||||
- Difference
|
||||
|
||||
Supported search helpers:
|
||||
|
||||
- IndexOf
|
||||
- LastIndexOf
|
||||
- Find
|
||||
- Min
|
||||
- Max
|
||||
- Last
|
||||
- Nth
|
||||
|
||||
Other functional programming helpers:
|
||||
|
||||
- Ternary (1 line if/else statement)
|
||||
- If / ElseIf / Else
|
||||
- Switch / Case / Default
|
||||
- ToPtr
|
||||
- ToSlicePtr
|
||||
|
||||
Constraints:
|
||||
|
||||
- Clonable
|
@ -0,0 +1,8 @@
|
||||
|
||||
FROM golang:1.18
|
||||
|
||||
WORKDIR /go/src/github.com/samber/lo
|
||||
|
||||
COPY Makefile go.* ./
|
||||
|
||||
RUN make tools
|
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Samuel Berthe
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -0,0 +1,51 @@
|
||||
|
||||
BIN=go
|
||||
# BIN=go1.18beta1
|
||||
|
||||
go1.18beta1:
|
||||
go install golang.org/dl/go1.18beta1@latest
|
||||
go1.18beta1 download
|
||||
|
||||
build:
|
||||
${BIN} build -v ./...
|
||||
|
||||
test:
|
||||
go test -race -v ./...
|
||||
watch-test:
|
||||
reflex -t 50ms -s -- sh -c 'gotest -race -v ./...'
|
||||
|
||||
bench:
|
||||
go test -benchmem -count 3 -bench ./...
|
||||
watch-bench:
|
||||
reflex -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...'
|
||||
|
||||
coverage:
|
||||
${BIN} test -v -coverprofile cover.out .
|
||||
${BIN} tool cover -html=cover.out -o cover.html
|
||||
|
||||
# tools
|
||||
tools:
|
||||
${BIN} install github.com/cespare/reflex@latest
|
||||
${BIN} install github.com/rakyll/gotest@latest
|
||||
${BIN} install github.com/psampaz/go-mod-outdated@latest
|
||||
${BIN} install github.com/jondot/goweight@latest
|
||||
${BIN} install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
${BIN} get -t -u golang.org/x/tools/cmd/cover
|
||||
${BIN} get -t -u github.com/sonatype-nexus-community/nancy@latest
|
||||
go mod tidy
|
||||
|
||||
lint:
|
||||
golangci-lint run --timeout 60s --max-same-issues 50 ./...
|
||||
lint-fix:
|
||||
golangci-lint run --timeout 60s --max-same-issues 50 --fix ./...
|
||||
|
||||
audit: tools
|
||||
${BIN} mod tidy
|
||||
${BIN} list -json -m all | nancy sleuth
|
||||
|
||||
outdated: tools
|
||||
${BIN} mod tidy
|
||||
${BIN} list -u -m -json all | go-mod-outdated -update -direct
|
||||
|
||||
weight: tools
|
||||
goweight
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,95 @@
|
||||
package lo
|
||||
|
||||
import "sync"
|
||||
|
||||
type synchronize struct {
|
||||
locker sync.Locker
|
||||
}
|
||||
|
||||
func (s *synchronize) Do(cb func()) {
|
||||
s.locker.Lock()
|
||||
Try0(cb)
|
||||
s.locker.Unlock()
|
||||
}
|
||||
|
||||
// Synchronize wraps the underlying callback in a mutex. It receives an optional mutex.
|
||||
func Synchronize(opt ...sync.Locker) *synchronize {
|
||||
if len(opt) > 1 {
|
||||
panic("unexpected arguments")
|
||||
} else if len(opt) == 0 {
|
||||
opt = append(opt, &sync.Mutex{})
|
||||
}
|
||||
|
||||
return &synchronize{
|
||||
locker: opt[0],
|
||||
}
|
||||
}
|
||||
|
||||
// Async executes a function in a goroutine and returns the result in a channel.
|
||||
func Async[A any](f func() A) chan A {
|
||||
ch := make(chan A)
|
||||
go func() {
|
||||
ch <- f()
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// Async0 executes a function in a goroutine and returns a channel set once the function finishes.
|
||||
func Async0(f func()) chan struct{} {
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
f()
|
||||
ch <- struct{}{}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// Async1 is an alias to Async.
|
||||
func Async1[A any](f func() A) chan A {
|
||||
return Async(f)
|
||||
}
|
||||
|
||||
// Async2 has the same behavior as Async, but returns the 2 results as a tuple inside the channel.
|
||||
func Async2[A any, B any](f func() (A, B)) chan Tuple2[A, B] {
|
||||
ch := make(chan Tuple2[A, B])
|
||||
go func() {
|
||||
ch <- T2(f())
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// Async3 has the same behavior as Async, but returns the 3 results as a tuple inside the channel.
|
||||
func Async3[A any, B any, C any](f func() (A, B, C)) chan Tuple3[A, B, C] {
|
||||
ch := make(chan Tuple3[A, B, C])
|
||||
go func() {
|
||||
ch <- T3(f())
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// Async4 has the same behavior as Async, but returns the 4 results as a tuple inside the channel.
|
||||
func Async4[A any, B any, C any, D any](f func() (A, B, C, D)) chan Tuple4[A, B, C, D] {
|
||||
ch := make(chan Tuple4[A, B, C, D])
|
||||
go func() {
|
||||
ch <- T4(f())
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// Async5 has the same behavior as Async, but returns the 5 results as a tuple inside the channel.
|
||||
func Async5[A any, B any, C any, D any, E any](f func() (A, B, C, D, E)) chan Tuple5[A, B, C, D, E] {
|
||||
ch := make(chan Tuple5[A, B, C, D, E])
|
||||
go func() {
|
||||
ch <- T5(f())
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// Async6 has the same behavior as Async, but returns the 6 results as a tuple inside the channel.
|
||||
func Async6[A any, B any, C any, D any, E any, F any](f func() (A, B, C, D, E, F)) chan Tuple6[A, B, C, D, E, F] {
|
||||
ch := make(chan Tuple6[A, B, C, D, E, F])
|
||||
go func() {
|
||||
ch <- T6(f())
|
||||
}()
|
||||
return ch
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
package lo
|
||||
|
||||
// Ternary is a 1 line if/else statement.
|
||||
func Ternary[T any](condition bool, ifOutput T, elseOutput T) T {
|
||||
if condition {
|
||||
return ifOutput
|
||||
}
|
||||
|
||||
return elseOutput
|
||||
}
|
||||
|
||||
type ifElse[T any] struct {
|
||||
result T
|
||||
done bool
|
||||
}
|
||||
|
||||
// If.
|
||||
func If[T any](condition bool, result T) *ifElse[T] {
|
||||
if condition {
|
||||
return &ifElse[T]{result, true}
|
||||
}
|
||||
|
||||
var t T
|
||||
return &ifElse[T]{t, false}
|
||||
}
|
||||
|
||||
// IfF.
|
||||
func IfF[T any](condition bool, resultF func() T) *ifElse[T] {
|
||||
if condition {
|
||||
return &ifElse[T]{resultF(), true}
|
||||
}
|
||||
|
||||
var t T
|
||||
return &ifElse[T]{t, false}
|
||||
}
|
||||
|
||||
// ElseIf.
|
||||
func (i *ifElse[T]) ElseIf(condition bool, result T) *ifElse[T] {
|
||||
if !i.done && condition {
|
||||
i.result = result
|
||||
i.done = true
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
// ElseIfF.
|
||||
func (i *ifElse[T]) ElseIfF(condition bool, resultF func() T) *ifElse[T] {
|
||||
if !i.done && condition {
|
||||
i.result = resultF()
|
||||
i.done = true
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
// Else.
|
||||
func (i *ifElse[T]) Else(result T) T {
|
||||
if i.done {
|
||||
return i.result
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ElseF.
|
||||
func (i *ifElse[T]) ElseF(resultF func() T) T {
|
||||
if i.done {
|
||||
return i.result
|
||||
}
|
||||
|
||||
return resultF()
|
||||
}
|
||||
|
||||
type switchCase[T comparable, R any] struct {
|
||||
predicate T
|
||||
result R
|
||||
done bool
|
||||
}
|
||||
|
||||
// Switch is a pure functional switch/case/default statement.
|
||||
func Switch[T comparable, R any](predicate T) *switchCase[T, R] {
|
||||
var result R
|
||||
|
||||
return &switchCase[T, R]{
|
||||
predicate,
|
||||
result,
|
||||
false,
|
||||
}
|
||||
}
|
||||
|
||||
// Case.
|
||||
func (s *switchCase[T, R]) Case(val T, result R) *switchCase[T, R] {
|
||||
if !s.done && s.predicate == val {
|
||||
s.result = result
|
||||
s.done = true
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// CaseF.
|
||||
func (s *switchCase[T, R]) CaseF(val T, cb func() R) *switchCase[T, R] {
|
||||
if !s.done && s.predicate == val {
|
||||
s.result = cb()
|
||||
s.done = true
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Default.
|
||||
func (s *switchCase[T, R]) Default(result R) R {
|
||||
if !s.done {
|
||||
s.result = result
|
||||
}
|
||||
|
||||
return s.result
|
||||
}
|
||||
|
||||
// DefaultF.
|
||||
func (s *switchCase[T, R]) DefaultF(cb func() R) R {
|
||||
if !s.done {
|
||||
s.result = cb()
|
||||
}
|
||||
|
||||
return s.result
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package lo
|
||||
|
||||
// Clonable defines a constraint of types having Clone() T method.
|
||||
type Clonable[T any] interface {
|
||||
Clone() T
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
dev:
|
||||
build: .
|
||||
volumes:
|
||||
- ./:/go/src/github.com/samber/lo
|
||||
working_dir: /go/src/github.com/samber/lo
|
||||
command: bash -c 'make tools ; make watch-test'
|
@ -0,0 +1,166 @@
|
||||
package lo
|
||||
|
||||
// must panics if err is error or false.
|
||||
func must(err any) {
|
||||
b, isBool := err.(bool)
|
||||
if isBool && !b {
|
||||
panic("not ok")
|
||||
}
|
||||
|
||||
e, isError := err.(error)
|
||||
if isError {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Must is a helper that wraps a call to a function returning a value and an error
|
||||
// and panics if err is error or false.
|
||||
func Must[T any](val T, err any) T {
|
||||
must(err)
|
||||
return val
|
||||
}
|
||||
|
||||
// Must0 has the same behavior than Must, but callback returns no variable.
|
||||
func Must0(err any) {
|
||||
must(err)
|
||||
}
|
||||
|
||||
// Must1 is an alias to Must
|
||||
func Must1[T any](val T, err any) T {
|
||||
return Must(val, err)
|
||||
}
|
||||
|
||||
// Must2 has the same behavior than Must, but callback returns 2 variables.
|
||||
func Must2[T1 any, T2 any](val1 T1, val2 T2, err any) (T1, T2) {
|
||||
must(err)
|
||||
return val1, val2
|
||||
}
|
||||
|
||||
// Must3 has the same behavior than Must, but callback returns 3 variables.
|
||||
func Must3[T1 any, T2 any, T3 any](val1 T1, val2 T2, val3 T3, err any) (T1, T2, T3) {
|
||||
must(err)
|
||||
return val1, val2, val3
|
||||
}
|
||||
|
||||
// Must4 has the same behavior than Must, but callback returns 4 variables.
|
||||
func Must4[T1 any, T2 any, T3 any, T4 any](val1 T1, val2 T2, val3 T3, val4 T4, err any) (T1, T2, T3, T4) {
|
||||
must(err)
|
||||
return val1, val2, val3, val4
|
||||
}
|
||||
|
||||
// Must5 has the same behavior than Must, but callback returns 5 variables.
|
||||
func Must5[T1 any, T2 any, T3 any, T4 any, T5 any](val1 T1, val2 T2, val3 T3, val4 T4, val5 T5, err any) (T1, T2, T3, T4, T5) {
|
||||
must(err)
|
||||
return val1, val2, val3, val4, val5
|
||||
}
|
||||
|
||||
// Must6 has the same behavior than Must, but callback returns 6 variables.
|
||||
func Must6[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any](val1 T1, val2 T2, val3 T3, val4 T4, val5 T5, val6 T6, err any) (T1, T2, T3, T4, T5, T6) {
|
||||
must(err)
|
||||
return val1, val2, val3, val4, val5, val6
|
||||
}
|
||||
|
||||
// Try calls the function and return false in case of error.
|
||||
func Try(callback func() error) (ok bool) {
|
||||
ok = true
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
err := callback()
|
||||
if err != nil {
|
||||
ok = false
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Try0 has the same behavior than Try, but callback returns no variable.
|
||||
func Try0(callback func()) bool {
|
||||
return Try(func() error {
|
||||
callback()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Try1 is an alias to Try.
|
||||
func Try1[T any](callback func() error) bool {
|
||||
return Try(callback)
|
||||
}
|
||||
|
||||
// Try2 has the same behavior than Try, but callback returns 2 variables.
|
||||
func Try2[T any](callback func() (T, error)) bool {
|
||||
return Try(func() error {
|
||||
_, err := callback()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// Try3 has the same behavior than Try, but callback returns 3 variables.
|
||||
func Try3[T, R any](callback func() (T, R, error)) bool {
|
||||
return Try(func() error {
|
||||
_, _, err := callback()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// Try4 has the same behavior than Try, but callback returns 4 variables.
|
||||
func Try4[T, R, S any](callback func() (T, R, S, error)) bool {
|
||||
return Try(func() error {
|
||||
_, _, _, err := callback()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// Try5 has the same behavior than Try, but callback returns 5 variables.
|
||||
func Try5[T, R, S, Q any](callback func() (T, R, S, Q, error)) bool {
|
||||
return Try(func() error {
|
||||
_, _, _, _, err := callback()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// Try6 has the same behavior than Try, but callback returns 6 variables.
|
||||
func Try6[T, R, S, Q, U any](callback func() (T, R, S, Q, U, error)) bool {
|
||||
return Try(func() error {
|
||||
_, _, _, _, _, err := callback()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// TryWithErrorValue has the same behavior than Try, but also returns value passed to panic.
|
||||
func TryWithErrorValue(callback func() error) (errorValue any, ok bool) {
|
||||
ok = true
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ok = false
|
||||
errorValue = r
|
||||
}
|
||||
}()
|
||||
|
||||
err := callback()
|
||||
if err != nil {
|
||||
ok = false
|
||||
errorValue = err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TryCatch has the same behavior than Try, but calls the catch function in case of error.
|
||||
func TryCatch(callback func() error, catch func()) {
|
||||
if !Try(callback) {
|
||||
catch()
|
||||
}
|
||||
}
|
||||
|
||||
// TryCatchWithErrorValue has the same behavior than TryWithErrorValue, but calls the catch function in case of error.
|
||||
func TryCatchWithErrorValue(callback func() error, catch func(any)) {
|
||||
if err, ok := TryWithErrorValue(callback); !ok {
|
||||
catch(err)
|
||||
}
|
||||
}
|
@ -0,0 +1,240 @@
|
||||
package lo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// import "golang.org/x/exp/constraints"
|
||||
|
||||
// IndexOf returns the index at which the first occurrence of a value is found in an array or return -1
|
||||
// if the value cannot be found.
|
||||
func IndexOf[T comparable](collection []T, element T) int {
|
||||
for i, item := range collection {
|
||||
if item == element {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// LastIndexOf returns the index at which the last occurrence of a value is found in an array or return -1
|
||||
// if the value cannot be found.
|
||||
func LastIndexOf[T comparable](collection []T, element T) int {
|
||||
length := len(collection)
|
||||
|
||||
for i := length - 1; i >= 0; i-- {
|
||||
if collection[i] == element {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// Find search an element in a slice based on a predicate. It returns element and true if element was found.
|
||||
func Find[T any](collection []T, predicate func(T) bool) (T, bool) {
|
||||
for _, item := range collection {
|
||||
if predicate(item) {
|
||||
return item, true
|
||||
}
|
||||
}
|
||||
|
||||
var result T
|
||||
return result, false
|
||||
}
|
||||
|
||||
// FindIndexOf searches an element in a slice based on a predicate and returns the index and true.
|
||||
// It returns -1 and false if the element is not found.
|
||||
func FindIndexOf[T any](collection []T, predicate func(T) bool) (T, int, bool) {
|
||||
for i, item := range collection {
|
||||
if predicate(item) {
|
||||
return item, i, true
|
||||
}
|
||||
}
|
||||
|
||||
var result T
|
||||
return result, -1, false
|
||||
}
|
||||
|
||||
// FindLastIndexOf searches last element in a slice based on a predicate and returns the index and true.
|
||||
// It returns -1 and false if the element is not found.
|
||||
func FindLastIndexOf[T any](collection []T, predicate func(T) bool) (T, int, bool) {
|
||||
length := len(collection)
|
||||
|
||||
for i := length - 1; i >= 0; i-- {
|
||||
if predicate(collection[i]) {
|
||||
return collection[i], i, true
|
||||
}
|
||||
}
|
||||
|
||||
var result T
|
||||
return result, -1, false
|
||||
}
|
||||
|
||||
// FindOrElse search an element in a slice based on a predicate. It returns the element if found or a given fallback value otherwise.
|
||||
func FindOrElse[T any](collection []T, fallback T, predicate func(T) bool) T {
|
||||
for _, item := range collection {
|
||||
if predicate(item) {
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
||||
// Min search the minimum value of a collection.
|
||||
func Min[T constraints.Ordered](collection []T) T {
|
||||
var min T
|
||||
|
||||
if len(collection) == 0 {
|
||||
return min
|
||||
}
|
||||
|
||||
min = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if item < min {
|
||||
min = item
|
||||
}
|
||||
}
|
||||
|
||||
return min
|
||||
}
|
||||
|
||||
// MinBy search the minimum value of a collection using the given comparison function.
|
||||
// If several values of the collection are equal to the smallest value, returns the first such value.
|
||||
func MinBy[T any](collection []T, comparison func(T, T) bool) T {
|
||||
var min T
|
||||
|
||||
if len(collection) == 0 {
|
||||
return min
|
||||
}
|
||||
|
||||
min = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if comparison(item, min) {
|
||||
min = item
|
||||
}
|
||||
}
|
||||
|
||||
return min
|
||||
}
|
||||
|
||||
// Max searches the maximum value of a collection.
|
||||
func Max[T constraints.Ordered](collection []T) T {
|
||||
var max T
|
||||
|
||||
if len(collection) == 0 {
|
||||
return max
|
||||
}
|
||||
|
||||
max = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if item > max {
|
||||
max = item
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
||||
|
||||
// MaxBy search the maximum value of a collection using the given comparison function.
|
||||
// If several values of the collection are equal to the greatest value, returns the first such value.
|
||||
func MaxBy[T any](collection []T, comparison func(T, T) bool) T {
|
||||
var max T
|
||||
|
||||
if len(collection) == 0 {
|
||||
return max
|
||||
}
|
||||
|
||||
max = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if comparison(item, max) {
|
||||
max = item
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
||||
|
||||
// Last returns the last element of a collection or error if empty.
|
||||
func Last[T any](collection []T) (T, error) {
|
||||
length := len(collection)
|
||||
|
||||
if length == 0 {
|
||||
var t T
|
||||
return t, fmt.Errorf("last: cannot extract the last element of an empty slice")
|
||||
}
|
||||
|
||||
return collection[length-1], nil
|
||||
}
|
||||
|
||||
// Nth returns the element at index `nth` of collection. If `nth` is negative, the nth element
|
||||
// from the end is returned. An error is returned when nth is out of slice bounds.
|
||||
func Nth[T any](collection []T, nth int) (T, error) {
|
||||
if int(math.Abs(float64(nth))) >= len(collection) {
|
||||
var t T
|
||||
return t, fmt.Errorf("nth: %d out of slice bounds", nth)
|
||||
}
|
||||
|
||||
length := len(collection)
|
||||
|
||||
if nth >= 0 {
|
||||
return collection[nth], nil
|
||||
}
|
||||
|
||||
return collection[length+nth], nil
|
||||
}
|
||||
|
||||
// Sample returns a random item from collection.
|
||||
func Sample[T any](collection []T) T {
|
||||
size := len(collection)
|
||||
if size == 0 {
|
||||
return Empty[T]()
|
||||
}
|
||||
|
||||
return collection[rand.Intn(size)]
|
||||
}
|
||||
|
||||
// Samples returns N random unique items from collection.
|
||||
func Samples[T any](collection []T, count int) []T {
|
||||
size := len(collection)
|
||||
|
||||
// put values into a map, for faster deletion
|
||||
cOpy := make([]T, 0, size)
|
||||
for _, v := range collection {
|
||||
cOpy = append(cOpy, v)
|
||||
}
|
||||
|
||||
results := []T{}
|
||||
|
||||
for i := 0; i < size && i < count; i++ {
|
||||
copyLength := size - i
|
||||
|
||||
index := rand.Intn(size - i)
|
||||
results = append(results, cOpy[index])
|
||||
|
||||
// Removes element.
|
||||
// It is faster to swap with last element and remove it.
|
||||
cOpy[index] = cOpy[copyLength-1]
|
||||
cOpy = cOpy[:copyLength-1]
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
package lo
|
||||
|
||||
// Contains returns true if an element is present in a collection.
|
||||
func Contains[T comparable](collection []T, element T) bool {
|
||||
for _, item := range collection {
|
||||
if item == element {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsBy returns true if predicate function return true.
|
||||
func ContainsBy[T any](collection []T, predicate func(T) bool) bool {
|
||||
for _, item := range collection {
|
||||
if predicate(item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Every returns true if all elements of a subset are contained into a collection or if the subset is empty.
|
||||
func Every[T comparable](collection []T, subset []T) bool {
|
||||
for _, elem := range subset {
|
||||
if !Contains(collection, elem) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// EveryBy returns true if the predicate returns true for all of the elements in the collection or if the collection is empty.
|
||||
func EveryBy[V any](collection []V, predicate func(V) bool) bool {
|
||||
for _, v := range collection {
|
||||
if !predicate(v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Some returns true if at least 1 element of a subset is contained into a collection.
|
||||
// If the subset is empty Some returns false.
|
||||
func Some[T comparable](collection []T, subset []T) bool {
|
||||
for _, elem := range subset {
|
||||
if Contains(collection, elem) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SomeBy returns true if the predicate returns true for any of the elements in the collection.
|
||||
// If the collection is empty SomeBy returns false.
|
||||
func SomeBy[V any](collection []V, predicate func(V) bool) bool {
|
||||
for _, v := range collection {
|
||||
if predicate(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// None returns true if no element of a subset are contained into a collection or if the subset is empty.
|
||||
func None[V comparable](collection []V, subset []V) bool {
|
||||
for _, elem := range subset {
|
||||
if Contains(collection, elem) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// NoneBy returns true if the predicate returns true for none of the elements in the collection or if the collection is empty.
|
||||
func NoneBy[V any](collection []V, predicate func(V) bool) bool {
|
||||
for _, v := range collection {
|
||||
if predicate(v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Intersect returns the intersection between two collections.
|
||||
func Intersect[T comparable](list1 []T, list2 []T) []T {
|
||||
result := []T{}
|
||||
seen := map[T]struct{}{}
|
||||
|
||||
for _, elem := range list1 {
|
||||
seen[elem] = struct{}{}
|
||||
}
|
||||
|
||||
for _, elem := range list2 {
|
||||
if _, ok := seen[elem]; ok {
|
||||
result = append(result, elem)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Difference returns the difference between two collections.
|
||||
// The first value is the collection of element absent of list2.
|
||||
// The second value is the collection of element absent of list1.
|
||||
func Difference[T comparable](list1 []T, list2 []T) ([]T, []T) {
|
||||
left := []T{}
|
||||
right := []T{}
|
||||
|
||||
seenLeft := map[T]struct{}{}
|
||||
seenRight := map[T]struct{}{}
|
||||
|
||||
for _, elem := range list1 {
|
||||
seenLeft[elem] = struct{}{}
|
||||
}
|
||||
|
||||
for _, elem := range list2 {
|
||||
seenRight[elem] = struct{}{}
|
||||
}
|
||||
|
||||
for _, elem := range list1 {
|
||||
if _, ok := seenRight[elem]; !ok {
|
||||
left = append(left, elem)
|
||||
}
|
||||
}
|
||||
|
||||
for _, elem := range list2 {
|
||||
if _, ok := seenLeft[elem]; !ok {
|
||||
right = append(right, elem)
|
||||
}
|
||||
}
|
||||
|
||||
return left, right
|
||||
}
|
||||
|
||||
// Union returns all distinct elements from both collections.
|
||||
// result returns will not change the order of elements relatively.
|
||||
func Union[T comparable](list1 []T, list2 []T) []T {
|
||||
result := []T{}
|
||||
|
||||
seen := map[T]struct{}{}
|
||||
hasAdd := map[T]struct{}{}
|
||||
|
||||
for _, e := range list1 {
|
||||
seen[e] = struct{}{}
|
||||
}
|
||||
|
||||
for _, e := range list2 {
|
||||
seen[e] = struct{}{}
|
||||
}
|
||||
|
||||
for _, e := range list1 {
|
||||
if _, ok := seen[e]; ok {
|
||||
result = append(result, e)
|
||||
hasAdd[e] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for _, e := range list2 {
|
||||
if _, ok := hasAdd[e]; ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[e]; ok {
|
||||
result = append(result, e)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
package lo
|
||||
|
||||
// Keys creates an array of the map keys.
|
||||
func Keys[K comparable, V any](in map[K]V) []K {
|
||||
result := make([]K, 0, len(in))
|
||||
|
||||
for k := range in {
|
||||
result = append(result, k)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Values creates an array of the map values.
|
||||
func Values[K comparable, V any](in map[K]V) []V {
|
||||
result := make([]V, 0, len(in))
|
||||
|
||||
for _, v := range in {
|
||||
result = append(result, v)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// PickBy returns same map type filtered by given predicate.
|
||||
func PickBy[K comparable, V any](in map[K]V, predicate func(K, V) bool) map[K]V {
|
||||
r := map[K]V{}
|
||||
for k, v := range in {
|
||||
if predicate(k, v) {
|
||||
r[k] = v
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// PickByKeys returns same map type filtered by given keys.
|
||||
func PickByKeys[K comparable, V any](in map[K]V, keys []K) map[K]V {
|
||||
r := map[K]V{}
|
||||
for k, v := range in {
|
||||
if Contains(keys, k) {
|
||||
r[k] = v
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// PickByValues returns same map type filtered by given values.
|
||||
func PickByValues[K comparable, V comparable](in map[K]V, values []V) map[K]V {
|
||||
r := map[K]V{}
|
||||
for k, v := range in {
|
||||
if Contains(values, v) {
|
||||
r[k] = v
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// PickBy returns same map type filtered by given predicate.
|
||||
func OmitBy[K comparable, V any](in map[K]V, predicate func(K, V) bool) map[K]V {
|
||||
r := map[K]V{}
|
||||
for k, v := range in {
|
||||
if !predicate(k, v) {
|
||||
r[k] = v
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// OmitByKeys returns same map type filtered by given keys.
|
||||
func OmitByKeys[K comparable, V any](in map[K]V, keys []K) map[K]V {
|
||||
r := map[K]V{}
|
||||
for k, v := range in {
|
||||
if !Contains(keys, k) {
|
||||
r[k] = v
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// OmitByValues returns same map type filtered by given values.
|
||||
func OmitByValues[K comparable, V comparable](in map[K]V, values []V) map[K]V {
|
||||
r := map[K]V{}
|
||||
for k, v := range in {
|
||||
if !Contains(values, v) {
|
||||
r[k] = v
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Entries transforms a map into array of key/value pairs.
|
||||
func Entries[K comparable, V any](in map[K]V) []Entry[K, V] {
|
||||
entries := make([]Entry[K, V], 0, len(in))
|
||||
|
||||
for k, v := range in {
|
||||
entries = append(entries, Entry[K, V]{
|
||||
Key: k,
|
||||
Value: v,
|
||||
})
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
// FromEntries transforms an array of key/value pairs into a map.
|
||||
func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V {
|
||||
out := map[K]V{}
|
||||
|
||||
for _, v := range entries {
|
||||
out[v.Key] = v.Value
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Invert creates a map composed of the inverted keys and values. If map
|
||||
// contains duplicate values, subsequent values overwrite property assignments
|
||||
// of previous values.
|
||||
func Invert[K comparable, V comparable](in map[K]V) map[V]K {
|
||||
out := map[V]K{}
|
||||
|
||||
for k, v := range in {
|
||||
out[v] = k
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Assign merges multiple maps from left to right.
|
||||
func Assign[K comparable, V any](maps ...map[K]V) map[K]V {
|
||||
out := map[K]V{}
|
||||
|
||||
for _, m := range maps {
|
||||
for k, v := range m {
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// MapKeys manipulates a map keys and transforms it to a map of another type.
|
||||
func MapKeys[K comparable, V any, R comparable](in map[K]V, iteratee func(V, K) R) map[R]V {
|
||||
result := map[R]V{}
|
||||
|
||||
for k, v := range in {
|
||||
result[iteratee(v, k)] = v
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// MapValues manipulates a map values and transforms it to a map of another type.
|
||||
func MapValues[K comparable, V any, R any](in map[K]V, iteratee func(V, K) R) map[K]R {
|
||||
result := map[K]R{}
|
||||
|
||||
for k, v := range in {
|
||||
result[k] = iteratee(v, k)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package lo
|
||||
|
||||
import "golang.org/x/exp/constraints"
|
||||
|
||||
// Range creates an array of numbers (positive and/or negative) with given length.
|
||||
func Range(elementNum int) []int {
|
||||
length := If(elementNum < 0, -elementNum).Else(elementNum)
|
||||
result := make([]int, length)
|
||||
step := If(elementNum < 0, -1).Else(1)
|
||||
for i, j := 0, 0; i < length; i, j = i+1, j+step {
|
||||
result[i] = j
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// RangeFrom creates an array of numbers from start with specified length.
|
||||
func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum int) []T {
|
||||
length := If(elementNum < 0, -elementNum).Else(elementNum)
|
||||
result := make([]T, length)
|
||||
step := If(elementNum < 0, -1).Else(1)
|
||||
for i, j := 0, start; i < length; i, j = i+1, j+T(step) {
|
||||
result[i] = j
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// RangeWithSteps creates an array of numbers (positive and/or negative) progressing from start up to, but not including end.
|
||||
// step set to zero will return empty array.
|
||||
func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step T) []T {
|
||||
result := []T{}
|
||||
if start == end || step == 0 {
|
||||
return result
|
||||
}
|
||||
if start < end {
|
||||
if step < 0 {
|
||||
return result
|
||||
}
|
||||
for i := start; i < end; i += step {
|
||||
result = append(result, i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
if step > 0 {
|
||||
return result
|
||||
}
|
||||
for i := start; i > end; i += step {
|
||||
result = append(result, i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Clamp clamps number within the inclusive lower and upper bounds.
|
||||
func Clamp[T constraints.Ordered](value T, min T, max T) T {
|
||||
if value < min {
|
||||
return min
|
||||
} else if value > max {
|
||||
return max
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Summarizes the values in a collection.
|
||||
func SumBy[T any, R constraints.Float | constraints.Integer](collection []T, iteratee func(T) R) R {
|
||||
var sum R = 0
|
||||
for _, item := range collection {
|
||||
sum = sum + iteratee(item)
|
||||
}
|
||||
return sum
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package lo
|
||||
|
||||
// ToPtr returns a pointer copy of value.
|
||||
func ToPtr[T any](x T) *T {
|
||||
return &x
|
||||
}
|
||||
|
||||
// ToSlicePtr returns a slice of pointer copy of value.
|
||||
func ToSlicePtr[T any](collection []T) []*T {
|
||||
return Map(collection, func(x T, _ int) *T {
|
||||
return &x
|
||||
})
|
||||
}
|
||||
|
||||
// Empty returns an empty value.
|
||||
func Empty[T any]() T {
|
||||
var t T
|
||||
return t
|
||||
}
|
||||
|
||||
// Coalesce returns the first non-empty arguments. Arguments must be comparable.
|
||||
func Coalesce[T comparable](v ...T) (result T, ok bool) {
|
||||
for _, e := range v {
|
||||
if e != result {
|
||||
result = e
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package lo
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type debounce struct {
|
||||
after time.Duration
|
||||
mu *sync.Mutex
|
||||
timer *time.Timer
|
||||
done bool
|
||||
callbacks []func()
|
||||
}
|
||||
|
||||
func (d *debounce) reset() *debounce {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
if d.done {
|
||||
return d
|
||||
}
|
||||
|
||||
if d.timer != nil {
|
||||
d.timer.Stop()
|
||||
}
|
||||
|
||||
d.timer = time.AfterFunc(d.after, func() {
|
||||
for _, f := range d.callbacks {
|
||||
f()
|
||||
}
|
||||
})
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *debounce) cancel() {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
if d.timer != nil {
|
||||
d.timer.Stop()
|
||||
d.timer = nil
|
||||
}
|
||||
|
||||
d.done = true
|
||||
}
|
||||
|
||||
// NewDebounce creates a debounced instance that delays invoking functions given until after wait milliseconds have elapsed.
|
||||
func NewDebounce(duration time.Duration, f ...func()) (func(), func()) {
|
||||
d := &debounce{
|
||||
after: duration,
|
||||
mu: new(sync.Mutex),
|
||||
timer: nil,
|
||||
done: false,
|
||||
callbacks: f,
|
||||
}
|
||||
|
||||
return func() {
|
||||
d.reset()
|
||||
}, d.cancel
|
||||
}
|
||||
|
||||
// Attempt invokes a function N times until it returns valid output. Returning either the caught error or nil. When first argument is less than `1`, the function runs until a sucessfull response is returned.
|
||||
func Attempt(maxIteration int, f func(int) error) (int, error) {
|
||||
var err error
|
||||
|
||||
for i := 0; maxIteration <= 0 || i < maxIteration; i++ {
|
||||
// for retries >= 0 {
|
||||
err = f(i)
|
||||
if err == nil {
|
||||
return i + 1, nil
|
||||
}
|
||||
}
|
||||
|
||||
return maxIteration, err
|
||||
}
|
||||
|
||||
// AttemptWithDelay invokes a function N times until it returns valid output,
|
||||
// with a pause betwwen each call. Returning either the caught error or nil.
|
||||
// When first argument is less than `1`, the function runs until a sucessfull
|
||||
// response is returned.
|
||||
func AttemptWithDelay(maxIteration int, delay time.Duration, f func(int, time.Duration) error) (int, time.Duration, error) {
|
||||
var err error
|
||||
|
||||
start := time.Now()
|
||||
|
||||
for i := 0; maxIteration <= 0 || i < maxIteration; i++ {
|
||||
err = f(i, time.Since(start))
|
||||
if err == nil {
|
||||
return i + 1, time.Since(start), nil
|
||||
}
|
||||
|
||||
if maxIteration <= 0 || i+1 < maxIteration {
|
||||
time.Sleep(delay)
|
||||
}
|
||||
}
|
||||
|
||||
return maxIteration, time.Since(start), err
|
||||
}
|
||||
|
||||
// throttle ?
|
@ -0,0 +1,408 @@
|
||||
package lo
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// Filter iterates over elements of collection, returning an array of all elements predicate returns truthy for.
|
||||
func Filter[V any](collection []V, predicate func(V, int) bool) []V {
|
||||
result := []V{}
|
||||
|
||||
for i, item := range collection {
|
||||
if predicate(item, i) {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Map manipulates a slice and transforms it to a slice of another type.
|
||||
func Map[T any, R any](collection []T, iteratee func(T, int) R) []R {
|
||||
result := make([]R, len(collection))
|
||||
|
||||
for i, item := range collection {
|
||||
result[i] = iteratee(item, i)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// FilterMap returns a slice which obtained after both filtering and mapping using the given callback function.
|
||||
// The callback function should return two values:
|
||||
// - the result of the mapping operation and
|
||||
// - whether the result element should be included or not.
|
||||
func FilterMap[T any, R any](collection []T, callback func(T, int) (R, bool)) []R {
|
||||
result := []R{}
|
||||
|
||||
for i, item := range collection {
|
||||
if r, ok := callback(item, i); ok {
|
||||
result = append(result, r)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// FlatMap manipulates a slice and transforms and flattens it to a slice of another type.
|
||||
func FlatMap[T any, R any](collection []T, iteratee func(T, int) []R) []R {
|
||||
result := []R{}
|
||||
|
||||
for i, item := range collection {
|
||||
result = append(result, iteratee(item, i)...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Reduce reduces collection to a value which is the accumulated result of running each element in collection
|
||||
// through accumulator, where each successive invocation is supplied the return value of the previous.
|
||||
func Reduce[T any, R any](collection []T, accumulator func(R, T, int) R, initial R) R {
|
||||
for i, item := range collection {
|
||||
initial = accumulator(initial, item, i)
|
||||
}
|
||||
|
||||
return initial
|
||||
}
|
||||
|
||||
// ForEach iterates over elements of collection and invokes iteratee for each element.
|
||||
func ForEach[T any](collection []T, iteratee func(T, int)) {
|
||||
for i, item := range collection {
|
||||
iteratee(item, i)
|
||||
}
|
||||
}
|
||||
|
||||
// Times invokes the iteratee n times, returning an array of the results of each invocation.
|
||||
// The iteratee is invoked with index as argument.
|
||||
func Times[T any](count int, iteratee func(int) T) []T {
|
||||
result := make([]T, count)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
result[i] = iteratee(i)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Uniq returns a duplicate-free version of an array, in which only the first occurrence of each element is kept.
|
||||
// The order of result values is determined by the order they occur in the array.
|
||||
func Uniq[T comparable](collection []T) []T {
|
||||
result := make([]T, 0, len(collection))
|
||||
seen := make(map[T]struct{}, len(collection))
|
||||
|
||||
for _, item := range collection {
|
||||
if _, ok := seen[item]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
seen[item] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// UniqBy returns a duplicate-free version of an array, in which only the first occurrence of each element is kept.
|
||||
// The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is
|
||||
// invoked for each element in array to generate the criterion by which uniqueness is computed.
|
||||
func UniqBy[T any, U comparable](collection []T, iteratee func(T) U) []T {
|
||||
result := make([]T, 0, len(collection))
|
||||
seen := make(map[U]struct{}, len(collection))
|
||||
|
||||
for _, item := range collection {
|
||||
key := iteratee(item)
|
||||
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
seen[key] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GroupBy returns an object composed of keys generated from the results of running each element of collection through iteratee.
|
||||
func GroupBy[T any, U comparable](collection []T, iteratee func(T) U) map[U][]T {
|
||||
result := map[U][]T{}
|
||||
|
||||
for _, item := range collection {
|
||||
key := iteratee(item)
|
||||
|
||||
result[key] = append(result[key], item)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Chunk returns an array of elements split into groups the length of size. If array can't be split evenly,
|
||||
// the final chunk will be the remaining elements.
|
||||
func Chunk[T any](collection []T, size int) [][]T {
|
||||
if size <= 0 {
|
||||
panic("Second parameter must be greater than 0")
|
||||
}
|
||||
|
||||
result := make([][]T, 0, len(collection)/2+1)
|
||||
length := len(collection)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
chunk := i / size
|
||||
|
||||
if i%size == 0 {
|
||||
result = append(result, make([]T, 0, size))
|
||||
}
|
||||
|
||||
result[chunk] = append(result[chunk], collection[i])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// PartitionBy returns an array of elements split into groups. The order of grouped values is
|
||||
// determined by the order they occur in collection. The grouping is generated from the results
|
||||
// of running each element of collection through iteratee.
|
||||
func PartitionBy[T any, K comparable](collection []T, iteratee func(x T) K) [][]T {
|
||||
result := [][]T{}
|
||||
seen := map[K]int{}
|
||||
|
||||
for _, item := range collection {
|
||||
key := iteratee(item)
|
||||
|
||||
resultIndex, ok := seen[key]
|
||||
if !ok {
|
||||
resultIndex = len(result)
|
||||
seen[key] = resultIndex
|
||||
result = append(result, []T{})
|
||||
}
|
||||
|
||||
result[resultIndex] = append(result[resultIndex], item)
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
// unordered:
|
||||
// groups := GroupBy[T, K](collection, iteratee)
|
||||
// return Values[K, []T](groups)
|
||||
}
|
||||
|
||||
// Flatten returns an array a single level deep.
|
||||
func Flatten[T any](collection [][]T) []T {
|
||||
result := []T{}
|
||||
|
||||
for _, item := range collection {
|
||||
result = append(result, item...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Shuffle returns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm.
|
||||
func Shuffle[T any](collection []T) []T {
|
||||
rand.Shuffle(len(collection), func(i, j int) {
|
||||
collection[i], collection[j] = collection[j], collection[i]
|
||||
})
|
||||
|
||||
return collection
|
||||
}
|
||||
|
||||
// Reverse reverses array so that the first element becomes the last, the second element becomes the second to last, and so on.
|
||||
func Reverse[T any](collection []T) []T {
|
||||
length := len(collection)
|
||||
half := length / 2
|
||||
|
||||
for i := 0; i < half; i = i + 1 {
|
||||
j := length - 1 - i
|
||||
collection[i], collection[j] = collection[j], collection[i]
|
||||
}
|
||||
|
||||
return collection
|
||||
}
|
||||
|
||||
// Fill fills elements of array with `initial` value.
|
||||
func Fill[T Clonable[T]](collection []T, initial T) []T {
|
||||
result := make([]T, 0, len(collection))
|
||||
|
||||
for range collection {
|
||||
result = append(result, initial.Clone())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Repeat builds a slice with N copies of initial value.
|
||||
func Repeat[T Clonable[T]](count int, initial T) []T {
|
||||
result := make([]T, 0, count)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
result = append(result, initial.Clone())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// RepeatBy builds a slice with values returned by N calls of callback.
|
||||
func RepeatBy[T any](count int, predicate func(int) T) []T {
|
||||
result := make([]T, 0, count)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
result = append(result, predicate(i))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// KeyBy transforms a slice or an array of structs to a map based on a pivot callback.
|
||||
func KeyBy[K comparable, V any](collection []V, iteratee func(V) K) map[K]V {
|
||||
result := make(map[K]V, len(collection))
|
||||
|
||||
for _, v := range collection {
|
||||
k := iteratee(v)
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Drop drops n elements from the beginning of a slice or array.
|
||||
func Drop[T any](collection []T, n int) []T {
|
||||
if len(collection) <= n {
|
||||
return make([]T, 0)
|
||||
}
|
||||
|
||||
result := make([]T, len(collection)-n)
|
||||
for i := n; i < len(collection); i++ {
|
||||
result[i-n] = collection[i]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// DropWhile drops elements from the beginning of a slice or array while the predicate returns true.
|
||||
func DropWhile[T any](collection []T, predicate func(T) bool) []T {
|
||||
i := 0
|
||||
for ; i < len(collection); i++ {
|
||||
if !predicate(collection[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]T, len(collection)-i)
|
||||
|
||||
for j := 0; i < len(collection); i, j = i+1, j+1 {
|
||||
result[j] = collection[i]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// DropRight drops n elements from the end of a slice or array.
|
||||
func DropRight[T any](collection []T, n int) []T {
|
||||
if len(collection) <= n {
|
||||
return make([]T, 0)
|
||||
}
|
||||
|
||||
result := make([]T, len(collection)-n)
|
||||
for i := len(collection) - 1 - n; i >= 0; i-- {
|
||||
result[i] = collection[i]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// DropRightWhile drops elements from the end of a slice or array while the predicate returns true.
|
||||
func DropRightWhile[T any](collection []T, predicate func(T) bool) []T {
|
||||
i := len(collection) - 1
|
||||
for ; i >= 0; i-- {
|
||||
if !predicate(collection[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]T, i+1)
|
||||
|
||||
for ; i >= 0; i-- {
|
||||
result[i] = collection[i]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Reject is the opposite of Filter, this method returns the elements of collection that predicate does not return truthy for.
|
||||
func Reject[V any](collection []V, predicate func(V, int) bool) []V {
|
||||
result := []V{}
|
||||
|
||||
for i, item := range collection {
|
||||
if !predicate(item, i) {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Count counts the number of elements in the collection that compare equal to value.
|
||||
func Count[T comparable](collection []T, value T) (count int) {
|
||||
for _, item := range collection {
|
||||
if item == value {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// CountBy counts the number of elements in the collection for which predicate is true.
|
||||
func CountBy[T any](collection []T, predicate func(T) bool) (count int) {
|
||||
for _, item := range collection {
|
||||
if predicate(item) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// Subset return part of a slice.
|
||||
func Subset[T any](collection []T, offset int, length uint) []T {
|
||||
size := len(collection)
|
||||
|
||||
if offset < 0 {
|
||||
offset = size + offset
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
}
|
||||
|
||||
if offset > size {
|
||||
return []T{}
|
||||
}
|
||||
|
||||
if length > uint(size)-uint(offset) {
|
||||
length = uint(size - offset)
|
||||
}
|
||||
|
||||
return collection[offset : offset+int(length)]
|
||||
}
|
||||
|
||||
// Replace returns a copy of the slice with the first n non-overlapping instances of old replaced by new.
|
||||
func Replace[T comparable](collection []T, old T, new T, n int) []T {
|
||||
size := len(collection)
|
||||
result := make([]T, 0, size)
|
||||
|
||||
for _, item := range collection {
|
||||
if item == old && n != 0 {
|
||||
result = append(result, new)
|
||||
n--
|
||||
} else {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ReplaceAll returns a copy of the slice with all non-overlapping instances of old replaced by new.
|
||||
func ReplaceAll[T comparable](collection []T, old T, new T) []T {
|
||||
return Replace[T](collection, old, new, -1)
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package lo
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
// Substring return part of a string.
|
||||
func Substring[T ~string](str T, offset int, length uint) T {
|
||||
size := len(str)
|
||||
|
||||
if offset < 0 {
|
||||
offset = size + offset
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
}
|
||||
|
||||
if offset > size {
|
||||
return Empty[T]()
|
||||
}
|
||||
|
||||
if length > uint(size)-uint(offset) {
|
||||
length = uint(size - offset)
|
||||
}
|
||||
|
||||
return str[offset : offset+int(length)]
|
||||
}
|
||||
|
||||
// RuneLength is an alias to utf8.RuneCountInString which returns the number of runes in string.
|
||||
func RuneLength(str string) int {
|
||||
return utf8.RuneCountInString(str)
|
||||
}
|
@ -0,0 +1,481 @@
|
||||
package lo
|
||||
|
||||
// T2 creates a tuple from a list of values.
|
||||
func T2[A any, B any](a A, b B) Tuple2[A, B] {
|
||||
return Tuple2[A, B]{A: a, B: b}
|
||||
}
|
||||
|
||||
// T3 creates a tuple from a list of values.
|
||||
func T3[A any, B any, C any](a A, b B, c C) Tuple3[A, B, C] {
|
||||
return Tuple3[A, B, C]{A: a, B: b, C: c}
|
||||
}
|
||||
|
||||
// T4 creates a tuple from a list of values.
|
||||
func T4[A any, B any, C any, D any](a A, b B, c C, d D) Tuple4[A, B, C, D] {
|
||||
return Tuple4[A, B, C, D]{A: a, B: b, C: c, D: d}
|
||||
}
|
||||
|
||||
// T5 creates a tuple from a list of values.
|
||||
func T5[A any, B any, C any, D any, E any](a A, b B, c C, d D, e E) Tuple5[A, B, C, D, E] {
|
||||
return Tuple5[A, B, C, D, E]{A: a, B: b, C: c, D: d, E: e}
|
||||
}
|
||||
|
||||
// T6 creates a tuple from a list of values.
|
||||
func T6[A any, B any, C any, D any, E any, F any](a A, b B, c C, d D, e E, f F) Tuple6[A, B, C, D, E, F] {
|
||||
return Tuple6[A, B, C, D, E, F]{A: a, B: b, C: c, D: d, E: e, F: f}
|
||||
}
|
||||
|
||||
// T7 creates a tuple from a list of values.
|
||||
func T7[A any, B any, C any, D any, E any, F any, G any](a A, b B, c C, d D, e E, f F, g G) Tuple7[A, B, C, D, E, F, G] {
|
||||
return Tuple7[A, B, C, D, E, F, G]{A: a, B: b, C: c, D: d, E: e, F: f, G: g}
|
||||
}
|
||||
|
||||
// T8 creates a tuple from a list of values.
|
||||
func T8[A any, B any, C any, D any, E any, F any, G any, H any](a A, b B, c C, d D, e E, f F, g G, h H) Tuple8[A, B, C, D, E, F, G, H] {
|
||||
return Tuple8[A, B, C, D, E, F, G, H]{A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h}
|
||||
}
|
||||
|
||||
// T8 creates a tuple from a list of values.
|
||||
func T9[A any, B any, C any, D any, E any, F any, G any, H any, I any](a A, b B, c C, d D, e E, f F, g G, h H, i I) Tuple9[A, B, C, D, E, F, G, H, I] {
|
||||
return Tuple9[A, B, C, D, E, F, G, H, I]{A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i}
|
||||
}
|
||||
|
||||
// Unpack2 returns values contained in tuple.
|
||||
func Unpack2[A any, B any](tuple Tuple2[A, B]) (A, B) {
|
||||
return tuple.A, tuple.B
|
||||
}
|
||||
|
||||
// Unpack3 returns values contained in tuple.
|
||||
func Unpack3[A any, B any, C any](tuple Tuple3[A, B, C]) (A, B, C) {
|
||||
return tuple.A, tuple.B, tuple.C
|
||||
}
|
||||
|
||||
// Unpack4 returns values contained in tuple.
|
||||
func Unpack4[A any, B any, C any, D any](tuple Tuple4[A, B, C, D]) (A, B, C, D) {
|
||||
return tuple.A, tuple.B, tuple.C, tuple.D
|
||||
}
|
||||
|
||||
// Unpack5 returns values contained in tuple.
|
||||
func Unpack5[A any, B any, C any, D any, E any](tuple Tuple5[A, B, C, D, E]) (A, B, C, D, E) {
|
||||
return tuple.A, tuple.B, tuple.C, tuple.D, tuple.E
|
||||
}
|
||||
|
||||
// Unpack6 returns values contained in tuple.
|
||||
func Unpack6[A any, B any, C any, D any, E any, F any](tuple Tuple6[A, B, C, D, E, F]) (A, B, C, D, E, F) {
|
||||
return tuple.A, tuple.B, tuple.C, tuple.D, tuple.E, tuple.F
|
||||
}
|
||||
|
||||
// Unpack7 returns values contained in tuple.
|
||||
func Unpack7[A any, B any, C any, D any, E any, F any, G any](tuple Tuple7[A, B, C, D, E, F, G]) (A, B, C, D, E, F, G) {
|
||||
return tuple.A, tuple.B, tuple.C, tuple.D, tuple.E, tuple.F, tuple.G
|
||||
}
|
||||
|
||||
// Unpack8 returns values contained in tuple.
|
||||
func Unpack8[A any, B any, C any, D any, E any, F any, G any, H any](tuple Tuple8[A, B, C, D, E, F, G, H]) (A, B, C, D, E, F, G, H) {
|
||||
return tuple.A, tuple.B, tuple.C, tuple.D, tuple.E, tuple.F, tuple.G, tuple.H
|
||||
}
|
||||
|
||||
// Unpack9 returns values contained in tuple.
|
||||
func Unpack9[A any, B any, C any, D any, E any, F any, G any, H any, I any](tuple Tuple9[A, B, C, D, E, F, G, H, I]) (A, B, C, D, E, F, G, H, I) {
|
||||
return tuple.A, tuple.B, tuple.C, tuple.D, tuple.E, tuple.F, tuple.G, tuple.H, tuple.I
|
||||
}
|
||||
|
||||
// Zip2 creates a slice of grouped elements, the first of which contains the first elements
|
||||
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
|
||||
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||
func Zip2[A any, B any](a []A, b []B) []Tuple2[A, B] {
|
||||
size := Max[int]([]int{len(a), len(b)})
|
||||
|
||||
result := make([]Tuple2[A, B], 0, size)
|
||||
|
||||
for index := 0; index < size; index++ {
|
||||
_a, _ := Nth[A](a, index)
|
||||
_b, _ := Nth[B](b, index)
|
||||
|
||||
result = append(result, Tuple2[A, B]{
|
||||
A: _a,
|
||||
B: _b,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Zip3 creates a slice of grouped elements, the first of which contains the first elements
|
||||
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
|
||||
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||
func Zip3[A any, B any, C any](a []A, b []B, c []C) []Tuple3[A, B, C] {
|
||||
size := Max[int]([]int{len(a), len(b), len(c)})
|
||||
|
||||
result := make([]Tuple3[A, B, C], 0, size)
|
||||
|
||||
for index := 0; index < size; index++ {
|
||||
_a, _ := Nth[A](a, index)
|
||||
_b, _ := Nth[B](b, index)
|
||||
_c, _ := Nth[C](c, index)
|
||||
|
||||
result = append(result, Tuple3[A, B, C]{
|
||||
A: _a,
|
||||
B: _b,
|
||||
C: _c,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Zip4 creates a slice of grouped elements, the first of which contains the first elements
|
||||
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
|
||||
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||
func Zip4[A any, B any, C any, D any](a []A, b []B, c []C, d []D) []Tuple4[A, B, C, D] {
|
||||
size := Max[int]([]int{len(a), len(b), len(c), len(d)})
|
||||
|
||||
result := make([]Tuple4[A, B, C, D], 0, size)
|
||||
|
||||
for index := 0; index < size; index++ {
|
||||
_a, _ := Nth[A](a, index)
|
||||
_b, _ := Nth[B](b, index)
|
||||
_c, _ := Nth[C](c, index)
|
||||
_d, _ := Nth[D](d, index)
|
||||
|
||||
result = append(result, Tuple4[A, B, C, D]{
|
||||
A: _a,
|
||||
B: _b,
|
||||
C: _c,
|
||||
D: _d,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Zip5 creates a slice of grouped elements, the first of which contains the first elements
|
||||
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
|
||||
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||
func Zip5[A any, B any, C any, D any, E any](a []A, b []B, c []C, d []D, e []E) []Tuple5[A, B, C, D, E] {
|
||||
size := Max[int]([]int{len(a), len(b), len(c), len(d), len(e)})
|
||||
|
||||
result := make([]Tuple5[A, B, C, D, E], 0, size)
|
||||
|
||||
for index := 0; index < size; index++ {
|
||||
_a, _ := Nth[A](a, index)
|
||||
_b, _ := Nth[B](b, index)
|
||||
_c, _ := Nth[C](c, index)
|
||||
_d, _ := Nth[D](d, index)
|
||||
_e, _ := Nth[E](e, index)
|
||||
|
||||
result = append(result, Tuple5[A, B, C, D, E]{
|
||||
A: _a,
|
||||
B: _b,
|
||||
C: _c,
|
||||
D: _d,
|
||||
E: _e,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Zip6 creates a slice of grouped elements, the first of which contains the first elements
|
||||
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
|
||||
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||
func Zip6[A any, B any, C any, D any, E any, F any](a []A, b []B, c []C, d []D, e []E, f []F) []Tuple6[A, B, C, D, E, F] {
|
||||
size := Max[int]([]int{len(a), len(b), len(c), len(d), len(e), len(f)})
|
||||
|
||||
result := make([]Tuple6[A, B, C, D, E, F], 0, size)
|
||||
|
||||
for index := 0; index < size; index++ {
|
||||
_a, _ := Nth[A](a, index)
|
||||
_b, _ := Nth[B](b, index)
|
||||
_c, _ := Nth[C](c, index)
|
||||
_d, _ := Nth[D](d, index)
|
||||
_e, _ := Nth[E](e, index)
|
||||
_f, _ := Nth[F](f, index)
|
||||
|
||||
result = append(result, Tuple6[A, B, C, D, E, F]{
|
||||
A: _a,
|
||||
B: _b,
|
||||
C: _c,
|
||||
D: _d,
|
||||
E: _e,
|
||||
F: _f,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Zip7 creates a slice of grouped elements, the first of which contains the first elements
|
||||
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
|
||||
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||
func Zip7[A any, B any, C any, D any, E any, F any, G any](a []A, b []B, c []C, d []D, e []E, f []F, g []G) []Tuple7[A, B, C, D, E, F, G] {
|
||||
size := Max[int]([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g)})
|
||||
|
||||
result := make([]Tuple7[A, B, C, D, E, F, G], 0, size)
|
||||
|
||||
for index := 0; index < size; index++ {
|
||||
_a, _ := Nth[A](a, index)
|
||||
_b, _ := Nth[B](b, index)
|
||||
_c, _ := Nth[C](c, index)
|
||||
_d, _ := Nth[D](d, index)
|
||||
_e, _ := Nth[E](e, index)
|
||||
_f, _ := Nth[F](f, index)
|
||||
_g, _ := Nth[G](g, index)
|
||||
|
||||
result = append(result, Tuple7[A, B, C, D, E, F, G]{
|
||||
A: _a,
|
||||
B: _b,
|
||||
C: _c,
|
||||
D: _d,
|
||||
E: _e,
|
||||
F: _f,
|
||||
G: _g,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Zip8 creates a slice of grouped elements, the first of which contains the first elements
|
||||
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
|
||||
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||
func Zip8[A any, B any, C any, D any, E any, F any, G any, H any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H) []Tuple8[A, B, C, D, E, F, G, H] {
|
||||
size := Max[int]([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h)})
|
||||
|
||||
result := make([]Tuple8[A, B, C, D, E, F, G, H], 0, size)
|
||||
|
||||
for index := 0; index < size; index++ {
|
||||
_a, _ := Nth[A](a, index)
|
||||
_b, _ := Nth[B](b, index)
|
||||
_c, _ := Nth[C](c, index)
|
||||
_d, _ := Nth[D](d, index)
|
||||
_e, _ := Nth[E](e, index)
|
||||
_f, _ := Nth[F](f, index)
|
||||
_g, _ := Nth[G](g, index)
|
||||
_h, _ := Nth[H](h, index)
|
||||
|
||||
result = append(result, Tuple8[A, B, C, D, E, F, G, H]{
|
||||
A: _a,
|
||||
B: _b,
|
||||
C: _c,
|
||||
D: _d,
|
||||
E: _e,
|
||||
F: _f,
|
||||
G: _g,
|
||||
H: _h,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Zip9 creates a slice of grouped elements, the first of which contains the first elements
|
||||
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
|
||||
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||
func Zip9[A any, B any, C any, D any, E any, F any, G any, H any, I any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I) []Tuple9[A, B, C, D, E, F, G, H, I] {
|
||||
size := Max[int]([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i)})
|
||||
|
||||
result := make([]Tuple9[A, B, C, D, E, F, G, H, I], 0, size)
|
||||
|
||||
for index := 0; index < size; index++ {
|
||||
_a, _ := Nth[A](a, index)
|
||||
_b, _ := Nth[B](b, index)
|
||||
_c, _ := Nth[C](c, index)
|
||||
_d, _ := Nth[D](d, index)
|
||||
_e, _ := Nth[E](e, index)
|
||||
_f, _ := Nth[F](f, index)
|
||||
_g, _ := Nth[G](g, index)
|
||||
_h, _ := Nth[H](h, index)
|
||||
_i, _ := Nth[I](i, index)
|
||||
|
||||
result = append(result, Tuple9[A, B, C, D, E, F, G, H, I]{
|
||||
A: _a,
|
||||
B: _b,
|
||||
C: _c,
|
||||
D: _d,
|
||||
E: _e,
|
||||
F: _f,
|
||||
G: _g,
|
||||
H: _h,
|
||||
I: _i,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Unzip2 accepts an array of grouped elements and creates an array regrouping the elements
|
||||
// to their pre-zip configuration.
|
||||
func Unzip2[A any, B any](tuples []Tuple2[A, B]) ([]A, []B) {
|
||||
size := len(tuples)
|
||||
r1 := make([]A, 0, size)
|
||||
r2 := make([]B, 0, size)
|
||||
|
||||
for _, tuple := range tuples {
|
||||
r1 = append(r1, tuple.A)
|
||||
r2 = append(r2, tuple.B)
|
||||
}
|
||||
|
||||
return r1, r2
|
||||
}
|
||||
|
||||
// Unzip3 accepts an array of grouped elements and creates an array regrouping the elements
|
||||
// to their pre-zip configuration.
|
||||
func Unzip3[A any, B any, C any](tuples []Tuple3[A, B, C]) ([]A, []B, []C) {
|
||||
size := len(tuples)
|
||||
r1 := make([]A, 0, size)
|
||||
r2 := make([]B, 0, size)
|
||||
r3 := make([]C, 0, size)
|
||||
|
||||
for _, tuple := range tuples {
|
||||
r1 = append(r1, tuple.A)
|
||||
r2 = append(r2, tuple.B)
|
||||
r3 = append(r3, tuple.C)
|
||||
}
|
||||
|
||||
return r1, r2, r3
|
||||
}
|
||||
|
||||
// Unzip4 accepts an array of grouped elements and creates an array regrouping the elements
|
||||
// to their pre-zip configuration.
|
||||
func Unzip4[A any, B any, C any, D any](tuples []Tuple4[A, B, C, D]) ([]A, []B, []C, []D) {
|
||||
size := len(tuples)
|
||||
r1 := make([]A, 0, size)
|
||||
r2 := make([]B, 0, size)
|
||||
r3 := make([]C, 0, size)
|
||||
r4 := make([]D, 0, size)
|
||||
|
||||
for _, tuple := range tuples {
|
||||
r1 = append(r1, tuple.A)
|
||||
r2 = append(r2, tuple.B)
|
||||
r3 = append(r3, tuple.C)
|
||||
r4 = append(r4, tuple.D)
|
||||
}
|
||||
|
||||
return r1, r2, r3, r4
|
||||
}
|
||||
|
||||
// Unzip5 accepts an array of grouped elements and creates an array regrouping the elements
|
||||
// to their pre-zip configuration.
|
||||
func Unzip5[A any, B any, C any, D any, E any](tuples []Tuple5[A, B, C, D, E]) ([]A, []B, []C, []D, []E) {
|
||||
size := len(tuples)
|
||||
r1 := make([]A, 0, size)
|
||||
r2 := make([]B, 0, size)
|
||||
r3 := make([]C, 0, size)
|
||||
r4 := make([]D, 0, size)
|
||||
r5 := make([]E, 0, size)
|
||||
|
||||
for _, tuple := range tuples {
|
||||
r1 = append(r1, tuple.A)
|
||||
r2 = append(r2, tuple.B)
|
||||
r3 = append(r3, tuple.C)
|
||||
r4 = append(r4, tuple.D)
|
||||
r5 = append(r5, tuple.E)
|
||||
}
|
||||
|
||||
return r1, r2, r3, r4, r5
|
||||
}
|
||||
|
||||
// Unzip6 accepts an array of grouped elements and creates an array regrouping the elements
|
||||
// to their pre-zip configuration.
|
||||
func Unzip6[A any, B any, C any, D any, E any, F any](tuples []Tuple6[A, B, C, D, E, F]) ([]A, []B, []C, []D, []E, []F) {
|
||||
size := len(tuples)
|
||||
r1 := make([]A, 0, size)
|
||||
r2 := make([]B, 0, size)
|
||||
r3 := make([]C, 0, size)
|
||||
r4 := make([]D, 0, size)
|
||||
r5 := make([]E, 0, size)
|
||||
r6 := make([]F, 0, size)
|
||||
|
||||
for _, tuple := range tuples {
|
||||
r1 = append(r1, tuple.A)
|
||||
r2 = append(r2, tuple.B)
|
||||
r3 = append(r3, tuple.C)
|
||||
r4 = append(r4, tuple.D)
|
||||
r5 = append(r5, tuple.E)
|
||||
r6 = append(r6, tuple.F)
|
||||
}
|
||||
|
||||
return r1, r2, r3, r4, r5, r6
|
||||
}
|
||||
|
||||
// Unzip7 accepts an array of grouped elements and creates an array regrouping the elements
|
||||
// to their pre-zip configuration.
|
||||
func Unzip7[A any, B any, C any, D any, E any, F any, G any](tuples []Tuple7[A, B, C, D, E, F, G]) ([]A, []B, []C, []D, []E, []F, []G) {
|
||||
size := len(tuples)
|
||||
r1 := make([]A, 0, size)
|
||||
r2 := make([]B, 0, size)
|
||||
r3 := make([]C, 0, size)
|
||||
r4 := make([]D, 0, size)
|
||||
r5 := make([]E, 0, size)
|
||||
r6 := make([]F, 0, size)
|
||||
r7 := make([]G, 0, size)
|
||||
|
||||
for _, tuple := range tuples {
|
||||
r1 = append(r1, tuple.A)
|
||||
r2 = append(r2, tuple.B)
|
||||
r3 = append(r3, tuple.C)
|
||||
r4 = append(r4, tuple.D)
|
||||
r5 = append(r5, tuple.E)
|
||||
r6 = append(r6, tuple.F)
|
||||
r7 = append(r7, tuple.G)
|
||||
}
|
||||
|
||||
return r1, r2, r3, r4, r5, r6, r7
|
||||
}
|
||||
|
||||
// Unzip8 accepts an array of grouped elements and creates an array regrouping the elements
|
||||
// to their pre-zip configuration.
|
||||
func Unzip8[A any, B any, C any, D any, E any, F any, G any, H any](tuples []Tuple8[A, B, C, D, E, F, G, H]) ([]A, []B, []C, []D, []E, []F, []G, []H) {
|
||||
size := len(tuples)
|
||||
r1 := make([]A, 0, size)
|
||||
r2 := make([]B, 0, size)
|
||||
r3 := make([]C, 0, size)
|
||||
r4 := make([]D, 0, size)
|
||||
r5 := make([]E, 0, size)
|
||||
r6 := make([]F, 0, size)
|
||||
r7 := make([]G, 0, size)
|
||||
r8 := make([]H, 0, size)
|
||||
|
||||
for _, tuple := range tuples {
|
||||
r1 = append(r1, tuple.A)
|
||||
r2 = append(r2, tuple.B)
|
||||
r3 = append(r3, tuple.C)
|
||||
r4 = append(r4, tuple.D)
|
||||
r5 = append(r5, tuple.E)
|
||||
r6 = append(r6, tuple.F)
|
||||
r7 = append(r7, tuple.G)
|
||||
r8 = append(r8, tuple.H)
|
||||
}
|
||||
|
||||
return r1, r2, r3, r4, r5, r6, r7, r8
|
||||
}
|
||||
|
||||
// Unzip9 accepts an array of grouped elements and creates an array regrouping the elements
|
||||
// to their pre-zip configuration.
|
||||
func Unzip9[A any, B any, C any, D any, E any, F any, G any, H any, I any](tuples []Tuple9[A, B, C, D, E, F, G, H, I]) ([]A, []B, []C, []D, []E, []F, []G, []H, []I) {
|
||||
size := len(tuples)
|
||||
r1 := make([]A, 0, size)
|
||||
r2 := make([]B, 0, size)
|
||||
r3 := make([]C, 0, size)
|
||||
r4 := make([]D, 0, size)
|
||||
r5 := make([]E, 0, size)
|
||||
r6 := make([]F, 0, size)
|
||||
r7 := make([]G, 0, size)
|
||||
r8 := make([]H, 0, size)
|
||||
r9 := make([]I, 0, size)
|
||||
|
||||
for _, tuple := range tuples {
|
||||
r1 = append(r1, tuple.A)
|
||||
r2 = append(r2, tuple.B)
|
||||
r3 = append(r3, tuple.C)
|
||||
r4 = append(r4, tuple.D)
|
||||
r5 = append(r5, tuple.E)
|
||||
r6 = append(r6, tuple.F)
|
||||
r7 = append(r7, tuple.G)
|
||||
r8 = append(r8, tuple.H)
|
||||
r9 = append(r9, tuple.I)
|
||||
}
|
||||
|
||||
return r1, r2, r3, r4, r5, r6, r7, r8, r9
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package lo
|
||||
|
||||
// Entry defines a key/value pairs.
|
||||
type Entry[K comparable, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
|
||||
// Tuple2 is a group of 2 elements (pair).
|
||||
type Tuple2[A any, B any] struct {
|
||||
A A
|
||||
B B
|
||||
}
|
||||
|
||||
// Tuple3 is a group of 3 elements.
|
||||
type Tuple3[A any, B any, C any] struct {
|
||||
A A
|
||||
B B
|
||||
C C
|
||||
}
|
||||
|
||||
// Tuple4 is a group of 4 elements.
|
||||
type Tuple4[A any, B any, C any, D any] struct {
|
||||
A A
|
||||
B B
|
||||
C C
|
||||
D D
|
||||
}
|
||||
|
||||
// Tuple5 is a group of 5 elements.
|
||||
type Tuple5[A any, B any, C any, D any, E any] struct {
|
||||
A A
|
||||
B B
|
||||
C C
|
||||
D D
|
||||
E E
|
||||
}
|
||||
|
||||
// Tuple6 is a group of 6 elements.
|
||||
type Tuple6[A any, B any, C any, D any, E any, F any] struct {
|
||||
A A
|
||||
B B
|
||||
C C
|
||||
D D
|
||||
E E
|
||||
F F
|
||||
}
|
||||
|
||||
// Tuple7 is a group of 7 elements.
|
||||
type Tuple7[A any, B any, C any, D any, E any, F any, G any] struct {
|
||||
A A
|
||||
B B
|
||||
C C
|
||||
D D
|
||||
E E
|
||||
F F
|
||||
G G
|
||||
}
|
||||
|
||||
// Tuple8 is a group of 8 elements.
|
||||
type Tuple8[A any, B any, C any, D any, E any, F any, G any, H any] struct {
|
||||
A A
|
||||
B B
|
||||
C C
|
||||
D D
|
||||
E E
|
||||
F F
|
||||
G G
|
||||
H H
|
||||
}
|
||||
|
||||
// Tuple9 is a group of 9 elements.
|
||||
type Tuple9[A any, B any, C any, D any, E any, F any, G any, H any, I any] struct {
|
||||
A A
|
||||
B B
|
||||
C C
|
||||
D D
|
||||
E E
|
||||
F F
|
||||
G G
|
||||
H H
|
||||
I I
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
module github.com/sirupsen/logrus
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.1.1 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894
|
||||
)
|
@ -1,16 +0,0 @@
|
||||
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/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
@ -1,22 +1,21 @@
|
||||
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
|
||||
MIT License
|
||||
|
||||
Please consider promoting this project if you find it useful.
|
||||
Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal 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:
|
||||
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 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.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
@ -0,0 +1,394 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type CompareType int
|
||||
|
||||
const (
|
||||
compareLess CompareType = iota - 1
|
||||
compareEqual
|
||||
compareGreater
|
||||
)
|
||||
|
||||
var (
|
||||
intType = reflect.TypeOf(int(1))
|
||||
int8Type = reflect.TypeOf(int8(1))
|
||||
int16Type = reflect.TypeOf(int16(1))
|
||||
int32Type = reflect.TypeOf(int32(1))
|
||||
int64Type = reflect.TypeOf(int64(1))
|
||||
|
||||
uintType = reflect.TypeOf(uint(1))
|
||||
uint8Type = reflect.TypeOf(uint8(1))
|
||||
uint16Type = reflect.TypeOf(uint16(1))
|
||||
uint32Type = reflect.TypeOf(uint32(1))
|
||||
uint64Type = reflect.TypeOf(uint64(1))
|
||||
|
||||
float32Type = reflect.TypeOf(float32(1))
|
||||
float64Type = reflect.TypeOf(float64(1))
|
||||
|
||||
stringType = reflect.TypeOf("")
|
||||
)
|
||||
|
||||
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
obj1Value := reflect.ValueOf(obj1)
|
||||
obj2Value := reflect.ValueOf(obj2)
|
||||
|
||||
// throughout this switch we try and avoid calling .Convert() if possible,
|
||||
// as this has a pretty big performance impact
|
||||
switch kind {
|
||||
case reflect.Int:
|
||||
{
|
||||
intobj1, ok := obj1.(int)
|
||||
if !ok {
|
||||
intobj1 = obj1Value.Convert(intType).Interface().(int)
|
||||
}
|
||||
intobj2, ok := obj2.(int)
|
||||
if !ok {
|
||||
intobj2 = obj2Value.Convert(intType).Interface().(int)
|
||||
}
|
||||
if intobj1 > intobj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if intobj1 == intobj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if intobj1 < intobj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Int8:
|
||||
{
|
||||
int8obj1, ok := obj1.(int8)
|
||||
if !ok {
|
||||
int8obj1 = obj1Value.Convert(int8Type).Interface().(int8)
|
||||
}
|
||||
int8obj2, ok := obj2.(int8)
|
||||
if !ok {
|
||||
int8obj2 = obj2Value.Convert(int8Type).Interface().(int8)
|
||||
}
|
||||
if int8obj1 > int8obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if int8obj1 == int8obj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if int8obj1 < int8obj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Int16:
|
||||
{
|
||||
int16obj1, ok := obj1.(int16)
|
||||
if !ok {
|
||||
int16obj1 = obj1Value.Convert(int16Type).Interface().(int16)
|
||||
}
|
||||
int16obj2, ok := obj2.(int16)
|
||||
if !ok {
|
||||
int16obj2 = obj2Value.Convert(int16Type).Interface().(int16)
|
||||
}
|
||||
if int16obj1 > int16obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if int16obj1 == int16obj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if int16obj1 < int16obj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Int32:
|
||||
{
|
||||
int32obj1, ok := obj1.(int32)
|
||||
if !ok {
|
||||
int32obj1 = obj1Value.Convert(int32Type).Interface().(int32)
|
||||
}
|
||||
int32obj2, ok := obj2.(int32)
|
||||
if !ok {
|
||||
int32obj2 = obj2Value.Convert(int32Type).Interface().(int32)
|
||||
}
|
||||
if int32obj1 > int32obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if int32obj1 == int32obj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if int32obj1 < int32obj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Int64:
|
||||
{
|
||||
int64obj1, ok := obj1.(int64)
|
||||
if !ok {
|
||||
int64obj1 = obj1Value.Convert(int64Type).Interface().(int64)
|
||||
}
|
||||
int64obj2, ok := obj2.(int64)
|
||||
if !ok {
|
||||
int64obj2 = obj2Value.Convert(int64Type).Interface().(int64)
|
||||
}
|
||||
if int64obj1 > int64obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if int64obj1 == int64obj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if int64obj1 < int64obj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Uint:
|
||||
{
|
||||
uintobj1, ok := obj1.(uint)
|
||||
if !ok {
|
||||
uintobj1 = obj1Value.Convert(uintType).Interface().(uint)
|
||||
}
|
||||
uintobj2, ok := obj2.(uint)
|
||||
if !ok {
|
||||
uintobj2 = obj2Value.Convert(uintType).Interface().(uint)
|
||||
}
|
||||
if uintobj1 > uintobj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if uintobj1 == uintobj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if uintobj1 < uintobj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Uint8:
|
||||
{
|
||||
uint8obj1, ok := obj1.(uint8)
|
||||
if !ok {
|
||||
uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8)
|
||||
}
|
||||
uint8obj2, ok := obj2.(uint8)
|
||||
if !ok {
|
||||
uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8)
|
||||
}
|
||||
if uint8obj1 > uint8obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if uint8obj1 == uint8obj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if uint8obj1 < uint8obj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Uint16:
|
||||
{
|
||||
uint16obj1, ok := obj1.(uint16)
|
||||
if !ok {
|
||||
uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16)
|
||||
}
|
||||
uint16obj2, ok := obj2.(uint16)
|
||||
if !ok {
|
||||
uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16)
|
||||
}
|
||||
if uint16obj1 > uint16obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if uint16obj1 == uint16obj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if uint16obj1 < uint16obj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Uint32:
|
||||
{
|
||||
uint32obj1, ok := obj1.(uint32)
|
||||
if !ok {
|
||||
uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32)
|
||||
}
|
||||
uint32obj2, ok := obj2.(uint32)
|
||||
if !ok {
|
||||
uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32)
|
||||
}
|
||||
if uint32obj1 > uint32obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if uint32obj1 == uint32obj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if uint32obj1 < uint32obj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Uint64:
|
||||
{
|
||||
uint64obj1, ok := obj1.(uint64)
|
||||
if !ok {
|
||||
uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64)
|
||||
}
|
||||
uint64obj2, ok := obj2.(uint64)
|
||||
if !ok {
|
||||
uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64)
|
||||
}
|
||||
if uint64obj1 > uint64obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if uint64obj1 == uint64obj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if uint64obj1 < uint64obj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Float32:
|
||||
{
|
||||
float32obj1, ok := obj1.(float32)
|
||||
if !ok {
|
||||
float32obj1 = obj1Value.Convert(float32Type).Interface().(float32)
|
||||
}
|
||||
float32obj2, ok := obj2.(float32)
|
||||
if !ok {
|
||||
float32obj2 = obj2Value.Convert(float32Type).Interface().(float32)
|
||||
}
|
||||
if float32obj1 > float32obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if float32obj1 == float32obj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if float32obj1 < float32obj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.Float64:
|
||||
{
|
||||
float64obj1, ok := obj1.(float64)
|
||||
if !ok {
|
||||
float64obj1 = obj1Value.Convert(float64Type).Interface().(float64)
|
||||
}
|
||||
float64obj2, ok := obj2.(float64)
|
||||
if !ok {
|
||||
float64obj2 = obj2Value.Convert(float64Type).Interface().(float64)
|
||||
}
|
||||
if float64obj1 > float64obj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if float64obj1 == float64obj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if float64obj1 < float64obj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
case reflect.String:
|
||||
{
|
||||
stringobj1, ok := obj1.(string)
|
||||
if !ok {
|
||||
stringobj1 = obj1Value.Convert(stringType).Interface().(string)
|
||||
}
|
||||
stringobj2, ok := obj2.(string)
|
||||
if !ok {
|
||||
stringobj2 = obj2Value.Convert(stringType).Interface().(string)
|
||||
}
|
||||
if stringobj1 > stringobj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if stringobj1 == stringobj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if stringobj1 < stringobj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return compareEqual, false
|
||||
}
|
||||
|
||||
// Greater asserts that the first element is greater than the second
|
||||
//
|
||||
// assert.Greater(t, 2, 1)
|
||||
// assert.Greater(t, float64(2), float64(1))
|
||||
// assert.Greater(t, "b", "a")
|
||||
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// GreaterOrEqual asserts that the first element is greater than or equal to the second
|
||||
//
|
||||
// assert.GreaterOrEqual(t, 2, 1)
|
||||
// assert.GreaterOrEqual(t, 2, 2)
|
||||
// assert.GreaterOrEqual(t, "b", "a")
|
||||
// assert.GreaterOrEqual(t, "b", "b")
|
||||
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// Less asserts that the first element is less than the second
|
||||
//
|
||||
// assert.Less(t, 1, 2)
|
||||
// assert.Less(t, float64(1), float64(2))
|
||||
// assert.Less(t, "a", "b")
|
||||
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// LessOrEqual asserts that the first element is less than or equal to the second
|
||||
//
|
||||
// assert.LessOrEqual(t, 1, 2)
|
||||
// assert.LessOrEqual(t, 2, 2)
|
||||
// assert.LessOrEqual(t, "a", "b")
|
||||
// assert.LessOrEqual(t, "b", "b")
|
||||
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// Positive asserts that the specified element is positive
|
||||
//
|
||||
// assert.Positive(t, 1)
|
||||
// assert.Positive(t, 1.23)
|
||||
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
zero := reflect.Zero(reflect.TypeOf(e))
|
||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs)
|
||||
}
|
||||
|
||||
// Negative asserts that the specified element is negative
|
||||
//
|
||||
// assert.Negative(t, -1)
|
||||
// assert.Negative(t, -1.23)
|
||||
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
zero := reflect.Zero(reflect.TypeOf(e))
|
||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs)
|
||||
}
|
||||
|
||||
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
e1Kind := reflect.ValueOf(e1).Kind()
|
||||
e2Kind := reflect.ValueOf(e2).Kind()
|
||||
if e1Kind != e2Kind {
|
||||
return Fail(t, "Elements should be the same type", msgAndArgs...)
|
||||
}
|
||||
|
||||
compareResult, isComparable := compare(e1, e2, e1Kind)
|
||||
if !isComparable {
|
||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
|
||||
}
|
||||
|
||||
if !containsValue(allowedComparesResults, compareResult) {
|
||||
return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func containsValue(values []CompareType, value CompareType) bool {
|
||||
for _, v := range values {
|
||||
if v == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// isOrdered checks that collection contains orderable elements.
|
||||
func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
|
||||
objKind := reflect.TypeOf(object).Kind()
|
||||
if objKind != reflect.Slice && objKind != reflect.Array {
|
||||
return false
|
||||
}
|
||||
|
||||
objValue := reflect.ValueOf(object)
|
||||
objLen := objValue.Len()
|
||||
|
||||
if objLen <= 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
value := objValue.Index(0)
|
||||
valueInterface := value.Interface()
|
||||
firstValueKind := value.Kind()
|
||||
|
||||
for i := 1; i < objLen; i++ {
|
||||
prevValue := value
|
||||
prevValueInterface := valueInterface
|
||||
|
||||
value = objValue.Index(i)
|
||||
valueInterface = value.Interface()
|
||||
|
||||
compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind)
|
||||
|
||||
if !isComparable {
|
||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...)
|
||||
}
|
||||
|
||||
if !containsValue(allowedComparesResults, compareResult) {
|
||||
return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsIncreasing asserts that the collection is increasing
|
||||
//
|
||||
// assert.IsIncreasing(t, []int{1, 2, 3})
|
||||
// assert.IsIncreasing(t, []float{1, 2})
|
||||
// assert.IsIncreasing(t, []string{"a", "b"})
|
||||
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// IsNonIncreasing asserts that the collection is not increasing
|
||||
//
|
||||
// assert.IsNonIncreasing(t, []int{2, 1, 1})
|
||||
// assert.IsNonIncreasing(t, []float{2, 1})
|
||||
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
||||
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// IsDecreasing asserts that the collection is decreasing
|
||||
//
|
||||
// assert.IsDecreasing(t, []int{2, 1, 0})
|
||||
// assert.IsDecreasing(t, []float{2, 1})
|
||||
// assert.IsDecreasing(t, []string{"b", "a"})
|
||||
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
|
||||
}
|
||||
|
||||
// IsNonDecreasing asserts that the collection is not decreasing
|
||||
//
|
||||
// assert.IsNonDecreasing(t, []int{1, 1, 2})
|
||||
// assert.IsNonDecreasing(t, []float{1, 2})
|
||||
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
||||
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
@ -0,0 +1,3 @@
|
||||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
@ -0,0 +1,50 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package constraints defines a set of useful constraints to be used
|
||||
// with type parameters.
|
||||
package constraints
|
||||
|
||||
// Signed is a constraint that permits any signed integer type.
|
||||
// If future releases of Go add new predeclared signed integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Signed interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||
}
|
||||
|
||||
// Unsigned is a constraint that permits any unsigned integer type.
|
||||
// If future releases of Go add new predeclared unsigned integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Unsigned interface {
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||
}
|
||||
|
||||
// Integer is a constraint that permits any integer type.
|
||||
// If future releases of Go add new predeclared integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Integer interface {
|
||||
Signed | Unsigned
|
||||
}
|
||||
|
||||
// Float is a constraint that permits any floating-point type.
|
||||
// If future releases of Go add new predeclared floating-point types,
|
||||
// this constraint will be modified to include them.
|
||||
type Float interface {
|
||||
~float32 | ~float64
|
||||
}
|
||||
|
||||
// Complex is a constraint that permits any complex numeric type.
|
||||
// If future releases of Go add new predeclared complex numeric types,
|
||||
// this constraint will be modified to include them.
|
||||
type Complex interface {
|
||||
~complex64 | ~complex128
|
||||
}
|
||||
|
||||
// Ordered is a constraint that permits any ordered type: any type
|
||||
// that supports the operators < <= >= >.
|
||||
// If future releases of Go add new ordered types,
|
||||
// this constraint will be modified to include them.
|
||||
type Ordered interface {
|
||||
Integer | Float | ~string
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package unsafeheader contains header declarations for the Go runtime's
|
||||
// slice and string implementations.
|
||||
//
|
||||
// This package allows x/sys to use types equivalent to
|
||||
// reflect.SliceHeader and reflect.StringHeader without introducing
|
||||
// a dependency on the (relatively heavy) "reflect" package.
|
||||
package unsafeheader
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Slice is the runtime representation of a slice.
|
||||
// It cannot be used safely or portably and its representation may change in a later release.
|
||||
type Slice struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
|
||||
// String is the runtime representation of a string.
|
||||
// It cannot be used safely or portably and its representation may change in a later release.
|
||||
type String struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
//go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
// +build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for AMD64, Darwin
|
||||
//
|
||||
// System call support for AMD64 BSD
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
@ -1,14 +1,14 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
//go:build (freebsd || netbsd || openbsd) && gc
|
||||
// +build freebsd netbsd openbsd
|
||||
// +build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM, NetBSD
|
||||
//
|
||||
// System call support for ARM BSD
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
@ -1,14 +1,14 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||
// +build darwin freebsd netbsd openbsd
|
||||
// +build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for AMD64, NetBSD
|
||||
//
|
||||
// System call support for ARM64 BSD
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
@ -1,30 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
// +build arm,darwin
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM, Darwin
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
B syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·RawSyscall6(SB)
|
@ -1,30 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
// +build arm64,darwin
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for AMD64, Darwin
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
B syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
B syscall·RawSyscall6(SB)
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for AMD64, DragonFly
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for 386, FreeBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for AMD64, FreeBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM, FreeBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
B syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·RawSyscall6(SB)
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM64, FreeBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
@ -0,0 +1,54 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && loong64 && gc
|
||||
// +build linux
|
||||
// +build loong64
|
||||
// +build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
JAL runtime·entersyscall(SB)
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R11 // syscall entry
|
||||
SYSCALL
|
||||
MOVV R4, r1+32(FP)
|
||||
MOVV R5, r2+40(FP)
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R11 // syscall entry
|
||||
SYSCALL
|
||||
MOVV R4, r1+32(FP)
|
||||
MOVV R5, r2+40(FP)
|
||||
RET
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for 386, NetBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM64, NetBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
B syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
B syscall·RawSyscall6(SB)
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for 386, OpenBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for AMD64, OpenBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM, OpenBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
B syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·RawSyscall6(SB)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue