Compare commits

4 Commits

5 changed files with 764 additions and 0 deletions

View File

@@ -22,6 +22,20 @@ num := 42
ptr := common.ToPtr(num) // *int ptr := common.ToPtr(num) // *int
``` ```
**IsNilOrZero** - Check if a pointer is nil or points to a zero value
```go
import "github.com/aykhans/go-utils/common"
var ptr *int
common.IsNilOrZero(ptr) // true (nil pointer)
num := 0
common.IsNilOrZero(&num) // true (zero value)
num = 42
common.IsNilOrZero(&num) // false (non-zero value)
```
### parser ### parser
String parsing utilities with generic type support. String parsing utilities with generic type support.
@@ -94,6 +108,27 @@ maps.UpdateMap(&old, new)
// old is now: {"a": 1, "b": 3, "c": 4} // old is now: {"a": 1, "b": 3, "c": 4}
``` ```
### number
Number utility functions.
**NumLen** - Calculate the number of digits in an integer
```go
import "github.com/aykhans/go-utils/number"
number.NumLen(42) // returns 2
number.NumLen(-128) // returns 3
number.NumLen(0) // returns 1
number.NumLen(1000) // returns 4
// Works with all integer types
number.NumLen(int8(99)) // returns 2
number.NumLen(int64(123456789)) // returns 9
number.NumLen(uint32(4294967295)) // returns 10
```
Supported types: `int`, `int8`, `int16`, `int32`, `int64`, `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `uintptr`
### errors ### errors
Advanced error handling utilities. Advanced error handling utilities.

10
common/compare.go Normal file
View File

@@ -0,0 +1,10 @@
package common
func IsNilOrZero[T comparable](value *T) bool {
if value == nil {
return true
}
var zero T
return *value == zero
}

350
common/compare_test.go Normal file
View File

@@ -0,0 +1,350 @@
package common
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsNilOrZero(t *testing.T) {
t.Run("nil pointer returns true", func(t *testing.T) {
var intPtr *int
assert.True(t, IsNilOrZero(intPtr))
var strPtr *string
assert.True(t, IsNilOrZero(strPtr))
var boolPtr *bool
assert.True(t, IsNilOrZero(boolPtr))
var floatPtr *float64
assert.True(t, IsNilOrZero(floatPtr))
})
t.Run("pointer to zero value returns true", func(t *testing.T) {
t.Run("int zero", func(t *testing.T) {
val := 0
assert.True(t, IsNilOrZero(&val))
})
t.Run("string zero", func(t *testing.T) {
val := ""
assert.True(t, IsNilOrZero(&val))
})
t.Run("bool zero", func(t *testing.T) {
val := false
assert.True(t, IsNilOrZero(&val))
})
t.Run("float64 zero", func(t *testing.T) {
val := 0.0
assert.True(t, IsNilOrZero(&val))
})
t.Run("uint zero", func(t *testing.T) {
val := uint(0)
assert.True(t, IsNilOrZero(&val))
})
})
t.Run("pointer to non-zero value returns false", func(t *testing.T) {
t.Run("int non-zero", func(t *testing.T) {
val := 42
assert.False(t, IsNilOrZero(&val))
negVal := -1
assert.False(t, IsNilOrZero(&negVal))
})
t.Run("string non-zero", func(t *testing.T) {
val := "hello"
assert.False(t, IsNilOrZero(&val))
spaceVal := " "
assert.False(t, IsNilOrZero(&spaceVal))
})
t.Run("bool non-zero", func(t *testing.T) {
val := true
assert.False(t, IsNilOrZero(&val))
})
t.Run("float64 non-zero", func(t *testing.T) {
val := 3.14
assert.False(t, IsNilOrZero(&val))
negVal := -2.5
assert.False(t, IsNilOrZero(&negVal))
})
t.Run("uint non-zero", func(t *testing.T) {
val := uint(100)
assert.False(t, IsNilOrZero(&val))
})
})
t.Run("works with all integer types", func(t *testing.T) {
t.Run("int8", func(t *testing.T) {
var nilPtr *int8
assert.True(t, IsNilOrZero(nilPtr))
zero := int8(0)
assert.True(t, IsNilOrZero(&zero))
nonZero := int8(127)
assert.False(t, IsNilOrZero(&nonZero))
})
t.Run("int16", func(t *testing.T) {
var nilPtr *int16
assert.True(t, IsNilOrZero(nilPtr))
zero := int16(0)
assert.True(t, IsNilOrZero(&zero))
nonZero := int16(1000)
assert.False(t, IsNilOrZero(&nonZero))
})
t.Run("int32", func(t *testing.T) {
var nilPtr *int32
assert.True(t, IsNilOrZero(nilPtr))
zero := int32(0)
assert.True(t, IsNilOrZero(&zero))
nonZero := int32(100000)
assert.False(t, IsNilOrZero(&nonZero))
})
t.Run("int64", func(t *testing.T) {
var nilPtr *int64
assert.True(t, IsNilOrZero(nilPtr))
zero := int64(0)
assert.True(t, IsNilOrZero(&zero))
nonZero := int64(9223372036854775807)
assert.False(t, IsNilOrZero(&nonZero))
})
t.Run("uint8", func(t *testing.T) {
var nilPtr *uint8
assert.True(t, IsNilOrZero(nilPtr))
zero := uint8(0)
assert.True(t, IsNilOrZero(&zero))
nonZero := uint8(255)
assert.False(t, IsNilOrZero(&nonZero))
})
t.Run("uint16", func(t *testing.T) {
var nilPtr *uint16
assert.True(t, IsNilOrZero(nilPtr))
zero := uint16(0)
assert.True(t, IsNilOrZero(&zero))
nonZero := uint16(65535)
assert.False(t, IsNilOrZero(&nonZero))
})
t.Run("uint32", func(t *testing.T) {
var nilPtr *uint32
assert.True(t, IsNilOrZero(nilPtr))
zero := uint32(0)
assert.True(t, IsNilOrZero(&zero))
nonZero := uint32(4294967295)
assert.False(t, IsNilOrZero(&nonZero))
})
t.Run("uint64", func(t *testing.T) {
var nilPtr *uint64
assert.True(t, IsNilOrZero(nilPtr))
zero := uint64(0)
assert.True(t, IsNilOrZero(&zero))
nonZero := uint64(18446744073709551615)
assert.False(t, IsNilOrZero(&nonZero))
})
})
t.Run("works with float types", func(t *testing.T) {
t.Run("float32", func(t *testing.T) {
var nilPtr *float32
assert.True(t, IsNilOrZero(nilPtr))
zero := float32(0.0)
assert.True(t, IsNilOrZero(&zero))
nonZero := float32(1.5)
assert.False(t, IsNilOrZero(&nonZero))
})
t.Run("float64", func(t *testing.T) {
var nilPtr *float64
assert.True(t, IsNilOrZero(nilPtr))
zero := float64(0.0)
assert.True(t, IsNilOrZero(&zero))
nonZero := float64(123.456)
assert.False(t, IsNilOrZero(&nonZero))
})
})
t.Run("works with struct types", func(t *testing.T) {
type Person struct {
Name string
Age int
}
t.Run("nil struct pointer", func(t *testing.T) {
var nilPtr *Person
assert.True(t, IsNilOrZero(nilPtr))
})
t.Run("pointer to zero struct", func(t *testing.T) {
zero := Person{}
assert.True(t, IsNilOrZero(&zero))
})
t.Run("pointer to non-zero struct", func(t *testing.T) {
nonZero := Person{Name: "John", Age: 30}
assert.False(t, IsNilOrZero(&nonZero))
partiallyFilled := Person{Name: "Jane"}
assert.False(t, IsNilOrZero(&partiallyFilled))
onlyAge := Person{Age: 25}
assert.False(t, IsNilOrZero(&onlyAge))
})
})
t.Run("works with array types", func(t *testing.T) {
t.Run("nil array pointer", func(t *testing.T) {
var nilPtr *[3]int
assert.True(t, IsNilOrZero(nilPtr))
})
t.Run("pointer to zero array", func(t *testing.T) {
zero := [3]int{0, 0, 0}
assert.True(t, IsNilOrZero(&zero))
})
t.Run("pointer to non-zero array", func(t *testing.T) {
nonZero := [3]int{1, 2, 3}
assert.False(t, IsNilOrZero(&nonZero))
partiallyFilled := [3]int{1, 0, 0}
assert.False(t, IsNilOrZero(&partiallyFilled))
})
})
t.Run("edge cases", func(t *testing.T) {
t.Run("pointer to negative number", func(t *testing.T) {
val := -1
assert.False(t, IsNilOrZero(&val))
})
t.Run("pointer to whitespace string", func(t *testing.T) {
val := " "
assert.False(t, IsNilOrZero(&val))
tab := "\t"
assert.False(t, IsNilOrZero(&tab))
newline := "\n"
assert.False(t, IsNilOrZero(&newline))
})
t.Run("pointer to byte (uint8) zero", func(t *testing.T) {
val := byte(0)
assert.True(t, IsNilOrZero(&val))
nonZero := byte('A')
assert.False(t, IsNilOrZero(&nonZero))
})
t.Run("pointer to rune (int32) zero", func(t *testing.T) {
val := rune(0)
assert.True(t, IsNilOrZero(&val))
nonZero := rune('Z')
assert.False(t, IsNilOrZero(&nonZero))
})
})
t.Run("consistency with ToPtr", func(t *testing.T) {
t.Run("ToPtr of zero value is nil or zero", func(t *testing.T) {
intPtr := ToPtr(0)
assert.True(t, IsNilOrZero(intPtr))
strPtr := ToPtr("")
assert.True(t, IsNilOrZero(strPtr))
boolPtr := ToPtr(false)
assert.True(t, IsNilOrZero(boolPtr))
})
t.Run("ToPtr of non-zero value is not nil or zero", func(t *testing.T) {
intPtr := ToPtr(42)
assert.False(t, IsNilOrZero(intPtr))
strPtr := ToPtr("hello")
assert.False(t, IsNilOrZero(strPtr))
boolPtr := ToPtr(true)
assert.False(t, IsNilOrZero(boolPtr))
})
})
t.Run("real-world usage scenarios", func(t *testing.T) {
t.Run("optional configuration value", func(t *testing.T) {
type Config struct {
Port *int
Timeout *int
}
// No port configured (nil)
config1 := Config{}
assert.True(t, IsNilOrZero(config1.Port))
// Port explicitly set to 0
zero := 0
config2 := Config{Port: &zero}
assert.True(t, IsNilOrZero(config2.Port))
// Port configured to 8080
port := 8080
config3 := Config{Port: &port}
assert.False(t, IsNilOrZero(config3.Port))
})
t.Run("optional string field", func(t *testing.T) {
type User struct {
Name string
Nickname *string
}
// No nickname provided
user1 := User{Name: "John"}
assert.True(t, IsNilOrZero(user1.Nickname))
// Nickname explicitly empty
emptyNick := ""
user2 := User{Name: "Jane", Nickname: &emptyNick}
assert.True(t, IsNilOrZero(user2.Nickname))
// Nickname provided
nick := "Johnny"
user3 := User{Name: "John", Nickname: &nick}
assert.False(t, IsNilOrZero(user3.Nickname))
})
})
}

26
number/base.go Normal file
View File

@@ -0,0 +1,26 @@
package number
type Number interface {
int | int8 | int16 | int32 | int64 |
uint | uint8 | uint16 | uint32 | uint64 | uintptr
}
func NumLen[T Number](number T) T {
if number == 0 {
return 1
}
var count T = 0
if number < 0 {
for number < 0 {
number /= 10
count++
}
} else {
for number > 0 {
number /= 10
count++
}
}
return count
}

343
number/base_test.go Normal file
View File

@@ -0,0 +1,343 @@
package number
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNumLen(t *testing.T) {
t.Run("NumLen with zero", func(t *testing.T) {
assert.Equal(t, 1, NumLen(0))
assert.Equal(t, int8(1), NumLen(int8(0)))
assert.Equal(t, int16(1), NumLen(int16(0)))
assert.Equal(t, int32(1), NumLen(int32(0)))
assert.Equal(t, int64(1), NumLen(int64(0)))
assert.Equal(t, uint(1), NumLen(uint(0)))
assert.Equal(t, uint8(1), NumLen(uint8(0)))
assert.Equal(t, uint16(1), NumLen(uint16(0)))
assert.Equal(t, uint32(1), NumLen(uint32(0)))
assert.Equal(t, uint64(1), NumLen(uint64(0)))
})
t.Run("NumLen with single digit positive numbers", func(t *testing.T) {
tests := []struct {
name string
input int
expected int
}{
{"one", 1, 1},
{"five", 5, 1},
{"nine", 9, 1},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with single digit negative numbers", func(t *testing.T) {
tests := []struct {
name string
input int
expected int
}{
{"negative one", -1, 1},
{"negative five", -5, 1},
{"negative nine", -9, 1},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with multi-digit positive numbers", func(t *testing.T) {
tests := []struct {
name string
input int
expected int
}{
{"two digits", 42, 2},
{"three digits", 123, 3},
{"four digits", 9999, 4},
{"five digits", 12345, 5},
{"six digits", 100000, 6},
{"seven digits", 9876543, 7},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with multi-digit negative numbers", func(t *testing.T) {
tests := []struct {
name string
input int
expected int
}{
{"negative two digits", -42, 2},
{"negative three digits", -123, 3},
{"negative four digits", -9999, 4},
{"negative five digits", -12345, 5},
{"negative six digits", -100000, 6},
{"negative seven digits", -9876543, 7},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with int8", func(t *testing.T) {
tests := []struct {
name string
input int8
expected int8
}{
{"zero", 0, 1},
{"positive single", 9, 1},
{"positive double", 99, 2},
{"positive triple", 127, 3},
{"negative single", -9, 1},
{"negative double", -99, 2},
{"negative triple", -128, 3},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with int16", func(t *testing.T) {
tests := []struct {
name string
input int16
expected int16
}{
{"zero", 0, 1},
{"positive single", 5, 1},
{"positive double", 42, 2},
{"positive triple", 999, 3},
{"positive max", 32767, 5},
{"negative single", -5, 1},
{"negative double", -42, 2},
{"negative triple", -999, 3},
{"negative min", -32768, 5},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with int32", func(t *testing.T) {
tests := []struct {
name string
input int32
expected int32
}{
{"zero", 0, 1},
{"positive single", 7, 1},
{"positive five", 12345, 5},
{"positive max", 2147483647, 10},
{"negative single", -7, 1},
{"negative five", -12345, 5},
{"negative min", -2147483648, 10},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with int64", func(t *testing.T) {
tests := []struct {
name string
input int64
expected int64
}{
{"zero", 0, 1},
{"positive single", 3, 1},
{"positive ten", 1234567890, 10},
{"positive max", 9223372036854775807, 19},
{"negative single", -3, 1},
{"negative ten", -1234567890, 10},
{"negative min", -9223372036854775808, 19},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with uint", func(t *testing.T) {
tests := []struct {
name string
input uint
expected uint
}{
{"zero", 0, 1},
{"single digit", 5, 1},
{"double digit", 42, 2},
{"triple digit", 999, 3},
{"large number", 123456789, 9},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with uint8", func(t *testing.T) {
tests := []struct {
name string
input uint8
expected uint8
}{
{"zero", 0, 1},
{"single digit", 9, 1},
{"double digit", 99, 2},
{"max value", 255, 3},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with uint16", func(t *testing.T) {
tests := []struct {
name string
input uint16
expected uint16
}{
{"zero", 0, 1},
{"single digit", 8, 1},
{"double digit", 50, 2},
{"triple digit", 500, 3},
{"max value", 65535, 5},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with uint32", func(t *testing.T) {
tests := []struct {
name string
input uint32
expected uint32
}{
{"zero", 0, 1},
{"single digit", 6, 1},
{"five digits", 54321, 5},
{"max value", 4294967295, 10},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with uint64", func(t *testing.T) {
tests := []struct {
name string
input uint64
expected uint64
}{
{"zero", 0, 1},
{"single digit", 4, 1},
{"ten digits", 9876543210, 10},
{"max value", 18446744073709551615, 20},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := NumLen(test.input)
assert.Equal(t, test.expected, result)
})
}
})
t.Run("NumLen with boundary values", func(t *testing.T) {
t.Run("powers of 10", func(t *testing.T) {
assert.Equal(t, 1, NumLen(1))
assert.Equal(t, 2, NumLen(10))
assert.Equal(t, 3, NumLen(100))
assert.Equal(t, 4, NumLen(1000))
assert.Equal(t, 5, NumLen(10000))
assert.Equal(t, 6, NumLen(100000))
assert.Equal(t, 7, NumLen(1000000))
})
t.Run("one less than powers of 10", func(t *testing.T) {
assert.Equal(t, 1, NumLen(9))
assert.Equal(t, 2, NumLen(99))
assert.Equal(t, 3, NumLen(999))
assert.Equal(t, 4, NumLen(9999))
assert.Equal(t, 5, NumLen(99999))
assert.Equal(t, 6, NumLen(999999))
})
t.Run("negative powers of 10", func(t *testing.T) {
assert.Equal(t, 1, NumLen(-1))
assert.Equal(t, 2, NumLen(-10))
assert.Equal(t, 3, NumLen(-100))
assert.Equal(t, 4, NumLen(-1000))
assert.Equal(t, 5, NumLen(-10000))
})
})
t.Run("NumLen consistency across types", func(t *testing.T) {
// Same value, different types should return same digit count
assert.Equal(t, 2, NumLen(42))
assert.Equal(t, int8(2), NumLen(int8(42)))
assert.Equal(t, int16(2), NumLen(int16(42)))
assert.Equal(t, int32(2), NumLen(int32(42)))
assert.Equal(t, int64(2), NumLen(int64(42)))
assert.Equal(t, uint(2), NumLen(uint(42)))
assert.Equal(t, uint8(2), NumLen(uint8(42)))
assert.Equal(t, uint16(2), NumLen(uint16(42)))
assert.Equal(t, uint32(2), NumLen(uint32(42)))
assert.Equal(t, uint64(2), NumLen(uint64(42)))
})
}