diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..4c39a33 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "ci" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..65a4e95 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: ci.yml + +on: + pull_request: + push: + branches: + - main + - master + +permissions: + contents: read + +env: + GO111MODULE: on + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-go@v6 + with: + go-version: stable + + - name: golangci-lint + uses: golangci/golangci-lint-action@v8 + with: + version: v2.1 + + build: + name: Build and Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-go@v6 + with: + go-version: 1.25 + + - name: go vet + run: go vet -x ./... + + - name: Check Formatting + run: test -z "$(gofmt -s -l -w . | tee /dev/stderr)" + + - name: Run Tests + run: go test -v ./... + + - name: Coverage (profile.cov) + run: go test -covermode=count -coverprofile=profile.cov + + - name: Coveralls + uses: coverallsapp/github-action@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + file: profile.cov + format: golang \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c84fe79..0000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -sudo: false - -language: go -go: - - 1.11 - - tip - -install: true -env: - - GO111MODULE=on - -script: - - go vet -x ./... - - test -z "$(golint ./...)" - - test -z "$(gofmt -s -l -w . | tee /dev/stderr)" - - go test -v ./... - - go test -covermode=count -coverprofile=profile.cov - - test -z "$(apicompat -before ${TRAVIS_COMMIT_RANGE%...*} -after ${TRAVIS_COMMIT_RANGE#*...} ./... | tee /dev/stderr)" - -after_script: - - goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/README.md b/README.md index 4bfbea6..4630ca8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# go-linq [![GoDoc](https://godoc.org/github.com/ahmetb/go-linq?status.svg)](https://godoc.org/github.com/ahmetb/go-linq) [![Build Status](https://travis-ci.org/ahmetb/go-linq.svg?branch=master)](https://travis-ci.org/ahmetb/go-linq) [![Coverage Status](https://coveralls.io/repos/github/ahmetb/go-linq/badge.svg?branch=master)](https://coveralls.io/github/ahmetb/go-linq?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ahmetb/go-linq)](https://goreportcard.com/report/github.com/ahmetb/go-linq) +# go-linq [![GoDoc](https://godoc.org/github.com/ahmetb/go-linq?status.svg)](https://godoc.org/github.com/ahmetb/go-linq) [![Build Status](https://github.com/ahmetb/go-linq/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/ahmetb/go-linq/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/github/ahmetb/go-linq/badge.svg?branch=master)](https://coveralls.io/github/ahmetb/go-linq?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ahmetb/go-linq)](https://goreportcard.com/report/github.com/ahmetb/go-linq) A powerful language integrated query (LINQ) library for Go. diff --git a/compare.go b/compare.go index 2b33ee6..a86b5e1 100644 --- a/compare.go +++ b/compare.go @@ -6,17 +6,18 @@ type comparer func(interface{}, interface{}) int // elements in order to work with linq. // // Example: -// func (f foo) CompareTo(c Comparable) int { -// a, b := f.f1, c.(foo).f1 // -// if a < b { -// return -1 -// } else if a > b { -// return 1 -// } +// func (f foo) CompareTo(c Comparable) int { +// a, b := f.f1, c.(foo).f1 // -// return 0 -// } +// if a < b { +// return -1 +// } else if a > b { +// return 1 +// } +// +// return 0 +// } type Comparable interface { CompareTo(Comparable) int } diff --git a/example_test.go b/example_test.go index 41e6c00..cd4720c 100644 --- a/example_test.go +++ b/example_test.go @@ -326,8 +326,8 @@ func ExampleQuery_Append() { // 5 } -//The following code example demonstrates how to use Average -//to calculate the average of a slice of values. +// The following code example demonstrates how to use Average +// to calculate the average of a slice of values. func ExampleQuery_Average() { grades := []int{78, 92, 100, 37, 81} average := From(grades).Average() @@ -360,8 +360,8 @@ func ExampleQuery_Contains() { // Does the slice contains 5? true } -//The following code example demonstrates how to use CountWith -//to count the even numbers in an array. +// The following code example demonstrates how to use CountWith +// to count the even numbers in an array. func ExampleQuery_CountWith() { slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} @@ -445,8 +445,8 @@ func ExampleQuery_DefaultIfEmpty() { } -//The following code example demonstrates how to use Distinct -//to return distinct elements from a slice of integers. +// The following code example demonstrates how to use Distinct +// to return distinct elements from a slice of integers. func ExampleQuery_Distinct() { ages := []int{21, 46, 46, 55, 17, 21, 55, 55} @@ -567,7 +567,7 @@ func ExampleQuery_First() { } -//The following code example demonstrates how to use FirstWith +// The following code example demonstrates how to use FirstWith // to return the first element of an array that satisfies a condition. func ExampleQuery_FirstWith() { numbers := []int{9, 34, 65, 92, 87, 435, 3, 54, 83, 23, 87, 435, 67, 12, 19} @@ -583,8 +583,8 @@ func ExampleQuery_FirstWith() { } -//The following code example demonstrates how to use Intersect -//to return the elements that appear in each of two slices of integers. +// The following code example demonstrates how to use Intersect +// to return the elements that appear in each of two slices of integers. func ExampleQuery_Intersect() { id1 := []int{44, 26, 92, 30, 71, 38} id2 := []int{39, 59, 83, 47, 26, 4, 30} @@ -603,8 +603,8 @@ func ExampleQuery_Intersect() { } -//The following code example demonstrates how to use IntersectBy -//to return the elements that appear in each of two slices of products with same Code. +// The following code example demonstrates how to use IntersectBy +// to return the elements that appear in each of two slices of products with same Code. func ExampleQuery_IntersectBy() { type Product struct { Name string @@ -716,10 +716,10 @@ func ExampleQuery_OrderByDescending() { // The following code example demonstrates how to use ThenByDescending to perform // a secondary ordering of the elements in a slice in descending order. func ExampleOrderedQuery_ThenByDescending() { - fruits := []string{"apPLe", "baNanA", "apple", "APple", "orange", "BAnana", "ORANGE", "apPLE"} + fruits := []string{"apPLe", "baNanA", "apple", "APple", "orange", "BAnana", "ORANGE"} // Sort the strings first ascending by their length and - // then descending using a custom case insensitive comparer. + // then descending using a custom case-insensitive comparer. var query []string From(fruits). OrderBy( @@ -728,21 +728,22 @@ func ExampleOrderedQuery_ThenByDescending() { ThenByDescending( func(fruit interface{}) interface{} { return fruit.(string)[0] }, ). + ThenByDescending( + func(fruit interface{}) interface{} { return fruit.(string)[3] }, + ). ToSlice(&query) for _, fruit := range query { fmt.Println(fruit) } // Output: - // apPLe - // apPLE // apple + // apPLe // APple // orange // baNanA // ORANGE // BAnana - } // The following code example demonstrates how to use Concat @@ -1281,7 +1282,7 @@ func ExampleQuery_SumUInts() { } // The following code example demonstrates how to use Take -// to return elements from the start of a slice. +// to return elements from the start of a slice. func ExampleQuery_Take() { grades := []int{59, 82, 70, 56, 92, 98, 85} @@ -1910,7 +1911,7 @@ func ExampleQuery_GroupByT() { } // The following code example demonstrates how to use GroupJoinT -// to perform a grouped join on two slices. +// to perform a grouped join on two slices. func ExampleQuery_GroupJoinT() { type Person struct { @@ -2405,7 +2406,7 @@ func ExampleQuery_SelectManyByIndexedT() { } -//The following code example demonstrates how to use SingleWithT +// The following code example demonstrates how to use SingleWithT // to select the only element of a slice that satisfies a condition. func ExampleQuery_SingleWithT() { fruits := []string{"apple", "banana", "mango", "orange", "passionfruit", "grape"} diff --git a/genericfunc_test.go b/genericfunc_test.go index 6087374..d6230c5 100644 --- a/genericfunc_test.go +++ b/genericfunc_test.go @@ -66,7 +66,7 @@ func TestNewGenericFunc(t *testing.T) { for _, test := range tests { _, err := newGenericFunc(test.methodName, test.paramName, test.function, test.validationFunc) - if !(err == test.exception || err.Error() == test.exception.Error()) { + if err != test.exception && err.Error() != test.exception.Error() { t.Errorf("Validate expect error: %s, actual: %s", test.exception, err) } } @@ -112,7 +112,7 @@ func TestCall(t *testing.T) { func() { defer func() { r := recover() - if !(r == test.exception || r == test.exception.Error()) { + if r != test.exception && r != test.exception.Error() { t.Errorf("expect error: nil, actual: %s", r) } }() diff --git a/orderby.go b/orderby.go index 110559d..5199d92 100644 --- a/orderby.go +++ b/orderby.go @@ -91,6 +91,7 @@ func (q Query) OrderByDescending(selector func(interface{}) interface{}) Ordered // OrderByDescendingT is the typed version of OrderByDescending. // - selectorFn is of type "func(TSource) TKey" +// // NOTE: OrderByDescending has better performance than OrderByDescendingT. func (q Query) OrderByDescendingT(selectorFn interface{}) OrderedQuery { selectorGenericFunc, err := newGenericFunc( @@ -138,6 +139,7 @@ func (oq OrderedQuery) ThenBy( // ThenByT is the typed version of ThenBy. // - selectorFn is of type "func(TSource) TKey" +// // NOTE: ThenBy has better performance than ThenByT. func (oq OrderedQuery) ThenByT(selectorFn interface{}) OrderedQuery { selectorGenericFunc, err := newGenericFunc( @@ -184,6 +186,7 @@ func (oq OrderedQuery) ThenByDescending(selector func(interface{}) interface{}) // ThenByDescendingT is the typed version of ThenByDescending. // - selectorFn is of type "func(TSource) TKey" +// // NOTE: ThenByDescending has better performance than ThenByDescendingT. func (oq OrderedQuery) ThenByDescendingT(selectorFn interface{}) OrderedQuery { selectorFunc, ok := selectorFn.(func(interface{}) interface{}) @@ -230,6 +233,7 @@ func (q Query) Sort(less func(i, j interface{}) bool) Query { // SortT is the typed version of Sort. // - lessFn is of type "func(TSource,TSource) bool" +// // NOTE: Sort has better performance than SortT. func (q Query) SortT(lessFn interface{}) Query { lessGenericFunc, err := newGenericFunc( diff --git a/result_test.go b/result_test.go index 8faeb6d..b2781a4 100644 --- a/result_test.go +++ b/result_test.go @@ -220,7 +220,7 @@ func TestForEach(t *testing.T) { func TestForEachT_PanicWhenActionFnIsInvalid(t *testing.T) { mustPanicWithError(t, "ForEachT: parameter [actionFn] has a invalid function signature. Expected: 'func(T)', actual: 'func(int,int)'", func() { - From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).ForEachT(func(item, idx int) { item = item + 2 }) + From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).ForEachT(func(item, idx int) {}) }) } @@ -247,7 +247,7 @@ func TestForEachIndexed(t *testing.T) { func TestForEachIndexedT_PanicWhenActionFnIsInvalid(t *testing.T) { mustPanicWithError(t, "ForEachIndexedT: parameter [actionFn] has a invalid function signature. Expected: 'func(int,T)', actual: 'func(int)'", func() { - From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).ForEachIndexedT(func(item int) { item = item + 2 }) + From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).ForEachIndexedT(func(item int) {}) }) } diff --git a/select.go b/select.go index 5dc8cc7..6a32dc6 100644 --- a/select.go +++ b/select.go @@ -32,6 +32,7 @@ func (q Query) Select(selector func(interface{}) interface{}) Query { // SelectT is the typed version of Select. // - selectorFn is of type "func(TSource)TResult" +// // NOTE: Select has better performance than SelectT. func (q Query) SelectT(selectorFn interface{}) Query { @@ -91,6 +92,7 @@ func (q Query) SelectIndexed(selector func(int, interface{}) interface{}) Query // SelectIndexedT is the typed version of SelectIndexed. // - selectorFn is of type "func(int,TSource)TResult" +// // NOTE: SelectIndexed has better performance than SelectIndexedT. func (q Query) SelectIndexedT(selectorFn interface{}) Query { selectGenericFunc, err := newGenericFunc( diff --git a/setup_test.go b/setup_test.go index a4f4de5..a54eb3c 100644 --- a/setup_test.go +++ b/setup_test.go @@ -68,7 +68,7 @@ func validateQuery(q Query, output []interface{}) bool { _, ok := next() _, ok2 := next() - return !(ok || ok2) + return !ok && !ok2 } func mustPanicWithError(t *testing.T, expectedErr string, f func()) {