Skip to content
Snippets Groups Projects
Verified Commit b5af8729 authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

feat: New BollValue Type #16, enhance pathvalue and update go lang to 1.22

parent e3c31328
No related branches found
No related tags found
No related merge requests found
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>
\ No newline at end of file
......@@ -2,5 +2,6 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../http-negotiation" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
......
......@@ -167,6 +167,10 @@
enterShell = ''
export CGO_CFLAGS=-O2 -U_FORTIFY_SOURCE;CGO_ENABLED=0
export CGO_LDFLAGS=-U_FORTIFY_SOURCE
cat <<'EOF' > CONTRIBUTING.md
# Contributing to schukai GmbH Projects
......
......@@ -15,6 +15,8 @@ github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOS
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg=
github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/r3labs/diff/v3 v3.0.1 h1:CBKqf3XmNRHXKmdU7mZP1w7TV0pDyVCis1AUHtA4Xtg=
......@@ -47,12 +49,16 @@ golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcH
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
......
......@@ -15,8 +15,10 @@ func TestReadExample3(t *testing.T) {
config := struct {
Host string
Flag bool
}{
Host: "localhost",
Flag: true,
}
c := New(config)
......
......@@ -14,7 +14,7 @@ func TestGetMapForProperties(t *testing.T) {
config.A = "A"
config.B = true
config.C.CA = "CA"
config.C.CB = true
config.C.CB = false
config.C.CD.CDA = "CDA"
s := New(config)
......@@ -23,10 +23,10 @@ func TestGetMapForProperties(t *testing.T) {
assert.Equal(t, len(e), 0)
assert.Equal(t, m["A"], "A")
assert.Equal(t, m["B"], "true")
assert.Equal(t, m["C.CA"], "CA")
assert.Equal(t, m["C.CB"], "true")
assert.Equal(t, m["C.CB.CDA"], "CDA")
assert.Equal(t, "A", m["A"])
assert.Equal(t, "true", m["B"])
assert.Equal(t, "CA", m["C.CA"])
assert.Equal(t, "false", m["C.CB"])
assert.Equal(t, "CDA", m["C.CB.CDA"])
}
// Copyright 2022 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0
// Copyright 2022 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0
package configuration
import (
"encoding/json"
"errors"
"gopkg.in/yaml.v3"
"strings"
)
type BoolValuer interface {
IsTrue() bool
IsFalse() bool
ToBool() bool
IsValid() bool
String() string
Raw() string
}
// BoolValue represents a boolean value stored as a string
type BoolValue struct {
value bool // Holds the value bool value
valid bool // Tracks if the value is valid ("true" or "false")
raw string
}
// NewBoolValue creates a new BoolValue and validates it
func NewBoolValue(raw string) (BoolValue, error) {
bv := BoolValue{raw: raw}
err := bv.parseAndValidate()
return bv, err
}
// parseAndValidate processes and validates the raw string
func (p *BoolValue) parseAndValidate() error {
switch strings.ToLower(p.raw) {
case "true", "yes", "1", "on":
p.value = true
p.valid = true
case "false", "no", "0", "off":
p.value = false
p.valid = true
default:
p.valid = false
return errors.New("invalid BoolValue: must be 'true' or 'false'")
}
return nil
}
// IsTrue returns true if the value represents a logical true
func (p BoolValue) IsTrue() bool {
return p.value && p.valid
}
// IsFalse returns true if the value represents a logical false
func (p BoolValue) IsFalse() bool {
return !p.IsTrue()
}
// ToBool converts the BoolValue to a native bool type
func (p BoolValue) ToBool() bool {
return p.IsTrue()
}
// IsValid checks whether the value is valid
func (p BoolValue) IsValid() bool {
return p.valid
}
// FromBool converts a native bool to a BoolValue
func FromBool(b bool) BoolValue {
if b {
return BoolValue{value: true, valid: true, raw: "true"}
}
return BoolValue{value: false, valid: true, raw: "false"}
}
// String returns the raw string value of BoolValue
func (p BoolValue) Raw() string {
return p.raw
}
// String returns either "true" or "false" or if the value is invalid, the raw value
func (p BoolValue) String() string {
if p.valid {
if p.value {
return "true"
}
return "false"
}
return p.raw
}
// MarshalJSON serializes the BoolValue to JSON
func (p BoolValue) MarshalJSON() ([]byte, error) {
return json.Marshal(p.IsTrue())
}
// UnmarshalJSON deserializes the BoolValue from JSON
func (p *BoolValue) UnmarshalJSON(data []byte) error {
var b string
var err error
if err = json.Unmarshal(data, &b); err != nil {
return err
}
*p, err = NewBoolValue(b)
if err != nil {
return err
}
return nil
}
// MarshalYAML serializes the BoolValue to YAML
func (p BoolValue) MarshalYAML() (interface{}, error) {
return p.IsTrue(), nil
}
// UnmarshalYAML deserializes the BoolValue from YAML
func (p *BoolValue) UnmarshalYAML(value *yaml.Node) error {
var b string
var err error
if err = value.Decode(&b); err != nil {
return err
}
*p, err = NewBoolValue(b)
if err != nil {
return err
}
return nil
}
// MarshalTOML serializes the BoolValue to TOML
func (p BoolValue) MarshalTOML() ([]byte, error) {
return []byte(p.raw), nil
}
// UnmarshalTOML deserializes the BoolValue from TOML
func (p *BoolValue) UnmarshalTOML(data []byte) error {
p.raw = strings.Trim(string(data), `"`)
return p.parseAndValidate()
}
package configuration
import (
"encoding/json"
yaml "gopkg.in/yaml.v3"
"strings"
"testing"
)
func TestBoolValueString(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"true", "true"},
{"false", "false"},
{"one", "one"},
{"", ""},
{" ", " "},
{"off", "false"},
{"on", "true"},
}
for _, tt := range tests {
t.Run(string(tt.input), func(t *testing.T) {
bv, _ := NewBoolValue(tt.input)
result := bv.String()
if result != tt.expected {
t.Errorf("result = %s; expected %s", result, tt.expected)
}
})
}
}
func TestBoolValueRaw(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"true", "true"},
{"false", "false"},
{"one", "one"},
{"", ""},
{" ", " "},
{"off", "off"},
{"on", "on"},
}
for _, tt := range tests {
t.Run(string(tt.input), func(t *testing.T) {
bv, _ := NewBoolValue(tt.input)
result := bv.Raw()
if result != tt.expected {
t.Errorf("result = %s; expected %s", result, tt.expected)
}
})
}
}
func TestBoolValueIsTrue(t *testing.T) {
tests := []struct {
input string
expected bool
}{
{"true", true},
{"false", false},
{"one", false},
{"", false},
{" ", false},
{"gurke", false},
{"off", false},
{"on", true},
{"no", false},
{"yes", true},
{"0", false},
{"1", true},
}
for _, tt := range tests {
t.Run(string(tt.input), func(t *testing.T) {
bv, _ := NewBoolValue(tt.input)
result := bv.IsTrue()
if result != tt.expected {
t.Errorf("result = %t; expected %t", result, tt.expected)
}
})
}
}
// TestBoolValueYAMLSerialization tests the YAML serialization and deserialization of BoolValue
func TestBoolValueYAMLMarshaling(t *testing.T) {
cases := []struct {
name string
boolValue BoolValue
expected string
shouldFail bool
}{
{
name: "True value",
boolValue: BoolValue{value: true, valid: true, raw: "true"},
expected: "true",
},
{
name: "False value",
boolValue: BoolValue{value: false, valid: true, raw: "false"},
expected: "false",
},
{
name: "Invalid value",
boolValue: BoolValue{valid: false, raw: "maybe"},
expected: "false",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
data, err := yaml.Marshal(&c.boolValue)
if err != nil {
if !c.shouldFail {
t.Errorf("Marshaling failed when it should not have: %v", err)
}
return
}
if strings.TrimSpace(string(data)) != c.expected {
t.Errorf("Expected %s but got %s", c.expected, string(data))
}
})
}
}
func TestBoolValueYAMLUnmarshaling(t *testing.T) {
cases := []struct {
name string
yamlContent string
expected string
shouldFail bool
}{
{
name: "True value",
yamlContent: `true`,
expected: "true",
},
{
name: "False value",
yamlContent: `false`,
expected: "false",
},
{
name: "Invalid value",
yamlContent: `maybe`,
shouldFail: true,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var unmarshal BoolValue
data := []byte(c.yamlContent)
err := yaml.Unmarshal(data, &unmarshal)
if err != nil {
if !c.shouldFail {
t.Errorf("Unmarshaling failed when it should not have: %v", err)
}
return
}
if unmarshal.String() != c.expected {
t.Errorf("Unmarshaled value %v does not match original %v", unmarshal, c.expected)
}
})
}
}
type BoolValueTestStruct struct {
Value BoolValue
}
func TestBoolValueJsonDecode(t *testing.T) {
cases := []struct {
name string
jsonContent string
expected string
shouldFail bool
}{
{
name: "True value",
jsonContent: "{\"value\":\"true\"}",
expected: "true",
},
{
name: "False value",
jsonContent: "{\"value\":\"false\"}",
expected: "false",
},
{
name: "Invalid value",
jsonContent: "{\"value\":\"gurke\"}",
shouldFail: true,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var unmarshal BoolValueTestStruct
data := []byte(c.jsonContent)
err := json.Unmarshal(data, &unmarshal)
if err != nil {
if !c.shouldFail {
t.Errorf("Unmarshaling failed when it should not have: %v", err)
}
return
}
if unmarshal.Value.String() != c.expected {
t.Errorf("Unmarshaled value %v does not match original %v", unmarshal, c.expected)
}
})
}
}
func TestBoolValueYamlDecode(t *testing.T) {
cases := []struct {
name string
yamlContent string
expected string
shouldFail bool
}{
{
name: "True value",
yamlContent: `
value: true
`,
expected: "true",
},
{
name: "False value",
yamlContent: `
value: false
`,
expected: "false",
},
{
name: "Invalid value",
yamlContent: `
value: gurke
`,
shouldFail: true,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var unmarshal BoolValueTestStruct
data := []byte(c.yamlContent)
err := yaml.Unmarshal(data, &unmarshal)
if err != nil {
if !c.shouldFail {
t.Errorf("Unmarshaling failed when it should not have: %v", err)
}
return
}
if unmarshal.Value.String() != c.expected {
t.Errorf("Unmarshaled value %v does not match original %v", unmarshal, c.expected)
}
})
}
}
......@@ -5,13 +5,25 @@ package configuration
import "path"
type pathInterface interface {
// deprecated
type PathValueInterface interface {
String() string
Dir() string
Ext() string
Base() string
}
// PathValuer is an interface for path values
type PathValuer interface {
String() string
Dir() string
Ext() string
Base() string
IsAbs() bool
Join(parts ...string) PathValue
Clean() PathValue
}
type PathValue string
func (p PathValue) Dir() string {
......@@ -29,3 +41,15 @@ func (p PathValue) String() string {
func (p PathValue) Base() string {
return path.Base(string(p))
}
func (p PathValue) IsAbs() bool {
return path.IsAbs(string(p))
}
func (p PathValue) Clean() PathValue {
return PathValue(path.Clean(string(p)))
}
func (p PathValue) Join(parts ...string) PathValue {
return PathValue(path.Join(append([]string{string(p)}, parts...)...))
}
File moved
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment