From 03f6fd14d1dc262d1ad9d8c0a69e87e7fbf18e00 Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Sun, 1 Sep 2024 16:01:58 +0200 Subject: [PATCH] Initial checkin --- .github/FUNDING.yml | 6 +++ .github/workflows/codecov.yml | 39 +++++++++++++++ .github/workflows/golangci-lint.yml | 49 ++++++++++++++++++ .github/workflows/reuse.yml | 15 ++++++ .gitignore | 1 + .golangci.toml | 14 ++++++ LICENSE | 2 +- go.mod | 7 +++ niljson.go | 60 ++++++++++++++++++++++ niljson_test.go | 77 +++++++++++++++++++++++++++++ 10 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/codecov.yml create mode 100644 .github/workflows/golangci-lint.yml create mode 100644 .github/workflows/reuse.yml create mode 100644 .golangci.toml create mode 100644 go.mod create mode 100644 niljson.go create mode 100644 niljson_test.go diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e56844d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2023 Winni Neessen +# +# SPDX-License-Identifier: CC0-1.0 + +github: wneessen +ko_fi: winni diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 0000000..73d1f5f --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: 2024 Winni Neessen +# +# SPDX-License-Identifier: CC0-1.0 + +name: Codecov workflow +on: + push: + branches: + - main + paths: + - '**.go' + - 'go.*' + - '.forgejo/**' + - 'codecov.yml' + pull_request: + branches: + - main + paths: + - '**.go' + - 'go.*' + - '.forgejo/**' + - 'codecov.yml' +jobs: + run: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@master + - name: Setup go + uses: actions/setup-go@v4 + with: + go-version: '1.23' + - name: Run Tests + run: | + go test -v -shuffle=on -race --coverprofile=coverage.coverprofile --covermode=atomic ./... + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..602c06d --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: 2024 Winni Neessen +# +# SPDX-License-Identifier: CC0-1.0 + +name: golangci-lint +on: + push: + tags: + - v* + branches: + - main + pull_request: +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + # pull-requests: read +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v4 + with: + go-version: '1.23' + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: latest + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the all caching functionality will be complete disabled, + # takes precedence over all other caching options. + # skip-cache: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml new file mode 100644 index 0000000..2361ee8 --- /dev/null +++ b/.github/workflows/reuse.yml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2024 Winni Neessen +# +# SPDX-License-Identifier: CC0-1.0 + +name: REUSE Compliance Check + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: REUSE Compliance Check + uses: fsfe/reuse-action@v1 diff --git a/.gitignore b/.gitignore index 6f72f89..c6279d5 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ go.work.sum # env file .env +.idea/ \ No newline at end of file diff --git a/.golangci.toml b/.golangci.toml new file mode 100644 index 0000000..3470dbf --- /dev/null +++ b/.golangci.toml @@ -0,0 +1,14 @@ +## SPDX-FileCopyrightText: 2022 Winni Neessen +## +## SPDX-License-Identifier: MIT + +[run] +go = "1.20" +tests = true + +[linters] +enable = ["stylecheck", "whitespace", "containedctx", "contextcheck", "decorder", + "errname", "errorlint", "gofmt", "gofumpt", "goimports"] + +[linters-settings.goimports] +local-prefixes = "github.com/wneessen/niljson" diff --git a/LICENSE b/LICENSE index d931c7d..0058da1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Winni Neessen +Copyright (c) 2024 Winni Neessen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..50cfd61 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2024 Winni Neessen +// +// SPDX-License-Identifier: MIT + +module github.com/wneessen/niljson + +go 1.23 diff --git a/niljson.go b/niljson.go new file mode 100644 index 0000000..6d8b209 --- /dev/null +++ b/niljson.go @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2024 Winni Neessen +// +// SPDX-License-Identifier: MIT + +package niljson + +import "encoding/json" + +// Variable is a generic variable type that can be null. +type Variable[T any] struct { + value T + notNil bool +} + +// Get the value of the Variable +func (v *Variable[T]) Get() T { + return v.value +} + +// NotNil returns true when a Variable is not nil +func (v *Variable[T]) NotNil() bool { + return v.notNil +} + +// IsNil returns true when a Variable is nil +func (v *Variable[T]) IsNil() bool { + return !v.notNil +} + +// Reset resets the value to the Variable to a zero value and sets it to be nil +func (v *Variable[T]) Reset() { + var newVal T + v.value = newVal + v.notNil = false +} + +// NilBoolean is an boolean type that can be nil +type NilBoolean = Variable[bool] + +// NilInt is an int type that can be nil +type NilInt = Variable[int] + +// NilInt64 is an int64 type that can be nil +type NilInt64 = Variable[int64] + +// NilFloat64 is an float64 type that can be nil +type NilFloat64 = Variable[float64] + +// NilString is a string type that can be nil +type NilString = Variable[string] + +// UnmarshalJSON interprets the generic Nil types and sets the value and notnil of the type +func (v *Variable[T]) UnmarshalJSON(data []byte) error { + if string(data) != "null" { + v.value = *new(T) + v.notNil = true + return json.Unmarshal(data, &v.value) + } + return nil +} diff --git a/niljson_test.go b/niljson_test.go new file mode 100644 index 0000000..b93a8fc --- /dev/null +++ b/niljson_test.go @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2024 Winni Neessen +// +// SPDX-License-Identifier: MIT + +package niljson + +import ( + "encoding/json" + "testing" +) + +func TestVariable_UnmarshalJSON(t *testing.T) { + jsonBytes := []byte(`{"string":"test", "int":123, "int64": 12345678901234, "float":123.456, "nil":null, "bool":true}`) + type JSONType struct { + Bool NilBoolean `json:"bool"` + Float64 NilFloat64 `json:"float"` + Int NilInt `json:"int"` + Int64 NilInt64 `json:"int64"` + NullString NilString `json:"nil"` + String NilString `json:"string"` + } + var jt JSONType + if err := json.Unmarshal(jsonBytes, &jt); err != nil { + t.Errorf("failed to unmarshal json with nil types: %v", err) + } + if jt.Float64.IsNil() { + t.Errorf("expected not nil float64") + } + if jt.Int.IsNil() { + t.Errorf("expected not nil int") + } + if jt.Int64.IsNil() { + t.Errorf("expected not nil int64") + } + if jt.String.IsNil() { + t.Errorf("expected not nil string") + } + if jt.NullString.NotNil() { + t.Errorf("expected nil string") + } + if !jt.Bool.Get() { + t.Errorf("expected bool to be true, got %t", jt.Bool.Get()) + } + if jt.Float64.Get() != 123.456 { + t.Errorf("expected float64 to be 123.456, got %f", jt.Float64.Get()) + } + if jt.Int.Get() != 123 { + t.Errorf("expected int to be 123, got %d", jt.Int.Get()) + } + if jt.Int64.Get() != 12345678901234 { + t.Errorf("expected int to be 12345678901234, got %d", jt.Int64.Get()) + } + if jt.String.Get() != "test" { + t.Errorf("expected string to be 'test', got %s", jt.String.Get()) + } + + jt.Bool.Reset() + if jt.Bool.NotNil() { + t.Errorf("expected bool to be nil after reset") + } + jt.Float64.Reset() + if jt.Float64.NotNil() { + t.Errorf("expected float64 to be nil after reset") + } + jt.Int.Reset() + if jt.Int.NotNil() { + t.Errorf("expected int to be nil after reset") + } + jt.Int64.Reset() + if jt.Int64.NotNil() { + t.Errorf("expected int64 to be nil after reset") + } + jt.String.Reset() + if jt.String.NotNil() { + t.Errorf("expected string to be nil after reset") + } +}