initial commit

This commit is contained in:
Jürgen Edelbluth 2023-09-28 23:05:11 +02:00
commit 8044cf7086
Signed by: jed
GPG Key ID: 6DEAEDD5CDB646DF
15 changed files with 565 additions and 0 deletions

171
.gitignore vendored Normal file
View File

@ -0,0 +1,171 @@
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Linux template
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### Go template
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# 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/
# Go workspace file
go.work
### Windows template
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
### macOS template
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

9
.idea/commons.iml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/commons.iml" filepath="$PROJECT_DIR$/.idea/commons.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

4
README.md Normal file
View File

@ -0,0 +1,4 @@
# Goblins Commons
Common helpers for all day use.

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.codebau.dev/goblins/commons
go 1.21

6
pkg/flag/bool.go Normal file
View File

@ -0,0 +1,6 @@
package flag
// IsTrue returns true, when a boolean is not nil and true
func IsTrue(f *bool) bool {
return f != nil && *f
}

33
pkg/flag/bool_test.go Normal file
View File

@ -0,0 +1,33 @@
package flag
import (
"fmt"
tt "git.codebau.dev/goblins/commons/pkg/types/testing"
"testing"
)
func TestIsTrue(t *testing.T) {
tr := true
fa := false
testCases := tt.TestCases[*bool, bool]{
{
Input: &tr,
Expected: true,
},
{
Input: &fa,
Expected: false,
},
{
Input: nil,
Expected: false,
},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%v", tc), func(subTest *testing.T) {
if val := IsTrue(tc.Input); val != tc.Expected {
subTest.Fatalf("compare failed (%v vs. %v)", val, tc.Expected)
}
})
}
}

6
pkg/ref/main.go Normal file
View File

@ -0,0 +1,6 @@
package ref
// AsRef returns a reference to the element given as t.
func AsRef[T any](t T) *T {
return &t
}

52
pkg/ref/main_test.go Normal file
View File

@ -0,0 +1,52 @@
package ref
import (
"fmt"
"testing"
)
type testStruct struct {
intValue int
intPtr *int
strValue string
strPtr *string
}
func TestAsRef(t *testing.T) {
aInt := 14
aStr := "TestString"
testCases := []any{
1,
uint(1),
int64(11111),
uint32(222222),
"Test",
'x',
[]string{"a", "b", "c"},
[]int{1, -5, 7, 9},
map[string]string{
"a": "b",
"c": "d",
},
false,
testStruct{
intValue: 898234,
intPtr: &aInt,
strValue: "some test one",
strPtr: &aStr,
},
}
for index, testCase := range testCases {
t.Run(fmt.Sprintf("test #%d: %v", index, testCase), func(subTest *testing.T) {
ref := AsRef(testCase)
if ref == nil {
subTest.Fatal("ref is nil")
}
refAddr := fmt.Sprintf("%p", testCase)
tcAddr := fmt.Sprintf("%p", *ref)
if refAddr != tcAddr {
subTest.Fatalf("references do not match (%s vs. %s)", refAddr, tcAddr)
}
})
}
}

28
pkg/types/dict.go Normal file
View File

@ -0,0 +1,28 @@
package types
// Dict is a generic key-value dictionary with string keys
type Dict[T any] map[string]T
// Contains returns true, when a key exists in the dictionary
func (d *Dict[T]) Contains(key string) bool {
if _, ok := (*d)[key]; ok {
return true
}
return false
}
// GetDefault returns the corresponding value for a given key from a dictionary if it exists, the default value if not
func (d *Dict[T]) GetDefault(key string, defaultValue T) T {
if v, ok := (*d)[key]; ok {
return v
}
return defaultValue
}
// Get returns the corresponding value ptr for a given key, or nil if the key doesn't exist
func (d *Dict[T]) Get(key string) *T {
if v, ok := (*d)[key]; ok {
return &v
}
return nil
}

220
pkg/types/dict_test.go Normal file
View File

@ -0,0 +1,220 @@
package types
import (
"fmt"
"git.codebau.dev/goblins/commons/pkg/ref"
tt "git.codebau.dev/goblins/commons/pkg/types/testing"
"testing"
)
type testContainsAndGetCase struct {
input Dict[any]
queryKey string
}
type testGetDefaultCase struct {
input Dict[any]
queryKey string
defaultValue string
}
func TestDict_Contains(t *testing.T) {
testCases := tt.TestCases[testContainsAndGetCase, bool]{
{
testContainsAndGetCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "a",
},
true,
},
{
testContainsAndGetCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "b",
},
false,
},
{
testContainsAndGetCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "abc",
},
false,
},
{
testContainsAndGetCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "e",
},
true,
},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%v", tc), func(subTest *testing.T) {
if r := tc.Input.input.Contains(tc.Input.queryKey); r != tc.Expected {
subTest.Fatalf("unexpected evaluation (%v vs. %v)", r, tc.Expected)
}
})
}
}
func TestDict_GetDefault(t *testing.T) {
testCases := tt.TestCases[testGetDefaultCase, string]{
{
testGetDefaultCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "x",
defaultValue: "I was default",
},
"I was default",
},
{
testGetDefaultCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "a",
defaultValue: "c",
},
"b",
},
{
testGetDefaultCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "",
defaultValue: "g",
},
"g",
},
{
testGetDefaultCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "",
defaultValue: "",
},
"",
},
{
testGetDefaultCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "a",
defaultValue: "b",
},
"b",
},
{
testGetDefaultCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "b",
defaultValue: "",
},
"",
},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%v", tc), func(subTest *testing.T) {
if val := tc.Input.input.GetDefault(tc.Input.queryKey, tc.Input.defaultValue); val != tc.Expected {
subTest.Fatalf("result does not match (%v vs. %v)", val, tc.Expected)
}
})
}
}
func TestDict_Get(t *testing.T) {
testCases := tt.TestCases[testContainsAndGetCase, *string]{
{
testContainsAndGetCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "a",
},
ref.AsRef("b"),
},
{
testContainsAndGetCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "b",
},
nil,
},
{
testContainsAndGetCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "abc",
},
nil,
},
{
testContainsAndGetCase{
input: Dict[any]{
"a": "b",
"c": "d",
"e": "f",
},
queryKey: "e",
},
ref.AsRef("f"),
},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%v", tc), func(subTest *testing.T) {
val := tc.Input.input.Get(tc.Input.queryKey)
if tc.Expected == nil && val != nil {
subTest.Fatalf("value does not match (%v vs. <nil>)", val)
} else if tc.Expected != nil && val == nil {
subTest.Fatalf("value does not match (<nil> vs. %v)", tc.Expected)
} else if tc.Expected != nil && val != nil && *tc.Expected != *val {
subTest.Fatalf("value does not match (%v vs. %v)", val, tc.Expected)
}
})
}
}

View File

@ -0,0 +1,7 @@
package testing
// TestCase represents a single test case
type TestCase[I any, E any] struct {
Input I
Expected E
}

View File

@ -0,0 +1,4 @@
package testing
// TestCases represents a list (slice) of test cases
type TestCases[I any, E any] []TestCase[I, E]