pax_global_header00006660000000000000000000000064143065764110014521gustar00rootroot0000000000000052 comment=4ba6d65a0960c67a218c07b23b0a615459e2eb4f helpers-0.4.0/000077500000000000000000000000001430657641100131645ustar00rootroot00000000000000helpers-0.4.0/.github/000077500000000000000000000000001430657641100145245ustar00rootroot00000000000000helpers-0.4.0/.github/FUNDING.yml000066400000000000000000000000151430657641100163350ustar00rootroot00000000000000github: [bep]helpers-0.4.0/.github/workflows/000077500000000000000000000000001430657641100165615ustar00rootroot00000000000000helpers-0.4.0/.github/workflows/test.yml000066400000000000000000000032421430657641100202640ustar00rootroot00000000000000on: push: branches: [ main ] pull_request: name: Test jobs: test: strategy: matrix: go-version: [1.18.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - name: Install Go uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} - name: Install staticcheck run: go install honnef.co/go/tools/cmd/staticcheck@latest shell: bash - name: Install golint run: go install golang.org/x/lint/golint@latest shell: bash - name: Update PATH run: echo "$(go env GOPATH)/bin" >> $GITHUB_PATH shell: bash - name: Checkout code uses: actions/checkout@v1 - name: Fmt if: matrix.platform != 'windows-latest' # :( run: "diff <(gofmt -d .) <(printf '')" shell: bash - name: Vet run: go vet ./... - name: Staticcheck run: staticcheck ./... - name: Lint run: golint ./... - name: Test run: go test -race ./... -coverpkg=./... -coverprofile=coverage.txt -covermode=atomic - name: Upload coverage if: success() && matrix.platform == 'ubuntu-latest' run: | curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import # One-time step curl -Os https://uploader.codecov.io/latest/linux/codecov curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig gpgv codecov.SHA256SUM.sig codecov.SHA256SUM shasum -a 256 -c codecov.SHA256SUM chmod +x codecov ./codecov helpers-0.4.0/.gitignore000066400000000000000000000004151430657641100151540ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/ helpers-0.4.0/LICENSE000066400000000000000000000020651430657641100141740ustar00rootroot00000000000000MIT License Copyright (c) 2022 Bjørn Erik Pedersen 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. helpers-0.4.0/README.md000066400000000000000000000012571430657641100144500ustar00rootroot00000000000000[![Tests on Linux, MacOS and Windows](https://github.com/bep/helpers/workflows/Test/badge.svg)](https://github.com/bep/helpers/actions?query=workflow:Test) [![Go Report Card](https://goreportcard.com/badge/github.com/bep/helpers)](https://goreportcard.com/report/github.com/bep/helpers) [![codecov](https://codecov.io/gh/bep/helpers/branch/main/graph/badge.svg?token=5Q38Z05KDK)](https://codecov.io/gh/bep/helpers) [![GoDoc](https://godoc.org/github.com/bep/helpers?status.svg)](https://godoc.org/github.com/bep/helpers) Some helper packages with some helper code that I have had a tendency to copy from project to project over the years, a clear sign that I should consider some reuse.helpers-0.4.0/archivehelpers/000077500000000000000000000000001430657641100161705ustar00rootroot00000000000000helpers-0.4.0/archivehelpers/archivehelpers.go000066400000000000000000000075441430657641100215350ustar00rootroot00000000000000package archivehelpers import ( "archive/tar" "compress/gzip" "fmt" "io" "os" "path/filepath" "strings" ) const ( // TypeUnknown is the default value for an unknown type. TypeUnknown Type = iota // TypeTarGz is a tar.gz archive. TypeTarGz ) // New returns a new Archiver for the given type. func New(typ Type) (Archiver, error) { switch typ { case TypeTarGz: return &archivist{archiver: &tarGzExtractor{}}, nil default: return nil, fmt.Errorf("unknown type %d", typ) } } // Archiver is an interface for archiving files and directories. type Archiver interface { Extracter // ArchiveDirectory archives the given directory into the given output stream // for all files matching predicate. // out is closed by this method. ArchiveDirectory(directory string, predicate func(string) bool, out io.WriteCloser) error } type Extracter interface { // Extract extracts the given archive into the given directory. Extract(in io.ReadCloser, targetDir string) error } type Type int func (t Type) String() string { switch t { case TypeTarGz: return "tar.gz" default: return "unknown" } } type archiveAdder interface { Add(filename string, info os.FileInfo, targetPath string) error Close() error } type archiver interface { Extracter NewArchiveAdder(out io.WriteCloser) archiveAdder } type archivist struct { archiver } func (a *archivist) ArchiveDirectory(directory string, predicate func(string) bool, out io.WriteCloser) (err error) { archive := a.archiver.NewArchiveAdder(out) defer func() { closeErr := archive.Close() if err == nil { err = closeErr } }() err = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } if !predicate(path) { return nil } targetPath := strings.Trim(filepath.ToSlash(strings.TrimPrefix(path, directory)), "/") return archive.Add(path, info, targetPath) }) return } type tarGzArchiver struct { out io.WriteCloser gw *gzip.Writer tw *tar.Writer } func (a *tarGzArchiver) Add(filename string, info os.FileInfo, targetPath string) error { f, err := os.Open(filename) if err != nil { return err } defer f.Close() header, err := tar.FileInfoHeader(info, "") // TODO(bep) symlink handling? if err != nil { return err } header.Name = targetPath err = a.tw.WriteHeader(header) if err != nil { return err } _, err = io.Copy(a.tw, f) if err != nil { return err } return nil } func (a *tarGzArchiver) Close() error { if err := a.tw.Close(); err != nil { return err } if err := a.gw.Close(); err != nil { return err } return a.out.Close() } type tarGzExtractor struct { } func (e *tarGzExtractor) NewArchiveAdder(out io.WriteCloser) archiveAdder { a := &tarGzArchiver{ out: out, } gw, _ := gzip.NewWriterLevel(out, gzip.BestCompression) tw := tar.NewWriter(gw) a.gw = gw a.tw = tw return struct { archiveAdder Extracter }{ a, &tarGzExtractor{}, } } func (a *tarGzExtractor) Extract(in io.ReadCloser, targetDir string) error { defer in.Close() gzr, err := gzip.NewReader(in) if err != nil { return err } tr := tar.NewReader(gzr) for { header, err := tr.Next() if err == io.EOF { break } if err != nil { return err } target := filepath.Join(targetDir, header.Name) switch header.Typeflag { case tar.TypeDir: if err := os.MkdirAll(target, os.FileMode(header.Mode)); err != nil { return err } case tar.TypeReg: if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil && !os.IsExist(err) { return err } f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) if err != nil { return err } if _, err := io.Copy(f, tr); err != nil { return err } f.Close() default: return fmt.Errorf("unable to untar type: %c in file %s", header.Typeflag, target) } } return nil } helpers-0.4.0/archivehelpers/archivehelpers_test.go000066400000000000000000000043051430657641100225640ustar00rootroot00000000000000package archivehelpers import ( "fmt" "io/fs" "os" "path/filepath" "strings" "testing" qt "github.com/frankban/quicktest" ) func TestArchive(t *testing.T) { c := qt.New(t) _, err := New(32) c.Assert(err, qt.IsNotNil) c.Assert(TypeTarGz.String(), qt.Equals, "tar.gz") c.Assert(TypeUnknown.String(), qt.Equals, "unknown") tempDir := t.TempDir() for _, tp := range []Type{TypeTarGz} { archiveFilename := filepath.Join(tempDir, "myarchive1."+tp.String()) f, err := os.Create(archiveFilename) c.Assert(err, qt.IsNil) a, err := New(tp) c.Assert(err, qt.IsNil) sourceDir := t.TempDir() subDir := filepath.Join(sourceDir, "subdir") c.Assert(os.MkdirAll(subDir, 0755), qt.IsNil) c.Assert(os.WriteFile(filepath.Join(sourceDir, "file1.txt"), []byte("hello"), 0644), qt.IsNil) c.Assert(os.WriteFile(filepath.Join(subDir, "file2.txt"), []byte("world"), 0643), qt.IsNil) matchAll := func(string) bool { return true } c.Assert(a.ArchiveDirectory(sourceDir, matchAll, f), qt.IsNil) assertArchive := func(ararchiveFilename string, predicate func(string) bool) { resultDir := t.TempDir() f, err = os.Open(archiveFilename) c.Assert(err, qt.IsNil) c.Assert(a.Extract(f, resultDir), qt.IsNil) dirList := func(dirname string) string { var sb strings.Builder err := filepath.WalkDir(dirname, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if d.IsDir() { return nil } if !predicate(path) { return nil } fi, err := d.Info() if err != nil { return err } sb.WriteString(fmt.Sprintf("%s %04o %s\n", fi.Mode(), fi.Mode().Perm(), fi.Name())) return nil }) c.Assert(err, qt.IsNil) return sb.String() } dirList1 := dirList(sourceDir) dirList2 := dirList(resultDir) c.Assert(dirList1, qt.Equals, dirList2) } assertArchive(archiveFilename, matchAll) archiveFilename = filepath.Join(tempDir, "myarchive2."+tp.String()) f, err = os.Create(archiveFilename) matchSome := func(s string) bool { return filepath.Base(s) == "file1.txt" } c.Assert(a.ArchiveDirectory(sourceDir, matchSome, f), qt.IsNil) assertArchive(archiveFilename, matchSome) } } helpers-0.4.0/codecov.yml000066400000000000000000000002161430657641100153300ustar00rootroot00000000000000coverage: status: project: default: target: auto threshold: 0.5% patch: off comment: require_changes: true helpers-0.4.0/envhelpers/000077500000000000000000000000001430657641100153375ustar00rootroot00000000000000helpers-0.4.0/envhelpers/env.go000066400000000000000000000012121430657641100164520ustar00rootroot00000000000000package envhelpers import "strings" // SetEnvVars sets vars on the form key=value in the oldVars slice. func SetEnvVars(oldVars *[]string, keyValues ...string) { for i := 0; i < len(keyValues); i += 2 { setEnvVar(oldVars, keyValues[i], keyValues[i+1]) } } // SplitEnvVar splits an env var into key and value. func SplitEnvVar(v string) (string, string) { name, value, _ := strings.Cut(v, "=") return name, value } func setEnvVar(vars *[]string, key, value string) { for i := range *vars { if strings.HasPrefix((*vars)[i], key+"=") { (*vars)[i] = key + "=" + value return } } // New var. *vars = append(*vars, key+"="+value) } helpers-0.4.0/envhelpers/env_test.go000066400000000000000000000007061430657641100175200ustar00rootroot00000000000000package envhelpers import ( "testing" qt "github.com/frankban/quicktest" ) func TestSetEnvVars(t *testing.T) { t.Parallel() c := qt.New(t) vars := []string{"FOO=bar", "HUGO=cool", "BAR=foo"} SetEnvVars(&vars, "HUGO", "rocking!", "NEW", "bar") c.Assert(vars, qt.DeepEquals, []string{"FOO=bar", "HUGO=rocking!", "BAR=foo", "NEW=bar"}) key, val := SplitEnvVar("HUGO=rocks") c.Assert(key, qt.Equals, "HUGO") c.Assert(val, qt.Equals, "rocks") } helpers-0.4.0/envhelpers/varexpand.go000066400000000000000000000023711430657641100176610ustar00rootroot00000000000000package envhelpers import ( "regexp" "strings" ) var ( varUnquouteRe = regexp.MustCompile(`[\"']\$\{(\w+)@U\}[\"']`) varRe = regexp.MustCompile(`\$\{(\w+)\}`) ) // Expand replaces ${var} (and only that) in the string based on the mapping function. // This signature is identical to the one in the Go standard library, // but has a more restrictive scope with less ambiguity. // The value inside ${} must match \w+ (see regexp.MatchString). // // A special form of environment variable syntax is supported to allow for removing surrounding // quoutes (both single and double), useful to setting numeric values in TOML/JSON config files etc. // Use the "@U" suffix to signal unquoting, e.g. `"${myvar@U}"` or `'${myvar@U}'`. Note that when using the "@U" suffix, // the variable expression needs to be surrounded by quoutes. func Expand(s string, mapping func(string) string) string { if !strings.Contains(s, "${") { return s } firstPass := varUnquouteRe.ReplaceAllStringFunc(s, func(match string) string { // Remove quoutes and "@U" suffix before passing to mapping function. return mapping(match[3 : len(match)-4]) }) return varRe.ReplaceAllStringFunc(firstPass, func(varName string) string { return mapping(varName[2 : len(varName)-1]) }) } helpers-0.4.0/envhelpers/varexpand_test.go000066400000000000000000000021511430657641100207140ustar00rootroot00000000000000package envhelpers import ( "testing" qt "github.com/frankban/quicktest" ) func TestExpand(t *testing.T) { c := qt.New(t) m := func(s string) string { return s + "-expanded" } c.Assert(Expand("", m), qt.Equals, "") c.Assert(Expand("no vars", m), qt.Equals, "no vars") c.Assert(Expand("one var: ${myvar}.", m), qt.Equals, "one var: myvar-expanded.") c.Assert(Expand("two vars: first: ${first}, second ${second}.", m), qt.Equals, "two vars: first: first-expanded, second second-expanded.") c.Assert(Expand("with space: ${ myvar }.", m), qt.Equals, "with space: ${ myvar }.") c.Assert(Expand("with special char: ${myvar&}.", m), qt.Equals, "with special char: ${myvar&}.") c.Assert(Expand("not without brackets: $myvar", m), qt.Equals, "not without brackets: $myvar") c.Assert(Expand("multiline: ${first}\n\nanother: ${second}", m), qt.Equals, "multiline: first-expanded\n\nanother: second-expanded") c.Assert(Expand("unquoute single: '${myvar@U}'.", m), qt.Equals, "unquoute single: myvar-expanded.") c.Assert(Expand("unquoute double: \"${myvar@U}\".", m), qt.Equals, "unquoute double: myvar-expanded.") } helpers-0.4.0/filehelpers/000077500000000000000000000000001430657641100154665ustar00rootroot00000000000000helpers-0.4.0/filehelpers/filehelpers.go000066400000000000000000000025771430657641100203320ustar00rootroot00000000000000package filehelpers import ( "fmt" "io" "os" "path/filepath" ) // CopyFile copies a file. func CopyFile(from, to string) error { sf, err := os.Open(from) if err != nil { return err } defer sf.Close() df, err := os.Create(to) if err != nil { return err } defer df.Close() _, err = io.Copy(df, sf) if err != nil { return err } si, err := os.Stat(from) if err == nil { err = os.Chmod(to, si.Mode()) if err != nil { return err } } return nil } // CopyDir copies a directory. Any directory or file matching the filter will be copied. func CopyDir(from, to string, filter func(filename string) bool) error { fi, err := os.Stat(from) if err != nil { return err } if !fi.IsDir() { return fmt.Errorf("%q is not a directory", from) } err = os.MkdirAll(to, 0777) // before umask if err != nil { return err } entries, err := os.ReadDir(from) if err != nil { return err } if filter == nil { filter = func(filename string) bool { return true } } for _, entry := range entries { fromFilename := filepath.Join(from, entry.Name()) toFilename := filepath.Join(to, entry.Name()) if !filter(fromFilename) { continue } if entry.IsDir() { if err := CopyDir(fromFilename, toFilename, filter); err != nil { return err } } else { if err := CopyFile(fromFilename, toFilename); err != nil { return err } } } return nil } helpers-0.4.0/filehelpers/filehelpers_test.go000066400000000000000000000037671430657641100213730ustar00rootroot00000000000000package filehelpers import ( "fmt" "os" "path/filepath" "runtime" "strings" "testing" qt "github.com/frankban/quicktest" ) func TestCopyFile(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("skipping on windows") } tempDir := t.TempDir() abs := func(s string) string { return filepath.Join(tempDir, s) } c := qt.New(t) _, err := os.OpenFile(abs("f1.txt"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0700) c.Assert(err, qt.IsNil) c.Assert(CopyFile(abs("f1.txt"), abs("f2.txt")), qt.IsNil) fi1, err := os.Stat(abs("f1.txt")) c.Assert(err, qt.IsNil) fi2, err := os.Stat(abs("f2.txt")) c.Assert(err, qt.IsNil) c.Assert(fi1.Mode(), qt.Equals, fi2.Mode()) // Error cases. c.Assert(CopyFile(abs("doesnotexist.txt"), abs("f2.txt")), qt.IsNotNil) c.Assert(CopyFile(abs("f1.txt"), abs("doesnotexist/f2.txt")), qt.IsNotNil) } func TestCopyDir(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("skipping on windows") } tempDir := t.TempDir() abs := func(s string) string { return filepath.Join(tempDir, s) } c := qt.New(t) c.Assert(os.MkdirAll(abs("a/b/c"), 0755), qt.IsNil) _, err := os.Create(abs("a/b/c/f1.txt")) c.Assert(err, qt.IsNil) _, err = os.Create(abs("a/b/c/f2.txt")) c.Assert(err, qt.IsNil) _, err = os.Create(abs("a/b/c/f3.txt")) c.Assert(err, qt.IsNil) c.Assert(CopyDir(abs("a"), abs("b"), func(filename string) bool { return !strings.Contains(filename, "f3") }), qt.IsNil) for i := 1; i <= 2; i++ { fi1, err := os.Stat(abs(fmt.Sprintf("a/b/c/f%d.txt", i))) c.Assert(err, qt.IsNil) fi2, err := os.Stat(abs(fmt.Sprintf("b/b/c/f%d.txt", i))) c.Assert(err, qt.IsNil) c.Assert(fi1.Mode(), qt.Equals, fi2.Mode()) } rdir2, _ := os.ReadDir(abs("b/b/c")) c.Assert(len(rdir2), qt.Equals, 2) c.Assert(CopyDir(abs("a"), abs("b"), nil), qt.IsNil) rdir2, _ = os.ReadDir(abs("b/b/c")) c.Assert(len(rdir2), qt.Equals, 3) // Error cases. c.Assert(CopyDir(abs("doesnotexist"), abs("b"), nil), qt.IsNotNil) c.Assert(CopyDir(abs("a/b/c/f3.txt"), abs("b"), nil), qt.IsNotNil) } helpers-0.4.0/go.mod000066400000000000000000000004241430657641100142720ustar00rootroot00000000000000module github.com/bep/helpers go 1.18 require github.com/frankban/quicktest v1.14.3 require ( github.com/google/go-cmp v0.5.8 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect ) helpers-0.4.0/go.sum000066400000000000000000000033371430657641100143250ustar00rootroot00000000000000github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= helpers-0.4.0/slicehelpers/000077500000000000000000000000001430657641100156465ustar00rootroot00000000000000helpers-0.4.0/slicehelpers/slicehelpers.go000066400000000000000000000007001430657641100206540ustar00rootroot00000000000000package slicehelpers // Chunk splits the slice s into n number of chunks. func Chunk[T any](s []T, n int) [][]T { if len(s) == 0 { return nil } var partitions [][]T sizeDefault := len(s) / n sizeBig := len(s) - sizeDefault*n size := sizeDefault + 1 for i, idx := 0, 0; i < n; i++ { if i == sizeBig { size-- if size == 0 { break } } partitions = append(partitions, s[idx:idx+size]) idx += size } return partitions } helpers-0.4.0/slicehelpers/slicehelpers_test.go000066400000000000000000000022211430657641100217130ustar00rootroot00000000000000package slicehelpers import ( "testing" qt "github.com/frankban/quicktest" ) func TestChunk(t *testing.T) { c := qt.New(t) c.Assert(Chunk( []int{1, 2, 3, 4, 5}, 2), qt.DeepEquals, [][]int{ {1, 2, 3}, {4, 5}, }, ) c.Assert(Chunk( []int{1, 2}, 3), qt.DeepEquals, [][]int{ {1}, {2}, }, ) c.Assert(Chunk( []int{1}, 2), qt.DeepEquals, [][]int{ {1}, }, ) c.Assert(Chunk( []int{}, 2), qt.IsNil, ) c.Assert( Chunk([]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}, 3), qt.DeepEquals, [][]string{ {"a", "b", "c", "d", "e", "f", "g", "h", "i"}, {"j", "k", "l", "m", "n", "o", "p", "q", "r"}, {"s", "t", "u", "v", "w", "x", "y", "z"}, }, ) c.Assert( Chunk([]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}, 7), qt.DeepEquals, [][]string{ {"a", "b", "c", "d"}, {"e", "f", "g", "h"}, {"i", "j", "k", "l"}, {"m", "n", "o", "p"}, {"q", "r", "s", "t"}, {"u", "v", "w"}, {"x", "y", "z"}, }, ) }