mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-19 12:12:42 +02:00
Merge branch 'master' into stash-untracked-changes
This commit is contained in:
commit
a47e72892a
@ -132,6 +132,34 @@ If you want to trigger a debug session from VSCode, you can use the following sn
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Profiling
|
||||||
|
|
||||||
|
If you want to investigate what's contributing to CPU usage you can add the following to the top of the `main()` function in `main.go`
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "runtime/pprof"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f, err := os.Create("cpu.prof")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("could not create CPU profile: ", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
|
log.Fatal("could not start CPU profile: ", err)
|
||||||
|
}
|
||||||
|
defer pprof.StopCPUProfile()
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run lazygit, and afterwards, from your terminal, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go tool pprof --web cpu.prof
|
||||||
|
```
|
||||||
|
|
||||||
|
That should open an application which allows you to view the breakdown of CPU usage.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Lazygit has two kinds of tests: unit tests and integration tests. Unit tests go in files that end in `_test.go`, and are written in Go. For integration tests, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
|
Lazygit has two kinds of tests: unit tests and integration tests. Unit tests go in files that end in `_test.go`, and are written in Go. For integration tests, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
|
||||||
@ -140,7 +168,7 @@ Lazygit has two kinds of tests: unit tests and integration tests. Unit tests go
|
|||||||
|
|
||||||
Sometimes you will need to make a change in the gocui fork (https://github.com/jesseduffield/gocui). Gocui is the package responsible for rendering windows and handling user input. Here's the typical process to follow:
|
Sometimes you will need to make a change in the gocui fork (https://github.com/jesseduffield/gocui). Gocui is the package responsible for rendering windows and handling user input. Here's the typical process to follow:
|
||||||
|
|
||||||
1. Make the changes in gocui inside the vendor directory so it's easy to test against lazygit
|
1. Make the changes in gocui inside lazygit's vendor directory so it's easy to test against lazygit
|
||||||
2. Copy the changes over to the actual gocui repo (clone it if you haven't already, and use the `awesome` branch, not `master`)
|
2. Copy the changes over to the actual gocui repo (clone it if you haven't already, and use the `awesome` branch, not `master`)
|
||||||
3. Raise a PR on the gocui repo with your changes
|
3. Raise a PR on the gocui repo with your changes
|
||||||
4. After that PR is merged, make a PR in lazygit bumping the gocui version. You can bump the version by running the following at the lazygit repo root:
|
4. After that PR is merged, make a PR in lazygit bumping the gocui version. You can bump the version by running the following at the lazygit repo root:
|
||||||
@ -151,6 +179,21 @@ Sometimes you will need to make a change in the gocui fork (https://github.com/j
|
|||||||
|
|
||||||
5. Raise a PR in lazygit with those changes
|
5. Raise a PR in lazygit with those changes
|
||||||
|
|
||||||
|
## Updating Lazycore
|
||||||
|
|
||||||
|
[Lazycore](https://github.com/jesseduffield/lazycore) is a repo containing shared functionality between lazygit and lazydocker. Sometimes you will need to make a change to that repo and import the changes into lazygit. Similar to updating Gocui, here's what you do:
|
||||||
|
|
||||||
|
1. Make the changes in lazycore inside lazygit's vendor directory so it's easy to test against lazygit
|
||||||
|
2. Copy the changes over to the actual lazycore repo (clone it if you haven't already, and use the `master` branch)
|
||||||
|
3. Raise a PR on the lazycore repo with your changes
|
||||||
|
4. After that PR is merged, make a PR in lazygit bumping the lazycore version. You can bump the version by running the following at the lazygit repo root:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./scripts/bump_lazycore.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Raise a PR in lazygit with those changes
|
||||||
|
|
||||||
## Improvements
|
## Improvements
|
||||||
|
|
||||||
If you can think of any way to improve these docs let us know.
|
If you can think of any way to improve these docs let us know.
|
||||||
|
@ -223,6 +223,7 @@ keybinding:
|
|||||||
viewBisectOptions: 'b'
|
viewBisectOptions: 'b'
|
||||||
stash:
|
stash:
|
||||||
popStash: 'g'
|
popStash: 'g'
|
||||||
|
renameStash: 'r'
|
||||||
commitFiles:
|
commitFiles:
|
||||||
checkoutCommitFile: 'c'
|
checkoutCommitFile: 'c'
|
||||||
main:
|
main:
|
||||||
@ -435,6 +436,14 @@ gui:
|
|||||||
|
|
||||||
For all possible keybinding options, check [Custom_Keybindings.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md)
|
For all possible keybinding options, check [Custom_Keybindings.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md)
|
||||||
|
|
||||||
|
You can disable certain key bindings by specifying `null`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
keybinding:
|
||||||
|
universal:
|
||||||
|
edit: null # disable 'edit file'
|
||||||
|
```
|
||||||
|
|
||||||
### Example Keybindings For Colemak Users
|
### Example Keybindings For Colemak Users
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -238,6 +238,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>g</kbd>: pop
|
<kbd>g</kbd>: pop
|
||||||
<kbd>d</kbd>: drop
|
<kbd>d</kbd>: drop
|
||||||
<kbd>n</kbd>: new branch
|
<kbd>n</kbd>: new branch
|
||||||
|
<kbd>r</kbd>: rename stash
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd>enter</kbd>: view selected item's files
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>g</kbd>: pop
|
<kbd>g</kbd>: pop
|
||||||
<kbd>d</kbd>: drop
|
<kbd>d</kbd>: drop
|
||||||
<kbd>n</kbd>: 新しいブランチを作成
|
<kbd>n</kbd>: 新しいブランチを作成
|
||||||
|
<kbd>r</kbd>: Stashを変更
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd>enter</kbd>: view selected item's files
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>g</kbd>: pop
|
<kbd>g</kbd>: pop
|
||||||
<kbd>d</kbd>: drop
|
<kbd>d</kbd>: drop
|
||||||
<kbd>n</kbd>: 새 브랜치 생성
|
<kbd>n</kbd>: 새 브랜치 생성
|
||||||
|
<kbd>r</kbd>: rename stash
|
||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd>enter</kbd>: view selected item's files
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
@ -238,6 +238,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>g</kbd>: pop
|
<kbd>g</kbd>: pop
|
||||||
<kbd>d</kbd>: laten vallen
|
<kbd>d</kbd>: laten vallen
|
||||||
<kbd>n</kbd>: nieuwe branch
|
<kbd>n</kbd>: nieuwe branch
|
||||||
|
<kbd>r</kbd>: rename stash
|
||||||
<kbd>enter</kbd>: bekijk gecommite bestanden
|
<kbd>enter</kbd>: bekijk gecommite bestanden
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
@ -231,6 +231,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>g</kbd>: wyciągnij
|
<kbd>g</kbd>: wyciągnij
|
||||||
<kbd>d</kbd>: porzuć
|
<kbd>d</kbd>: porzuć
|
||||||
<kbd>n</kbd>: nowa gałąź
|
<kbd>n</kbd>: nowa gałąź
|
||||||
|
<kbd>r</kbd>: rename stash
|
||||||
<kbd>enter</kbd>: przeglądaj pliki commita
|
<kbd>enter</kbd>: przeglądaj pliki commita
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
@ -264,6 +264,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>g</kbd>: 应用并删除
|
<kbd>g</kbd>: 应用并删除
|
||||||
<kbd>d</kbd>: 删除
|
<kbd>d</kbd>: 删除
|
||||||
<kbd>n</kbd>: 新分支
|
<kbd>n</kbd>: 新分支
|
||||||
|
<kbd>r</kbd>: rename stash
|
||||||
<kbd>enter</kbd>: 查看提交的文件
|
<kbd>enter</kbd>: 查看提交的文件
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
13
go.mod
13
go.mod
@ -18,8 +18,9 @@ require (
|
|||||||
github.com/integrii/flaggy v1.4.0
|
github.com/integrii/flaggy v1.4.0
|
||||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
|
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20221003162644-fead10f7b360
|
github.com/jesseduffield/gocui v0.3.1-0.20221016041636-16c24668f71c
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
||||||
|
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
||||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||||
github.com/jesseduffield/yaml v2.1.0+incompatible
|
github.com/jesseduffield/yaml v2.1.0+incompatible
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||||
@ -29,15 +30,15 @@ require (
|
|||||||
github.com/mgutz/str v1.2.0
|
github.com/mgutz/str v1.2.0
|
||||||
github.com/pmezard/go-difflib v1.0.0
|
github.com/pmezard/go-difflib v1.0.0
|
||||||
github.com/sahilm/fuzzy v0.1.0
|
github.com/sahilm/fuzzy v0.1.0
|
||||||
github.com/samber/lo v1.10.1
|
github.com/samber/lo v1.31.0
|
||||||
github.com/sanity-io/litter v1.5.2
|
github.com/sanity-io/litter v1.5.2
|
||||||
github.com/sasha-s/go-deadlock v0.3.1
|
github.com/sasha-s/go-deadlock v0.3.1
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.4.2
|
||||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.8.0
|
||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
||||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -67,8 +68,8 @@ require (
|
|||||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
|
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
|
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
|
||||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
|
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
|
||||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
|
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect
|
||||||
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 // indirect
|
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.8 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
)
|
)
|
||||||
|
28
go.sum
28
go.sum
@ -72,10 +72,12 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
|
|||||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
|
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
|
||||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
|
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
|
||||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20221003162644-fead10f7b360 h1:43F6SAmNzsjwhNa7hBfYCXUgPSl76b+3IogJIloMDnU=
|
github.com/jesseduffield/gocui v0.3.1-0.20221016041636-16c24668f71c h1:ttqCvM86hyp4V3DtRPxLo9imD1s+n6ry/zshPx0tt98=
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20221003162644-fead10f7b360/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
github.com/jesseduffield/gocui v0.3.1-0.20221016041636-16c24668f71c/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
||||||
|
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
|
||||||
|
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
|
||||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
|
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
|
||||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
|
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
|
||||||
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
|
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
|
||||||
@ -139,8 +141,8 @@ github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
|
|||||||
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
||||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||||
github.com/samber/lo v1.10.1 h1:0D3h7i0U3hRAbaCeQ82DLe67n0A7Bbl0/cEoWqFGp+U=
|
github.com/samber/lo v1.31.0 h1:Sfa+/064Tdo4SvlohQUQzBhgSer9v/coGvKQI/XLWAM=
|
||||||
github.com/samber/lo v1.10.1/go.mod h1:2I7tgIv8Q1SG2xEIkRq0F2i2zgxVpnyPOP0d3Gj2r+A=
|
github.com/samber/lo v1.31.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
|
||||||
github.com/sanity-io/litter v1.5.2 h1:AnC8s9BMORWH5a4atZ4D6FPVvKGzHcnc5/IVTa87myw=
|
github.com/sanity-io/litter v1.5.2 h1:AnC8s9BMORWH5a4atZ4D6FPVvKGzHcnc5/IVTa87myw=
|
||||||
github.com/sanity-io/litter v1.5.2/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0=
|
github.com/sanity-io/litter v1.5.2/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0=
|
||||||
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
|
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
|
||||||
@ -152,14 +154,17 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
|||||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
|
||||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
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/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
||||||
github.com/urfave/cli v1.20.1-0.20180226030253-8e01ec4cd3e2/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.1-0.20180226030253-8e01ec4cd3e2/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||||
@ -195,15 +200,16 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4=
|
||||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w=
|
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w=
|
||||||
golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@ -223,5 +229,5 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Check() {
|
func Check() {
|
||||||
dir := GetDir()
|
dir := GetKeybindingsDir()
|
||||||
tmpDir := filepath.Join(os.TempDir(), "lazygit_cheatsheet")
|
tmpDir := filepath.Join(os.TempDir(), "lazygit_cheatsheet")
|
||||||
err := os.RemoveAll(tmpDir)
|
err := os.RemoveAll(tmpDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -15,12 +15,12 @@ import (
|
|||||||
|
|
||||||
"github.com/jesseduffield/generics/maps"
|
"github.com/jesseduffield/generics/maps"
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
|
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||||
"github.com/jesseduffield/lazygit/pkg/app"
|
"github.com/jesseduffield/lazygit/pkg/app"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,8 +44,8 @@ func CommandToRun() string {
|
|||||||
return "go run scripts/cheatsheet/main.go generate"
|
return "go run scripts/cheatsheet/main.go generate"
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDir() string {
|
func GetKeybindingsDir() string {
|
||||||
return utils.GetLazygitRootDirectory() + "/docs/keybindings"
|
return utils.GetLazyRootDirectory() + "/docs/keybindings"
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateAtDir(cheatsheetDir string) {
|
func generateAtDir(cheatsheetDir string) {
|
||||||
@ -75,7 +75,7 @@ func generateAtDir(cheatsheetDir string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Generate() {
|
func Generate() {
|
||||||
generateAtDir(GetDir())
|
generateAtDir(GetKeybindingsDir())
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeString(file *os.File, str string) {
|
func writeString(file *os.File, str string) {
|
||||||
|
@ -2,6 +2,7 @@ package git_commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
@ -46,6 +47,19 @@ func (self *StashCommands) Save(message string) error {
|
|||||||
return self.cmd.New("git stash save " + self.cmd.Quote(message)).Run()
|
return self.cmd.New("git stash save " + self.cmd.Quote(message)).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *StashCommands) Store(sha string, message string) error {
|
||||||
|
trimmedMessage := strings.Trim(message, " \t")
|
||||||
|
if len(trimmedMessage) > 0 {
|
||||||
|
return self.cmd.New(fmt.Sprintf("git stash store %s -m %s", self.cmd.Quote(sha), self.cmd.Quote(trimmedMessage))).Run()
|
||||||
|
}
|
||||||
|
return self.cmd.New(fmt.Sprintf("git stash store %s", self.cmd.Quote(sha))).Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StashCommands) Sha(index int) (string, error) {
|
||||||
|
sha, _, err := self.cmd.New(fmt.Sprintf("git rev-parse refs/stash@{%d}", index)).DontLog().RunWithOutputs()
|
||||||
|
return strings.Trim(sha, "\r\n"), err
|
||||||
|
}
|
||||||
|
|
||||||
func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
|
func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
|
||||||
cmdStr := fmt.Sprintf("git stash show -p --stat --color=%s --unified=%d stash@{%d}", self.UserConfig.Git.Paging.ColorArg, self.UserConfig.Git.DiffContextSize, index)
|
cmdStr := fmt.Sprintf("git stash show -p --stat --color=%s --unified=%d stash@{%d}", self.UserConfig.Git.Paging.ColorArg, self.UserConfig.Git.DiffContextSize, index)
|
||||||
|
|
||||||
@ -114,3 +128,20 @@ func (self *StashCommands) StashIncludeUntrackedChanges(message string) error {
|
|||||||
return self.cmd.New(fmt.Sprintf("git stash save %s --include-untracked", self.cmd.Quote(message))).Run()
|
return self.cmd.New(fmt.Sprintf("git stash save %s --include-untracked", self.cmd.Quote(message))).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *StashCommands) Rename(index int, message string) error {
|
||||||
|
sha, err := self.Sha(index)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.Drop(index); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = self.Store(sha, message)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func TestStashDrop(t *testing.T) {
|
func TestStashDrop(t *testing.T) {
|
||||||
runner := oscommands.NewFakeRunner(t).
|
runner := oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"stash", "drop", "stash@{1}"}, "", nil)
|
ExpectGitArgs([]string{"stash", "drop", "stash@{1}"}, "Dropped refs/stash@{1} (98e9cca532c37c766107093010c72e26f2c24c04)\n", nil)
|
||||||
instance := buildStashCommands(commonDeps{runner: runner})
|
instance := buildStashCommands(commonDeps{runner: runner})
|
||||||
|
|
||||||
assert.NoError(t, instance.Drop(1))
|
assert.NoError(t, instance.Drop(1))
|
||||||
@ -44,6 +44,59 @@ func TestStashSave(t *testing.T) {
|
|||||||
runner.CheckForMissingCalls()
|
runner.CheckForMissingCalls()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStashStore(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
sha string
|
||||||
|
message string
|
||||||
|
expected []string
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
testName: "Non-empty message",
|
||||||
|
sha: "0123456789abcdef",
|
||||||
|
message: "New stash name",
|
||||||
|
expected: []string{"stash", "store", "0123456789abcdef", "-m", "New stash name"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Empty message",
|
||||||
|
sha: "0123456789abcdef",
|
||||||
|
message: "",
|
||||||
|
expected: []string{"stash", "store", "0123456789abcdef"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Space message",
|
||||||
|
sha: "0123456789abcdef",
|
||||||
|
message: " ",
|
||||||
|
expected: []string{"stash", "store", "0123456789abcdef"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
s := s
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
runner := oscommands.NewFakeRunner(t).
|
||||||
|
ExpectGitArgs(s.expected, "", nil)
|
||||||
|
instance := buildStashCommands(commonDeps{runner: runner})
|
||||||
|
|
||||||
|
assert.NoError(t, instance.Store(s.sha, s.message))
|
||||||
|
runner.CheckForMissingCalls()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStashSha(t *testing.T) {
|
||||||
|
runner := oscommands.NewFakeRunner(t).
|
||||||
|
ExpectGitArgs([]string{"rev-parse", "refs/stash@{5}"}, "14d94495194651adfd5f070590df566c11d28243\n", nil)
|
||||||
|
instance := buildStashCommands(commonDeps{runner: runner})
|
||||||
|
|
||||||
|
sha, err := instance.Sha(5)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "14d94495194651adfd5f070590df566c11d28243", sha)
|
||||||
|
runner.CheckForMissingCalls()
|
||||||
|
}
|
||||||
|
|
||||||
func TestStashStashEntryCmdObj(t *testing.T) {
|
func TestStashStashEntryCmdObj(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
@ -79,3 +132,50 @@ func TestStashStashEntryCmdObj(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStashRename(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
index int
|
||||||
|
message string
|
||||||
|
expectedShaCmd []string
|
||||||
|
shaResult string
|
||||||
|
expectedDropCmd []string
|
||||||
|
expectedStoreCmd []string
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
testName: "Default case",
|
||||||
|
index: 3,
|
||||||
|
message: "New message",
|
||||||
|
expectedShaCmd: []string{"rev-parse", "refs/stash@{3}"},
|
||||||
|
shaResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
|
||||||
|
expectedDropCmd: []string{"stash", "drop", "stash@{3}"},
|
||||||
|
expectedStoreCmd: []string{"stash", "store", "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd", "-m", "New message"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Empty message",
|
||||||
|
index: 4,
|
||||||
|
message: "",
|
||||||
|
expectedShaCmd: []string{"rev-parse", "refs/stash@{4}"},
|
||||||
|
shaResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
|
||||||
|
expectedDropCmd: []string{"stash", "drop", "stash@{4}"},
|
||||||
|
expectedStoreCmd: []string{"stash", "store", "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
s := s
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
runner := oscommands.NewFakeRunner(t).
|
||||||
|
ExpectGitArgs(s.expectedShaCmd, s.shaResult, nil).
|
||||||
|
ExpectGitArgs(s.expectedDropCmd, "", nil).
|
||||||
|
ExpectGitArgs(s.expectedStoreCmd, "", nil)
|
||||||
|
instance := buildStashCommands(commonDeps{runner: runner})
|
||||||
|
|
||||||
|
err := instance.Rename(s.index, s.message)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -65,8 +65,8 @@ outer:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
|
func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
|
||||||
rawString, _ := self.cmd.New("git stash list --pretty='%gs'").DontLog().RunWithOutput()
|
rawString, _ := self.cmd.New("git stash list -z --pretty='%gs'").DontLog().RunWithOutput()
|
||||||
return slices.MapWithIndex(utils.SplitLines(rawString), func(line string, index int) *models.StashEntry {
|
return slices.MapWithIndex(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
|
||||||
return self.stashEntryFromLine(line, index)
|
return self.stashEntryFromLine(line, index)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ func TestGetStashEntries(t *testing.T) {
|
|||||||
"No stash entries found",
|
"No stash entries found",
|
||||||
"",
|
"",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git stash list --pretty='%gs'`, "", nil),
|
Expect(`git stash list -z --pretty='%gs'`, "", nil),
|
||||||
[]*models.StashEntry{},
|
[]*models.StashEntry{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -30,8 +30,8 @@ func TestGetStashEntries(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(
|
Expect(
|
||||||
`git stash list --pretty='%gs'`,
|
`git stash list -z --pretty='%gs'`,
|
||||||
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build\nWIP on master: bb86a3f update github template",
|
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build\x00WIP on master: bb86a3f update github template\x00",
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
[]*models.StashEntry{
|
[]*models.StashEntry{
|
||||||
|
@ -266,6 +266,7 @@ type KeybindingCommitsConfig struct {
|
|||||||
|
|
||||||
type KeybindingStashConfig struct {
|
type KeybindingStashConfig struct {
|
||||||
PopStash string `yaml:"popStash"`
|
PopStash string `yaml:"popStash"`
|
||||||
|
RenameStash string `yaml:"renameStash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type KeybindingCommitFilesConfig struct {
|
type KeybindingCommitFilesConfig struct {
|
||||||
@ -548,6 +549,7 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
},
|
},
|
||||||
Stash: KeybindingStashConfig{
|
Stash: KeybindingStashConfig{
|
||||||
PopStash: "g",
|
PopStash: "g",
|
||||||
|
RenameStash: "r",
|
||||||
},
|
},
|
||||||
CommitFiles: KeybindingCommitFilesConfig{
|
CommitFiles: KeybindingCommitFilesConfig{
|
||||||
CheckoutCommitFile: "c",
|
CheckoutCommitFile: "c",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package gui
|
package gui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/boxlayout"
|
"github.com/jesseduffield/lazycore/pkg/boxlayout"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
@ -1,380 +0,0 @@
|
|||||||
package boxlayout
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestArrangeWindows(t *testing.T) {
|
|
||||||
type scenario struct {
|
|
||||||
testName string
|
|
||||||
root *Box
|
|
||||||
x0 int
|
|
||||||
y0 int
|
|
||||||
width int
|
|
||||||
height int
|
|
||||||
test func(result map[string]Dimensions)
|
|
||||||
}
|
|
||||||
|
|
||||||
scenarios := []scenario{
|
|
||||||
{
|
|
||||||
testName: "Empty box",
|
|
||||||
root: &Box{},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(t, result, map[string]Dimensions{})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Box with static and dynamic panel",
|
|
||||||
root: &Box{Children: []*Box{{Size: 1, Window: "static"}, {Weight: 1, Window: "dynamic"}}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"dynamic": {X0: 0, X1: 9, Y0: 1, Y1: 9},
|
|
||||||
"static": {X0: 0, X1: 9, Y0: 0, Y1: 0},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Box with static and two dynamic panels",
|
|
||||||
root: &Box{Children: []*Box{{Size: 1, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"static": {X0: 0, X1: 9, Y0: 0, Y1: 0},
|
|
||||||
"dynamic1": {X0: 0, X1: 9, Y0: 1, Y1: 3},
|
|
||||||
"dynamic2": {X0: 0, X1: 9, Y0: 4, Y1: 9},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Box with COLUMN direction",
|
|
||||||
root: &Box{Direction: COLUMN, Children: []*Box{{Size: 1, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"static": {X0: 0, X1: 0, Y0: 0, Y1: 9},
|
|
||||||
"dynamic1": {X0: 1, X1: 3, Y0: 0, Y1: 9},
|
|
||||||
"dynamic2": {X0: 4, X1: 9, Y0: 0, Y1: 9},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Box with COLUMN direction only on wide boxes with narrow box",
|
|
||||||
root: &Box{ConditionalDirection: func(width int, height int) Direction {
|
|
||||||
if width > 4 {
|
|
||||||
return COLUMN
|
|
||||||
} else {
|
|
||||||
return ROW
|
|
||||||
}
|
|
||||||
}, Children: []*Box{{Weight: 1, Window: "dynamic1"}, {Weight: 1, Window: "dynamic2"}}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 4,
|
|
||||||
height: 4,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"dynamic1": {X0: 0, X1: 3, Y0: 0, Y1: 1},
|
|
||||||
"dynamic2": {X0: 0, X1: 3, Y0: 2, Y1: 3},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Box with COLUMN direction only on wide boxes with wide box",
|
|
||||||
root: &Box{ConditionalDirection: func(width int, height int) Direction {
|
|
||||||
if width > 4 {
|
|
||||||
return COLUMN
|
|
||||||
} else {
|
|
||||||
return ROW
|
|
||||||
}
|
|
||||||
}, Children: []*Box{{Weight: 1, Window: "dynamic1"}, {Weight: 1, Window: "dynamic2"}}},
|
|
||||||
// 5 / 2 = 2 remainder 1. That remainder goes to the first box.
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 5,
|
|
||||||
height: 5,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"dynamic1": {X0: 0, X1: 2, Y0: 0, Y1: 4},
|
|
||||||
"dynamic2": {X0: 3, X1: 4, Y0: 0, Y1: 4},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Box with conditional children where box is wide",
|
|
||||||
root: &Box{ConditionalChildren: func(width int, height int) []*Box {
|
|
||||||
if width > 4 {
|
|
||||||
return []*Box{{Window: "wide", Weight: 1}}
|
|
||||||
} else {
|
|
||||||
return []*Box{{Window: "narrow", Weight: 1}}
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 5,
|
|
||||||
height: 5,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"wide": {X0: 0, X1: 4, Y0: 0, Y1: 4},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Box with conditional children where box is narrow",
|
|
||||||
root: &Box{ConditionalChildren: func(width int, height int) []*Box {
|
|
||||||
if width > 4 {
|
|
||||||
return []*Box{{Window: "wide", Weight: 1}}
|
|
||||||
} else {
|
|
||||||
return []*Box{{Window: "narrow", Weight: 1}}
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 4,
|
|
||||||
height: 4,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"narrow": {X0: 0, X1: 3, Y0: 0, Y1: 3},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Box with static child with size too large",
|
|
||||||
root: &Box{Direction: COLUMN, Children: []*Box{{Size: 11, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"static": {X0: 0, X1: 9, Y0: 0, Y1: 9},
|
|
||||||
// not sure if X0: 10, X1: 9 makes any sense, but testing this in the
|
|
||||||
// actual GUI it seems harmless
|
|
||||||
"dynamic1": {X0: 10, X1: 9, Y0: 0, Y1: 9},
|
|
||||||
"dynamic2": {X0: 10, X1: 9, Y0: 0, Y1: 9},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// 10 total space minus 2 from the status box leaves us with 8.
|
|
||||||
// Total weight is 3, 8 / 3 = 2 with 2 remainder.
|
|
||||||
// We want to end up with 2, 3, 5 (one unit from remainder to each dynamic box)
|
|
||||||
testName: "Distributing remainder across weighted boxes",
|
|
||||||
root: &Box{Direction: COLUMN, Children: []*Box{{Size: 2, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"static": {X0: 0, X1: 1, Y0: 0, Y1: 9}, // 2
|
|
||||||
"dynamic1": {X0: 2, X1: 4, Y0: 0, Y1: 9}, // 3
|
|
||||||
"dynamic2": {X0: 5, X1: 9, Y0: 0, Y1: 9}, // 5
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// 9 total space.
|
|
||||||
// total weight is 5, 9 / 5 = 1 with 4 remainder
|
|
||||||
// we want to give 2 of that remainder to the first, 1 to the second, and 1 to the last.
|
|
||||||
// Reason being that we just give units to each box evenly and consider weight in subsequent passes.
|
|
||||||
testName: "Distributing remainder across weighted boxes 2",
|
|
||||||
root: &Box{Direction: COLUMN, Children: []*Box{{Weight: 2, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}, {Weight: 1, Window: "dynamic3"}}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 9,
|
|
||||||
height: 10,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"dynamic1": {X0: 0, X1: 3, Y0: 0, Y1: 9}, // 4
|
|
||||||
"dynamic2": {X0: 4, X1: 6, Y0: 0, Y1: 9}, // 3
|
|
||||||
"dynamic3": {X0: 7, X1: 8, Y0: 0, Y1: 9}, // 2
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// 9 total space.
|
|
||||||
// total weight is 5, 9 / 5 = 1 with 4 remainder
|
|
||||||
// we want to give 2 of that remainder to the first, 1 to the second, and 1 to the last.
|
|
||||||
// Reason being that we just give units to each box evenly and consider weight in subsequent passes.
|
|
||||||
testName: "Distributing remainder across weighted boxes with unnormalized weights",
|
|
||||||
root: &Box{Direction: COLUMN, Children: []*Box{{Weight: 4, Window: "dynamic1"}, {Weight: 4, Window: "dynamic2"}, {Weight: 2, Window: "dynamic3"}}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 9,
|
|
||||||
height: 10,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"dynamic1": {X0: 0, X1: 3, Y0: 0, Y1: 9}, // 4
|
|
||||||
"dynamic2": {X0: 4, X1: 6, Y0: 0, Y1: 9}, // 3
|
|
||||||
"dynamic3": {X0: 7, X1: 8, Y0: 0, Y1: 9}, // 2
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Another distribution test",
|
|
||||||
root: &Box{Direction: COLUMN, Children: []*Box{
|
|
||||||
{Weight: 3, Window: "dynamic1"},
|
|
||||||
{Weight: 1, Window: "dynamic2"},
|
|
||||||
{Weight: 1, Window: "dynamic3"},
|
|
||||||
}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 9,
|
|
||||||
height: 10,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"dynamic1": {X0: 0, X1: 4, Y0: 0, Y1: 9}, // 5
|
|
||||||
"dynamic2": {X0: 5, X1: 6, Y0: 0, Y1: 9}, // 2
|
|
||||||
"dynamic3": {X0: 7, X1: 8, Y0: 0, Y1: 9}, // 2
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "Box with zero weight",
|
|
||||||
root: &Box{Direction: COLUMN, Children: []*Box{
|
|
||||||
{Weight: 1, Window: "dynamic1"},
|
|
||||||
{Weight: 0, Window: "dynamic2"},
|
|
||||||
}},
|
|
||||||
x0: 0,
|
|
||||||
y0: 0,
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
test: func(result map[string]Dimensions) {
|
|
||||||
assert.EqualValues(
|
|
||||||
t,
|
|
||||||
result,
|
|
||||||
map[string]Dimensions{
|
|
||||||
"dynamic1": {X0: 0, X1: 9, Y0: 0, Y1: 9},
|
|
||||||
"dynamic2": {X0: 10, X1: 9, Y0: 0, Y1: 9}, // when X0 > X1, we will hide the window
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range scenarios {
|
|
||||||
s := s
|
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
|
||||||
s.test(ArrangeWindows(s.root, s.x0, s.y0, s.width, s.height))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeWeights(t *testing.T) {
|
|
||||||
scenarios := []struct {
|
|
||||||
testName string
|
|
||||||
input []int
|
|
||||||
expected []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
testName: "empty",
|
|
||||||
input: []int{},
|
|
||||||
expected: []int{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "one item of value 1",
|
|
||||||
input: []int{1},
|
|
||||||
expected: []int{1},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "one item of value greater than 1",
|
|
||||||
input: []int{2},
|
|
||||||
expected: []int{1},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "slice contains 1",
|
|
||||||
input: []int{2, 1},
|
|
||||||
expected: []int{2, 1},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "slice contains 2 and 2",
|
|
||||||
input: []int{2, 2},
|
|
||||||
expected: []int{1, 1},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "no common multiple",
|
|
||||||
input: []int{2, 3},
|
|
||||||
expected: []int{2, 3},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "complex case",
|
|
||||||
input: []int{10, 10, 20},
|
|
||||||
expected: []int{1, 1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: "when a zero weight is included it is ignored",
|
|
||||||
input: []int{10, 10, 20, 0},
|
|
||||||
expected: []int{1, 1, 2, 0},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range scenarios {
|
|
||||||
s := s
|
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
|
||||||
assert.EqualValues(t, s.expected, normalizeWeights(s.input))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StashController struct {
|
type StashController struct {
|
||||||
@ -44,6 +45,11 @@ func (self *StashController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
|||||||
Handler: self.checkSelected(self.handleNewBranchOffStashEntry),
|
Handler: self.checkSelected(self.handleNewBranchOffStashEntry),
|
||||||
Description: self.c.Tr.LcNewBranch,
|
Description: self.c.Tr.LcNewBranch,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Stash.RenameStash),
|
||||||
|
Handler: self.checkSelected(self.handleRenameStashEntry),
|
||||||
|
Description: self.c.Tr.LcRenameStash,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return bindings
|
return bindings
|
||||||
@ -139,3 +145,27 @@ func (self *StashController) postStashRefresh() error {
|
|||||||
func (self *StashController) handleNewBranchOffStashEntry(stashEntry *models.StashEntry) error {
|
func (self *StashController) handleNewBranchOffStashEntry(stashEntry *models.StashEntry) error {
|
||||||
return self.helpers.Refs.NewBranch(stashEntry.RefName(), stashEntry.Description(), "")
|
return self.helpers.Refs.NewBranch(stashEntry.RefName(), stashEntry.Description(), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *StashController) handleRenameStashEntry(stashEntry *models.StashEntry) error {
|
||||||
|
message := utils.ResolvePlaceholderString(
|
||||||
|
self.c.Tr.RenameStashPrompt,
|
||||||
|
map[string]string{
|
||||||
|
"stashName": stashEntry.RefName(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.c.Prompt(types.PromptOpts{
|
||||||
|
Title: message,
|
||||||
|
InitialContent: stashEntry.Name,
|
||||||
|
HandleConfirm: func(response string) error {
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.RenameStash)
|
||||||
|
err := self.git.Stash.Rename(stashEntry.Index, response)
|
||||||
|
_ = self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.STASH}})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.context().SetSelectedLineIdx(0) // Select the renamed stash
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -89,6 +89,7 @@ func (gui *Gui) getSetTextareaTextFn(getView func() *gocui.View) func(string) {
|
|||||||
view := getView()
|
view := getView()
|
||||||
view.ClearTextArea()
|
view.ClearTextArea()
|
||||||
view.TextArea.TypeString(text)
|
view.TextArea.TypeString(text)
|
||||||
|
_ = gui.resizePopupPanel(view, view.TextArea.GetContent())
|
||||||
view.RenderTextArea()
|
view.RenderTextArea()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,6 +179,5 @@ func GetKey(key string) types.Key {
|
|||||||
} else if runeCount == 1 {
|
} else if runeCount == 1 {
|
||||||
return []rune(key)[0]
|
return []rune(key)[0]
|
||||||
}
|
}
|
||||||
log.Fatal("Key empty for keybinding: " + strings.ToLower(key))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ func (gui *Gui) handleCreateOptionsMenu() error {
|
|||||||
OpensMenu: binding.OpensMenu,
|
OpensMenu: binding.OpensMenu,
|
||||||
Label: binding.Description,
|
Label: binding.Description,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
if binding.Key == nil {
|
if binding.Handler == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ const (
|
|||||||
COMMIT_ICON = "\ufc16" // ﰖ
|
COMMIT_ICON = "\ufc16" // ﰖ
|
||||||
MERGE_COMMIT_ICON = "\ufb2c" // שּׁ
|
MERGE_COMMIT_ICON = "\ufb2c" // שּׁ
|
||||||
DEFAULT_REMOTE_ICON = "\uf7a1" //
|
DEFAULT_REMOTE_ICON = "\uf7a1" //
|
||||||
|
STASH_ICON = "\uf01c" //
|
||||||
)
|
)
|
||||||
|
|
||||||
type remoteIcon struct {
|
type remoteIcon struct {
|
||||||
@ -59,3 +60,7 @@ func IconForRemote(remote *models.Remote) string {
|
|||||||
}
|
}
|
||||||
return DEFAULT_REMOTE_ICON
|
return DEFAULT_REMOTE_ICON
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IconForStash(stash *models.StashEntry) string {
|
||||||
|
return STASH_ICON
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package presentation
|
|||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
||||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,5 +20,11 @@ func getStashEntryDisplayStrings(s *models.StashEntry, diffed bool) []string {
|
|||||||
if diffed {
|
if diffed {
|
||||||
textStyle = theme.DiffTerminalColor
|
textStyle = theme.DiffTerminalColor
|
||||||
}
|
}
|
||||||
return []string{textStyle.Sprint(s.Name)}
|
|
||||||
|
res := make([]string, 0, 2)
|
||||||
|
if icons.IsIconEnabled() {
|
||||||
|
res = append(res, textStyle.Sprint(icons.IconForStash(s)))
|
||||||
|
}
|
||||||
|
res = append(res, textStyle.Sprint(s.Name))
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
@ -139,6 +139,8 @@ func chineseTranslationSet() TranslationSet {
|
|||||||
SureApplyStashEntry: "您确定要应用此贮藏条目?",
|
SureApplyStashEntry: "您确定要应用此贮藏条目?",
|
||||||
NoTrackedStagedFilesStash: "没有可以贮藏的已跟踪/暂存文件",
|
NoTrackedStagedFilesStash: "没有可以贮藏的已跟踪/暂存文件",
|
||||||
StashChanges: "贮藏更改",
|
StashChanges: "贮藏更改",
|
||||||
|
LcRenameStash: "rename stash",
|
||||||
|
RenameStashPrompt: "Rename stash: {{.stashName}}",
|
||||||
OpenConfig: "打开配置文件",
|
OpenConfig: "打开配置文件",
|
||||||
EditConfig: "编辑配置文件",
|
EditConfig: "编辑配置文件",
|
||||||
ForcePush: "强制推送",
|
ForcePush: "强制推送",
|
||||||
@ -530,6 +532,7 @@ func chineseTranslationSet() TranslationSet {
|
|||||||
UpdateRemote: "更新远程",
|
UpdateRemote: "更新远程",
|
||||||
ApplyPatch: "应用补丁",
|
ApplyPatch: "应用补丁",
|
||||||
Stash: "贮藏 (Stash)",
|
Stash: "贮藏 (Stash)",
|
||||||
|
RenameStash: "Rename stash",
|
||||||
RemoveSubmodule: "删除子模块",
|
RemoveSubmodule: "删除子模块",
|
||||||
ResetSubmodule: "重置子模块",
|
ResetSubmodule: "重置子模块",
|
||||||
AddSubmodule: "添加子模块",
|
AddSubmodule: "添加子模块",
|
||||||
|
@ -105,6 +105,8 @@ func dutchTranslationSet() TranslationSet {
|
|||||||
SureApplyStashEntry: "Weet je zeker dat je deze stash entry wil toepassen?",
|
SureApplyStashEntry: "Weet je zeker dat je deze stash entry wil toepassen?",
|
||||||
NoTrackedStagedFilesStash: "Je hebt geen tracked/staged bestanden om te laten stashen",
|
NoTrackedStagedFilesStash: "Je hebt geen tracked/staged bestanden om te laten stashen",
|
||||||
StashChanges: "Stash veranderingen",
|
StashChanges: "Stash veranderingen",
|
||||||
|
LcRenameStash: "rename stash",
|
||||||
|
RenameStashPrompt: "Rename stash: {{.stashName}}",
|
||||||
NoChangedFiles: "Geen veranderde bestanden",
|
NoChangedFiles: "Geen veranderde bestanden",
|
||||||
OpenConfig: "open config bestand",
|
OpenConfig: "open config bestand",
|
||||||
EditConfig: "verander config bestand",
|
EditConfig: "verander config bestand",
|
||||||
|
@ -128,6 +128,8 @@ type TranslationSet struct {
|
|||||||
NoTrackedStagedFilesStash string
|
NoTrackedStagedFilesStash string
|
||||||
NoFilesToStash string
|
NoFilesToStash string
|
||||||
StashChanges string
|
StashChanges string
|
||||||
|
LcRenameStash string
|
||||||
|
RenameStashPrompt string
|
||||||
OpenConfig string
|
OpenConfig string
|
||||||
EditConfig string
|
EditConfig string
|
||||||
ForcePush string
|
ForcePush string
|
||||||
@ -603,6 +605,7 @@ type Actions struct {
|
|||||||
UpdateRemote string
|
UpdateRemote string
|
||||||
ApplyPatch string
|
ApplyPatch string
|
||||||
Stash string
|
Stash string
|
||||||
|
RenameStash string
|
||||||
RemoveSubmodule string
|
RemoveSubmodule string
|
||||||
ResetSubmodule string
|
ResetSubmodule string
|
||||||
AddSubmodule string
|
AddSubmodule string
|
||||||
@ -771,6 +774,8 @@ func EnglishTranslationSet() TranslationSet {
|
|||||||
NoTrackedStagedFilesStash: "You have no tracked/staged files to stash",
|
NoTrackedStagedFilesStash: "You have no tracked/staged files to stash",
|
||||||
NoFilesToStash: "You have no files to stash",
|
NoFilesToStash: "You have no files to stash",
|
||||||
StashChanges: "Stash changes",
|
StashChanges: "Stash changes",
|
||||||
|
LcRenameStash: "rename stash",
|
||||||
|
RenameStashPrompt: "Rename stash: {{.stashName}}",
|
||||||
OpenConfig: "open config file",
|
OpenConfig: "open config file",
|
||||||
EditConfig: "edit config file",
|
EditConfig: "edit config file",
|
||||||
ForcePush: "Force push",
|
ForcePush: "Force push",
|
||||||
@ -1230,6 +1235,7 @@ func EnglishTranslationSet() TranslationSet {
|
|||||||
UpdateRemote: "Update remote",
|
UpdateRemote: "Update remote",
|
||||||
ApplyPatch: "Apply patch",
|
ApplyPatch: "Apply patch",
|
||||||
Stash: "Stash",
|
Stash: "Stash",
|
||||||
|
RenameStash: "Rename stash",
|
||||||
RemoveSubmodule: "Remove submodule",
|
RemoveSubmodule: "Remove submodule",
|
||||||
ResetSubmodule: "Reset submodule",
|
ResetSubmodule: "Reset submodule",
|
||||||
AddSubmodule: "Add submodule",
|
AddSubmodule: "Add submodule",
|
||||||
|
@ -130,6 +130,8 @@ func japaneseTranslationSet() TranslationSet {
|
|||||||
SureApplyStashEntry: "Stashを適用します。よろしいですか?",
|
SureApplyStashEntry: "Stashを適用します。よろしいですか?",
|
||||||
// NoTrackedStagedFilesStash: "You have no tracked/staged files to stash",
|
// NoTrackedStagedFilesStash: "You have no tracked/staged files to stash",
|
||||||
StashChanges: "変更をStash",
|
StashChanges: "変更をStash",
|
||||||
|
LcRenameStash: "Stashを変更",
|
||||||
|
RenameStashPrompt: "Stash名を変更: {{.stashName}}",
|
||||||
OpenConfig: "設定ファイルを開く",
|
OpenConfig: "設定ファイルを開く",
|
||||||
EditConfig: "設定ファイルを編集",
|
EditConfig: "設定ファイルを編集",
|
||||||
ForcePush: "Force push",
|
ForcePush: "Force push",
|
||||||
@ -556,6 +558,7 @@ func japaneseTranslationSet() TranslationSet {
|
|||||||
UpdateRemote: "リモートを更新",
|
UpdateRemote: "リモートを更新",
|
||||||
ApplyPatch: "パッチを適用",
|
ApplyPatch: "パッチを適用",
|
||||||
Stash: "Stash",
|
Stash: "Stash",
|
||||||
|
RenameStash: "Stash名を変更",
|
||||||
RemoveSubmodule: "サブモジュールを削除",
|
RemoveSubmodule: "サブモジュールを削除",
|
||||||
ResetSubmodule: "サブモジュールをリセット",
|
ResetSubmodule: "サブモジュールをリセット",
|
||||||
AddSubmodule: "サブモジュールを追加",
|
AddSubmodule: "サブモジュールを追加",
|
||||||
|
@ -131,6 +131,8 @@ func koreanTranslationSet() TranslationSet {
|
|||||||
SureApplyStashEntry: "정말로 Stash를 적용하시겠습니까?",
|
SureApplyStashEntry: "정말로 Stash를 적용하시겠습니까?",
|
||||||
NoTrackedStagedFilesStash: "You have no tracked/staged files to stash",
|
NoTrackedStagedFilesStash: "You have no tracked/staged files to stash",
|
||||||
StashChanges: "변경을 Stash",
|
StashChanges: "변경을 Stash",
|
||||||
|
LcRenameStash: "rename stash",
|
||||||
|
RenameStashPrompt: "Rename stash: {{.stashName}}",
|
||||||
OpenConfig: "설정 파일 열기",
|
OpenConfig: "설정 파일 열기",
|
||||||
EditConfig: "설정 파일 수정",
|
EditConfig: "설정 파일 수정",
|
||||||
ForcePush: "강제 푸시",
|
ForcePush: "강제 푸시",
|
||||||
@ -561,6 +563,7 @@ func koreanTranslationSet() TranslationSet {
|
|||||||
UpdateRemote: "Update remote",
|
UpdateRemote: "Update remote",
|
||||||
ApplyPatch: "Apply patch",
|
ApplyPatch: "Apply patch",
|
||||||
Stash: "Stash",
|
Stash: "Stash",
|
||||||
|
RenameStash: "Rename stash",
|
||||||
RemoveSubmodule: "서브모듈 삭제",
|
RemoveSubmodule: "서브모듈 삭제",
|
||||||
ResetSubmodule: "서브모듈 Reset",
|
ResetSubmodule: "서브모듈 Reset",
|
||||||
AddSubmodule: "서브모듈 추가",
|
AddSubmodule: "서브모듈 추가",
|
||||||
|
@ -83,6 +83,8 @@ func polishTranslationSet() TranslationSet {
|
|||||||
SureDropStashEntry: "Jesteś pewny, że chcesz porzucić tę pozycję w schowku?",
|
SureDropStashEntry: "Jesteś pewny, że chcesz porzucić tę pozycję w schowku?",
|
||||||
NoTrackedStagedFilesStash: "Nie masz śledzonych/zatwierdzonych plików do przechowania",
|
NoTrackedStagedFilesStash: "Nie masz śledzonych/zatwierdzonych plików do przechowania",
|
||||||
StashChanges: "Przechowaj zmiany",
|
StashChanges: "Przechowaj zmiany",
|
||||||
|
LcRenameStash: "rename stash",
|
||||||
|
RenameStashPrompt: "Rename stash: {{.stashName}}",
|
||||||
OpenConfig: "otwórz konfigurację",
|
OpenConfig: "otwórz konfigurację",
|
||||||
EditConfig: "edytuj konfigurację",
|
EditConfig: "edytuj konfigurację",
|
||||||
ForcePush: "Wymuś wysłanie",
|
ForcePush: "Wymuś wysłanie",
|
||||||
|
@ -84,3 +84,5 @@ go run pkg/integration/deprecated/cmd/tui/main.go
|
|||||||
```
|
```
|
||||||
|
|
||||||
The tests in the old format live in test/integration. In the old format, test definitions are co-located with the snapshots. The setup step is done in a `setup.sh` shell script and the `recording.json` file contains the recorded keypresses to be replayed during the test.
|
The tests in the old format live in test/integration. In the old format, test definitions are co-located with the snapshots. The setup step is done in a `setup.sh` shell script and the `recording.json` file contains the recorded keypresses to be replayed during the test.
|
||||||
|
|
||||||
|
If you have rewritten an integration test under the new pattern, be sure to delete the old integration test directory.
|
||||||
|
@ -9,12 +9,12 @@ import (
|
|||||||
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration/components"
|
"github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration/tests"
|
"github.com/jesseduffield/lazygit/pkg/integration/tests"
|
||||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// This program lets you run integration tests from a TUI. See pkg/integration/README.md for more info.
|
// This program lets you run integration tests from a TUI. See pkg/integration/README.md for more info.
|
||||||
@ -22,7 +22,7 @@ import (
|
|||||||
var SLOW_KEY_PRESS_DELAY = 300
|
var SLOW_KEY_PRESS_DELAY = 300
|
||||||
|
|
||||||
func RunTUI() {
|
func RunTUI() {
|
||||||
rootDir := utils.GetLazygitRootDirectory()
|
rootDir := utils.GetLazyRootDirectory()
|
||||||
testDir := filepath.Join(rootDir, "test", "integration")
|
testDir := filepath.Join(rootDir, "test", "integration")
|
||||||
|
|
||||||
app := newApp(testDir)
|
app := newApp(testDir)
|
||||||
|
@ -7,8 +7,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// this is the integration runner for the new and improved integration interface
|
// this is the integration runner for the new and improved integration interface
|
||||||
@ -44,7 +44,7 @@ func RunTests(
|
|||||||
keyPressDelay int,
|
keyPressDelay int,
|
||||||
maxAttempts int,
|
maxAttempts int,
|
||||||
) error {
|
) error {
|
||||||
projectRootDir := utils.GetLazygitRootDirectory()
|
projectRootDir := utils.GetLazyRootDirectory()
|
||||||
err := os.Chdir(projectRootDir)
|
err := os.Chdir(projectRootDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -111,3 +111,8 @@ func (s *Shell) CreateNCommits(n int) *Shell {
|
|||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Shell) StashWithMessage(message string) *Shell {
|
||||||
|
s.RunCommand(fmt.Sprintf(`git stash -m "%s"`, message))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
37
pkg/integration/tests/stash/rename.go
Normal file
37
pkg/integration/tests/stash/rename.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package stash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
|
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Rename = NewIntegrationTest(NewIntegrationTestArgs{
|
||||||
|
Description: "Try to rename the stash.",
|
||||||
|
ExtraCmdArgs: "",
|
||||||
|
Skip: false,
|
||||||
|
SetupConfig: func(config *config.AppConfig) {},
|
||||||
|
SetupRepo: func(shell *Shell) {
|
||||||
|
shell.
|
||||||
|
EmptyCommit("blah").
|
||||||
|
CreateFileAndAdd("file-1", "change to stash1").
|
||||||
|
StashWithMessage("foo").
|
||||||
|
CreateFileAndAdd("file-2", "change to stash2").
|
||||||
|
StashWithMessage("bar")
|
||||||
|
},
|
||||||
|
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
|
||||||
|
input.SwitchToStashWindow()
|
||||||
|
assert.CurrentViewName("stash")
|
||||||
|
|
||||||
|
assert.MatchSelectedLine(Equals("On master: bar"))
|
||||||
|
input.NextItem()
|
||||||
|
assert.MatchSelectedLine(Equals("On master: foo"))
|
||||||
|
input.PressKeys(keys.Stash.RenameStash)
|
||||||
|
assert.InPrompt()
|
||||||
|
assert.MatchCurrentViewTitle(Equals("Rename stash: stash@{1}"))
|
||||||
|
|
||||||
|
input.Type(" baz")
|
||||||
|
input.Confirm()
|
||||||
|
|
||||||
|
assert.MatchSelectedLine(Equals("On master: foo baz"))
|
||||||
|
},
|
||||||
|
})
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jesseduffield/generics/set"
|
"github.com/jesseduffield/generics/set"
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
|
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration/components"
|
"github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/bisect"
|
"github.com/jesseduffield/lazygit/pkg/integration/tests/bisect"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/branch"
|
"github.com/jesseduffield/lazygit/pkg/integration/tests/branch"
|
||||||
@ -41,6 +42,7 @@ var tests = []*components.IntegrationTest{
|
|||||||
custom_commands.FormPrompts,
|
custom_commands.FormPrompts,
|
||||||
stash.Stash,
|
stash.Stash,
|
||||||
stash.StashIncludingUntrackedFiles,
|
stash.StashIncludingUntrackedFiles,
|
||||||
|
stash.Rename,
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTests() []*components.IntegrationTest {
|
func GetTests() []*components.IntegrationTest {
|
||||||
@ -56,7 +58,7 @@ func GetTests() []*components.IntegrationTest {
|
|||||||
|
|
||||||
missingTestNames := []string{}
|
missingTestNames := []string{}
|
||||||
|
|
||||||
if err := filepath.Walk(filepath.Join(utils.GetLazygitRootDirectory(), "pkg/integration/tests"), func(path string, info os.FileInfo, err error) error {
|
if err := filepath.Walk(filepath.Join(utils.GetLazyRootDirectory(), "pkg/integration/tests"), func(path string, info os.FileInfo, err error) error {
|
||||||
if !info.IsDir() && strings.HasSuffix(path, ".go") {
|
if !info.IsDir() && strings.HasSuffix(path, ".go") {
|
||||||
// ignoring this current file
|
// ignoring this current file
|
||||||
if filepath.Base(path) == "tests.go" {
|
if filepath.Base(path) == "tests.go" {
|
||||||
|
@ -17,6 +17,14 @@ func SplitLines(multilineString string) []string {
|
|||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SplitNul(str string) []string {
|
||||||
|
if str == "" {
|
||||||
|
return make([]string, 0)
|
||||||
|
}
|
||||||
|
str = strings.TrimSuffix(str, "\x00")
|
||||||
|
return strings.Split(str, "\x00")
|
||||||
|
}
|
||||||
|
|
||||||
// NormalizeLinefeeds - Removes all Windows and Mac style line feeds
|
// NormalizeLinefeeds - Removes all Windows and Mac style line feeds
|
||||||
func NormalizeLinefeeds(str string) string {
|
func NormalizeLinefeeds(str string) string {
|
||||||
str = strings.Replace(str, "\r\n", "\n", -1)
|
str = strings.Replace(str, "\r\n", "\n", -1)
|
||||||
|
@ -36,6 +36,37 @@ func TestSplitLines(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSplitNul(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
multilineString string
|
||||||
|
expected []string
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"",
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"\x00",
|
||||||
|
[]string{
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hello world !\x00hello universe !\x00",
|
||||||
|
[]string{
|
||||||
|
"hello world !",
|
||||||
|
"hello universe !",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
assert.EqualValues(t, s.expected, SplitNul(s.multilineString))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestNormalizeLinefeeds is a function.
|
// TestNormalizeLinefeeds is a function.
|
||||||
func TestNormalizeLinefeeds(t *testing.T) {
|
func TestNormalizeLinefeeds(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
|
@ -135,30 +135,3 @@ func FilePath(skip int) string {
|
|||||||
_, path, _, _ := runtime.Caller(skip)
|
_, path, _, _ := runtime.Caller(skip)
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
// for our cheatsheet script and integration tests. Not to be confused with finding the
|
|
||||||
// root directory of _any_ random repo.
|
|
||||||
func GetLazygitRootDirectory() string {
|
|
||||||
path, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
_, err := os.Stat(filepath.Join(path, ".git"))
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
path = filepath.Dir(path)
|
|
||||||
|
|
||||||
if path == "/" {
|
|
||||||
log.Fatal("must run in lazygit folder or child folder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
5
scripts/bump_lazycore.sh
Executable file
5
scripts/bump_lazycore.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
# Go's proxy servers are not very up-to-date so that's why we use `GOPROXY=direct`
|
||||||
|
# We specify the `awesome` branch to avoid the default behaviour of looking for a semver tag.
|
||||||
|
GOPROXY=direct go get -u github.com/jesseduffield/lazycore@master && go mod vendor && go mod tidy
|
||||||
|
|
||||||
|
# Note to self if you ever want to fork a repo be sure to use this same approach: it's important to use the branch name (e.g. master)
|
@ -18,7 +18,7 @@ func main() {
|
|||||||
switch command {
|
switch command {
|
||||||
case "generate":
|
case "generate":
|
||||||
cheatsheet.Generate()
|
cheatsheet.Generate()
|
||||||
fmt.Printf("\nGenerated cheatsheets in %s\n", cheatsheet.GetDir())
|
fmt.Printf("\nGenerated cheatsheets in %s\n", cheatsheet.GetKeybindingsDir())
|
||||||
case "check":
|
case "check":
|
||||||
cheatsheet.Check()
|
cheatsheet.Check()
|
||||||
default:
|
default:
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
blah
|
@ -0,0 +1 @@
|
|||||||
|
ref: refs/heads/master
|
@ -0,0 +1 @@
|
|||||||
|
209b9fa9264fa15f128127817f080fd7372050cf
|
@ -0,0 +1,12 @@
|
|||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = true
|
||||||
|
bare = false
|
||||||
|
logallrefupdates = true
|
||||||
|
ignorecase = true
|
||||||
|
precomposeunicode = true
|
||||||
|
[user]
|
||||||
|
email = CI@example.com
|
||||||
|
name = CI
|
||||||
|
[commit]
|
||||||
|
gpgSign = false
|
@ -0,0 +1 @@
|
|||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
BIN
test/integration_new/stash/rename/expected/repo/.git_keep/index
Normal file
BIN
test/integration_new/stash/rename/expected/repo/.git_keep/index
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
|||||||
|
0000000000000000000000000000000000000000 209b9fa9264fa15f128127817f080fd7372050cf CI <CI@example.com> 1665905121 +0900 commit (initial): blah
|
||||||
|
209b9fa9264fa15f128127817f080fd7372050cf 209b9fa9264fa15f128127817f080fd7372050cf CI <CI@example.com> 1665905122 +0900 reset: moving to HEAD
|
||||||
|
209b9fa9264fa15f128127817f080fd7372050cf 209b9fa9264fa15f128127817f080fd7372050cf CI <CI@example.com> 1665905122 +0900 reset: moving to HEAD
|
@ -0,0 +1 @@
|
|||||||
|
0000000000000000000000000000000000000000 209b9fa9264fa15f128127817f080fd7372050cf CI <CI@example.com> 1665905121 +0900 commit (initial): blah
|
@ -0,0 +1,2 @@
|
|||||||
|
0000000000000000000000000000000000000000 a5272a200897df5558c8cad683c1af14f7184124 CI <CI@example.com> 1665905122 +0900 On master: bar
|
||||||
|
a5272a200897df5558c8cad683c1af14f7184124 de8f5ed6f8a58664edfbdecd20b75a6ea0633bf6 CI <CI@example.com> 1665905125 +0900 On master: foo baz
|
@ -0,0 +1,3 @@
|
|||||||
|
x�Í1
|
||||||
|
Ã0@ÑÎ>…öB‘]YXPB SŽa9
|
||||||
|
)ĸzüä]?~iµ¾;xzÞúa¤)Ä¥0…¢l*-’®ia )jF‚äò·oí€i†×4�öËõ³Û£´:€gŽ‚ÑwDwÕkÒíOîtÏ›;¢+¸
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,4 @@
|
|||||||
|
x��Э
|
||||||
|
Т0�=ч)і.Шю&Mzъcф�L[j�>ОёрныЬ|УжRц
|
||||||
|
LњTї�РГ�Rs�й)ЄЇЬ6ЈEVВVдs�лгв@Доfg[�u�И'6=��=цhЄaь0dсоuZwFИ
у#ЎlЯt kЙiнYќоТ-Ђhi�ЊщЯЙ���X(юеЈыЯ
|
||||||
|
ќгMтrЯ@%
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,3 @@
|
|||||||
|
x��½j1„]ßS¨˜ýѯ1ÁpÕU~i¥%Ëg.gÈã[.Ò§�ùfvGÖÞ¿wC6ö5Sˆ+{ª¬Ùá‚JIlÅbbòÉôÈ[»� ¤’4§ahF§H)D
|
||||||
|
´Dÿxï*¢Ä2.Ž”9x�’ŠŽ’
|
||||||
|
¼(Nù¹›™sž—KûÍýqkGYû§Aï]‚÷æÀ4Ô1boÿħëÝôü3ø“)y{¬OHW
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
x�ϱj1ÐÔ÷êfµ’v%LÀ•+Þ´K‘e.gÈçG�{·Ã›�©£÷ïÝaÌoû¦ê2eá’™“ß(d¶�¼´F5Éj@µ!$^î²ém¡¬Å¤ EŸÌcöÈÙ³Ak!Aµ§�A¢ND±ªúÆ€ Ô™Ñ`Õcù/�ýklî|qç˧þI¿ÿ衎~rž(HѽCXf:Oìú"_®7×åwú£³1þqHë
|
@ -0,0 +1 @@
|
|||||||
|
209b9fa9264fa15f128127817f080fd7372050cf
|
@ -0,0 +1 @@
|
|||||||
|
de8f5ed6f8a58664edfbdecd20b75a6ea0633bf6
|
2
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
2
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
@ -611,6 +611,8 @@ func (g *Gui) WhitelistKeybinding(k Key) error {
|
|||||||
// typed Key or rune.
|
// typed Key or rune.
|
||||||
func getKey(key interface{}) (Key, rune, error) {
|
func getKey(key interface{}) (Key, rune, error) {
|
||||||
switch t := key.(type) {
|
switch t := key.(type) {
|
||||||
|
case nil: // Ignore keybinding if `nil`
|
||||||
|
return 0, 0, nil
|
||||||
case Key:
|
case Key:
|
||||||
return t, 0, nil
|
return t, 0, nil
|
||||||
case rune:
|
case rune:
|
||||||
|
21
vendor/github.com/jesseduffield/lazycore/LICENSE
generated
vendored
Normal file
21
vendor/github.com/jesseduffield/lazycore/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Jesse Duffield
|
||||||
|
|
||||||
|
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.
|
@ -1,8 +1,7 @@
|
|||||||
package boxlayout
|
package boxlayout
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -95,7 +94,7 @@ func ArrangeWindows(root *Box, x0, y0, width, height int) map[string]Dimensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
func calcSizes(boxes []*Box, availableSpace int) []int {
|
func calcSizes(boxes []*Box, availableSpace int) []int {
|
||||||
normalizedWeights := normalizeWeights(slices.Map(boxes, func(box *Box) int { return box.Weight }))
|
normalizedWeights := normalizeWeights(lo.Map(boxes, func(box *Box, _ int) int { return box.Weight }))
|
||||||
|
|
||||||
totalWeight := 0
|
totalWeight := 0
|
||||||
reservedSpace := 0
|
reservedSpace := 0
|
||||||
@ -152,13 +151,13 @@ func normalizeWeights(weights []int) []int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// to spare us some computation we'll exit early if any of our weights is 1
|
// to spare us some computation we'll exit early if any of our weights is 1
|
||||||
if slices.Some(weights, func(weight int) bool { return weight == 1 }) {
|
if lo.SomeBy(weights, func(weight int) bool { return weight == 1 }) {
|
||||||
return weights
|
return weights
|
||||||
}
|
}
|
||||||
|
|
||||||
// map weights to factorSlices and find the lowest common factor
|
// map weights to factorSlices and find the lowest common factor
|
||||||
positiveWeights := slices.Filter(weights, func(weight int) bool { return weight > 0 })
|
positiveWeights := lo.Filter(weights, func(weight int, _ int) bool { return weight > 0 })
|
||||||
factorSlices := slices.Map(positiveWeights, func(weight int) []int { return calcFactors(weight) })
|
factorSlices := lo.Map(positiveWeights, func(weight int, _ int) []int { return calcFactors(weight) })
|
||||||
commonFactors := factorSlices[0]
|
commonFactors := factorSlices[0]
|
||||||
for _, factors := range factorSlices {
|
for _, factors := range factorSlices {
|
||||||
commonFactors = lo.Intersect(commonFactors, factors)
|
commonFactors = lo.Intersect(commonFactors, factors)
|
||||||
@ -168,7 +167,7 @@ func normalizeWeights(weights []int) []int {
|
|||||||
return weights
|
return weights
|
||||||
}
|
}
|
||||||
|
|
||||||
newWeights := slices.Map(weights, func(weight int) int { return weight / commonFactors[0] })
|
newWeights := lo.Map(weights, func(weight int, _ int) int { return weight / commonFactors[0] })
|
||||||
|
|
||||||
return normalizeWeights(newWeights)
|
return normalizeWeights(newWeights)
|
||||||
}
|
}
|
61
vendor/github.com/jesseduffield/lazycore/pkg/utils/utils.go
generated
vendored
Normal file
61
vendor/github.com/jesseduffield/lazycore/pkg/utils/utils.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Min returns the minimum of two integers
|
||||||
|
func Min(x, y int) int {
|
||||||
|
if x < y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max returns the maximum of two integers
|
||||||
|
func Max(x, y int) int {
|
||||||
|
if x > y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp returns a value x restricted between min and max
|
||||||
|
func Clamp(x int, min int, max int) int {
|
||||||
|
if x < min {
|
||||||
|
return min
|
||||||
|
} else if x > max {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLazyRootDirectory finds a lazy project root directory.
|
||||||
|
//
|
||||||
|
// It's used for cheatsheet scripts and integration tests. Not to be confused with finding the
|
||||||
|
// root directory of _any_ random repo.
|
||||||
|
func GetLazyRootDirectory() string {
|
||||||
|
path, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, err := os.Stat(filepath.Join(path, ".git"))
|
||||||
|
if err == nil {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
path = filepath.Dir(path)
|
||||||
|
|
||||||
|
if path == "/" {
|
||||||
|
log.Fatal("must run in lazy project folder or child folder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
vendor/github.com/samber/lo/.gitignore
generated
vendored
2
vendor/github.com/samber/lo/.gitignore
generated
vendored
@ -34,3 +34,5 @@ go.work
|
|||||||
cover.out
|
cover.out
|
||||||
cover.html
|
cover.html
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
276
vendor/github.com/samber/lo/CHANGELOG.md
generated
vendored
276
vendor/github.com/samber/lo/CHANGELOG.md
generated
vendored
@ -1,5 +1,281 @@
|
|||||||
# Changelog
|
# 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.31.0 (2022-10-06)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.SliceToChannel
|
||||||
|
- lo.Generator
|
||||||
|
- lo.Batch
|
||||||
|
- lo.BatchWithTimeout
|
||||||
|
|
||||||
|
## 1.30.1 (2022-10-06)
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
|
||||||
|
- lo.Try1: remove generic type
|
||||||
|
- lo.Validate: format error properly
|
||||||
|
|
||||||
|
## 1.30.0 (2022-10-04)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.TernaryF
|
||||||
|
- lo.Validate
|
||||||
|
|
||||||
|
## 1.29.0 (2022-10-02)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.ErrorAs
|
||||||
|
- lo.TryOr
|
||||||
|
- lo.TryOrX
|
||||||
|
|
||||||
|
## 1.28.0 (2022-09-05)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.ChannelDispatcher with 6 dispatching strategies:
|
||||||
|
- lo.DispatchingStrategyRoundRobin
|
||||||
|
- lo.DispatchingStrategyRandom
|
||||||
|
- lo.DispatchingStrategyWeightedRandom
|
||||||
|
- lo.DispatchingStrategyFirst
|
||||||
|
- lo.DispatchingStrategyLeast
|
||||||
|
- lo.DispatchingStrategyMost
|
||||||
|
|
||||||
|
## 1.27.1 (2022-08-15)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
|
||||||
|
- Removed comparable constraint for lo.FindKeyBy
|
||||||
|
|
||||||
|
## 1.27.0 (2022-07-29)
|
||||||
|
|
||||||
|
Breaking:
|
||||||
|
|
||||||
|
- Change of MapToSlice prototype: `MapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(V, K) R) []R` -> `MapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(K, V) R) []R`
|
||||||
|
|
||||||
|
Added:
|
||||||
|
|
||||||
|
- lo.ChunkString
|
||||||
|
- lo.SliceToMap (alias to lo.Associate)
|
||||||
|
|
||||||
|
## 1.26.0 (2022-07-24)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.Associate
|
||||||
|
- lo.ReduceRight
|
||||||
|
- lo.FromPtrOr
|
||||||
|
- lo.MapToSlice
|
||||||
|
- lo.IsSorted
|
||||||
|
- lo.IsSortedByKey
|
||||||
|
|
||||||
|
## 1.25.0 (2022-07-04)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.FindUniques
|
||||||
|
- lo.FindUniquesBy
|
||||||
|
- lo.FindDuplicates
|
||||||
|
- lo.FindDuplicatesBy
|
||||||
|
- lo.IsNotEmpty
|
||||||
|
|
||||||
|
## 1.24.0 (2022-07-04)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.Without
|
||||||
|
- lo.WithoutEmpty
|
||||||
|
|
||||||
|
## 1.23.0 (2022-07-04)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.FindKey
|
||||||
|
- lo.FindKeyBy
|
||||||
|
|
||||||
|
## 1.22.0 (2022-07-04)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.Slice
|
||||||
|
- lo.FromPtr
|
||||||
|
- lo.IsEmpty
|
||||||
|
- lo.Compact
|
||||||
|
- lo.ToPairs: alias to lo.Entries
|
||||||
|
- lo.FromPairs: alias to lo.FromEntries
|
||||||
|
- lo.Partial
|
||||||
|
|
||||||
|
Change:
|
||||||
|
|
||||||
|
- lo.Must + lo.MustX: add context to panic message
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
|
||||||
|
- lo.Nth: out of bound exception (#137)
|
||||||
|
|
||||||
|
## 1.21.0 (2022-05-10)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.ToAnySlice
|
||||||
|
- lo.FromAnySlice
|
||||||
|
|
||||||
|
## 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.Coalesce
|
||||||
|
|
||||||
|
## 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)
|
## 1.3.0 (2022-03-03)
|
||||||
|
|
||||||
Last and Nth return errors
|
Last and Nth return errors
|
||||||
|
4
vendor/github.com/samber/lo/Dockerfile
generated
vendored
4
vendor/github.com/samber/lo/Dockerfile
generated
vendored
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
FROM golang:1.18rc1-bullseye
|
FROM golang:1.18
|
||||||
|
|
||||||
WORKDIR /go/src/github.com/samber/lo
|
WORKDIR /go/src/github.com/samber/lo
|
||||||
|
|
||||||
COPY Makefile go.* /go/src/github.com/samber/lo/
|
COPY Makefile go.* ./
|
||||||
|
|
||||||
RUN make tools
|
RUN make tools
|
||||||
|
15
vendor/github.com/samber/lo/Makefile
generated
vendored
15
vendor/github.com/samber/lo/Makefile
generated
vendored
@ -1,10 +1,5 @@
|
|||||||
|
|
||||||
BIN=go
|
BIN=go
|
||||||
# BIN=go1.18beta1
|
|
||||||
|
|
||||||
go1.18beta1:
|
|
||||||
go install golang.org/dl/go1.18beta1@latest
|
|
||||||
go1.18beta1 download
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
${BIN} build -v ./...
|
${BIN} build -v ./...
|
||||||
@ -12,15 +7,15 @@ build:
|
|||||||
test:
|
test:
|
||||||
go test -race -v ./...
|
go test -race -v ./...
|
||||||
watch-test:
|
watch-test:
|
||||||
reflex -R assets.go -t 50ms -s -- sh -c 'gotest -race -v ./...'
|
reflex -t 50ms -s -- sh -c 'gotest -race -v ./...'
|
||||||
|
|
||||||
bench:
|
bench:
|
||||||
go test -benchmem -count 3 -bench ./...
|
go test -benchmem -count 3 -bench ./...
|
||||||
watch-bench:
|
watch-bench:
|
||||||
reflex -R assets.go -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...'
|
reflex -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...'
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
${BIN} test -v -coverprofile cover.out .
|
${BIN} test -v -coverprofile=cover.out -covermode=atomic .
|
||||||
${BIN} tool cover -html=cover.out -o cover.html
|
${BIN} tool cover -html=cover.out -o cover.html
|
||||||
|
|
||||||
# tools
|
# tools
|
||||||
@ -31,7 +26,7 @@ tools:
|
|||||||
${BIN} install github.com/jondot/goweight@latest
|
${BIN} install github.com/jondot/goweight@latest
|
||||||
${BIN} install github.com/golangci/golangci-lint/cmd/golangci-lint@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 golang.org/x/tools/cmd/cover
|
||||||
${BIN} get -t -u github.com/sonatype-nexus-community/nancy@latest
|
${BIN} install github.com/sonatype-nexus-community/nancy@latest
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
@ -40,11 +35,9 @@ lint-fix:
|
|||||||
golangci-lint run --timeout 60s --max-same-issues 50 --fix ./...
|
golangci-lint run --timeout 60s --max-same-issues 50 --fix ./...
|
||||||
|
|
||||||
audit: tools
|
audit: tools
|
||||||
${BIN} mod tidy
|
|
||||||
${BIN} list -json -m all | nancy sleuth
|
${BIN} list -json -m all | nancy sleuth
|
||||||
|
|
||||||
outdated: tools
|
outdated: tools
|
||||||
${BIN} mod tidy
|
|
||||||
${BIN} list -u -m -json all | go-mod-outdated -update -direct
|
${BIN} list -u -m -json all | go-mod-outdated -update -direct
|
||||||
|
|
||||||
weight: tools
|
weight: tools
|
||||||
|
1833
vendor/github.com/samber/lo/README.md
generated
vendored
1833
vendor/github.com/samber/lo/README.md
generated
vendored
File diff suppressed because it is too large
Load Diff
228
vendor/github.com/samber/lo/channel.go
generated
vendored
Normal file
228
vendor/github.com/samber/lo/channel.go
generated
vendored
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
package lo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DispatchingStrategy[T any] func(msg T, index uint64, channels []<-chan T) int
|
||||||
|
|
||||||
|
// ChannelDispatcher distributes messages from input channels into N child channels.
|
||||||
|
// Close events are propagated to children.
|
||||||
|
// Underlying channels can have a fixed buffer capacity or be unbuffered when cap is 0.
|
||||||
|
func ChannelDispatcher[T any](stream <-chan T, count int, channelBufferCap int, strategy DispatchingStrategy[T]) []<-chan T {
|
||||||
|
children := createChannels[T](count, channelBufferCap)
|
||||||
|
|
||||||
|
roChildren := channelsToReadOnly(children)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// propagate channel closing to children
|
||||||
|
defer closeChannels(children)
|
||||||
|
|
||||||
|
var i uint64 = 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
msg, ok := <-stream
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
destination := strategy(msg, i, roChildren) % count
|
||||||
|
children[destination] <- msg
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return roChildren
|
||||||
|
}
|
||||||
|
|
||||||
|
func createChannels[T any](count int, channelBufferCap int) []chan T {
|
||||||
|
children := make([]chan T, 0, count)
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
children = append(children, make(chan T, channelBufferCap))
|
||||||
|
}
|
||||||
|
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
func channelsToReadOnly[T any](children []chan T) []<-chan T {
|
||||||
|
roChildren := make([]<-chan T, 0, len(children))
|
||||||
|
|
||||||
|
for i := range children {
|
||||||
|
roChildren = append(roChildren, children[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return roChildren
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeChannels[T any](children []chan T) {
|
||||||
|
for i := 0; i < len(children); i++ {
|
||||||
|
close(children[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func channelIsNotFull[T any](ch <-chan T) bool {
|
||||||
|
return cap(ch) == 0 || len(ch) < cap(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyRoundRobin distributes messages in a rotating sequential manner.
|
||||||
|
// If the channel capacity is exceeded, the next channel will be selected and so on.
|
||||||
|
func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan T) int {
|
||||||
|
for {
|
||||||
|
i := int(index % uint64(len(channels)))
|
||||||
|
if channelIsNotFull(channels[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyRandom distributes messages in a random manner.
|
||||||
|
// If the channel capacity is exceeded, another random channel will be selected and so on.
|
||||||
|
func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T) int {
|
||||||
|
for {
|
||||||
|
i := rand.Intn(len(channels))
|
||||||
|
if channelIsNotFull(channels[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyRandom distributes messages in a weighted manner.
|
||||||
|
// If the channel capacity is exceeded, another random channel will be selected and so on.
|
||||||
|
func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy[T] {
|
||||||
|
seq := []int{}
|
||||||
|
|
||||||
|
for i := 0; i < len(weights); i++ {
|
||||||
|
for j := 0; j < weights[i]; j++ {
|
||||||
|
seq = append(seq, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(msg T, index uint64, channels []<-chan T) int {
|
||||||
|
for {
|
||||||
|
i := seq[rand.Intn(len(seq))]
|
||||||
|
if channelIsNotFull(channels[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyFirst distributes messages in the first non-full channel.
|
||||||
|
// If the capacity of the first channel is exceeded, the second channel will be selected and so on.
|
||||||
|
func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) int {
|
||||||
|
for {
|
||||||
|
for i := range channels {
|
||||||
|
if channelIsNotFull(channels[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyLeast distributes messages in the emptiest channel.
|
||||||
|
func DispatchingStrategyLeast[T any](msg T, index uint64, channels []<-chan T) int {
|
||||||
|
seq := Range(len(channels))
|
||||||
|
|
||||||
|
return MinBy(seq, func(item int, min int) bool {
|
||||||
|
return len(channels[item]) < len(channels[min])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyMost distributes messages in the fulliest channel.
|
||||||
|
// If the channel capacity is exceeded, the next channel will be selected and so on.
|
||||||
|
func DispatchingStrategyMost[T any](msg T, index uint64, channels []<-chan T) int {
|
||||||
|
seq := Range(len(channels))
|
||||||
|
|
||||||
|
return MaxBy(seq, func(item int, max int) bool {
|
||||||
|
return len(channels[item]) > len(channels[max]) && channelIsNotFull(channels[item])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceToChannel returns a read-only channels of collection elements.
|
||||||
|
func SliceToChannel[T any](bufferSize int, collection []T) <-chan T {
|
||||||
|
ch := make(chan T, bufferSize)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for _, item := range collection {
|
||||||
|
ch <- item
|
||||||
|
}
|
||||||
|
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generator implements the generator design pattern.
|
||||||
|
func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T {
|
||||||
|
ch := make(chan T, bufferSize)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// WARNING: infinite loop
|
||||||
|
generator(func(t T) {
|
||||||
|
ch <- t
|
||||||
|
})
|
||||||
|
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch creates a slice of n elements from a channel. Returns the slice and the slice length.
|
||||||
|
// @TODO: we should probaby provide an helper that reuse the same buffer.
|
||||||
|
func Batch[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) {
|
||||||
|
buffer := make([]T, 0, size)
|
||||||
|
index := 0
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for ; index < size; index++ {
|
||||||
|
item, ok := <-ch
|
||||||
|
if !ok {
|
||||||
|
return buffer, index, time.Since(now), false
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = append(buffer, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, index, time.Since(now), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length.
|
||||||
|
// @TODO: we should probaby provide an helper that reuse the same buffer.
|
||||||
|
func BatchWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) {
|
||||||
|
expire := time.NewTimer(timeout)
|
||||||
|
defer expire.Stop()
|
||||||
|
|
||||||
|
buffer := make([]T, 0, size)
|
||||||
|
index := 0
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for ; index < size; index++ {
|
||||||
|
select {
|
||||||
|
case item, ok := <-ch:
|
||||||
|
if !ok {
|
||||||
|
return buffer, index, time.Since(now), false
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = append(buffer, item)
|
||||||
|
|
||||||
|
case <-expire.C:
|
||||||
|
return buffer, index, time.Since(now), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, index, time.Since(now), true
|
||||||
|
}
|
95
vendor/github.com/samber/lo/concurrency.go
generated
vendored
Normal file
95
vendor/github.com/samber/lo/concurrency.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
51
vendor/github.com/samber/lo/condition.go
generated
vendored
51
vendor/github.com/samber/lo/condition.go
generated
vendored
@ -1,6 +1,7 @@
|
|||||||
package lo
|
package lo
|
||||||
|
|
||||||
// Ternary is a 1 line if/else statement.
|
// Ternary is a 1 line if/else statement.
|
||||||
|
// Play: https://go.dev/play/p/t-D7WBL44h2
|
||||||
func Ternary[T any](condition bool, ifOutput T, elseOutput T) T {
|
func Ternary[T any](condition bool, ifOutput T, elseOutput T) T {
|
||||||
if condition {
|
if condition {
|
||||||
return ifOutput
|
return ifOutput
|
||||||
@ -9,12 +10,23 @@ func Ternary[T any](condition bool, ifOutput T, elseOutput T) T {
|
|||||||
return elseOutput
|
return elseOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TernaryF is a 1 line if/else statement whose options are functions
|
||||||
|
// Play: https://go.dev/play/p/AO4VW20JoqM
|
||||||
|
func TernaryF[T any](condition bool, ifFunc func() T, elseFunc func() T) T {
|
||||||
|
if condition {
|
||||||
|
return ifFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
return elseFunc()
|
||||||
|
}
|
||||||
|
|
||||||
type ifElse[T any] struct {
|
type ifElse[T any] struct {
|
||||||
result T
|
result T
|
||||||
done bool
|
done bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// If.
|
// If.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
func If[T any](condition bool, result T) *ifElse[T] {
|
func If[T any](condition bool, result T) *ifElse[T] {
|
||||||
if condition {
|
if condition {
|
||||||
return &ifElse[T]{result, true}
|
return &ifElse[T]{result, true}
|
||||||
@ -24,7 +36,19 @@ func If[T any](condition bool, result T) *ifElse[T] {
|
|||||||
return &ifElse[T]{t, false}
|
return &ifElse[T]{t, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IfF.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
|
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.
|
// ElseIf.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
func (i *ifElse[T]) ElseIf(condition bool, result T) *ifElse[T] {
|
func (i *ifElse[T]) ElseIf(condition bool, result T) *ifElse[T] {
|
||||||
if !i.done && condition {
|
if !i.done && condition {
|
||||||
i.result = result
|
i.result = result
|
||||||
@ -34,7 +58,19 @@ func (i *ifElse[T]) ElseIf(condition bool, result T) *ifElse[T] {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ElseIfF.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
|
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.
|
// Else.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
func (i *ifElse[T]) Else(result T) T {
|
func (i *ifElse[T]) Else(result T) T {
|
||||||
if i.done {
|
if i.done {
|
||||||
return i.result
|
return i.result
|
||||||
@ -43,6 +79,16 @@ func (i *ifElse[T]) Else(result T) T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ElseF.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
|
func (i *ifElse[T]) ElseF(resultF func() T) T {
|
||||||
|
if i.done {
|
||||||
|
return i.result
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultF()
|
||||||
|
}
|
||||||
|
|
||||||
type switchCase[T comparable, R any] struct {
|
type switchCase[T comparable, R any] struct {
|
||||||
predicate T
|
predicate T
|
||||||
result R
|
result R
|
||||||
@ -50,6 +96,7 @@ type switchCase[T comparable, R any] struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch is a pure functional switch/case/default statement.
|
// Switch is a pure functional switch/case/default statement.
|
||||||
|
// Play: https://go.dev/play/p/TGbKUMAeRUd
|
||||||
func Switch[T comparable, R any](predicate T) *switchCase[T, R] {
|
func Switch[T comparable, R any](predicate T) *switchCase[T, R] {
|
||||||
var result R
|
var result R
|
||||||
|
|
||||||
@ -61,6 +108,7 @@ func Switch[T comparable, R any](predicate T) *switchCase[T, R] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Case.
|
// Case.
|
||||||
|
// Play: https://go.dev/play/p/TGbKUMAeRUd
|
||||||
func (s *switchCase[T, R]) Case(val T, result R) *switchCase[T, R] {
|
func (s *switchCase[T, R]) Case(val T, result R) *switchCase[T, R] {
|
||||||
if !s.done && s.predicate == val {
|
if !s.done && s.predicate == val {
|
||||||
s.result = result
|
s.result = result
|
||||||
@ -71,6 +119,7 @@ func (s *switchCase[T, R]) Case(val T, result R) *switchCase[T, R] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CaseF.
|
// CaseF.
|
||||||
|
// Play: https://go.dev/play/p/TGbKUMAeRUd
|
||||||
func (s *switchCase[T, R]) CaseF(val T, cb func() R) *switchCase[T, R] {
|
func (s *switchCase[T, R]) CaseF(val T, cb func() R) *switchCase[T, R] {
|
||||||
if !s.done && s.predicate == val {
|
if !s.done && s.predicate == val {
|
||||||
s.result = cb()
|
s.result = cb()
|
||||||
@ -81,6 +130,7 @@ func (s *switchCase[T, R]) CaseF(val T, cb func() R) *switchCase[T, R] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Default.
|
// Default.
|
||||||
|
// Play: https://go.dev/play/p/TGbKUMAeRUd
|
||||||
func (s *switchCase[T, R]) Default(result R) R {
|
func (s *switchCase[T, R]) Default(result R) R {
|
||||||
if !s.done {
|
if !s.done {
|
||||||
s.result = result
|
s.result = result
|
||||||
@ -90,6 +140,7 @@ func (s *switchCase[T, R]) Default(result R) R {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DefaultF.
|
// DefaultF.
|
||||||
|
// Play: https://go.dev/play/p/TGbKUMAeRUd
|
||||||
func (s *switchCase[T, R]) DefaultF(cb func() R) R {
|
func (s *switchCase[T, R]) DefaultF(cb func() R) R {
|
||||||
if !s.done {
|
if !s.done {
|
||||||
s.result = cb()
|
s.result = cb()
|
||||||
|
4
vendor/github.com/samber/lo/docker-compose.yml
generated
vendored
4
vendor/github.com/samber/lo/docker-compose.yml
generated
vendored
@ -2,8 +2,8 @@ version: '3'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
dev:
|
dev:
|
||||||
build: .
|
image: golang:1.18-bullseye
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/go/src/github.com/samber/lo
|
- ./:/go/src/github.com/samber/lo
|
||||||
working_dir: /go/src/github.com/samber/lo
|
working_dir: /go/src/github.com/samber/lo
|
||||||
command: bash -c 'make tools ; make watch-test'
|
command: make watch-test
|
||||||
|
65
vendor/github.com/samber/lo/drop.go
generated
vendored
65
vendor/github.com/samber/lo/drop.go
generated
vendored
@ -1,65 +0,0 @@
|
|||||||
package lo
|
|
||||||
|
|
||||||
//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
|
|
||||||
}
|
|
354
vendor/github.com/samber/lo/errors.go
generated
vendored
Normal file
354
vendor/github.com/samber/lo/errors.go
generated
vendored
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
package lo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate is a helper that creates an error when a condition is not met.
|
||||||
|
// Play: https://go.dev/play/p/vPyh51XpCBt
|
||||||
|
func Validate(ok bool, format string, args ...any) error {
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
||||||
|
if len(msgAndArgs) == 1 {
|
||||||
|
if msgAsStr, ok := msgAndArgs[0].(string); ok {
|
||||||
|
return msgAsStr
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%+v", msgAndArgs[0])
|
||||||
|
}
|
||||||
|
if len(msgAndArgs) > 1 {
|
||||||
|
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// must panics if err is error or false.
|
||||||
|
func must(err any, messageArgs ...interface{}) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e := err.(type) {
|
||||||
|
case bool:
|
||||||
|
if !e {
|
||||||
|
message := messageFromMsgAndArgs(messageArgs...)
|
||||||
|
if message == "" {
|
||||||
|
message = "not ok"
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
case error:
|
||||||
|
message := messageFromMsgAndArgs(messageArgs...)
|
||||||
|
if message != "" {
|
||||||
|
panic(message + ": " + e.Error())
|
||||||
|
} else {
|
||||||
|
panic(e.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("must: invalid err type '" + reflect.TypeOf(err).Name() + "', should either be a bool or an error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must[T any](val T, err any, messageArgs ...interface{}) T {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must0 has the same behavior than Must, but callback returns no variable.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must0(err any, messageArgs ...interface{}) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must1 is an alias to Must
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must1[T any](val T, err any, messageArgs ...interface{}) T {
|
||||||
|
return Must(val, err, messageArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must2 has the same behavior than Must, but callback returns 2 variables.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must2[T1 any, T2 any](val1 T1, val2 T2, err any, messageArgs ...interface{}) (T1, T2) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val1, val2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must3 has the same behavior than Must, but callback returns 3 variables.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must3[T1 any, T2 any, T3 any](val1 T1, val2 T2, val3 T3, err any, messageArgs ...interface{}) (T1, T2, T3) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val1, val2, val3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must4 has the same behavior than Must, but callback returns 4 variables.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must4[T1 any, T2 any, T3 any, T4 any](val1 T1, val2 T2, val3 T3, val4 T4, err any, messageArgs ...interface{}) (T1, T2, T3, T4) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val1, val2, val3, val4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must5 has the same behavior than Must, but callback returns 5 variables.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must5[T1 any, T2 any, T3 any, T4 any, T5 any](val1 T1, val2 T2, val3 T3, val4 T4, val5 T5, err any, messageArgs ...interface{}) (T1, T2, T3, T4, T5) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val1, val2, val3, val4, val5
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must6 has the same behavior than Must, but callback returns 6 variables.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
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, messageArgs ...interface{}) (T1, T2, T3, T4, T5, T6) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
func Try0(callback func()) bool {
|
||||||
|
return Try(func() error {
|
||||||
|
callback()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try1 is an alias to Try.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
func Try1(callback func() error) bool {
|
||||||
|
return Try(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try2 has the same behavior than Try, but callback returns 2 variables.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr has the same behavior than Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr[A any](callback func() (A, error), fallbackA A) (A, bool) {
|
||||||
|
return TryOr1(callback, fallbackA)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr1 has the same behavior than Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr1[A any](callback func() (A, error), fallbackA A) (A, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr2 has the same behavior than Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr2[A any, B any](callback func() (A, B, error), fallbackA A, fallbackB B) (A, B, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, b, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
fallbackB = b
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, fallbackB, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr3 has the same behavior than Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr3[A any, B any, C any](callback func() (A, B, C, error), fallbackA A, fallbackB B, fallbackC C) (A, B, C, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, b, c, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
fallbackB = b
|
||||||
|
fallbackC = c
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, fallbackB, fallbackC, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr4 has the same behavior than Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr4[A any, B any, C any, D any](callback func() (A, B, C, D, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D) (A, B, C, D, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, b, c, d, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
fallbackB = b
|
||||||
|
fallbackC = c
|
||||||
|
fallbackD = d
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, fallbackB, fallbackC, fallbackD, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr5 has the same behavior than Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr5[A any, B any, C any, D any, E any](callback func() (A, B, C, D, E, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D, fallbackE E) (A, B, C, D, E, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, b, c, d, e, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
fallbackB = b
|
||||||
|
fallbackC = c
|
||||||
|
fallbackD = d
|
||||||
|
fallbackE = e
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, fallbackB, fallbackC, fallbackD, fallbackE, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr6 has the same behavior than Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr6[A any, B any, C any, D any, E any, F any](callback func() (A, B, C, D, E, F, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D, fallbackE E, fallbackF F) (A, B, C, D, E, F, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, b, c, d, e, f, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
fallbackB = b
|
||||||
|
fallbackC = c
|
||||||
|
fallbackD = d
|
||||||
|
fallbackE = e
|
||||||
|
fallbackF = f
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, fallbackB, fallbackC, fallbackD, fallbackE, fallbackF, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryWithErrorValue has the same behavior than Try, but also returns value passed to panic.
|
||||||
|
// Play: https://go.dev/play/p/Kc7afQIT2Fs
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/PnOON-EqBiU
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/8Pc9gwX_GZO
|
||||||
|
func TryCatchWithErrorValue(callback func() error, catch func(any)) {
|
||||||
|
if err, ok := TryWithErrorValue(callback); !ok {
|
||||||
|
catch(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorsAs is a shortcut for errors.As(err, &&T).
|
||||||
|
// Play: https://go.dev/play/p/8wk5rH8UfrE
|
||||||
|
func ErrorsAs[T error](err error) (T, bool) {
|
||||||
|
var t T
|
||||||
|
ok := errors.As(err, &t)
|
||||||
|
return t, ok
|
||||||
|
}
|
247
vendor/github.com/samber/lo/find.go
generated
vendored
247
vendor/github.com/samber/lo/find.go
generated
vendored
@ -3,7 +3,7 @@ package lo
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"math"
|
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ func IndexOf[T comparable](collection []T, element T) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// IndexOf returns the index at which the last occurrence of a value is found in an array or 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.
|
// if the value cannot be found.
|
||||||
func LastIndexOf[T comparable](collection []T, element T) int {
|
func LastIndexOf[T comparable](collection []T, element T) int {
|
||||||
length := len(collection)
|
length := len(collection)
|
||||||
@ -47,6 +47,179 @@ func Find[T any](collection []T, predicate func(T) bool) (T, bool) {
|
|||||||
return result, false
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindKey returns the key of the first value matching.
|
||||||
|
func FindKey[K comparable, V comparable](object map[K]V, value V) (K, bool) {
|
||||||
|
for k, v := range object {
|
||||||
|
if v == value {
|
||||||
|
return k, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Empty[K](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindKeyBy returns the key of the first element predicate returns truthy for.
|
||||||
|
func FindKeyBy[K comparable, V any](object map[K]V, predicate func(K, V) bool) (K, bool) {
|
||||||
|
for k, v := range object {
|
||||||
|
if predicate(k, v) {
|
||||||
|
return k, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Empty[K](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindUniques returns a slice with all the unique elements of the collection.
|
||||||
|
// The order of result values is determined by the order they occur in the collection.
|
||||||
|
func FindUniques[T comparable](collection []T) []T {
|
||||||
|
isDupl := make(map[T]bool, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
duplicated, ok := isDupl[item]
|
||||||
|
if !ok {
|
||||||
|
isDupl[item] = false
|
||||||
|
} else if !duplicated {
|
||||||
|
isDupl[item] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-len(isDupl))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
if duplicated := isDupl[item]; !duplicated {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindUniquesBy returns a slice with all the unique elements of the collection.
|
||||||
|
// 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 FindUniquesBy[T any, U comparable](collection []T, iteratee func(T) U) []T {
|
||||||
|
isDupl := make(map[U]bool, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
duplicated, ok := isDupl[key]
|
||||||
|
if !ok {
|
||||||
|
isDupl[key] = false
|
||||||
|
} else if !duplicated {
|
||||||
|
isDupl[key] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-len(isDupl))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
if duplicated := isDupl[key]; !duplicated {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindDuplicates returns a slice with the first occurence of each duplicated elements of the collection.
|
||||||
|
// The order of result values is determined by the order they occur in the collection.
|
||||||
|
func FindDuplicates[T comparable](collection []T) []T {
|
||||||
|
isDupl := make(map[T]bool, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
duplicated, ok := isDupl[item]
|
||||||
|
if !ok {
|
||||||
|
isDupl[item] = false
|
||||||
|
} else if !duplicated {
|
||||||
|
isDupl[item] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-len(isDupl))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
if duplicated := isDupl[item]; duplicated {
|
||||||
|
result = append(result, item)
|
||||||
|
isDupl[item] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindDuplicatesBy returns a slice with the first occurence of each duplicated elements of the collection.
|
||||||
|
// 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 FindDuplicatesBy[T any, U comparable](collection []T, iteratee func(T) U) []T {
|
||||||
|
isDupl := make(map[U]bool, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
duplicated, ok := isDupl[key]
|
||||||
|
if !ok {
|
||||||
|
isDupl[key] = false
|
||||||
|
} else if !duplicated {
|
||||||
|
isDupl[key] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-len(isDupl))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
if duplicated := isDupl[key]; duplicated {
|
||||||
|
result = append(result, item)
|
||||||
|
isDupl[key] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Min search the minimum value of a collection.
|
// Min search the minimum value of a collection.
|
||||||
func Min[T constraints.Ordered](collection []T) T {
|
func Min[T constraints.Ordered](collection []T) T {
|
||||||
var min T
|
var min T
|
||||||
@ -60,7 +233,6 @@ func Min[T constraints.Ordered](collection []T) T {
|
|||||||
for i := 1; i < len(collection); i++ {
|
for i := 1; i < len(collection); i++ {
|
||||||
item := collection[i]
|
item := collection[i]
|
||||||
|
|
||||||
// if item.Less(min) {
|
|
||||||
if item < min {
|
if item < min {
|
||||||
min = item
|
min = item
|
||||||
}
|
}
|
||||||
@ -69,7 +241,29 @@ func Min[T constraints.Ordered](collection []T) T {
|
|||||||
return min
|
return min
|
||||||
}
|
}
|
||||||
|
|
||||||
// Max search the maximum value of a collection.
|
// 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 {
|
func Max[T constraints.Ordered](collection []T) T {
|
||||||
var max T
|
var max T
|
||||||
|
|
||||||
@ -90,6 +284,28 @@ func Max[T constraints.Ordered](collection []T) T {
|
|||||||
return max
|
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.
|
// Last returns the last element of a collection or error if empty.
|
||||||
func Last[T any](collection []T) (T, error) {
|
func Last[T any](collection []T) (T, error) {
|
||||||
length := len(collection)
|
length := len(collection)
|
||||||
@ -104,19 +320,18 @@ func Last[T any](collection []T) (T, error) {
|
|||||||
|
|
||||||
// Nth returns the element at index `nth` of collection. If `nth` is negative, the nth element
|
// 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.
|
// 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) {
|
func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) {
|
||||||
if int(math.Abs(float64(nth))) >= len(collection) {
|
n := int(nth)
|
||||||
|
l := len(collection)
|
||||||
|
if n >= l || -n > l {
|
||||||
var t T
|
var t T
|
||||||
return t, fmt.Errorf("nth: %d out of slice bounds", nth)
|
return t, fmt.Errorf("nth: %d out of slice bounds", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
length := len(collection)
|
if n >= 0 {
|
||||||
|
return collection[n], nil
|
||||||
if nth >= 0 {
|
|
||||||
return collection[nth], nil
|
|
||||||
}
|
}
|
||||||
|
return collection[l+n], nil
|
||||||
return collection[length+nth], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sample returns a random item from collection.
|
// Sample returns a random item from collection.
|
||||||
@ -133,11 +348,7 @@ func Sample[T any](collection []T) T {
|
|||||||
func Samples[T any](collection []T, count int) []T {
|
func Samples[T any](collection []T, count int) []T {
|
||||||
size := len(collection)
|
size := len(collection)
|
||||||
|
|
||||||
// put values into a map, for faster deletion
|
cOpy := append([]T{}, collection...)
|
||||||
cOpy := make([]T, 0, size)
|
|
||||||
for _, v := range collection {
|
|
||||||
cOpy = append(cOpy, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
results := []T{}
|
results := []T{}
|
||||||
|
|
||||||
|
8
vendor/github.com/samber/lo/func.go
generated
vendored
Normal file
8
vendor/github.com/samber/lo/func.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package lo
|
||||||
|
|
||||||
|
// Partial returns new function that, when called, has its first argument set to the provided value.
|
||||||
|
func Partial[T1, T2, R any](f func(T1, T2) R, arg1 T1) func(T2) R {
|
||||||
|
return func(t2 T2) R {
|
||||||
|
return f(arg1, t2)
|
||||||
|
}
|
||||||
|
}
|
73
vendor/github.com/samber/lo/intersect.go
generated
vendored
73
vendor/github.com/samber/lo/intersect.go
generated
vendored
@ -22,7 +22,7 @@ func ContainsBy[T any](collection []T, predicate func(T) bool) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every returns true if all elements of a subset are contained into a collection.
|
// 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 {
|
func Every[T comparable](collection []T, subset []T) bool {
|
||||||
for _, elem := range subset {
|
for _, elem := range subset {
|
||||||
if !Contains(collection, elem) {
|
if !Contains(collection, elem) {
|
||||||
@ -33,7 +33,19 @@ func Every[T comparable](collection []T, subset []T) bool {
|
|||||||
return true
|
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.
|
// 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 {
|
func Some[T comparable](collection []T, subset []T) bool {
|
||||||
for _, elem := range subset {
|
for _, elem := range subset {
|
||||||
if Contains(collection, elem) {
|
if Contains(collection, elem) {
|
||||||
@ -44,6 +56,40 @@ func Some[T comparable](collection []T, subset []T) bool {
|
|||||||
return false
|
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.
|
// Intersect returns the intersection between two collections.
|
||||||
func Intersect[T comparable](list1 []T, list2 []T) []T {
|
func Intersect[T comparable](list1 []T, list2 []T) []T {
|
||||||
result := []T{}
|
result := []T{}
|
||||||
@ -129,3 +175,28 @@ func Union[T comparable](list1 []T, list2 []T) []T {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Without returns slice excluding all given values.
|
||||||
|
func Without[T comparable](collection []T, exclude ...T) []T {
|
||||||
|
result := make([]T, 0, len(collection))
|
||||||
|
for _, e := range collection {
|
||||||
|
if !Contains(exclude, e) {
|
||||||
|
result = append(result, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutEmpty returns slice excluding empty values.
|
||||||
|
func WithoutEmpty[T comparable](collection []T) []T {
|
||||||
|
var empty T
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection))
|
||||||
|
for _, e := range collection {
|
||||||
|
if e != empty {
|
||||||
|
result = append(result, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
132
vendor/github.com/samber/lo/map.go
generated
vendored
132
vendor/github.com/samber/lo/map.go
generated
vendored
@ -1,10 +1,11 @@
|
|||||||
package lo
|
package lo
|
||||||
|
|
||||||
// Keys creates an array of the map keys.
|
// Keys creates an array of the map keys.
|
||||||
|
// Play: https://go.dev/play/p/Uu11fHASqrU
|
||||||
func Keys[K comparable, V any](in map[K]V) []K {
|
func Keys[K comparable, V any](in map[K]V) []K {
|
||||||
result := make([]K, 0, len(in))
|
result := make([]K, 0, len(in))
|
||||||
|
|
||||||
for k, _ := range in {
|
for k := range in {
|
||||||
result = append(result, k)
|
result = append(result, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ func Keys[K comparable, V any](in map[K]V) []K {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Values creates an array of the map values.
|
// Values creates an array of the map values.
|
||||||
|
// Play: https://go.dev/play/p/nnRTQkzQfF6
|
||||||
func Values[K comparable, V any](in map[K]V) []V {
|
func Values[K comparable, V any](in map[K]V) []V {
|
||||||
result := make([]V, 0, len(in))
|
result := make([]V, 0, len(in))
|
||||||
|
|
||||||
@ -22,7 +24,80 @@ func Values[K comparable, V any](in map[K]V) []V {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PickBy returns same map type filtered by given predicate.
|
||||||
|
// Play: https://go.dev/play/p/kdg8GR_QMmf
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/R1imbuci9qU
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/1zdzSvbfsJc
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// OmitBy returns same map type filtered by given predicate.
|
||||||
|
// Play: https://go.dev/play/p/EtBsR43bdsd
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/t1QjCrs-ysk
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/9UYZi-hrs8j
|
||||||
|
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.
|
// Entries transforms a map into array of key/value pairs.
|
||||||
|
// Play:
|
||||||
func Entries[K comparable, V any](in map[K]V) []Entry[K, V] {
|
func Entries[K comparable, V any](in map[K]V) []Entry[K, V] {
|
||||||
entries := make([]Entry[K, V], 0, len(in))
|
entries := make([]Entry[K, V], 0, len(in))
|
||||||
|
|
||||||
@ -36,7 +111,15 @@ func Entries[K comparable, V any](in map[K]V) []Entry[K, V] {
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToPairs transforms a map into array of key/value pairs.
|
||||||
|
// Alias of Entries().
|
||||||
|
// Play: https://go.dev/play/p/3Dhgx46gawJ
|
||||||
|
func ToPairs[K comparable, V any](in map[K]V) []Entry[K, V] {
|
||||||
|
return Entries(in)
|
||||||
|
}
|
||||||
|
|
||||||
// FromEntries transforms an array of key/value pairs into a map.
|
// FromEntries transforms an array of key/value pairs into a map.
|
||||||
|
// Play: https://go.dev/play/p/oIr5KHFGCEN
|
||||||
func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V {
|
func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V {
|
||||||
out := map[K]V{}
|
out := map[K]V{}
|
||||||
|
|
||||||
@ -47,7 +130,29 @@ func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromPairs transforms an array of key/value pairs into a map.
|
||||||
|
// Alias of FromEntries().
|
||||||
|
// Play: https://go.dev/play/p/oIr5KHFGCEN
|
||||||
|
func FromPairs[K comparable, V any](entries []Entry[K, V]) map[K]V {
|
||||||
|
return FromEntries(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert creates a map composed of the inverted keys and values. If map
|
||||||
|
// contains duplicate values, subsequent values overwrite property assignments
|
||||||
|
// of previous values.
|
||||||
|
// Play: https://go.dev/play/p/rFQ4rak6iA1
|
||||||
|
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.
|
// Assign merges multiple maps from left to right.
|
||||||
|
// Play: https://go.dev/play/p/VhwfJOyxf5o
|
||||||
func Assign[K comparable, V any](maps ...map[K]V) map[K]V {
|
func Assign[K comparable, V any](maps ...map[K]V) map[K]V {
|
||||||
out := map[K]V{}
|
out := map[K]V{}
|
||||||
|
|
||||||
@ -60,7 +165,20 @@ func Assign[K comparable, V any](maps ...map[K]V) map[K]V {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapKeys manipulates a map keys and transforms it to a map of another type.
|
||||||
|
// Play: https://go.dev/play/p/9_4WPIqOetJ
|
||||||
|
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.
|
// MapValues manipulates a map values and transforms it to a map of another type.
|
||||||
|
// Play: https://go.dev/play/p/T_8xAfvcf0W
|
||||||
func MapValues[K comparable, V any, R any](in map[K]V, iteratee func(V, K) R) map[K]R {
|
func MapValues[K comparable, V any, R any](in map[K]V, iteratee func(V, K) R) map[K]R {
|
||||||
result := map[K]R{}
|
result := map[K]R{}
|
||||||
|
|
||||||
@ -70,3 +188,15 @@ func MapValues[K comparable, V any, R any](in map[K]V, iteratee func(V, K) R) ma
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapToSlice transforms a map into a slice based on specific iteratee
|
||||||
|
// Play: https://go.dev/play/p/ZuiCZpDt6LD
|
||||||
|
func MapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(K, V) R) []R {
|
||||||
|
result := make([]R, 0, len(in))
|
||||||
|
|
||||||
|
for k, v := range in {
|
||||||
|
result = append(result, iteratee(k, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
24
vendor/github.com/samber/lo/util.go → vendor/github.com/samber/lo/math.go
generated
vendored
24
vendor/github.com/samber/lo/util.go → vendor/github.com/samber/lo/math.go
generated
vendored
@ -3,6 +3,7 @@ package lo
|
|||||||
import "golang.org/x/exp/constraints"
|
import "golang.org/x/exp/constraints"
|
||||||
|
|
||||||
// Range creates an array of numbers (positive and/or negative) with given length.
|
// Range creates an array of numbers (positive and/or negative) with given length.
|
||||||
|
// Play: https://go.dev/play/p/0r6VimXAi9H
|
||||||
func Range(elementNum int) []int {
|
func Range(elementNum int) []int {
|
||||||
length := If(elementNum < 0, -elementNum).Else(elementNum)
|
length := If(elementNum < 0, -elementNum).Else(elementNum)
|
||||||
result := make([]int, length)
|
result := make([]int, length)
|
||||||
@ -14,6 +15,7 @@ func Range(elementNum int) []int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RangeFrom creates an array of numbers from start with specified length.
|
// RangeFrom creates an array of numbers from start with specified length.
|
||||||
|
// Play: https://go.dev/play/p/0r6VimXAi9H
|
||||||
func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum int) []T {
|
func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum int) []T {
|
||||||
length := If(elementNum < 0, -elementNum).Else(elementNum)
|
length := If(elementNum < 0, -elementNum).Else(elementNum)
|
||||||
result := make([]T, length)
|
result := make([]T, length)
|
||||||
@ -26,6 +28,7 @@ func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum in
|
|||||||
|
|
||||||
// RangeWithSteps creates an array of numbers (positive and/or negative) progressing from start up to, but not including end.
|
// 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.
|
// step set to zero will return empty array.
|
||||||
|
// Play: https://go.dev/play/p/0r6VimXAi9H
|
||||||
func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step T) []T {
|
func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step T) []T {
|
||||||
result := []T{}
|
result := []T{}
|
||||||
if start == end || step == 0 {
|
if start == end || step == 0 {
|
||||||
@ -48,3 +51,24 @@ func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clamp clamps number within the inclusive lower and upper bounds.
|
||||||
|
// Play: https://go.dev/play/p/RU4lJNC2hlI
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// SumBy summarizes the values in a collection using the given return value from the iteration function. If collection is empty 0 is returned.
|
||||||
|
// Play: https://go.dev/play/p/Dz_a_7jN_ca
|
||||||
|
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
|
||||||
|
}
|
19
vendor/github.com/samber/lo/pointers.go
generated
vendored
19
vendor/github.com/samber/lo/pointers.go
generated
vendored
@ -1,19 +0,0 @@
|
|||||||
package lo
|
|
||||||
|
|
||||||
// ToPtr returns a pointer copy of value.
|
|
||||||
func ToPtr[T any](x T) *T {
|
|
||||||
return &x
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToPtr 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
|
|
||||||
}
|
|
89
vendor/github.com/samber/lo/retry.go
generated
vendored
89
vendor/github.com/samber/lo/retry.go
generated
vendored
@ -1,6 +1,68 @@
|
|||||||
package lo
|
package lo
|
||||||
|
|
||||||
// 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.
|
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.
|
||||||
|
// Play: https://go.dev/play/p/mz32VMK2nqe
|
||||||
|
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 successful response is returned.
|
||||||
|
// Play: https://go.dev/play/p/3ggJZ2ZKcMj
|
||||||
func Attempt(maxIteration int, f func(int) error) (int, error) {
|
func Attempt(maxIteration int, f func(int) error) (int, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@ -15,5 +77,28 @@ func Attempt(maxIteration int, f func(int) error) (int, error) {
|
|||||||
return maxIteration, err
|
return maxIteration, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AttemptWithDelay invokes a function N times until it returns valid output,
|
||||||
|
// with a pause between each call. Returning either the caught error or nil.
|
||||||
|
// When first argument is less than `1`, the function runs until a successful
|
||||||
|
// response is returned.
|
||||||
|
// Play: https://go.dev/play/p/tVs6CygC7m1
|
||||||
|
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 ?
|
// throttle ?
|
||||||
// debounce ?
|
|
||||||
|
319
vendor/github.com/samber/lo/slice.go
generated
vendored
319
vendor/github.com/samber/lo/slice.go
generated
vendored
@ -2,9 +2,12 @@ package lo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Filter iterates over elements of collection, returning an array of all elements predicate returns truthy for.
|
// Filter iterates over elements of collection, returning an array of all elements predicate returns truthy for.
|
||||||
|
// Play: https://go.dev/play/p/Apjg3WeSi7K
|
||||||
func Filter[V any](collection []V, predicate func(V, int) bool) []V {
|
func Filter[V any](collection []V, predicate func(V, int) bool) []V {
|
||||||
result := []V{}
|
result := []V{}
|
||||||
|
|
||||||
@ -18,6 +21,7 @@ func Filter[V any](collection []V, predicate func(V, int) bool) []V {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Map manipulates a slice and transforms it to a slice of another type.
|
// Map manipulates a slice and transforms it to a slice of another type.
|
||||||
|
// Play: https://go.dev/play/p/OkPcYAhBo0D
|
||||||
func Map[T any, R any](collection []T, iteratee func(T, int) R) []R {
|
func Map[T any, R any](collection []T, iteratee func(T, int) R) []R {
|
||||||
result := make([]R, len(collection))
|
result := make([]R, len(collection))
|
||||||
|
|
||||||
@ -28,7 +32,26 @@ func Map[T any, R any](collection []T, iteratee func(T, int) R) []R {
|
|||||||
return result
|
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.
|
||||||
|
//
|
||||||
|
// Play: https://go.dev/play/p/-AuYXfy7opz
|
||||||
|
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.
|
// FlatMap manipulates a slice and transforms and flattens it to a slice of another type.
|
||||||
|
// Play: https://go.dev/play/p/YSoYmQTA8-U
|
||||||
func FlatMap[T any, R any](collection []T, iteratee func(T, int) []R) []R {
|
func FlatMap[T any, R any](collection []T, iteratee func(T, int) []R) []R {
|
||||||
result := []R{}
|
result := []R{}
|
||||||
|
|
||||||
@ -41,6 +64,7 @@ func FlatMap[T any, R any](collection []T, iteratee func(T, int) []R) []R {
|
|||||||
|
|
||||||
// Reduce reduces collection to a value which is the accumulated result of running each element in collection
|
// 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.
|
// through accumulator, where each successive invocation is supplied the return value of the previous.
|
||||||
|
// Play: https://go.dev/play/p/R4UHXZNaaUG
|
||||||
func Reduce[T any, R any](collection []T, accumulator func(R, T, int) R, initial R) R {
|
func Reduce[T any, R any](collection []T, accumulator func(R, T, int) R, initial R) R {
|
||||||
for i, item := range collection {
|
for i, item := range collection {
|
||||||
initial = accumulator(initial, item, i)
|
initial = accumulator(initial, item, i)
|
||||||
@ -49,7 +73,18 @@ func Reduce[T any, R any](collection []T, accumulator func(R, T, int) R, initial
|
|||||||
return initial
|
return initial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReduceRight helper is like Reduce except that it iterates over elements of collection from right to left.
|
||||||
|
// Play: https://go.dev/play/p/Fq3W70l7wXF
|
||||||
|
func ReduceRight[T any, R any](collection []T, accumulator func(R, T, int) R, initial R) R {
|
||||||
|
for i := len(collection) - 1; i >= 0; i-- {
|
||||||
|
initial = accumulator(initial, collection[i], i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return initial
|
||||||
|
}
|
||||||
|
|
||||||
// ForEach iterates over elements of collection and invokes iteratee for each element.
|
// ForEach iterates over elements of collection and invokes iteratee for each element.
|
||||||
|
// Play: https://go.dev/play/p/oofyiUPRf8t
|
||||||
func ForEach[T any](collection []T, iteratee func(T, int)) {
|
func ForEach[T any](collection []T, iteratee func(T, int)) {
|
||||||
for i, item := range collection {
|
for i, item := range collection {
|
||||||
iteratee(item, i)
|
iteratee(item, i)
|
||||||
@ -58,6 +93,7 @@ func ForEach[T any](collection []T, iteratee func(T, int)) {
|
|||||||
|
|
||||||
// Times invokes the iteratee n times, returning an array of the results of each invocation.
|
// Times invokes the iteratee n times, returning an array of the results of each invocation.
|
||||||
// The iteratee is invoked with index as argument.
|
// The iteratee is invoked with index as argument.
|
||||||
|
// Play: https://go.dev/play/p/vgQj3Glr6lT
|
||||||
func Times[T any](count int, iteratee func(int) T) []T {
|
func Times[T any](count int, iteratee func(int) T) []T {
|
||||||
result := make([]T, count)
|
result := make([]T, count)
|
||||||
|
|
||||||
@ -70,6 +106,7 @@ func Times[T any](count int, iteratee func(int) T) []T {
|
|||||||
|
|
||||||
// Uniq returns a duplicate-free version of an array, in which only the first occurrence of each element is kept.
|
// 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.
|
// The order of result values is determined by the order they occur in the array.
|
||||||
|
// Play: https://go.dev/play/p/DTzbeXZ6iEN
|
||||||
func Uniq[T comparable](collection []T) []T {
|
func Uniq[T comparable](collection []T) []T {
|
||||||
result := make([]T, 0, len(collection))
|
result := make([]T, 0, len(collection))
|
||||||
seen := make(map[T]struct{}, len(collection))
|
seen := make(map[T]struct{}, len(collection))
|
||||||
@ -86,9 +123,10 @@ func Uniq[T comparable](collection []T) []T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uniq returns a duplicate-free version of an array, in which only the first occurrence of each element is kept.
|
// 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
|
// 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.
|
// invoked for each element in array to generate the criterion by which uniqueness is computed.
|
||||||
|
// Play: https://go.dev/play/p/g42Z3QSb53u
|
||||||
func UniqBy[T any, U comparable](collection []T, iteratee func(T) U) []T {
|
func UniqBy[T any, U comparable](collection []T, iteratee func(T) U) []T {
|
||||||
result := make([]T, 0, len(collection))
|
result := make([]T, 0, len(collection))
|
||||||
seen := make(map[U]struct{}, len(collection))
|
seen := make(map[U]struct{}, len(collection))
|
||||||
@ -108,16 +146,13 @@ func UniqBy[T any, U comparable](collection []T, iteratee func(T) U) []T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GroupBy returns an object composed of keys generated from the results of running each element of collection through iteratee.
|
// GroupBy returns an object composed of keys generated from the results of running each element of collection through iteratee.
|
||||||
|
// Play: https://go.dev/play/p/XnQBd_v6brd
|
||||||
func GroupBy[T any, U comparable](collection []T, iteratee func(T) U) map[U][]T {
|
func GroupBy[T any, U comparable](collection []T, iteratee func(T) U) map[U][]T {
|
||||||
result := map[U][]T{}
|
result := map[U][]T{}
|
||||||
|
|
||||||
for _, item := range collection {
|
for _, item := range collection {
|
||||||
key := iteratee(item)
|
key := iteratee(item)
|
||||||
|
|
||||||
if _, ok := result[key]; !ok {
|
|
||||||
result[key] = []T{}
|
|
||||||
}
|
|
||||||
|
|
||||||
result[key] = append(result[key], item)
|
result[key] = append(result[key], item)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,22 +161,25 @@ func GroupBy[T any, U comparable](collection []T, iteratee func(T) U) map[U][]T
|
|||||||
|
|
||||||
// Chunk returns an array of elements split into groups the length of size. If array can't be split evenly,
|
// 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.
|
// the final chunk will be the remaining elements.
|
||||||
|
// Play: https://go.dev/play/p/EeKl0AuTehH
|
||||||
func Chunk[T any](collection []T, size int) [][]T {
|
func Chunk[T any](collection []T, size int) [][]T {
|
||||||
if size <= 0 {
|
if size <= 0 {
|
||||||
panic("Second parameter must be greater than 0")
|
panic("Second parameter must be greater than 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([][]T, 0, len(collection)/2+1)
|
chunksNum := len(collection) / size
|
||||||
length := len(collection)
|
if len(collection)%size != 0 {
|
||||||
|
chunksNum += 1
|
||||||
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])
|
result := make([][]T, 0, chunksNum)
|
||||||
|
|
||||||
|
for i := 0; i < chunksNum; i++ {
|
||||||
|
last := (i + 1) * size
|
||||||
|
if last > len(collection) {
|
||||||
|
last = len(collection)
|
||||||
|
}
|
||||||
|
result = append(result, collection[i*size:last])
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -150,6 +188,7 @@ func Chunk[T any](collection []T, size int) [][]T {
|
|||||||
// PartitionBy returns an array of elements split into groups. The order of grouped values is
|
// 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
|
// determined by the order they occur in collection. The grouping is generated from the results
|
||||||
// of running each element of collection through iteratee.
|
// of running each element of collection through iteratee.
|
||||||
|
// Play: https://go.dev/play/p/NfQ_nGjkgXW
|
||||||
func PartitionBy[T any, K comparable](collection []T, iteratee func(x T) K) [][]T {
|
func PartitionBy[T any, K comparable](collection []T, iteratee func(x T) K) [][]T {
|
||||||
result := [][]T{}
|
result := [][]T{}
|
||||||
seen := map[K]int{}
|
seen := map[K]int{}
|
||||||
@ -174,18 +213,24 @@ func PartitionBy[T any, K comparable](collection []T, iteratee func(x T) K) [][]
|
|||||||
// return Values[K, []T](groups)
|
// return Values[K, []T](groups)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flattens returns an array a single level deep.
|
// Flatten returns an array a single level deep.
|
||||||
|
// Play: https://go.dev/play/p/rbp9ORaMpjw
|
||||||
func Flatten[T any](collection [][]T) []T {
|
func Flatten[T any](collection [][]T) []T {
|
||||||
result := []T{}
|
totalLen := 0
|
||||||
|
for i := range collection {
|
||||||
|
totalLen += len(collection[i])
|
||||||
|
}
|
||||||
|
|
||||||
for _, item := range collection {
|
result := make([]T, 0, totalLen)
|
||||||
result = append(result, item...)
|
for i := range collection {
|
||||||
|
result = append(result, collection[i]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shuffle returns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm.
|
// Shuffle returns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm.
|
||||||
|
// Play: https://go.dev/play/p/Qp73bnTDnc7
|
||||||
func Shuffle[T any](collection []T) []T {
|
func Shuffle[T any](collection []T) []T {
|
||||||
rand.Shuffle(len(collection), func(i, j int) {
|
rand.Shuffle(len(collection), func(i, j int) {
|
||||||
collection[i], collection[j] = collection[j], collection[i]
|
collection[i], collection[j] = collection[j], collection[i]
|
||||||
@ -195,6 +240,7 @@ func Shuffle[T any](collection []T) []T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reverse reverses array so that the first element becomes the last, the second element becomes the second to last, and so on.
|
// Reverse reverses array so that the first element becomes the last, the second element becomes the second to last, and so on.
|
||||||
|
// Play: https://go.dev/play/p/fhUMLvZ7vS6
|
||||||
func Reverse[T any](collection []T) []T {
|
func Reverse[T any](collection []T) []T {
|
||||||
length := len(collection)
|
length := len(collection)
|
||||||
half := length / 2
|
half := length / 2
|
||||||
@ -208,10 +254,11 @@ func Reverse[T any](collection []T) []T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fill fills elements of array with `initial` value.
|
// Fill fills elements of array with `initial` value.
|
||||||
|
// Play: https://go.dev/play/p/VwR34GzqEub
|
||||||
func Fill[T Clonable[T]](collection []T, initial T) []T {
|
func Fill[T Clonable[T]](collection []T, initial T) []T {
|
||||||
result := make([]T, 0, len(collection))
|
result := make([]T, 0, len(collection))
|
||||||
|
|
||||||
for _ = range collection {
|
for range collection {
|
||||||
result = append(result, initial.Clone())
|
result = append(result, initial.Clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,6 +266,7 @@ func Fill[T Clonable[T]](collection []T, initial T) []T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Repeat builds a slice with N copies of initial value.
|
// Repeat builds a slice with N copies of initial value.
|
||||||
|
// Play: https://go.dev/play/p/g3uHXbmc3b6
|
||||||
func Repeat[T Clonable[T]](count int, initial T) []T {
|
func Repeat[T Clonable[T]](count int, initial T) []T {
|
||||||
result := make([]T, 0, count)
|
result := make([]T, 0, count)
|
||||||
|
|
||||||
@ -229,7 +277,20 @@ func Repeat[T Clonable[T]](count int, initial T) []T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RepeatBy builds a slice with values returned by N calls of callback.
|
||||||
|
// Play: https://go.dev/play/p/ozZLCtX_hNU
|
||||||
|
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.
|
// KeyBy transforms a slice or an array of structs to a map based on a pivot callback.
|
||||||
|
// Play: https://go.dev/play/p/mdaClUAT-zZ
|
||||||
func KeyBy[K comparable, V any](collection []V, iteratee func(V) K) map[K]V {
|
func KeyBy[K comparable, V any](collection []V, iteratee func(V) K) map[K]V {
|
||||||
result := make(map[K]V, len(collection))
|
result := make(map[K]V, len(collection))
|
||||||
|
|
||||||
@ -240,3 +301,223 @@ func KeyBy[K comparable, V any](collection []V, iteratee func(V) K) map[K]V {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Associate returns a map containing key-value pairs provided by transform function applied to elements of the given slice.
|
||||||
|
// If any of two pairs would have the same key the last one gets added to the map.
|
||||||
|
// The order of keys in returned map is not specified and is not guaranteed to be the same from the original array.
|
||||||
|
// Play: https://go.dev/play/p/WHa2CfMO3Lr
|
||||||
|
func Associate[T any, K comparable, V any](collection []T, transform func(T) (K, V)) map[K]V {
|
||||||
|
result := make(map[K]V)
|
||||||
|
|
||||||
|
for _, t := range collection {
|
||||||
|
k, v := transform(t)
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceToMap returns a map containing key-value pairs provided by transform function applied to elements of the given slice.
|
||||||
|
// If any of two pairs would have the same key the last one gets added to the map.
|
||||||
|
// The order of keys in returned map is not specified and is not guaranteed to be the same from the original array.
|
||||||
|
// Alias of Associate().
|
||||||
|
// Play: https://go.dev/play/p/WHa2CfMO3Lr
|
||||||
|
func SliceToMap[T any, K comparable, V any](collection []T, transform func(T) (K, V)) map[K]V {
|
||||||
|
return Associate(collection, transform)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop drops n elements from the beginning of a slice or array.
|
||||||
|
// Play: https://go.dev/play/p/JswS7vXRJP2
|
||||||
|
func Drop[T any](collection []T, n int) []T {
|
||||||
|
if len(collection) <= n {
|
||||||
|
return make([]T, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-n)
|
||||||
|
|
||||||
|
return append(result, collection[n:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropRight drops n elements from the end of a slice or array.
|
||||||
|
// Play: https://go.dev/play/p/GG0nXkSJJa3
|
||||||
|
func DropRight[T any](collection []T, n int) []T {
|
||||||
|
if len(collection) <= n {
|
||||||
|
return []T{}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-n)
|
||||||
|
return append(result, collection[:len(collection)-n]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropWhile drops elements from the beginning of a slice or array while the predicate returns true.
|
||||||
|
// Play: https://go.dev/play/p/7gBPYw2IK16
|
||||||
|
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, 0, len(collection)-i)
|
||||||
|
return append(result, collection[i:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropRightWhile drops elements from the end of a slice or array while the predicate returns true.
|
||||||
|
// Play: https://go.dev/play/p/3-n71oEC0Hz
|
||||||
|
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, 0, i+1)
|
||||||
|
return append(result, collection[:i+1]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject is the opposite of Filter, this method returns the elements of collection that predicate does not return truthy for.
|
||||||
|
// Play: https://go.dev/play/p/YkLMODy1WEL
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/Y3FlK54yveC
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/ByQbNYQQi4X
|
||||||
|
func CountBy[T any](collection []T, predicate func(T) bool) (count int) {
|
||||||
|
for _, item := range collection {
|
||||||
|
if predicate(item) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subset returns a copy of a slice from `offset` up to `length` elements. Like `slice[start:start+length]`, but does not panic on overflow.
|
||||||
|
// Play: https://go.dev/play/p/tOQu1GhFcog
|
||||||
|
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)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice returns a copy of a slice from `start` up to, but not including `end`. Like `slice[start:end]`, but does not panic on overflow.
|
||||||
|
// Play: https://go.dev/play/p/8XWYhfMMA1h
|
||||||
|
func Slice[T any](collection []T, start int, end int) []T {
|
||||||
|
size := len(collection)
|
||||||
|
|
||||||
|
if start >= end {
|
||||||
|
return []T{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if start > size {
|
||||||
|
start = size
|
||||||
|
}
|
||||||
|
|
||||||
|
if end > size {
|
||||||
|
end = size
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace returns a copy of the slice with the first n non-overlapping instances of old replaced by new.
|
||||||
|
// Play: https://go.dev/play/p/XfPzmf9gql6
|
||||||
|
func Replace[T comparable](collection []T, old T, new T, n int) []T {
|
||||||
|
result := make([]T, len(collection))
|
||||||
|
copy(result, collection)
|
||||||
|
|
||||||
|
for i := range result {
|
||||||
|
if result[i] == old && n != 0 {
|
||||||
|
result[i] = new
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceAll returns a copy of the slice with all non-overlapping instances of old replaced by new.
|
||||||
|
// Play: https://go.dev/play/p/a9xZFUHfYcV
|
||||||
|
func ReplaceAll[T comparable](collection []T, old T, new T) []T {
|
||||||
|
return Replace(collection, old, new, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact returns a slice of all non-zero elements.
|
||||||
|
// Play: https://go.dev/play/p/tXiy-iK6PAc
|
||||||
|
func Compact[T comparable](collection []T) []T {
|
||||||
|
var zero T
|
||||||
|
|
||||||
|
result := []T{}
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
if item != zero {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSorted checks if a slice is sorted.
|
||||||
|
// Play: https://go.dev/play/p/mc3qR-t4mcx
|
||||||
|
func IsSorted[T constraints.Ordered](collection []T) bool {
|
||||||
|
for i := 1; i < len(collection); i++ {
|
||||||
|
if collection[i-1] > collection[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSortedByKey checks if a slice is sorted by iteratee.
|
||||||
|
// Play: https://go.dev/play/p/wiG6XyBBu49
|
||||||
|
func IsSortedByKey[T any, K constraints.Ordered](collection []T, iteratee func(T) K) bool {
|
||||||
|
size := len(collection)
|
||||||
|
|
||||||
|
for i := 0; i < size-1; i++ {
|
||||||
|
if iteratee(collection[i]) > iteratee(collection[i+1]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
65
vendor/github.com/samber/lo/string.go
generated
vendored
Normal file
65
vendor/github.com/samber/lo/string.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package lo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Substring return part of a string.
|
||||||
|
// Play: https://go.dev/play/p/TQlxQi82Lu1
|
||||||
|
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)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChunkString returns an array of strings split into groups the length of size. If array can't be split evenly,
|
||||||
|
// the final chunk will be the remaining elements.
|
||||||
|
// Play: https://go.dev/play/p/__FLTuJVz54
|
||||||
|
func ChunkString[T ~string](str T, size int) []T {
|
||||||
|
if size <= 0 {
|
||||||
|
panic("lo.ChunkString: Size parameter must be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(str) == 0 {
|
||||||
|
return []T{""}
|
||||||
|
}
|
||||||
|
|
||||||
|
if size >= len(str) {
|
||||||
|
return []T{str}
|
||||||
|
}
|
||||||
|
|
||||||
|
var chunks []T = make([]T, 0, ((len(str)-1)/size)+1)
|
||||||
|
currentLen := 0
|
||||||
|
currentStart := 0
|
||||||
|
for i := range str {
|
||||||
|
if currentLen == size {
|
||||||
|
chunks = append(chunks, str[currentStart:i])
|
||||||
|
currentLen = 0
|
||||||
|
currentStart = i
|
||||||
|
}
|
||||||
|
currentLen++
|
||||||
|
}
|
||||||
|
chunks = append(chunks, str[currentStart:])
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
// RuneLength is an alias to utf8.RuneCountInString which returns the number of runes in string.
|
||||||
|
// Play: https://go.dev/play/p/tuhgW_lWY8l
|
||||||
|
func RuneLength(str string) int {
|
||||||
|
return utf8.RuneCountInString(str)
|
||||||
|
}
|
32
vendor/github.com/samber/lo/test.go
generated
vendored
Normal file
32
vendor/github.com/samber/lo/test.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package lo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://github.com/stretchr/testify/issues/1101
|
||||||
|
func testWithTimeout(t *testing.T, timeout time.Duration) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
testFinished := make(chan struct{})
|
||||||
|
t.Cleanup(func() { close(testFinished) })
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-testFinished:
|
||||||
|
case <-time.After(timeout):
|
||||||
|
t.Errorf("test timed out after %s", timeout)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
type foo struct {
|
||||||
|
bar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f foo) Clone() foo {
|
||||||
|
return foo{f.bar}
|
||||||
|
}
|
220
vendor/github.com/samber/lo/tuples.go
generated
vendored
220
vendor/github.com/samber/lo/tuples.go
generated
vendored
@ -1,28 +1,113 @@
|
|||||||
package lo
|
package lo
|
||||||
|
|
||||||
func longestCollection(collections ...[]interface{}) int {
|
// T2 creates a tuple from a list of values.
|
||||||
max := 0
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
func T2[A any, B any](a A, b B) Tuple2[A, B] {
|
||||||
for _, collection := range collections {
|
return Tuple2[A, B]{A: a, B: b}
|
||||||
if len(collection) > max {
|
|
||||||
max = len(collection)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return max
|
// T3 creates a tuple from a list of values.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
func Unpack2[A any, B any](tuple Tuple2[A, B]) (A, B) {
|
||||||
|
return tuple.A, tuple.B
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack3 returns values contained in tuple.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
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
|
// 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.
|
// 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.
|
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
func Zip2[A any, B any](a []A, b []B) []Tuple2[A, B] {
|
func Zip2[A any, B any](a []A, b []B) []Tuple2[A, B] {
|
||||||
size := Max[int]([]int{len(a), len(b)})
|
size := Max([]int{len(a), len(b)})
|
||||||
|
|
||||||
result := make([]Tuple2[A, B], 0, size)
|
result := make([]Tuple2[A, B], 0, size)
|
||||||
|
|
||||||
for index := 0; index < size; index++ {
|
for index := 0; index < size; index++ {
|
||||||
_a, _ := Nth[A](a, index)
|
_a, _ := Nth(a, index)
|
||||||
_b, _ := Nth[B](b, index)
|
_b, _ := Nth(b, index)
|
||||||
|
|
||||||
result = append(result, Tuple2[A, B]{
|
result = append(result, Tuple2[A, B]{
|
||||||
A: _a,
|
A: _a,
|
||||||
@ -36,15 +121,16 @@ func Zip2[A any, B any](a []A, b []B) []Tuple2[A, B] {
|
|||||||
// Zip3 creates a slice of grouped elements, the first of which contains the first elements
|
// 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.
|
// 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.
|
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
func Zip3[A any, B any, C any](a []A, b []B, c []C) []Tuple3[A, B, C] {
|
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)})
|
size := Max([]int{len(a), len(b), len(c)})
|
||||||
|
|
||||||
result := make([]Tuple3[A, B, C], 0, size)
|
result := make([]Tuple3[A, B, C], 0, size)
|
||||||
|
|
||||||
for index := 0; index < size; index++ {
|
for index := 0; index < size; index++ {
|
||||||
_a, _ := Nth[A](a, index)
|
_a, _ := Nth(a, index)
|
||||||
_b, _ := Nth[B](b, index)
|
_b, _ := Nth(b, index)
|
||||||
_c, _ := Nth[C](c, index)
|
_c, _ := Nth(c, index)
|
||||||
|
|
||||||
result = append(result, Tuple3[A, B, C]{
|
result = append(result, Tuple3[A, B, C]{
|
||||||
A: _a,
|
A: _a,
|
||||||
@ -59,16 +145,17 @@ func Zip3[A any, B any, C any](a []A, b []B, c []C) []Tuple3[A, B, C] {
|
|||||||
// Zip4 creates a slice of grouped elements, the first of which contains the first elements
|
// 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.
|
// 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.
|
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
func Zip4[A any, B any, C any, D any](a []A, b []B, c []C, d []D) []Tuple4[A, B, C, D] {
|
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)})
|
size := Max([]int{len(a), len(b), len(c), len(d)})
|
||||||
|
|
||||||
result := make([]Tuple4[A, B, C, D], 0, size)
|
result := make([]Tuple4[A, B, C, D], 0, size)
|
||||||
|
|
||||||
for index := 0; index < size; index++ {
|
for index := 0; index < size; index++ {
|
||||||
_a, _ := Nth[A](a, index)
|
_a, _ := Nth(a, index)
|
||||||
_b, _ := Nth[B](b, index)
|
_b, _ := Nth(b, index)
|
||||||
_c, _ := Nth[C](c, index)
|
_c, _ := Nth(c, index)
|
||||||
_d, _ := Nth[D](d, index)
|
_d, _ := Nth(d, index)
|
||||||
|
|
||||||
result = append(result, Tuple4[A, B, C, D]{
|
result = append(result, Tuple4[A, B, C, D]{
|
||||||
A: _a,
|
A: _a,
|
||||||
@ -84,17 +171,18 @@ func Zip4[A any, B any, C any, D any](a []A, b []B, c []C, d []D) []Tuple4[A, B,
|
|||||||
// Zip5 creates a slice of grouped elements, the first of which contains the first elements
|
// 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.
|
// 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.
|
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
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] {
|
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)})
|
size := Max([]int{len(a), len(b), len(c), len(d), len(e)})
|
||||||
|
|
||||||
result := make([]Tuple5[A, B, C, D, E], 0, size)
|
result := make([]Tuple5[A, B, C, D, E], 0, size)
|
||||||
|
|
||||||
for index := 0; index < size; index++ {
|
for index := 0; index < size; index++ {
|
||||||
_a, _ := Nth[A](a, index)
|
_a, _ := Nth(a, index)
|
||||||
_b, _ := Nth[B](b, index)
|
_b, _ := Nth(b, index)
|
||||||
_c, _ := Nth[C](c, index)
|
_c, _ := Nth(c, index)
|
||||||
_d, _ := Nth[D](d, index)
|
_d, _ := Nth(d, index)
|
||||||
_e, _ := Nth[E](e, index)
|
_e, _ := Nth(e, index)
|
||||||
|
|
||||||
result = append(result, Tuple5[A, B, C, D, E]{
|
result = append(result, Tuple5[A, B, C, D, E]{
|
||||||
A: _a,
|
A: _a,
|
||||||
@ -111,18 +199,19 @@ func Zip5[A any, B any, C any, D any, E any](a []A, b []B, c []C, d []D, e []E)
|
|||||||
// Zip6 creates a slice of grouped elements, the first of which contains the first elements
|
// 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.
|
// 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.
|
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
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] {
|
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)})
|
size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f)})
|
||||||
|
|
||||||
result := make([]Tuple6[A, B, C, D, E, F], 0, size)
|
result := make([]Tuple6[A, B, C, D, E, F], 0, size)
|
||||||
|
|
||||||
for index := 0; index < size; index++ {
|
for index := 0; index < size; index++ {
|
||||||
_a, _ := Nth[A](a, index)
|
_a, _ := Nth(a, index)
|
||||||
_b, _ := Nth[B](b, index)
|
_b, _ := Nth(b, index)
|
||||||
_c, _ := Nth[C](c, index)
|
_c, _ := Nth(c, index)
|
||||||
_d, _ := Nth[D](d, index)
|
_d, _ := Nth(d, index)
|
||||||
_e, _ := Nth[E](e, index)
|
_e, _ := Nth(e, index)
|
||||||
_f, _ := Nth[F](f, index)
|
_f, _ := Nth(f, index)
|
||||||
|
|
||||||
result = append(result, Tuple6[A, B, C, D, E, F]{
|
result = append(result, Tuple6[A, B, C, D, E, F]{
|
||||||
A: _a,
|
A: _a,
|
||||||
@ -140,19 +229,20 @@ func Zip6[A any, B any, C any, D any, E any, F any](a []A, b []B, c []C, d []D,
|
|||||||
// Zip7 creates a slice of grouped elements, the first of which contains the first elements
|
// 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.
|
// 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.
|
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
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] {
|
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)})
|
size := Max([]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)
|
result := make([]Tuple7[A, B, C, D, E, F, G], 0, size)
|
||||||
|
|
||||||
for index := 0; index < size; index++ {
|
for index := 0; index < size; index++ {
|
||||||
_a, _ := Nth[A](a, index)
|
_a, _ := Nth(a, index)
|
||||||
_b, _ := Nth[B](b, index)
|
_b, _ := Nth(b, index)
|
||||||
_c, _ := Nth[C](c, index)
|
_c, _ := Nth(c, index)
|
||||||
_d, _ := Nth[D](d, index)
|
_d, _ := Nth(d, index)
|
||||||
_e, _ := Nth[E](e, index)
|
_e, _ := Nth(e, index)
|
||||||
_f, _ := Nth[F](f, index)
|
_f, _ := Nth(f, index)
|
||||||
_g, _ := Nth[G](g, index)
|
_g, _ := Nth(g, index)
|
||||||
|
|
||||||
result = append(result, Tuple7[A, B, C, D, E, F, G]{
|
result = append(result, Tuple7[A, B, C, D, E, F, G]{
|
||||||
A: _a,
|
A: _a,
|
||||||
@ -171,20 +261,21 @@ func Zip7[A any, B any, C any, D any, E any, F any, G any](a []A, b []B, c []C,
|
|||||||
// Zip8 creates a slice of grouped elements, the first of which contains the first elements
|
// 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.
|
// 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.
|
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
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] {
|
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)})
|
size := Max([]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)
|
result := make([]Tuple8[A, B, C, D, E, F, G, H], 0, size)
|
||||||
|
|
||||||
for index := 0; index < size; index++ {
|
for index := 0; index < size; index++ {
|
||||||
_a, _ := Nth[A](a, index)
|
_a, _ := Nth(a, index)
|
||||||
_b, _ := Nth[B](b, index)
|
_b, _ := Nth(b, index)
|
||||||
_c, _ := Nth[C](c, index)
|
_c, _ := Nth(c, index)
|
||||||
_d, _ := Nth[D](d, index)
|
_d, _ := Nth(d, index)
|
||||||
_e, _ := Nth[E](e, index)
|
_e, _ := Nth(e, index)
|
||||||
_f, _ := Nth[F](f, index)
|
_f, _ := Nth(f, index)
|
||||||
_g, _ := Nth[G](g, index)
|
_g, _ := Nth(g, index)
|
||||||
_h, _ := Nth[H](h, index)
|
_h, _ := Nth(h, index)
|
||||||
|
|
||||||
result = append(result, Tuple8[A, B, C, D, E, F, G, H]{
|
result = append(result, Tuple8[A, B, C, D, E, F, G, H]{
|
||||||
A: _a,
|
A: _a,
|
||||||
@ -204,21 +295,22 @@ func Zip8[A any, B any, C any, D any, E any, F any, G any, H any](a []A, b []B,
|
|||||||
// Zip9 creates a slice of grouped elements, the first of which contains the first elements
|
// 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.
|
// 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.
|
// When collections have different size, the Tuple attributes are filled with zero value.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
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] {
|
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)})
|
size := Max([]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)
|
result := make([]Tuple9[A, B, C, D, E, F, G, H, I], 0, size)
|
||||||
|
|
||||||
for index := 0; index < size; index++ {
|
for index := 0; index < size; index++ {
|
||||||
_a, _ := Nth[A](a, index)
|
_a, _ := Nth(a, index)
|
||||||
_b, _ := Nth[B](b, index)
|
_b, _ := Nth(b, index)
|
||||||
_c, _ := Nth[C](c, index)
|
_c, _ := Nth(c, index)
|
||||||
_d, _ := Nth[D](d, index)
|
_d, _ := Nth(d, index)
|
||||||
_e, _ := Nth[E](e, index)
|
_e, _ := Nth(e, index)
|
||||||
_f, _ := Nth[F](f, index)
|
_f, _ := Nth(f, index)
|
||||||
_g, _ := Nth[G](g, index)
|
_g, _ := Nth(g, index)
|
||||||
_h, _ := Nth[H](h, index)
|
_h, _ := Nth(h, index)
|
||||||
_i, _ := Nth[I](i, index)
|
_i, _ := Nth(i, index)
|
||||||
|
|
||||||
result = append(result, Tuple9[A, B, C, D, E, F, G, H, I]{
|
result = append(result, Tuple9[A, B, C, D, E, F, G, H, I]{
|
||||||
A: _a,
|
A: _a,
|
||||||
@ -238,6 +330,7 @@ func Zip9[A any, B any, C any, D any, E any, F any, G any, H any, I any](a []A,
|
|||||||
|
|
||||||
// Unzip2 accepts an array of grouped elements and creates an array regrouping the elements
|
// Unzip2 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
// to their pre-zip configuration.
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
func Unzip2[A any, B any](tuples []Tuple2[A, B]) ([]A, []B) {
|
func Unzip2[A any, B any](tuples []Tuple2[A, B]) ([]A, []B) {
|
||||||
size := len(tuples)
|
size := len(tuples)
|
||||||
r1 := make([]A, 0, size)
|
r1 := make([]A, 0, size)
|
||||||
@ -253,6 +346,7 @@ func Unzip2[A any, B any](tuples []Tuple2[A, B]) ([]A, []B) {
|
|||||||
|
|
||||||
// Unzip3 accepts an array of grouped elements and creates an array regrouping the elements
|
// Unzip3 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
// to their pre-zip configuration.
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
func Unzip3[A any, B any, C any](tuples []Tuple3[A, B, C]) ([]A, []B, []C) {
|
func Unzip3[A any, B any, C any](tuples []Tuple3[A, B, C]) ([]A, []B, []C) {
|
||||||
size := len(tuples)
|
size := len(tuples)
|
||||||
r1 := make([]A, 0, size)
|
r1 := make([]A, 0, size)
|
||||||
@ -270,6 +364,7 @@ func Unzip3[A any, B any, C any](tuples []Tuple3[A, B, C]) ([]A, []B, []C) {
|
|||||||
|
|
||||||
// Unzip4 accepts an array of grouped elements and creates an array regrouping the elements
|
// Unzip4 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
// to their pre-zip configuration.
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
func Unzip4[A any, B any, C any, D any](tuples []Tuple4[A, B, C, D]) ([]A, []B, []C, []D) {
|
func Unzip4[A any, B any, C any, D any](tuples []Tuple4[A, B, C, D]) ([]A, []B, []C, []D) {
|
||||||
size := len(tuples)
|
size := len(tuples)
|
||||||
r1 := make([]A, 0, size)
|
r1 := make([]A, 0, size)
|
||||||
@ -289,6 +384,7 @@ func Unzip4[A any, B any, C any, D any](tuples []Tuple4[A, B, C, D]) ([]A, []B,
|
|||||||
|
|
||||||
// Unzip5 accepts an array of grouped elements and creates an array regrouping the elements
|
// Unzip5 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
// to their pre-zip configuration.
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
func Unzip5[A any, B any, C any, D any, E any](tuples []Tuple5[A, B, C, D, E]) ([]A, []B, []C, []D, []E) {
|
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)
|
size := len(tuples)
|
||||||
r1 := make([]A, 0, size)
|
r1 := make([]A, 0, size)
|
||||||
@ -310,6 +406,7 @@ func Unzip5[A any, B any, C any, D any, E any](tuples []Tuple5[A, B, C, D, E]) (
|
|||||||
|
|
||||||
// Unzip6 accepts an array of grouped elements and creates an array regrouping the elements
|
// Unzip6 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
// to their pre-zip configuration.
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
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) {
|
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)
|
size := len(tuples)
|
||||||
r1 := make([]A, 0, size)
|
r1 := make([]A, 0, size)
|
||||||
@ -333,6 +430,7 @@ func Unzip6[A any, B any, C any, D any, E any, F any](tuples []Tuple6[A, B, C, D
|
|||||||
|
|
||||||
// Unzip7 accepts an array of grouped elements and creates an array regrouping the elements
|
// Unzip7 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
// to their pre-zip configuration.
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
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) {
|
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)
|
size := len(tuples)
|
||||||
r1 := make([]A, 0, size)
|
r1 := make([]A, 0, size)
|
||||||
@ -358,6 +456,7 @@ func Unzip7[A any, B any, C any, D any, E any, F any, G any](tuples []Tuple7[A,
|
|||||||
|
|
||||||
// Unzip8 accepts an array of grouped elements and creates an array regrouping the elements
|
// Unzip8 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
// to their pre-zip configuration.
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
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) {
|
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)
|
size := len(tuples)
|
||||||
r1 := make([]A, 0, size)
|
r1 := make([]A, 0, size)
|
||||||
@ -385,6 +484,7 @@ func Unzip8[A any, B any, C any, D any, E any, F any, G any, H any](tuples []Tup
|
|||||||
|
|
||||||
// Unzip9 accepts an array of grouped elements and creates an array regrouping the elements
|
// Unzip9 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
// to their pre-zip configuration.
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
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) {
|
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)
|
size := len(tuples)
|
||||||
r1 := make([]A, 0, size)
|
r1 := make([]A, 0, size)
|
||||||
|
88
vendor/github.com/samber/lo/type_manipulation.go
generated
vendored
Normal file
88
vendor/github.com/samber/lo/type_manipulation.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package lo
|
||||||
|
|
||||||
|
// ToPtr returns a pointer copy of value.
|
||||||
|
func ToPtr[T any](x T) *T {
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPtr returns the pointer value or empty.
|
||||||
|
func FromPtr[T any](x *T) T {
|
||||||
|
if x == nil {
|
||||||
|
return Empty[T]()
|
||||||
|
}
|
||||||
|
|
||||||
|
return *x
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPtrOr returns the pointer value or the fallback value.
|
||||||
|
func FromPtrOr[T any](x *T, fallback T) T {
|
||||||
|
if x == nil {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToAnySlice returns a slice with all elements mapped to `any` type
|
||||||
|
func ToAnySlice[T any](collection []T) []any {
|
||||||
|
result := make([]any, len(collection))
|
||||||
|
for i, item := range collection {
|
||||||
|
result[i] = item
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromAnySlice returns an `any` slice with all elements mapped to a type.
|
||||||
|
// Returns false in case of type conversion failure.
|
||||||
|
func FromAnySlice[T any](in []any) (out []T, ok bool) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
out = []T{}
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
result := make([]T, len(in))
|
||||||
|
for i, item := range in {
|
||||||
|
result[i] = item.(T)
|
||||||
|
}
|
||||||
|
return result, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty returns an empty value.
|
||||||
|
func Empty[T any]() T {
|
||||||
|
var zero T
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if argument is a zero value.
|
||||||
|
func IsEmpty[T comparable](v T) bool {
|
||||||
|
var zero T
|
||||||
|
return zero == v
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotEmpty returns true if argument is not a zero value.
|
||||||
|
func IsNotEmpty[T comparable](v T) bool {
|
||||||
|
var zero T
|
||||||
|
return zero != v
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
76
vendor/github.com/stretchr/testify/assert/assertion_compare.go
generated
vendored
76
vendor/github.com/stretchr/testify/assert/assertion_compare.go
generated
vendored
@ -1,8 +1,10 @@
|
|||||||
package assert
|
package assert
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CompareType int
|
type CompareType int
|
||||||
@ -30,6 +32,9 @@ var (
|
|||||||
float64Type = reflect.TypeOf(float64(1))
|
float64Type = reflect.TypeOf(float64(1))
|
||||||
|
|
||||||
stringType = reflect.TypeOf("")
|
stringType = reflect.TypeOf("")
|
||||||
|
|
||||||
|
timeType = reflect.TypeOf(time.Time{})
|
||||||
|
bytesType = reflect.TypeOf([]byte{})
|
||||||
)
|
)
|
||||||
|
|
||||||
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
@ -299,6 +304,47 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
|||||||
return compareLess, true
|
return compareLess, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check for known struct types we can check for compare results.
|
||||||
|
case reflect.Struct:
|
||||||
|
{
|
||||||
|
// All structs enter here. We're not interested in most types.
|
||||||
|
if !canConvert(obj1Value, timeType) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// time.Time can compared!
|
||||||
|
timeObj1, ok := obj1.(time.Time)
|
||||||
|
if !ok {
|
||||||
|
timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
timeObj2, ok := obj2.(time.Time)
|
||||||
|
if !ok {
|
||||||
|
timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64)
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
{
|
||||||
|
// We only care about the []byte type.
|
||||||
|
if !canConvert(obj1Value, bytesType) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// []byte can be compared!
|
||||||
|
bytesObj1, ok := obj1.([]byte)
|
||||||
|
if !ok {
|
||||||
|
bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte)
|
||||||
|
|
||||||
|
}
|
||||||
|
bytesObj2, ok := obj2.([]byte)
|
||||||
|
if !ok {
|
||||||
|
bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return compareEqual, false
|
return compareEqual, false
|
||||||
@ -310,7 +356,10 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
|||||||
// assert.Greater(t, float64(2), float64(1))
|
// assert.Greater(t, float64(2), float64(1))
|
||||||
// assert.Greater(t, "b", "a")
|
// assert.Greater(t, "b", "a")
|
||||||
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
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)
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
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
|
// GreaterOrEqual asserts that the first element is greater than or equal to the second
|
||||||
@ -320,7 +369,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface
|
|||||||
// assert.GreaterOrEqual(t, "b", "a")
|
// assert.GreaterOrEqual(t, "b", "a")
|
||||||
// assert.GreaterOrEqual(t, "b", "b")
|
// assert.GreaterOrEqual(t, "b", "b")
|
||||||
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
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)
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
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
|
// Less asserts that the first element is less than the second
|
||||||
@ -329,7 +381,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in
|
|||||||
// assert.Less(t, float64(1), float64(2))
|
// assert.Less(t, float64(1), float64(2))
|
||||||
// assert.Less(t, "a", "b")
|
// assert.Less(t, "a", "b")
|
||||||
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
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)
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
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
|
// LessOrEqual asserts that the first element is less than or equal to the second
|
||||||
@ -339,7 +394,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{})
|
|||||||
// assert.LessOrEqual(t, "a", "b")
|
// assert.LessOrEqual(t, "a", "b")
|
||||||
// assert.LessOrEqual(t, "b", "b")
|
// assert.LessOrEqual(t, "b", "b")
|
||||||
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
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)
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
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
|
// Positive asserts that the specified element is positive
|
||||||
@ -347,8 +405,11 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
|
|||||||
// assert.Positive(t, 1)
|
// assert.Positive(t, 1)
|
||||||
// assert.Positive(t, 1.23)
|
// assert.Positive(t, 1.23)
|
||||||
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
zero := reflect.Zero(reflect.TypeOf(e))
|
zero := reflect.Zero(reflect.TypeOf(e))
|
||||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs)
|
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative asserts that the specified element is negative
|
// Negative asserts that the specified element is negative
|
||||||
@ -356,8 +417,11 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
|||||||
// assert.Negative(t, -1)
|
// assert.Negative(t, -1)
|
||||||
// assert.Negative(t, -1.23)
|
// assert.Negative(t, -1.23)
|
||||||
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
zero := reflect.Zero(reflect.TypeOf(e))
|
zero := reflect.Zero(reflect.TypeOf(e))
|
||||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs)
|
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 {
|
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
|
||||||
|
16
vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go
generated
vendored
Normal file
16
vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//go:build go1.17
|
||||||
|
// +build go1.17
|
||||||
|
|
||||||
|
// TODO: once support for Go 1.16 is dropped, this file can be
|
||||||
|
// merged/removed with assertion_compare_go1.17_test.go and
|
||||||
|
// assertion_compare_legacy.go
|
||||||
|
|
||||||
|
package assert
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
// Wrapper around reflect.Value.CanConvert, for compatibility
|
||||||
|
// reasons.
|
||||||
|
func canConvert(value reflect.Value, to reflect.Type) bool {
|
||||||
|
return value.CanConvert(to)
|
||||||
|
}
|
16
vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go
generated
vendored
Normal file
16
vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//go:build !go1.17
|
||||||
|
// +build !go1.17
|
||||||
|
|
||||||
|
// TODO: once support for Go 1.16 is dropped, this file can be
|
||||||
|
// merged/removed with assertion_compare_go1.17_test.go and
|
||||||
|
// assertion_compare_can_convert.go
|
||||||
|
|
||||||
|
package assert
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
// Older versions of Go does not have the reflect.Value.CanConvert
|
||||||
|
// method.
|
||||||
|
func canConvert(value reflect.Value, to reflect.Type) bool {
|
||||||
|
return false
|
||||||
|
}
|
22
vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
22
vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
@ -123,6 +123,18 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int
|
|||||||
return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
|
return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)
|
||||||
|
// and that the error contains the specified substring.
|
||||||
|
//
|
||||||
|
// actualObj, err := SomeFunction()
|
||||||
|
// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted")
|
||||||
|
func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
|
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
|
||||||
// This is a wrapper for errors.Is.
|
// This is a wrapper for errors.Is.
|
||||||
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
|
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
|
||||||
@ -724,6 +736,16 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim
|
|||||||
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithinRangef asserts that a time is within a time range (inclusive).
|
||||||
|
//
|
||||||
|
// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||||
|
func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// YAMLEqf asserts that two YAML strings are equivalent.
|
// YAMLEqf asserts that two YAML strings are equivalent.
|
||||||
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
|
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
|
||||||
if h, ok := t.(tHelper); ok {
|
if h, ok := t.(tHelper); ok {
|
||||||
|
44
vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
44
vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
@ -222,6 +222,30 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ..
|
|||||||
return ErrorAsf(a.t, err, target, msg, args...)
|
return ErrorAsf(a.t, err, target, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorContains asserts that a function returned an error (i.e. not `nil`)
|
||||||
|
// and that the error contains the specified substring.
|
||||||
|
//
|
||||||
|
// actualObj, err := SomeFunction()
|
||||||
|
// a.ErrorContains(err, expectedErrorSubString)
|
||||||
|
func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return ErrorContains(a.t, theError, contains, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)
|
||||||
|
// and that the error contains the specified substring.
|
||||||
|
//
|
||||||
|
// actualObj, err := SomeFunction()
|
||||||
|
// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted")
|
||||||
|
func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return ErrorContainsf(a.t, theError, contains, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// ErrorIs asserts that at least one of the errors in err's chain matches target.
|
// ErrorIs asserts that at least one of the errors in err's chain matches target.
|
||||||
// This is a wrapper for errors.Is.
|
// This is a wrapper for errors.Is.
|
||||||
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
|
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
|
||||||
@ -1437,6 +1461,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta
|
|||||||
return WithinDurationf(a.t, expected, actual, delta, msg, args...)
|
return WithinDurationf(a.t, expected, actual, delta, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithinRange asserts that a time is within a time range (inclusive).
|
||||||
|
//
|
||||||
|
// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
|
||||||
|
func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return WithinRange(a.t, actual, start, end, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithinRangef asserts that a time is within a time range (inclusive).
|
||||||
|
//
|
||||||
|
// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||||
|
func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return WithinRangef(a.t, actual, start, end, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// YAMLEq asserts that two YAML strings are equivalent.
|
// YAMLEq asserts that two YAML strings are equivalent.
|
||||||
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool {
|
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||||
if h, ok := a.t.(tHelper); ok {
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
8
vendor/github.com/stretchr/testify/assert/assertion_order.go
generated
vendored
8
vendor/github.com/stretchr/testify/assert/assertion_order.go
generated
vendored
@ -50,7 +50,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT
|
|||||||
// assert.IsIncreasing(t, []float{1, 2})
|
// assert.IsIncreasing(t, []float{1, 2})
|
||||||
// assert.IsIncreasing(t, []string{"a", "b"})
|
// assert.IsIncreasing(t, []string{"a", "b"})
|
||||||
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
|
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNonIncreasing asserts that the collection is not increasing
|
// IsNonIncreasing asserts that the collection is not increasing
|
||||||
@ -59,7 +59,7 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo
|
|||||||
// assert.IsNonIncreasing(t, []float{2, 1})
|
// assert.IsNonIncreasing(t, []float{2, 1})
|
||||||
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
||||||
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
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)
|
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDecreasing asserts that the collection is decreasing
|
// IsDecreasing asserts that the collection is decreasing
|
||||||
@ -68,7 +68,7 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{})
|
|||||||
// assert.IsDecreasing(t, []float{2, 1})
|
// assert.IsDecreasing(t, []float{2, 1})
|
||||||
// assert.IsDecreasing(t, []string{"b", "a"})
|
// assert.IsDecreasing(t, []string{"b", "a"})
|
||||||
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
|
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNonDecreasing asserts that the collection is not decreasing
|
// IsNonDecreasing asserts that the collection is not decreasing
|
||||||
@ -77,5 +77,5 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo
|
|||||||
// assert.IsNonDecreasing(t, []float{1, 2})
|
// assert.IsNonDecreasing(t, []float{1, 2})
|
||||||
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
||||||
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
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)
|
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user