Skip to content

Commit

Permalink
Add linter and CI (#104)
Browse files Browse the repository at this point in the history
* Check json.Unmarshal errors

* Sorry Yoda...

* Fix import shadowing

* Change ifElseChain to switch

* Use strings.ReplaceAll instead of strings.Replace

* Fix empty string test

* Combine parameters with identical types

* Combine else if

* Use type switch instead of if else

* Fix appendAssign: append result not assigned to the same slice

* Add golangci-lint configuration

* Fix ioutilDeprecated: ioutil.ReadFile is deprecated

* Fix: stringXbytes: suggestion: !bytes.Equal(result, sample)

* Enable GitHub actions CI

* Update go.mod to include Go version
  • Loading branch information
mihaitodor authored Feb 14, 2023
1 parent 2806544 commit 286a8d7
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 63 deletions.
8 changes: 8 additions & 0 deletions .errcheck.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(*github.com/Jeffail/gabs/v2.Container).Array
(*github.com/Jeffail/gabs/v2.Container).ArrayAppend
(*github.com/Jeffail/gabs/v2.Container).ArrayAppend
(*github.com/Jeffail/gabs/v2.Container).ArrayConcat
(*github.com/Jeffail/gabs/v2.Container).ArrayConcatP
(*github.com/Jeffail/gabs/v2.Container).ArrayP
(*github.com/Jeffail/gabs/v2.Container).Set
(*github.com/Jeffail/gabs/v2.Container).SetIndex
35 changes: 35 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Test

on:
push:
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: 1.18.x

- name: Checkout code
uses: actions/checkout@v3

- name: Tidy
run: go mod tidy && git diff-index --quiet HEAD || { >&2 echo "Stale go.{mod,sum} detected. This can be fixed with 'go mod tidy'."; exit 1; }

- name: Test
run: go test -count 100 ./...

golangci-lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
args: --timeout 10m
41 changes: 41 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
run:
timeout: 30s

issues:
max-issues-per-linter: 0
max-same-issues: 0

linters-settings:
errcheck:
exclude: .errcheck.txt
gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style

linters:
disable-all: true
enable:
# Default linters reported by golangci-lint help linters` in v1.39.0
- gosimple
- staticcheck
- unused
- errcheck
- govet
- ineffassign
- typecheck
# Extra linters:
- wastedassign
- stylecheck
- gofmt
- goimports
- gocritic
- revive
- unconvert
- durationcheck
- depguard
- gosec
- bodyclose
86 changes: 44 additions & 42 deletions gabs.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"strconv"
"strings"
)
Expand Down Expand Up @@ -86,7 +86,7 @@ var (
// gabs paths, '~' needs to be encoded as '~0' and '/' needs to be encoded as
// '~1' when these characters appear in a reference key.
func JSONPointerToSlice(path string) ([]string, error) {
if len(path) == 0 {
if path == "" {
return nil, nil
}
if path[0] != '/' {
Expand All @@ -97,8 +97,8 @@ func JSONPointerToSlice(path string) ([]string, error) {
}
hierarchy := strings.Split(path, "/")[1:]
for i, v := range hierarchy {
v = strings.Replace(v, "~1", "/", -1)
v = strings.Replace(v, "~0", "~", -1)
v = strings.ReplaceAll(v, "~1", "/")
v = strings.ReplaceAll(v, "~0", "~")
hierarchy[i] = v
}
return hierarchy, nil
Expand All @@ -112,8 +112,8 @@ func JSONPointerToSlice(path string) ([]string, error) {
func DotPathToSlice(path string) []string {
hierarchy := strings.Split(path, ".")
for i, v := range hierarchy {
v = strings.Replace(v, "~1", ".", -1)
v = strings.Replace(v, "~0", "~", -1)
v = strings.ReplaceAll(v, "~1", ".")
v = strings.ReplaceAll(v, "~0", "~")
hierarchy[i] = v
}
return hierarchy
Expand Down Expand Up @@ -141,15 +141,16 @@ func (g *Container) searchStrict(allowWildcard bool, hierarchy ...string) (*Cont
object := g.Data()
for target := 0; target < len(hierarchy); target++ {
pathSeg := hierarchy[target]
if mmap, ok := object.(map[string]interface{}); ok {
object, ok = mmap[pathSeg]
if !ok {
switch typedObj := object.(type) {
case map[string]interface{}:
var ok bool
if object, ok = typedObj[pathSeg]; !ok {
return nil, fmt.Errorf("failed to resolve path segment '%v': key '%v' was not found", target, pathSeg)
}
} else if marray, ok := object.([]interface{}); ok {
case []interface{}:
if allowWildcard && pathSeg == "*" {
tmpArray := []interface{}{}
for _, val := range marray {
for _, val := range typedObj {
if (target + 1) >= len(hierarchy) {
tmpArray = append(tmpArray, val)
} else if res := Wrap(val).Search(hierarchy[target+1:]...); res != nil {
Expand All @@ -168,11 +169,11 @@ func (g *Container) searchStrict(allowWildcard bool, hierarchy ...string) (*Cont
if index < 0 {
return nil, fmt.Errorf("failed to resolve path segment '%v': found array but index '%v' is invalid", target, pathSeg)
}
if len(marray) <= index {
return nil, fmt.Errorf("failed to resolve path segment '%v': found array but index '%v' exceeded target array size of '%v'", target, pathSeg, len(marray))
if len(typedObj) <= index {
return nil, fmt.Errorf("failed to resolve path segment '%v': found array but index '%v' exceeded target array size of '%v'", target, pathSeg, len(typedObj))
}
object = marray[index]
} else {
object = typedObj[index]
default:
return nil, fmt.Errorf("failed to resolve path segment '%v': field '%v' was not found", target, pathSeg)
}
}
Expand Down Expand Up @@ -303,15 +304,16 @@ func (g *Container) Set(value interface{}, hierarchy ...string) (*Container, err

for target := 0; target < len(hierarchy); target++ {
pathSeg := hierarchy[target]
if mmap, ok := object.(map[string]interface{}); ok {
switch typedObj := object.(type) {
case map[string]interface{}:
if target == len(hierarchy)-1 {
object = value
mmap[pathSeg] = object
} else if object = mmap[pathSeg]; object == nil {
mmap[pathSeg] = map[string]interface{}{}
object = mmap[pathSeg]
typedObj[pathSeg] = object
} else if object = typedObj[pathSeg]; object == nil {
typedObj[pathSeg] = map[string]interface{}{}
object = typedObj[pathSeg]
}
} else if marray, ok := object.([]interface{}); ok {
case []interface{}:
if pathSeg == "-" {
if target < 1 {
return nil, errors.New("unable to append new array index at root of path")
Expand All @@ -321,8 +323,8 @@ func (g *Container) Set(value interface{}, hierarchy ...string) (*Container, err
} else {
object = map[string]interface{}{}
}
marray = append(marray, object)
if _, err := g.Set(marray, hierarchy[:target]...); err != nil {
typedObj = append(typedObj, object)
if _, err := g.Set(typedObj, hierarchy[:target]...); err != nil {
return nil, err
}
} else {
Expand All @@ -333,17 +335,17 @@ func (g *Container) Set(value interface{}, hierarchy ...string) (*Container, err
if index < 0 {
return nil, fmt.Errorf("failed to resolve path segment '%v': found array but index '%v' is invalid", target, pathSeg)
}
if len(marray) <= index {
return nil, fmt.Errorf("failed to resolve path segment '%v': found array but index '%v' exceeded target array size of '%v'", target, pathSeg, len(marray))
if len(typedObj) <= index {
return nil, fmt.Errorf("failed to resolve path segment '%v': found array but index '%v' exceeded target array size of '%v'", target, pathSeg, len(typedObj))
}
if target == len(hierarchy)-1 {
object = value
marray[index] = object
} else if object = marray[index]; object == nil {
typedObj[index] = object
} else if object = typedObj[index]; object == nil {
return nil, fmt.Errorf("failed to resolve path segment '%v': field '%v' was not found", target, pathSeg)
}
}
} else {
default:
return nil, ErrPathCollision
}
}
Expand Down Expand Up @@ -499,7 +501,9 @@ func (g *Container) MergeFn(source *Container, collisionFn func(destination, sou
var recursiveFnc func(map[string]interface{}, []string) error
recursiveFnc = func(mmap map[string]interface{}, path []string) error {
for key, value := range mmap {
newPath := append(path, key)
newPath := make([]string, len(path))
copy(newPath, path)
newPath = append(newPath, key)
if g.Exists(newPath...) {
existingData := g.Search(newPath...).Data()
switch t := value.(type) {
Expand All @@ -519,11 +523,9 @@ func (g *Container) MergeFn(source *Container, collisionFn func(destination, sou
return err
}
}
} else {
} else if _, err := g.Set(value, newPath...); err != nil {
// path doesn't exist. So set the value
if _, err := g.Set(value, newPath...); err != nil {
return err
}
return err
}
}
return nil
Expand Down Expand Up @@ -702,7 +704,7 @@ func (g *Container) ArrayCountP(path string) (int, error) {

//------------------------------------------------------------------------------

func walkObject(path string, obj map[string]interface{}, flat map[string]interface{}, includeEmpty bool) {
func walkObject(path string, obj, flat map[string]interface{}, includeEmpty bool) {
if includeEmpty && len(obj) == 0 {
flat[path] = struct{}{}
}
Expand Down Expand Up @@ -784,18 +786,18 @@ func (g *Container) flatten(includeEmpty bool) (map[string]interface{}, error) {

// Bytes marshals an element to a JSON []byte blob.
func (g *Container) Bytes() []byte {
if bytes, err := json.Marshal(g.Data()); err == nil {
return bytes
if data, err := json.Marshal(g.Data()); err == nil {
return data
}
return []byte("null")
}

// BytesIndent marshals an element to a JSON []byte blob formatted with a prefix
// and indent string.
func (g *Container) BytesIndent(prefix string, indent string) []byte {
func (g *Container) BytesIndent(prefix, indent string) []byte {
if g.object != nil {
if bytes, err := json.MarshalIndent(g.Data(), prefix, indent); err == nil {
return bytes
if data, err := json.MarshalIndent(g.Data(), prefix, indent); err == nil {
return data
}
}
return []byte("null")
Expand All @@ -808,7 +810,7 @@ func (g *Container) String() string {

// StringIndent marshals an element to a JSON string formatted with a prefix and
// indent string.
func (g *Container) StringIndent(prefix string, indent string) string {
func (g *Container) StringIndent(prefix, indent string) string {
return string(g.BytesIndent(prefix, indent))
}

Expand All @@ -823,7 +825,7 @@ func EncodeOptHTMLEscape(doEscape bool) EncodeOpt {
}

// EncodeOptIndent sets the encoder to indent the JSON output.
func EncodeOptIndent(prefix string, indent string) EncodeOpt {
func EncodeOptIndent(prefix, indent string) EncodeOpt {
return func(e *json.Encoder) {
e.SetIndent(prefix, indent)
}
Expand Down Expand Up @@ -885,7 +887,7 @@ func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) {
// ParseJSONFile reads a file and unmarshals the contents into a *Container.
func ParseJSONFile(path string) (*Container, error) {
if len(path) > 0 {
cBytes, err := ioutil.ReadFile(path)
cBytes, err := os.ReadFile(path)
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 286a8d7

Please sign in to comment.