diff --git a/.golangci.yml b/.golangci.yml index df8327f..abab1ab 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,18 +8,22 @@ formatters: - goimports linters: - enable: - - forbidigo - - gocritic - - lll - - reassign - - unconvert - - gosec - - errorlint - - godot - - revive - - testpackage - - unused + default: all + + disable: + - dupl # Dupl is disabled, since we're generating a lot of boilerplate code. + - cyclop # Cyclop is disabled, since cyclomatic complexities is very abstract metric, + # that sometimes lead to strange linter behaviour. + - wsl # WSL is disabled, since it's obsolete. Using WSL_v5. + - nlreturn # nlreturn is disabled, since it's duplicated by wsl_v5.return check. + - ireturn # ireturn is disabled, since it's not needed. + + exclusions: + rules: + - path-except: cmd/generator/*.go + linters: + - forbidigo # fmt functions are not forbidden here + - gochecknoglobals # global variables are not forbidden here settings: godot: @@ -27,3 +31,27 @@ linters: lll: line-length: 120 tab-width: 4 + wsl_v5: + allow-first-in-block: true + allow-whole-block: false + branch-max-lines: 2 + case-max-lines: 0 + default: all + depguard: + rules: + main: + files: + - "$all" + - "!$test" + allow: + - $gostd + - "github.com/vmihailenco/msgpack/v5" + - "golang.org/x/text" + test: + files: + - "$test" + allow: + - $gostd + - "github.com/stretchr/testify" + - "github.com/vmihailenco/msgpack/v5" + - "github.com/tarantool/go-option" diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bbbe49..d817bf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. ### Added +- Implemented optional types for builtin go types int*, uint*, float*, + bytes, string, bool. + + ### Changed ### Fixed diff --git a/bool_gen.go b/bool_gen.go new file mode 100644 index 0000000..dcc82f4 --- /dev/null +++ b/bool_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Bool represents an optional value of type bool. +// It can either hold a valid bool (IsSome == true) or be empty (IsZero == true). +type Bool struct { + value bool + exists bool +} + +// SomeBool creates an optional Bool with the given bool value. +// The returned Bool will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeBool(true) +// if o.IsSome() { +// v := o.Unwrap() // v == true +// } +func SomeBool(value bool) Bool { + return Bool{ + value: value, + exists: true, + } +} + +// NoneBool creates an empty optional Bool value. +// The returned Bool will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneBool() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneBool() Bool { + return Bool{ + exists: false, + value: zero[bool](), + } +} + +// IsSome returns true if the Bool contains a value. +// This indicates the value is explicitly set (not None). +func (o Bool) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Bool does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Bool) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Bool) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of bool, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Bool) Get() (bool, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Bool) MustGet() bool { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for bool. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Bool) Unwrap() bool { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneBool() +// v := o.UnwrapOr(someDefaultBool) +func (o Bool) UnwrapOr(defaultValue bool) bool { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneBool() +// v := o.UnwrapOrElse(func() bool { return computeDefault() }) +func (o Bool) UnwrapOrElse(defaultValue func() bool) bool { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Bool value using MessagePack format. +// - If the value is present, it is encoded as bool. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Bool) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Bool", encodeBool(encoder, o.value)) + } + + return newEncodeError("Bool", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Bool value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneBool) +// - bool: interpreted as a present value (SomeBool) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on bool: exists = true, value = decoded value +func (o *Bool) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Bool", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Bool", decoder.Skip()) + case checkBool(code): + o.value, err = decodeBool(decoder) + if err != nil { + return newDecodeError("Bool", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Bool", code) + } +} diff --git a/bool_gen_test.go b/bool_gen_test.go new file mode 100644 index 0000000..cf374b1 --- /dev/null +++ b/bool_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestBool_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBool := option.SomeBool(true) + assert.True(t, someBool.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBool := option.NoneBool() + assert.False(t, emptyBool.IsSome()) + }) +} + +func TestBool_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBool := option.SomeBool(true) + assert.False(t, someBool.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBool := option.NoneBool() + assert.True(t, emptyBool.IsZero()) + }) +} + +func TestBool_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBool := option.SomeBool(true) + assert.False(t, someBool.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBool := option.NoneBool() + assert.True(t, emptyBool.IsNil()) + }) +} + +func TestBool_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBool := option.SomeBool(true) + val, ok := someBool.Get() + require.True(t, ok) + assert.EqualValues(t, true, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBool := option.NoneBool() + _, ok := emptyBool.Get() + require.False(t, ok) + }) +} + +func TestBool_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBool := option.SomeBool(true) + assert.EqualValues(t, true, someBool.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBool := option.NoneBool() + assert.Panics(t, func() { + emptyBool.MustGet() + }) + }) +} + +func TestBool_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBool := option.SomeBool(true) + assert.EqualValues(t, true, someBool.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBool := option.NoneBool() + assert.NotPanics(t, func() { + emptyBool.Unwrap() + }) + }) +} + +func TestBool_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBool := option.SomeBool(true) + assert.EqualValues(t, true, someBool.UnwrapOr(false)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBool := option.NoneBool() + assert.EqualValues(t, false, emptyBool.UnwrapOr(false)) + }) +} + +func TestBool_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBool := option.SomeBool(true) + assert.EqualValues(t, true, someBool.UnwrapOrElse(func() bool { + return false + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBool := option.NoneBool() + assert.EqualValues(t, false, emptyBool.UnwrapOrElse(func() bool { + return false + })) + }) +} + +func TestBool_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someBool := option.SomeBool(true) + err := someBool.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Bool + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, true, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyBool := option.NoneBool() + err := emptyBool.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Bool + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/byte_gen.go b/byte_gen.go new file mode 100644 index 0000000..7a3a3ff --- /dev/null +++ b/byte_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Byte represents an optional value of type byte. +// It can either hold a valid byte (IsSome == true) or be empty (IsZero == true). +type Byte struct { + value byte + exists bool +} + +// SomeByte creates an optional Byte with the given byte value. +// The returned Byte will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeByte(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeByte(value byte) Byte { + return Byte{ + value: value, + exists: true, + } +} + +// NoneByte creates an empty optional Byte value. +// The returned Byte will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneByte() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneByte() Byte { + return Byte{ + exists: false, + value: zero[byte](), + } +} + +// IsSome returns true if the Byte contains a value. +// This indicates the value is explicitly set (not None). +func (o Byte) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Byte does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Byte) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Byte) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of byte, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Byte) Get() (byte, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Byte) MustGet() byte { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for byte. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Byte) Unwrap() byte { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneByte() +// v := o.UnwrapOr(someDefaultByte) +func (o Byte) UnwrapOr(defaultValue byte) byte { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneByte() +// v := o.UnwrapOrElse(func() byte { return computeDefault() }) +func (o Byte) UnwrapOrElse(defaultValue func() byte) byte { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Byte value using MessagePack format. +// - If the value is present, it is encoded as byte. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Byte) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Byte", encodeByte(encoder, o.value)) + } + + return newEncodeError("Byte", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Byte value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneByte) +// - byte: interpreted as a present value (SomeByte) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on byte: exists = true, value = decoded value +func (o *Byte) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Byte", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Byte", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeByte(decoder) + if err != nil { + return newDecodeError("Byte", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Byte", code) + } +} diff --git a/byte_gen_test.go b/byte_gen_test.go new file mode 100644 index 0000000..4733e31 --- /dev/null +++ b/byte_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestByte_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someByte := option.SomeByte(12) + assert.True(t, someByte.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyByte := option.NoneByte() + assert.False(t, emptyByte.IsSome()) + }) +} + +func TestByte_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someByte := option.SomeByte(12) + assert.False(t, someByte.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyByte := option.NoneByte() + assert.True(t, emptyByte.IsZero()) + }) +} + +func TestByte_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someByte := option.SomeByte(12) + assert.False(t, someByte.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyByte := option.NoneByte() + assert.True(t, emptyByte.IsNil()) + }) +} + +func TestByte_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someByte := option.SomeByte(12) + val, ok := someByte.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyByte := option.NoneByte() + _, ok := emptyByte.Get() + require.False(t, ok) + }) +} + +func TestByte_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someByte := option.SomeByte(12) + assert.EqualValues(t, 12, someByte.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyByte := option.NoneByte() + assert.Panics(t, func() { + emptyByte.MustGet() + }) + }) +} + +func TestByte_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someByte := option.SomeByte(12) + assert.EqualValues(t, 12, someByte.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyByte := option.NoneByte() + assert.NotPanics(t, func() { + emptyByte.Unwrap() + }) + }) +} + +func TestByte_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someByte := option.SomeByte(12) + assert.EqualValues(t, 12, someByte.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyByte := option.NoneByte() + assert.EqualValues(t, 13, emptyByte.UnwrapOr(13)) + }) +} + +func TestByte_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someByte := option.SomeByte(12) + assert.EqualValues(t, 12, someByte.UnwrapOrElse(func() byte { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyByte := option.NoneByte() + assert.EqualValues(t, 13, emptyByte.UnwrapOrElse(func() byte { + return 13 + })) + }) +} + +func TestByte_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someByte := option.SomeByte(12) + err := someByte.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Byte + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyByte := option.NoneByte() + err := emptyByte.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Byte + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/bytes_gen.go b/bytes_gen.go new file mode 100644 index 0000000..358dc0a --- /dev/null +++ b/bytes_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Bytes represents an optional value of type []byte. +// It can either hold a valid []byte (IsSome == true) or be empty (IsZero == true). +type Bytes struct { + value []byte + exists bool +} + +// SomeBytes creates an optional Bytes with the given []byte value. +// The returned Bytes will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeBytes([]byte("hello")) +// if o.IsSome() { +// v := o.Unwrap() // v == []byte("hello") +// } +func SomeBytes(value []byte) Bytes { + return Bytes{ + value: value, + exists: true, + } +} + +// NoneBytes creates an empty optional Bytes value. +// The returned Bytes will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneBytes() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneBytes() Bytes { + return Bytes{ + exists: false, + value: zero[[]byte](), + } +} + +// IsSome returns true if the Bytes contains a value. +// This indicates the value is explicitly set (not None). +func (o Bytes) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Bytes does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Bytes) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Bytes) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of []byte, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Bytes) Get() ([]byte, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Bytes) MustGet() []byte { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for []byte. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Bytes) Unwrap() []byte { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneBytes() +// v := o.UnwrapOr(someDefaultBytes) +func (o Bytes) UnwrapOr(defaultValue []byte) []byte { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneBytes() +// v := o.UnwrapOrElse(func() []byte { return computeDefault() }) +func (o Bytes) UnwrapOrElse(defaultValue func() []byte) []byte { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Bytes value using MessagePack format. +// - If the value is present, it is encoded as []byte. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Bytes) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Bytes", encodeBytes(encoder, o.value)) + } + + return newEncodeError("Bytes", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Bytes value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneBytes) +// - []byte: interpreted as a present value (SomeBytes) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on []byte: exists = true, value = decoded value +func (o *Bytes) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Bytes", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Bytes", decoder.Skip()) + case checkBytes(code): + o.value, err = decodeBytes(decoder) + if err != nil { + return newDecodeError("Bytes", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Bytes", code) + } +} diff --git a/bytes_gen_test.go b/bytes_gen_test.go new file mode 100644 index 0000000..83c0190 --- /dev/null +++ b/bytes_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestBytes_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBytes := option.SomeBytes([]byte("hello")) + assert.True(t, someBytes.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBytes := option.NoneBytes() + assert.False(t, emptyBytes.IsSome()) + }) +} + +func TestBytes_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBytes := option.SomeBytes([]byte("hello")) + assert.False(t, someBytes.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBytes := option.NoneBytes() + assert.True(t, emptyBytes.IsZero()) + }) +} + +func TestBytes_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBytes := option.SomeBytes([]byte("hello")) + assert.False(t, someBytes.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBytes := option.NoneBytes() + assert.True(t, emptyBytes.IsNil()) + }) +} + +func TestBytes_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBytes := option.SomeBytes([]byte("hello")) + val, ok := someBytes.Get() + require.True(t, ok) + assert.EqualValues(t, []byte("hello"), val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBytes := option.NoneBytes() + _, ok := emptyBytes.Get() + require.False(t, ok) + }) +} + +func TestBytes_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBytes := option.SomeBytes([]byte("hello")) + assert.EqualValues(t, []byte("hello"), someBytes.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBytes := option.NoneBytes() + assert.Panics(t, func() { + emptyBytes.MustGet() + }) + }) +} + +func TestBytes_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBytes := option.SomeBytes([]byte("hello")) + assert.EqualValues(t, []byte("hello"), someBytes.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBytes := option.NoneBytes() + assert.NotPanics(t, func() { + emptyBytes.Unwrap() + }) + }) +} + +func TestBytes_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBytes := option.SomeBytes([]byte("hello")) + assert.EqualValues(t, []byte("hello"), someBytes.UnwrapOr([]byte("henlo"))) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBytes := option.NoneBytes() + assert.EqualValues(t, []byte("henlo"), emptyBytes.UnwrapOr([]byte("henlo"))) + }) +} + +func TestBytes_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someBytes := option.SomeBytes([]byte("hello")) + assert.EqualValues(t, []byte("hello"), someBytes.UnwrapOrElse(func() []byte { + return []byte("henlo") + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyBytes := option.NoneBytes() + assert.EqualValues(t, []byte("henlo"), emptyBytes.UnwrapOrElse(func() []byte { + return []byte("henlo") + })) + }) +} + +func TestBytes_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someBytes := option.SomeBytes([]byte("hello")) + err := someBytes.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Bytes + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, []byte("hello"), unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyBytes := option.NoneBytes() + err := emptyBytes.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Bytes + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/cmd/generator/generator.go b/cmd/generator/generator.go new file mode 100644 index 0000000..bf3c535 --- /dev/null +++ b/cmd/generator/generator.go @@ -0,0 +1,729 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "go/format" + "os" + "path/filepath" + "slices" + "text/template" + + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +const ( + defaultGoPermissions = 0644 +) + +var ( + outputDirectory string + verbose bool +) + +type generatorDef struct { + Name string + Type string + DecodeFunc string + EncoderFunc string + CheckerFunc string + + TestingValue string + UnexpectedTestingValue string +} + +func structToMap(def generatorDef) map[string]interface{} { + caser := cases.Title(language.English) + + out := map[string]interface{}{ + "Name": caser.String(def.Name), + "Type": def.Name, + "DecodeFunc": def.DecodeFunc, + "EncoderFunc": def.EncoderFunc, + "CheckerFunc": def.CheckerFunc, + + "TestingValue": def.TestingValue, + "UnexpectedTestingValue": def.UnexpectedTestingValue, + } + + if def.Type != "" { + out["Type"] = def.Type + } + + return out +} + +var defaultTypes = []generatorDef{ + { + Name: "byte", + Type: "byte", + DecodeFunc: "decodeByte", + EncoderFunc: "encodeByte", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "int", + Type: "int", + DecodeFunc: "decodeInt", + EncoderFunc: "encodeInt", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "int8", + Type: "int8", + DecodeFunc: "decodeInt8", + EncoderFunc: "encodeInt8", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "int16", + Type: "int16", + DecodeFunc: "decodeInt16", + EncoderFunc: "encodeInt16", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "int32", + Type: "int32", + DecodeFunc: "decodeInt32", + EncoderFunc: "encodeInt32", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "int64", + Type: "int64", + DecodeFunc: "decodeInt64", + EncoderFunc: "encodeInt64", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "uint", + Type: "uint", + DecodeFunc: "decodeUint", + EncoderFunc: "encodeUint", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "uint8", + Type: "uint8", + DecodeFunc: "decodeUint8", + EncoderFunc: "encodeUint8", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "uint16", + Type: "uint16", + DecodeFunc: "decodeUint16", + EncoderFunc: "encodeUint16", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "uint32", + Type: "uint32", + DecodeFunc: "decodeUint32", + EncoderFunc: "encodeUint32", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "uint64", + Type: "uint64", + DecodeFunc: "decodeUint64", + EncoderFunc: "encodeUint64", + CheckerFunc: "checkNumber", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "float32", + Type: "float32", + DecodeFunc: "decodeFloat32", + EncoderFunc: "encodeFloat32", + CheckerFunc: "checkFloat", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "float64", + Type: "float64", + DecodeFunc: "decodeFloat64", + EncoderFunc: "encodeFloat64", + CheckerFunc: "checkFloat", + + TestingValue: "12", + UnexpectedTestingValue: "13", + }, + { + Name: "string", + Type: "string", + DecodeFunc: "decodeString", + EncoderFunc: "encodeString", + CheckerFunc: "checkString", + + TestingValue: "\"hello\"", + UnexpectedTestingValue: "\"henlo\"", + }, + { + Name: "bytes", + Type: "[]byte", + DecodeFunc: "decodeBytes", + EncoderFunc: "encodeBytes", + CheckerFunc: "checkBytes", + + TestingValue: "[]byte(\"hello\")", + UnexpectedTestingValue: "[]byte(\"henlo\")", + }, + { + Name: "bool", + Type: "bool", + DecodeFunc: "decodeBool", + EncoderFunc: "encodeBool", + CheckerFunc: "checkBool", + + TestingValue: "true", + UnexpectedTestingValue: "false", + }, +} + +var tplText = ` +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package {{ .packageName }} + +import ( + {{ range $i, $import := .imports }} + "{{ $import }}" + {{ end }} + + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// {{.Name}} represents an optional value of type {{.Type}}. +// It can either hold a valid {{.Type}} (IsSome == true) or be empty (IsZero == true). +type {{.Name}} struct { + value {{.Type}} + exists bool +} + +// Some{{.Name}} creates an optional {{.Name}} with the given {{.Type}} value. +// The returned {{.Name}} will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := Some{{.Name}}({{.TestingValue}}) +// if o.IsSome() { +// v := o.Unwrap() // v == {{.TestingValue}} +// } +func Some{{.Name}}(value {{.Type}}) {{.Name}} { + return {{.Name}}{ + value: value, + exists: true, + } +} + +// None{{.Name}} creates an empty optional {{.Name}} value. +// The returned {{.Name}} will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := None{{.Name}}() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func None{{.Name}}() {{.Name}} { + return {{.Name}}{ + exists: false, + value: zero[{{.Type}}](), + } +} + +// IsSome returns true if the {{.Name}} contains a value. +// This indicates the value is explicitly set (not None). +func (o {{.Name}}) IsSome() bool { + return o.exists +} + +// IsZero returns true if the {{.Name}} does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o {{.Name}}) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o {{.Name}}) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of {{.Type}}, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o {{.Name}}) Get() ({{.Type}}, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o {{.Name}}) MustGet() {{.Type}} { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for {{.Type}}. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o {{.Name}}) Unwrap() {{.Type}} { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := None{{.Name}}() +// v := o.UnwrapOr(someDefault{{.Name}}) +func (o {{.Name}}) UnwrapOr(defaultValue {{.Type}}) {{.Type}} { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := None{{.Name}}() +// v := o.UnwrapOrElse(func() {{.Type}} { return computeDefault() }) +func (o {{.Name}}) UnwrapOrElse(defaultValue func() {{.Type}}) {{.Type}} { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the {{.Name}} value using MessagePack format. +// - If the value is present, it is encoded as {{.Type}}. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o {{.Name}}) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("{{.Name}}", {{ .EncoderFunc }}(encoder, o.value)) + } + + return newEncodeError("{{.Name}}", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a {{.Name}} value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (None{{.Name}}) +// - {{.Type}}: interpreted as a present value (Some{{.Name}}) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on {{.Type}}: exists = true, value = decoded value +func (o *{{.Name}}) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("{{.Name}}", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("{{.Name}}", decoder.Skip()) + case {{ .CheckerFunc }}(code): + o.value, err = {{ .DecodeFunc }}(decoder) + if err != nil { + return newDecodeError("{{.Name}}", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("{{.Name}}", code) + } +}` + +var tplTestText = ` +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package {{ .packageName }}_test + +import ( + {{ range $i, $import := .imports }} + "{{ $import }}" + {{ end }} + + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func Test{{.Name}}_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + some{{.Name}} := option.Some{{.Name}}({{.TestingValue}}) + assert.True(t, some{{.Name}}.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + empty{{.Name}} := option.None{{.Name}}() + assert.False(t, empty{{.Name}}.IsSome()) + }) +} + +func Test{{.Name}}_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + some{{.Name}} := option.Some{{.Name}}({{.TestingValue}}) + assert.False(t, some{{.Name}}.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + empty{{.Name}} := option.None{{.Name}}() + assert.True(t, empty{{.Name}}.IsZero()) + }) +} + +func Test{{.Name}}_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + some{{.Name}} := option.Some{{.Name}}({{.TestingValue}}) + assert.False(t, some{{.Name}}.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + empty{{.Name}} := option.None{{.Name}}() + assert.True(t, empty{{.Name}}.IsNil()) + }) +} + +func Test{{.Name}}_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + some{{.Name}} := option.Some{{.Name}}({{.TestingValue}}) + val, ok := some{{.Name}}.Get() + require.True(t, ok) + assert.EqualValues(t, {{.TestingValue}}, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + empty{{.Name}} := option.None{{.Name}}() + _, ok := empty{{.Name}}.Get() + require.False(t, ok) + }) +} + +func Test{{.Name}}_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + some{{.Name}} := option.Some{{.Name}}({{.TestingValue}}) + assert.EqualValues(t, {{.TestingValue}}, some{{.Name}}.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + empty{{.Name}} := option.None{{.Name}}() + assert.Panics(t, func() { + empty{{.Name}}.MustGet() + }) + }) +} + +func Test{{.Name}}_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + some{{.Name}} := option.Some{{.Name}}({{.TestingValue}}) + assert.EqualValues(t, {{.TestingValue}}, some{{.Name}}.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + empty{{.Name}} := option.None{{.Name}}() + assert.NotPanics(t, func() { + empty{{.Name}}.Unwrap() + }) + }) +} + +func Test{{.Name}}_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + some{{.Name}} := option.Some{{.Name}}({{.TestingValue}}) + assert.EqualValues(t, {{.TestingValue}}, some{{.Name}}.UnwrapOr({{.UnexpectedTestingValue}})) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + empty{{.Name}} := option.None{{.Name}}() + assert.EqualValues(t, {{.UnexpectedTestingValue}}, empty{{.Name}}.UnwrapOr({{.UnexpectedTestingValue}})) + }) +} + +func Test{{.Name}}_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + some{{.Name}} := option.Some{{.Name}}({{.TestingValue}}) + assert.EqualValues(t, {{.TestingValue}}, some{{.Name}}.UnwrapOrElse(func() {{.Type}} { + return {{.UnexpectedTestingValue}} + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + empty{{.Name}} := option.None{{.Name}}() + assert.EqualValues(t, {{.UnexpectedTestingValue}}, empty{{.Name}}.UnwrapOrElse(func() {{.Type}} { + return {{.UnexpectedTestingValue}} + })) + }) +} + +func Test{{.Name}}_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + some{{.Name}} := option.Some{{.Name}}({{.TestingValue}}) + err := some{{.Name}}.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.{{.Name}} + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, {{.TestingValue}}, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + empty{{.Name}} := option.None{{.Name}}() + err := empty{{.Name}}.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.{{.Name}} + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} +` + +func printFile(prefix string, data []byte) { + for lineNo, line := range bytes.Split(data, []byte("\n")) { + fmt.Printf("%03d%s%s\n", lineNo, prefix, string(line)) + } +} + +func generateAndWrite() error { + tmpl, err := template.New("internal").Parse(tplText) + if err != nil { + return fmt.Errorf("failed to parse template: %w", err) + } + + tmpl_test, err := template.New("internal_test").Parse(tplTestText) + if err != nil { + return fmt.Errorf("failed to parse testing template: %w", err) + } + + outputData := make(map[string][]byte, 2*len(defaultTypes)) //nolint:mnd + + var data bytes.Buffer + + // 1. Generate code for each type. + for _, generatedType := range defaultTypes { + tmplData := structToMap(generatedType) + + tmplData["packageName"] = "option" // Package name is option, since generator is used for option only right now. + tmplData["imports"] = []string{} // No additional imports are needed right now. + + // Generate code of an Optional type. + { + err := tmpl.Execute(&data, tmplData) + if err != nil { + return fmt.Errorf("failed to execute template: %w", err) + } + + outputData[generatedType.Name+"_gen.go"] = slices.Clone(data.Bytes()) + data.Reset() + } + + // Generate code for tests of an Optional type. + { + err := tmpl_test.Execute(&data, tmplData) + if err != nil { + return fmt.Errorf("failed to execute test template: %w", err) + } + + outputData[generatedType.Name+"_gen_test.go"] = slices.Clone(data.Bytes()) + data.Reset() + } + } + + // 2. Just in case format code using gofmt. + for name, origData := range outputData { + data, err := format.Source(origData) + if err != nil { + if verbose { + printFile("> ", origData) + } + + return fmt.Errorf("failed to format code: %w", err) + } + + outputData[name] = data + } + + // 3. Write resulting code to files. + for name, data := range outputData { + err = os.WriteFile(filepath.Join(outputDirectory, name), data, defaultGoPermissions) + if err != nil { + return fmt.Errorf("failed to write file: %w", err) + } + } + + return nil +} + +func main() { + flag.StringVar(&outputDirectory, "output", ".", "output directory") + flag.BoolVar(&verbose, "verbose", false, "print verbose output") + flag.Parse() + + // Get absolute path for output directory. + absOutputDirectory, err := filepath.Abs(outputDirectory) + if err != nil { + fmt.Printf("failed to get absolute path for output directory (%s): %s\n", outputDirectory, err) + os.Exit(1) + } + + // Check if output directory exists and is directory. + switch fInfo, err := os.Stat(absOutputDirectory); { + case err != nil: + fmt.Println("failed to stat output directory: ", err.Error()) + os.Exit(1) + case !fInfo.IsDir(): + fmt.Printf("output directory '%s' is not a directory\n", absOutputDirectory) + os.Exit(1) + } + + err = generateAndWrite() + if err != nil { + fmt.Println("failed to generate or write code: ", err) + os.Exit(1) + } +} diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..d0e6b85 --- /dev/null +++ b/errors.go @@ -0,0 +1,60 @@ +package option + +import ( + "fmt" +) + +// DecodeError is returned when decoding failed due to invalid code in msgpack stream. +type DecodeError struct { + Type string + Code Byte + Parent error +} + +// Error returns the text representation of error. +func (d DecodeError) Error() string { + if d.Code.IsSome() { + return fmt.Sprintf("failed to decode %s, invalid code: %d", d.Type, d.Code.Unwrap()) + } + + return fmt.Sprintf("failed to decode %s: %s", d.Type, d.Parent) +} + +func newDecodeWithCodeError(operationType string, code byte) error { + return DecodeError{ + Type: operationType, + Code: SomeByte(code), + Parent: nil, + } +} + +func newDecodeError(operationType string, err error) error { + if err == nil { + return nil + } + + return DecodeError{ + Type: operationType, + Code: NoneByte(), + Parent: err, + } +} + +// EncodeError is returned when encoding failed due to stream errors. +type EncodeError struct { + Type string + Parent error +} + +// Error returns the text representation of error. +func (e EncodeError) Error() string { + return fmt.Sprintf("failed to encode %s: %s", e.Type, e.Parent) +} + +func newEncodeError(operationType string, err error) error { + if err == nil { + return nil + } + + return EncodeError{Type: operationType, Parent: err} +} diff --git a/errors_test.go b/errors_test.go new file mode 100644 index 0000000..b911420 --- /dev/null +++ b/errors_test.go @@ -0,0 +1,65 @@ +package option //nolint:testpackage +// this is unit test, that checks internal logic of error constructing. + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + errTest = errors.New("some error") +) + +func TestDecodeError_Error(t *testing.T) { + t.Parallel() + + t.Run("newEncodeError with error", func(t *testing.T) { + t.Parallel() + + a := newEncodeError("Byte", errTest) + + require.Error(t, a) + assert.Equal(t, "failed to encode Byte: some error", a.Error()) + }) + + t.Run("newEncodeError without error", func(t *testing.T) { + t.Parallel() + + a := newEncodeError("Byte", nil) + + require.NoError(t, a) + }) +} + +func TestEncodeError_Error(t *testing.T) { + t.Parallel() + + t.Run("newDecodeError with error", func(t *testing.T) { + t.Parallel() + + a := newDecodeError("Byte", errTest) + + require.Error(t, a) + assert.Equal(t, "failed to decode Byte: some error", a.Error()) + }) + + t.Run("newDecodeError without error", func(t *testing.T) { + t.Parallel() + + a := newDecodeError("Byte", nil) + + require.NoError(t, a) + }) + + t.Run("newDecodeWithCodeError", func(t *testing.T) { + t.Parallel() + + a := newDecodeWithCodeError("Byte", 1) + + require.Error(t, a) + assert.Equal(t, "failed to decode Byte, invalid code: 1", a.Error()) + }) +} diff --git a/float32_gen.go b/float32_gen.go new file mode 100644 index 0000000..83e248e --- /dev/null +++ b/float32_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Float32 represents an optional value of type float32. +// It can either hold a valid float32 (IsSome == true) or be empty (IsZero == true). +type Float32 struct { + value float32 + exists bool +} + +// SomeFloat32 creates an optional Float32 with the given float32 value. +// The returned Float32 will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeFloat32(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeFloat32(value float32) Float32 { + return Float32{ + value: value, + exists: true, + } +} + +// NoneFloat32 creates an empty optional Float32 value. +// The returned Float32 will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneFloat32() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneFloat32() Float32 { + return Float32{ + exists: false, + value: zero[float32](), + } +} + +// IsSome returns true if the Float32 contains a value. +// This indicates the value is explicitly set (not None). +func (o Float32) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Float32 does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Float32) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Float32) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of float32, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Float32) Get() (float32, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Float32) MustGet() float32 { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for float32. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Float32) Unwrap() float32 { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneFloat32() +// v := o.UnwrapOr(someDefaultFloat32) +func (o Float32) UnwrapOr(defaultValue float32) float32 { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneFloat32() +// v := o.UnwrapOrElse(func() float32 { return computeDefault() }) +func (o Float32) UnwrapOrElse(defaultValue func() float32) float32 { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Float32 value using MessagePack format. +// - If the value is present, it is encoded as float32. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Float32) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Float32", encodeFloat32(encoder, o.value)) + } + + return newEncodeError("Float32", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Float32 value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneFloat32) +// - float32: interpreted as a present value (SomeFloat32) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on float32: exists = true, value = decoded value +func (o *Float32) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Float32", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Float32", decoder.Skip()) + case checkFloat(code): + o.value, err = decodeFloat32(decoder) + if err != nil { + return newDecodeError("Float32", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Float32", code) + } +} diff --git a/float32_gen_test.go b/float32_gen_test.go new file mode 100644 index 0000000..a623449 --- /dev/null +++ b/float32_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestFloat32_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat32 := option.SomeFloat32(12) + assert.True(t, someFloat32.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat32 := option.NoneFloat32() + assert.False(t, emptyFloat32.IsSome()) + }) +} + +func TestFloat32_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat32 := option.SomeFloat32(12) + assert.False(t, someFloat32.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat32 := option.NoneFloat32() + assert.True(t, emptyFloat32.IsZero()) + }) +} + +func TestFloat32_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat32 := option.SomeFloat32(12) + assert.False(t, someFloat32.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat32 := option.NoneFloat32() + assert.True(t, emptyFloat32.IsNil()) + }) +} + +func TestFloat32_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat32 := option.SomeFloat32(12) + val, ok := someFloat32.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat32 := option.NoneFloat32() + _, ok := emptyFloat32.Get() + require.False(t, ok) + }) +} + +func TestFloat32_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat32 := option.SomeFloat32(12) + assert.EqualValues(t, 12, someFloat32.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat32 := option.NoneFloat32() + assert.Panics(t, func() { + emptyFloat32.MustGet() + }) + }) +} + +func TestFloat32_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat32 := option.SomeFloat32(12) + assert.EqualValues(t, 12, someFloat32.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat32 := option.NoneFloat32() + assert.NotPanics(t, func() { + emptyFloat32.Unwrap() + }) + }) +} + +func TestFloat32_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat32 := option.SomeFloat32(12) + assert.EqualValues(t, 12, someFloat32.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat32 := option.NoneFloat32() + assert.EqualValues(t, 13, emptyFloat32.UnwrapOr(13)) + }) +} + +func TestFloat32_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat32 := option.SomeFloat32(12) + assert.EqualValues(t, 12, someFloat32.UnwrapOrElse(func() float32 { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat32 := option.NoneFloat32() + assert.EqualValues(t, 13, emptyFloat32.UnwrapOrElse(func() float32 { + return 13 + })) + }) +} + +func TestFloat32_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someFloat32 := option.SomeFloat32(12) + err := someFloat32.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Float32 + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyFloat32 := option.NoneFloat32() + err := emptyFloat32.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Float32 + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/float64_gen.go b/float64_gen.go new file mode 100644 index 0000000..dc5fdf6 --- /dev/null +++ b/float64_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Float64 represents an optional value of type float64. +// It can either hold a valid float64 (IsSome == true) or be empty (IsZero == true). +type Float64 struct { + value float64 + exists bool +} + +// SomeFloat64 creates an optional Float64 with the given float64 value. +// The returned Float64 will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeFloat64(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeFloat64(value float64) Float64 { + return Float64{ + value: value, + exists: true, + } +} + +// NoneFloat64 creates an empty optional Float64 value. +// The returned Float64 will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneFloat64() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneFloat64() Float64 { + return Float64{ + exists: false, + value: zero[float64](), + } +} + +// IsSome returns true if the Float64 contains a value. +// This indicates the value is explicitly set (not None). +func (o Float64) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Float64 does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Float64) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Float64) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of float64, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Float64) Get() (float64, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Float64) MustGet() float64 { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for float64. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Float64) Unwrap() float64 { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneFloat64() +// v := o.UnwrapOr(someDefaultFloat64) +func (o Float64) UnwrapOr(defaultValue float64) float64 { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneFloat64() +// v := o.UnwrapOrElse(func() float64 { return computeDefault() }) +func (o Float64) UnwrapOrElse(defaultValue func() float64) float64 { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Float64 value using MessagePack format. +// - If the value is present, it is encoded as float64. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Float64) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Float64", encodeFloat64(encoder, o.value)) + } + + return newEncodeError("Float64", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Float64 value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneFloat64) +// - float64: interpreted as a present value (SomeFloat64) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on float64: exists = true, value = decoded value +func (o *Float64) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Float64", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Float64", decoder.Skip()) + case checkFloat(code): + o.value, err = decodeFloat64(decoder) + if err != nil { + return newDecodeError("Float64", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Float64", code) + } +} diff --git a/float64_gen_test.go b/float64_gen_test.go new file mode 100644 index 0000000..bd3c13a --- /dev/null +++ b/float64_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestFloat64_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat64 := option.SomeFloat64(12) + assert.True(t, someFloat64.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat64 := option.NoneFloat64() + assert.False(t, emptyFloat64.IsSome()) + }) +} + +func TestFloat64_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat64 := option.SomeFloat64(12) + assert.False(t, someFloat64.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat64 := option.NoneFloat64() + assert.True(t, emptyFloat64.IsZero()) + }) +} + +func TestFloat64_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat64 := option.SomeFloat64(12) + assert.False(t, someFloat64.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat64 := option.NoneFloat64() + assert.True(t, emptyFloat64.IsNil()) + }) +} + +func TestFloat64_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat64 := option.SomeFloat64(12) + val, ok := someFloat64.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat64 := option.NoneFloat64() + _, ok := emptyFloat64.Get() + require.False(t, ok) + }) +} + +func TestFloat64_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat64 := option.SomeFloat64(12) + assert.EqualValues(t, 12, someFloat64.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat64 := option.NoneFloat64() + assert.Panics(t, func() { + emptyFloat64.MustGet() + }) + }) +} + +func TestFloat64_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat64 := option.SomeFloat64(12) + assert.EqualValues(t, 12, someFloat64.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat64 := option.NoneFloat64() + assert.NotPanics(t, func() { + emptyFloat64.Unwrap() + }) + }) +} + +func TestFloat64_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat64 := option.SomeFloat64(12) + assert.EqualValues(t, 12, someFloat64.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat64 := option.NoneFloat64() + assert.EqualValues(t, 13, emptyFloat64.UnwrapOr(13)) + }) +} + +func TestFloat64_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someFloat64 := option.SomeFloat64(12) + assert.EqualValues(t, 12, someFloat64.UnwrapOrElse(func() float64 { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyFloat64 := option.NoneFloat64() + assert.EqualValues(t, 13, emptyFloat64.UnwrapOrElse(func() float64 { + return 13 + })) + }) +} + +func TestFloat64_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someFloat64 := option.SomeFloat64(12) + err := someFloat64.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Float64 + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyFloat64 := option.NoneFloat64() + err := emptyFloat64.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Float64 + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/generate.go b/generate.go new file mode 100644 index 0000000..2b7e663 --- /dev/null +++ b/generate.go @@ -0,0 +1,2 @@ +//go:generate go run ./cmd/generator --output ./ --verbose +package option diff --git a/go.mod b/go.mod index 1287fed..a343002 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,20 @@ module github.com/tarantool/go-option -go 1.23 +go 1.23.0 + +toolchain go1.24.4 + +require ( + github.com/stretchr/testify v1.10.0 + github.com/vmihailenco/msgpack/v5 v5.4.1 + golang.org/x/text v0.28.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index e69de29..ca5934d 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,28 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/int16_gen.go b/int16_gen.go new file mode 100644 index 0000000..ea0e7c3 --- /dev/null +++ b/int16_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Int16 represents an optional value of type int16. +// It can either hold a valid int16 (IsSome == true) or be empty (IsZero == true). +type Int16 struct { + value int16 + exists bool +} + +// SomeInt16 creates an optional Int16 with the given int16 value. +// The returned Int16 will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeInt16(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeInt16(value int16) Int16 { + return Int16{ + value: value, + exists: true, + } +} + +// NoneInt16 creates an empty optional Int16 value. +// The returned Int16 will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneInt16() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneInt16() Int16 { + return Int16{ + exists: false, + value: zero[int16](), + } +} + +// IsSome returns true if the Int16 contains a value. +// This indicates the value is explicitly set (not None). +func (o Int16) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Int16 does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Int16) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Int16) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of int16, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Int16) Get() (int16, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Int16) MustGet() int16 { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for int16. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Int16) Unwrap() int16 { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneInt16() +// v := o.UnwrapOr(someDefaultInt16) +func (o Int16) UnwrapOr(defaultValue int16) int16 { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneInt16() +// v := o.UnwrapOrElse(func() int16 { return computeDefault() }) +func (o Int16) UnwrapOrElse(defaultValue func() int16) int16 { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Int16 value using MessagePack format. +// - If the value is present, it is encoded as int16. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Int16) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Int16", encodeInt16(encoder, o.value)) + } + + return newEncodeError("Int16", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Int16 value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneInt16) +// - int16: interpreted as a present value (SomeInt16) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on int16: exists = true, value = decoded value +func (o *Int16) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Int16", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Int16", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeInt16(decoder) + if err != nil { + return newDecodeError("Int16", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Int16", code) + } +} diff --git a/int16_gen_test.go b/int16_gen_test.go new file mode 100644 index 0000000..4a391ae --- /dev/null +++ b/int16_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestInt16_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt16 := option.SomeInt16(12) + assert.True(t, someInt16.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt16 := option.NoneInt16() + assert.False(t, emptyInt16.IsSome()) + }) +} + +func TestInt16_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt16 := option.SomeInt16(12) + assert.False(t, someInt16.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt16 := option.NoneInt16() + assert.True(t, emptyInt16.IsZero()) + }) +} + +func TestInt16_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt16 := option.SomeInt16(12) + assert.False(t, someInt16.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt16 := option.NoneInt16() + assert.True(t, emptyInt16.IsNil()) + }) +} + +func TestInt16_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt16 := option.SomeInt16(12) + val, ok := someInt16.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt16 := option.NoneInt16() + _, ok := emptyInt16.Get() + require.False(t, ok) + }) +} + +func TestInt16_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt16 := option.SomeInt16(12) + assert.EqualValues(t, 12, someInt16.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt16 := option.NoneInt16() + assert.Panics(t, func() { + emptyInt16.MustGet() + }) + }) +} + +func TestInt16_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt16 := option.SomeInt16(12) + assert.EqualValues(t, 12, someInt16.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt16 := option.NoneInt16() + assert.NotPanics(t, func() { + emptyInt16.Unwrap() + }) + }) +} + +func TestInt16_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt16 := option.SomeInt16(12) + assert.EqualValues(t, 12, someInt16.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt16 := option.NoneInt16() + assert.EqualValues(t, 13, emptyInt16.UnwrapOr(13)) + }) +} + +func TestInt16_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt16 := option.SomeInt16(12) + assert.EqualValues(t, 12, someInt16.UnwrapOrElse(func() int16 { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt16 := option.NoneInt16() + assert.EqualValues(t, 13, emptyInt16.UnwrapOrElse(func() int16 { + return 13 + })) + }) +} + +func TestInt16_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someInt16 := option.SomeInt16(12) + err := someInt16.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Int16 + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyInt16 := option.NoneInt16() + err := emptyInt16.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Int16 + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/int32_gen.go b/int32_gen.go new file mode 100644 index 0000000..8256dfa --- /dev/null +++ b/int32_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Int32 represents an optional value of type int32. +// It can either hold a valid int32 (IsSome == true) or be empty (IsZero == true). +type Int32 struct { + value int32 + exists bool +} + +// SomeInt32 creates an optional Int32 with the given int32 value. +// The returned Int32 will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeInt32(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeInt32(value int32) Int32 { + return Int32{ + value: value, + exists: true, + } +} + +// NoneInt32 creates an empty optional Int32 value. +// The returned Int32 will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneInt32() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneInt32() Int32 { + return Int32{ + exists: false, + value: zero[int32](), + } +} + +// IsSome returns true if the Int32 contains a value. +// This indicates the value is explicitly set (not None). +func (o Int32) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Int32 does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Int32) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Int32) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of int32, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Int32) Get() (int32, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Int32) MustGet() int32 { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for int32. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Int32) Unwrap() int32 { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneInt32() +// v := o.UnwrapOr(someDefaultInt32) +func (o Int32) UnwrapOr(defaultValue int32) int32 { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneInt32() +// v := o.UnwrapOrElse(func() int32 { return computeDefault() }) +func (o Int32) UnwrapOrElse(defaultValue func() int32) int32 { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Int32 value using MessagePack format. +// - If the value is present, it is encoded as int32. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Int32) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Int32", encodeInt32(encoder, o.value)) + } + + return newEncodeError("Int32", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Int32 value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneInt32) +// - int32: interpreted as a present value (SomeInt32) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on int32: exists = true, value = decoded value +func (o *Int32) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Int32", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Int32", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeInt32(decoder) + if err != nil { + return newDecodeError("Int32", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Int32", code) + } +} diff --git a/int32_gen_test.go b/int32_gen_test.go new file mode 100644 index 0000000..841a26c --- /dev/null +++ b/int32_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestInt32_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt32 := option.SomeInt32(12) + assert.True(t, someInt32.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt32 := option.NoneInt32() + assert.False(t, emptyInt32.IsSome()) + }) +} + +func TestInt32_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt32 := option.SomeInt32(12) + assert.False(t, someInt32.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt32 := option.NoneInt32() + assert.True(t, emptyInt32.IsZero()) + }) +} + +func TestInt32_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt32 := option.SomeInt32(12) + assert.False(t, someInt32.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt32 := option.NoneInt32() + assert.True(t, emptyInt32.IsNil()) + }) +} + +func TestInt32_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt32 := option.SomeInt32(12) + val, ok := someInt32.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt32 := option.NoneInt32() + _, ok := emptyInt32.Get() + require.False(t, ok) + }) +} + +func TestInt32_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt32 := option.SomeInt32(12) + assert.EqualValues(t, 12, someInt32.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt32 := option.NoneInt32() + assert.Panics(t, func() { + emptyInt32.MustGet() + }) + }) +} + +func TestInt32_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt32 := option.SomeInt32(12) + assert.EqualValues(t, 12, someInt32.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt32 := option.NoneInt32() + assert.NotPanics(t, func() { + emptyInt32.Unwrap() + }) + }) +} + +func TestInt32_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt32 := option.SomeInt32(12) + assert.EqualValues(t, 12, someInt32.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt32 := option.NoneInt32() + assert.EqualValues(t, 13, emptyInt32.UnwrapOr(13)) + }) +} + +func TestInt32_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt32 := option.SomeInt32(12) + assert.EqualValues(t, 12, someInt32.UnwrapOrElse(func() int32 { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt32 := option.NoneInt32() + assert.EqualValues(t, 13, emptyInt32.UnwrapOrElse(func() int32 { + return 13 + })) + }) +} + +func TestInt32_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someInt32 := option.SomeInt32(12) + err := someInt32.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Int32 + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyInt32 := option.NoneInt32() + err := emptyInt32.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Int32 + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/int64_gen.go b/int64_gen.go new file mode 100644 index 0000000..c98a34a --- /dev/null +++ b/int64_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Int64 represents an optional value of type int64. +// It can either hold a valid int64 (IsSome == true) or be empty (IsZero == true). +type Int64 struct { + value int64 + exists bool +} + +// SomeInt64 creates an optional Int64 with the given int64 value. +// The returned Int64 will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeInt64(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeInt64(value int64) Int64 { + return Int64{ + value: value, + exists: true, + } +} + +// NoneInt64 creates an empty optional Int64 value. +// The returned Int64 will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneInt64() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneInt64() Int64 { + return Int64{ + exists: false, + value: zero[int64](), + } +} + +// IsSome returns true if the Int64 contains a value. +// This indicates the value is explicitly set (not None). +func (o Int64) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Int64 does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Int64) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Int64) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of int64, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Int64) Get() (int64, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Int64) MustGet() int64 { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for int64. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Int64) Unwrap() int64 { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneInt64() +// v := o.UnwrapOr(someDefaultInt64) +func (o Int64) UnwrapOr(defaultValue int64) int64 { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneInt64() +// v := o.UnwrapOrElse(func() int64 { return computeDefault() }) +func (o Int64) UnwrapOrElse(defaultValue func() int64) int64 { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Int64 value using MessagePack format. +// - If the value is present, it is encoded as int64. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Int64) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Int64", encodeInt64(encoder, o.value)) + } + + return newEncodeError("Int64", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Int64 value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneInt64) +// - int64: interpreted as a present value (SomeInt64) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on int64: exists = true, value = decoded value +func (o *Int64) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Int64", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Int64", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeInt64(decoder) + if err != nil { + return newDecodeError("Int64", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Int64", code) + } +} diff --git a/int64_gen_test.go b/int64_gen_test.go new file mode 100644 index 0000000..8ec8228 --- /dev/null +++ b/int64_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestInt64_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt64 := option.SomeInt64(12) + assert.True(t, someInt64.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt64 := option.NoneInt64() + assert.False(t, emptyInt64.IsSome()) + }) +} + +func TestInt64_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt64 := option.SomeInt64(12) + assert.False(t, someInt64.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt64 := option.NoneInt64() + assert.True(t, emptyInt64.IsZero()) + }) +} + +func TestInt64_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt64 := option.SomeInt64(12) + assert.False(t, someInt64.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt64 := option.NoneInt64() + assert.True(t, emptyInt64.IsNil()) + }) +} + +func TestInt64_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt64 := option.SomeInt64(12) + val, ok := someInt64.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt64 := option.NoneInt64() + _, ok := emptyInt64.Get() + require.False(t, ok) + }) +} + +func TestInt64_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt64 := option.SomeInt64(12) + assert.EqualValues(t, 12, someInt64.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt64 := option.NoneInt64() + assert.Panics(t, func() { + emptyInt64.MustGet() + }) + }) +} + +func TestInt64_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt64 := option.SomeInt64(12) + assert.EqualValues(t, 12, someInt64.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt64 := option.NoneInt64() + assert.NotPanics(t, func() { + emptyInt64.Unwrap() + }) + }) +} + +func TestInt64_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt64 := option.SomeInt64(12) + assert.EqualValues(t, 12, someInt64.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt64 := option.NoneInt64() + assert.EqualValues(t, 13, emptyInt64.UnwrapOr(13)) + }) +} + +func TestInt64_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt64 := option.SomeInt64(12) + assert.EqualValues(t, 12, someInt64.UnwrapOrElse(func() int64 { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt64 := option.NoneInt64() + assert.EqualValues(t, 13, emptyInt64.UnwrapOrElse(func() int64 { + return 13 + })) + }) +} + +func TestInt64_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someInt64 := option.SomeInt64(12) + err := someInt64.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Int64 + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyInt64 := option.NoneInt64() + err := emptyInt64.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Int64 + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/int8_gen.go b/int8_gen.go new file mode 100644 index 0000000..6e661cc --- /dev/null +++ b/int8_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Int8 represents an optional value of type int8. +// It can either hold a valid int8 (IsSome == true) or be empty (IsZero == true). +type Int8 struct { + value int8 + exists bool +} + +// SomeInt8 creates an optional Int8 with the given int8 value. +// The returned Int8 will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeInt8(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeInt8(value int8) Int8 { + return Int8{ + value: value, + exists: true, + } +} + +// NoneInt8 creates an empty optional Int8 value. +// The returned Int8 will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneInt8() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneInt8() Int8 { + return Int8{ + exists: false, + value: zero[int8](), + } +} + +// IsSome returns true if the Int8 contains a value. +// This indicates the value is explicitly set (not None). +func (o Int8) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Int8 does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Int8) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Int8) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of int8, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Int8) Get() (int8, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Int8) MustGet() int8 { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for int8. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Int8) Unwrap() int8 { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneInt8() +// v := o.UnwrapOr(someDefaultInt8) +func (o Int8) UnwrapOr(defaultValue int8) int8 { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneInt8() +// v := o.UnwrapOrElse(func() int8 { return computeDefault() }) +func (o Int8) UnwrapOrElse(defaultValue func() int8) int8 { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Int8 value using MessagePack format. +// - If the value is present, it is encoded as int8. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Int8) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Int8", encodeInt8(encoder, o.value)) + } + + return newEncodeError("Int8", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Int8 value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneInt8) +// - int8: interpreted as a present value (SomeInt8) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on int8: exists = true, value = decoded value +func (o *Int8) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Int8", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Int8", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeInt8(decoder) + if err != nil { + return newDecodeError("Int8", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Int8", code) + } +} diff --git a/int8_gen_test.go b/int8_gen_test.go new file mode 100644 index 0000000..c242be4 --- /dev/null +++ b/int8_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestInt8_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt8 := option.SomeInt8(12) + assert.True(t, someInt8.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt8 := option.NoneInt8() + assert.False(t, emptyInt8.IsSome()) + }) +} + +func TestInt8_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt8 := option.SomeInt8(12) + assert.False(t, someInt8.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt8 := option.NoneInt8() + assert.True(t, emptyInt8.IsZero()) + }) +} + +func TestInt8_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt8 := option.SomeInt8(12) + assert.False(t, someInt8.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt8 := option.NoneInt8() + assert.True(t, emptyInt8.IsNil()) + }) +} + +func TestInt8_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt8 := option.SomeInt8(12) + val, ok := someInt8.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt8 := option.NoneInt8() + _, ok := emptyInt8.Get() + require.False(t, ok) + }) +} + +func TestInt8_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt8 := option.SomeInt8(12) + assert.EqualValues(t, 12, someInt8.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt8 := option.NoneInt8() + assert.Panics(t, func() { + emptyInt8.MustGet() + }) + }) +} + +func TestInt8_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt8 := option.SomeInt8(12) + assert.EqualValues(t, 12, someInt8.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt8 := option.NoneInt8() + assert.NotPanics(t, func() { + emptyInt8.Unwrap() + }) + }) +} + +func TestInt8_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt8 := option.SomeInt8(12) + assert.EqualValues(t, 12, someInt8.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt8 := option.NoneInt8() + assert.EqualValues(t, 13, emptyInt8.UnwrapOr(13)) + }) +} + +func TestInt8_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt8 := option.SomeInt8(12) + assert.EqualValues(t, 12, someInt8.UnwrapOrElse(func() int8 { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt8 := option.NoneInt8() + assert.EqualValues(t, 13, emptyInt8.UnwrapOrElse(func() int8 { + return 13 + })) + }) +} + +func TestInt8_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someInt8 := option.SomeInt8(12) + err := someInt8.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Int8 + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyInt8 := option.NoneInt8() + err := emptyInt8.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Int8 + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/int_gen.go b/int_gen.go new file mode 100644 index 0000000..ad9e97d --- /dev/null +++ b/int_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Int represents an optional value of type int. +// It can either hold a valid int (IsSome == true) or be empty (IsZero == true). +type Int struct { + value int + exists bool +} + +// SomeInt creates an optional Int with the given int value. +// The returned Int will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeInt(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeInt(value int) Int { + return Int{ + value: value, + exists: true, + } +} + +// NoneInt creates an empty optional Int value. +// The returned Int will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneInt() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneInt() Int { + return Int{ + exists: false, + value: zero[int](), + } +} + +// IsSome returns true if the Int contains a value. +// This indicates the value is explicitly set (not None). +func (o Int) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Int does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Int) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Int) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of int, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Int) Get() (int, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Int) MustGet() int { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for int. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Int) Unwrap() int { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneInt() +// v := o.UnwrapOr(someDefaultInt) +func (o Int) UnwrapOr(defaultValue int) int { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneInt() +// v := o.UnwrapOrElse(func() int { return computeDefault() }) +func (o Int) UnwrapOrElse(defaultValue func() int) int { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Int value using MessagePack format. +// - If the value is present, it is encoded as int. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Int) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Int", encodeInt(encoder, o.value)) + } + + return newEncodeError("Int", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Int value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneInt) +// - int: interpreted as a present value (SomeInt) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on int: exists = true, value = decoded value +func (o *Int) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Int", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Int", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeInt(decoder) + if err != nil { + return newDecodeError("Int", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Int", code) + } +} diff --git a/int_gen_test.go b/int_gen_test.go new file mode 100644 index 0000000..02ecc11 --- /dev/null +++ b/int_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestInt_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt := option.SomeInt(12) + assert.True(t, someInt.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt := option.NoneInt() + assert.False(t, emptyInt.IsSome()) + }) +} + +func TestInt_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt := option.SomeInt(12) + assert.False(t, someInt.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt := option.NoneInt() + assert.True(t, emptyInt.IsZero()) + }) +} + +func TestInt_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt := option.SomeInt(12) + assert.False(t, someInt.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt := option.NoneInt() + assert.True(t, emptyInt.IsNil()) + }) +} + +func TestInt_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt := option.SomeInt(12) + val, ok := someInt.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt := option.NoneInt() + _, ok := emptyInt.Get() + require.False(t, ok) + }) +} + +func TestInt_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt := option.SomeInt(12) + assert.EqualValues(t, 12, someInt.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt := option.NoneInt() + assert.Panics(t, func() { + emptyInt.MustGet() + }) + }) +} + +func TestInt_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt := option.SomeInt(12) + assert.EqualValues(t, 12, someInt.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt := option.NoneInt() + assert.NotPanics(t, func() { + emptyInt.Unwrap() + }) + }) +} + +func TestInt_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt := option.SomeInt(12) + assert.EqualValues(t, 12, someInt.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt := option.NoneInt() + assert.EqualValues(t, 13, emptyInt.UnwrapOr(13)) + }) +} + +func TestInt_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someInt := option.SomeInt(12) + assert.EqualValues(t, 12, someInt.UnwrapOrElse(func() int { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyInt := option.NoneInt() + assert.EqualValues(t, 13, emptyInt.UnwrapOrElse(func() int { + return 13 + })) + }) +} + +func TestInt_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someInt := option.SomeInt(12) + err := someInt.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Int + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyInt := option.NoneInt() + err := emptyInt.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Int + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/msgpack.go b/msgpack.go new file mode 100644 index 0000000..263ce5f --- /dev/null +++ b/msgpack.go @@ -0,0 +1,169 @@ +package option + +// This file provides utility functions for decoding and encoding basic types used in MessagePack serialization. +// It includes type checks (e.g., checkNumber, checkString) to ensure compatibility with expected data types and +// wrappers for consistent error handling. +// Note: encodeInt and encodeUint differ from others by converting to int64/uint64 explicitly, +// as the underlying library requires it for correct encoding. + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +func checkNumber(code byte) bool { + switch { + case msgpcode.IsFixedNum(code): + return true + case code == msgpcode.Int8 || code == msgpcode.Int16 || code == msgpcode.Int32 || code == msgpcode.Int64: + return true + case code == msgpcode.Uint8 || code == msgpcode.Uint16 || code == msgpcode.Uint32 || code == msgpcode.Uint64: + return true + default: + return false + } +} + +func decodeInt(decoder *msgpack.Decoder) (int, error) { + return decoder.DecodeInt() //nolint:wrapcheck +} + +func decodeInt8(decoder *msgpack.Decoder) (int8, error) { + return decoder.DecodeInt8() //nolint:wrapcheck +} + +func decodeInt16(decoder *msgpack.Decoder) (int16, error) { + return decoder.DecodeInt16() //nolint:wrapcheck +} + +func decodeInt32(decoder *msgpack.Decoder) (int32, error) { + return decoder.DecodeInt32() //nolint:wrapcheck +} + +func decodeInt64(decoder *msgpack.Decoder) (int64, error) { + return decoder.DecodeInt64() //nolint:wrapcheck +} + +func decodeUint(decoder *msgpack.Decoder) (uint, error) { + return decoder.DecodeUint() //nolint:wrapcheck +} + +func decodeUint8(decoder *msgpack.Decoder) (uint8, error) { + return decoder.DecodeUint8() //nolint:wrapcheck +} + +func decodeUint16(decoder *msgpack.Decoder) (uint16, error) { + return decoder.DecodeUint16() //nolint:wrapcheck +} + +func decodeUint32(decoder *msgpack.Decoder) (uint32, error) { + return decoder.DecodeUint32() //nolint:wrapcheck +} + +func decodeUint64(decoder *msgpack.Decoder) (uint64, error) { + return decoder.DecodeUint64() //nolint:wrapcheck +} + +func encodeInt(encoder *msgpack.Encoder, val int) error { + return encoder.EncodeInt(int64(val)) //nolint:wrapcheck +} + +func encodeInt8(encoder *msgpack.Encoder, val int8) error { + return encoder.EncodeInt8(val) //nolint:wrapcheck +} + +func encodeInt16(encoder *msgpack.Encoder, val int16) error { + return encoder.EncodeInt16(val) //nolint:wrapcheck +} + +func encodeInt32(encoder *msgpack.Encoder, val int32) error { + return encoder.EncodeInt32(val) //nolint:wrapcheck +} + +func encodeInt64(encoder *msgpack.Encoder, val int64) error { + return encoder.EncodeInt64(val) //nolint:wrapcheck +} + +func encodeUint(encoder *msgpack.Encoder, val uint) error { + return encoder.EncodeUint(uint64(val)) //nolint:wrapcheck +} + +func encodeUint8(encoder *msgpack.Encoder, val uint8) error { + return encoder.EncodeUint8(val) //nolint:wrapcheck +} + +func encodeUint16(encoder *msgpack.Encoder, val uint16) error { + return encoder.EncodeUint16(val) //nolint:wrapcheck +} + +func encodeUint32(encoder *msgpack.Encoder, val uint32) error { + return encoder.EncodeUint32(val) //nolint:wrapcheck +} + +func encodeUint64(encoder *msgpack.Encoder, val uint64) error { + return encoder.EncodeUint64(val) //nolint:wrapcheck +} + +func checkFloat(code byte) bool { + return checkNumber(code) || code == msgpcode.Float || code == msgpcode.Double +} + +func decodeFloat32(decoder *msgpack.Decoder) (float32, error) { + return decoder.DecodeFloat32() //nolint:wrapcheck +} + +func encodeFloat32(encoder *msgpack.Encoder, val float32) error { + return encoder.EncodeFloat32(val) //nolint:wrapcheck +} + +func decodeFloat64(decoder *msgpack.Decoder) (float64, error) { + return decoder.DecodeFloat64() //nolint:wrapcheck +} + +func encodeFloat64(encoder *msgpack.Encoder, val float64) error { + return encoder.EncodeFloat64(val) //nolint:wrapcheck +} + +func checkString(code byte) bool { + return msgpcode.IsBin(code) || msgpcode.IsString(code) +} + +func decodeString(decoder *msgpack.Decoder) (string, error) { + return decoder.DecodeString() //nolint:wrapcheck +} + +func encodeString(encoder *msgpack.Encoder, val string) error { + return encoder.EncodeString(val) //nolint:wrapcheck +} + +func checkBytes(code byte) bool { + return msgpcode.IsBin(code) || msgpcode.IsString(code) +} + +func decodeBytes(decoder *msgpack.Decoder) ([]byte, error) { + return decoder.DecodeBytes() //nolint:wrapcheck +} + +func encodeBytes(encoder *msgpack.Encoder, b []byte) error { + return encoder.EncodeBytes(b) //nolint:wrapcheck +} + +func checkBool(code byte) bool { + return code == msgpcode.True || code == msgpcode.False +} + +func decodeBool(decoder *msgpack.Decoder) (bool, error) { + return decoder.DecodeBool() //nolint:wrapcheck +} + +func encodeBool(encoder *msgpack.Encoder, b bool) error { + return encoder.EncodeBool(b) //nolint:wrapcheck +} + +func decodeByte(decoder *msgpack.Decoder) (byte, error) { + return decoder.DecodeUint8() //nolint:wrapcheck +} + +func encodeByte(encoder *msgpack.Encoder, b byte) error { + return encoder.EncodeUint8(b) //nolint:wrapcheck +} diff --git a/string_gen.go b/string_gen.go new file mode 100644 index 0000000..2e50eb0 --- /dev/null +++ b/string_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// String represents an optional value of type string. +// It can either hold a valid string (IsSome == true) or be empty (IsZero == true). +type String struct { + value string + exists bool +} + +// SomeString creates an optional String with the given string value. +// The returned String will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeString("hello") +// if o.IsSome() { +// v := o.Unwrap() // v == "hello" +// } +func SomeString(value string) String { + return String{ + value: value, + exists: true, + } +} + +// NoneString creates an empty optional String value. +// The returned String will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneString() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneString() String { + return String{ + exists: false, + value: zero[string](), + } +} + +// IsSome returns true if the String contains a value. +// This indicates the value is explicitly set (not None). +func (o String) IsSome() bool { + return o.exists +} + +// IsZero returns true if the String does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o String) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o String) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of string, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o String) Get() (string, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o String) MustGet() string { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for string. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o String) Unwrap() string { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneString() +// v := o.UnwrapOr(someDefaultString) +func (o String) UnwrapOr(defaultValue string) string { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneString() +// v := o.UnwrapOrElse(func() string { return computeDefault() }) +func (o String) UnwrapOrElse(defaultValue func() string) string { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the String value using MessagePack format. +// - If the value is present, it is encoded as string. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o String) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("String", encodeString(encoder, o.value)) + } + + return newEncodeError("String", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a String value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneString) +// - string: interpreted as a present value (SomeString) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on string: exists = true, value = decoded value +func (o *String) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("String", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("String", decoder.Skip()) + case checkString(code): + o.value, err = decodeString(decoder) + if err != nil { + return newDecodeError("String", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("String", code) + } +} diff --git a/string_gen_test.go b/string_gen_test.go new file mode 100644 index 0000000..af660c6 --- /dev/null +++ b/string_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestString_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someString := option.SomeString("hello") + assert.True(t, someString.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyString := option.NoneString() + assert.False(t, emptyString.IsSome()) + }) +} + +func TestString_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someString := option.SomeString("hello") + assert.False(t, someString.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyString := option.NoneString() + assert.True(t, emptyString.IsZero()) + }) +} + +func TestString_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someString := option.SomeString("hello") + assert.False(t, someString.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyString := option.NoneString() + assert.True(t, emptyString.IsNil()) + }) +} + +func TestString_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someString := option.SomeString("hello") + val, ok := someString.Get() + require.True(t, ok) + assert.EqualValues(t, "hello", val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyString := option.NoneString() + _, ok := emptyString.Get() + require.False(t, ok) + }) +} + +func TestString_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someString := option.SomeString("hello") + assert.EqualValues(t, "hello", someString.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyString := option.NoneString() + assert.Panics(t, func() { + emptyString.MustGet() + }) + }) +} + +func TestString_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someString := option.SomeString("hello") + assert.EqualValues(t, "hello", someString.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyString := option.NoneString() + assert.NotPanics(t, func() { + emptyString.Unwrap() + }) + }) +} + +func TestString_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someString := option.SomeString("hello") + assert.EqualValues(t, "hello", someString.UnwrapOr("henlo")) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyString := option.NoneString() + assert.EqualValues(t, "henlo", emptyString.UnwrapOr("henlo")) + }) +} + +func TestString_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someString := option.SomeString("hello") + assert.EqualValues(t, "hello", someString.UnwrapOrElse(func() string { + return "henlo" + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyString := option.NoneString() + assert.EqualValues(t, "henlo", emptyString.UnwrapOrElse(func() string { + return "henlo" + })) + }) +} + +func TestString_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someString := option.SomeString("hello") + err := someString.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.String + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, "hello", unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyString := option.NoneString() + err := emptyString.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.String + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/uint16_gen.go b/uint16_gen.go new file mode 100644 index 0000000..30048a6 --- /dev/null +++ b/uint16_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Uint16 represents an optional value of type uint16. +// It can either hold a valid uint16 (IsSome == true) or be empty (IsZero == true). +type Uint16 struct { + value uint16 + exists bool +} + +// SomeUint16 creates an optional Uint16 with the given uint16 value. +// The returned Uint16 will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeUint16(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeUint16(value uint16) Uint16 { + return Uint16{ + value: value, + exists: true, + } +} + +// NoneUint16 creates an empty optional Uint16 value. +// The returned Uint16 will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneUint16() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneUint16() Uint16 { + return Uint16{ + exists: false, + value: zero[uint16](), + } +} + +// IsSome returns true if the Uint16 contains a value. +// This indicates the value is explicitly set (not None). +func (o Uint16) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Uint16 does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Uint16) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Uint16) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of uint16, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Uint16) Get() (uint16, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Uint16) MustGet() uint16 { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for uint16. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Uint16) Unwrap() uint16 { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneUint16() +// v := o.UnwrapOr(someDefaultUint16) +func (o Uint16) UnwrapOr(defaultValue uint16) uint16 { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneUint16() +// v := o.UnwrapOrElse(func() uint16 { return computeDefault() }) +func (o Uint16) UnwrapOrElse(defaultValue func() uint16) uint16 { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Uint16 value using MessagePack format. +// - If the value is present, it is encoded as uint16. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Uint16) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Uint16", encodeUint16(encoder, o.value)) + } + + return newEncodeError("Uint16", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Uint16 value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneUint16) +// - uint16: interpreted as a present value (SomeUint16) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on uint16: exists = true, value = decoded value +func (o *Uint16) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Uint16", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Uint16", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeUint16(decoder) + if err != nil { + return newDecodeError("Uint16", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Uint16", code) + } +} diff --git a/uint16_gen_test.go b/uint16_gen_test.go new file mode 100644 index 0000000..74115ee --- /dev/null +++ b/uint16_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestUint16_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint16 := option.SomeUint16(12) + assert.True(t, someUint16.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint16 := option.NoneUint16() + assert.False(t, emptyUint16.IsSome()) + }) +} + +func TestUint16_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint16 := option.SomeUint16(12) + assert.False(t, someUint16.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint16 := option.NoneUint16() + assert.True(t, emptyUint16.IsZero()) + }) +} + +func TestUint16_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint16 := option.SomeUint16(12) + assert.False(t, someUint16.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint16 := option.NoneUint16() + assert.True(t, emptyUint16.IsNil()) + }) +} + +func TestUint16_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint16 := option.SomeUint16(12) + val, ok := someUint16.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint16 := option.NoneUint16() + _, ok := emptyUint16.Get() + require.False(t, ok) + }) +} + +func TestUint16_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint16 := option.SomeUint16(12) + assert.EqualValues(t, 12, someUint16.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint16 := option.NoneUint16() + assert.Panics(t, func() { + emptyUint16.MustGet() + }) + }) +} + +func TestUint16_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint16 := option.SomeUint16(12) + assert.EqualValues(t, 12, someUint16.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint16 := option.NoneUint16() + assert.NotPanics(t, func() { + emptyUint16.Unwrap() + }) + }) +} + +func TestUint16_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint16 := option.SomeUint16(12) + assert.EqualValues(t, 12, someUint16.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint16 := option.NoneUint16() + assert.EqualValues(t, 13, emptyUint16.UnwrapOr(13)) + }) +} + +func TestUint16_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint16 := option.SomeUint16(12) + assert.EqualValues(t, 12, someUint16.UnwrapOrElse(func() uint16 { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint16 := option.NoneUint16() + assert.EqualValues(t, 13, emptyUint16.UnwrapOrElse(func() uint16 { + return 13 + })) + }) +} + +func TestUint16_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someUint16 := option.SomeUint16(12) + err := someUint16.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Uint16 + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyUint16 := option.NoneUint16() + err := emptyUint16.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Uint16 + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/uint32_gen.go b/uint32_gen.go new file mode 100644 index 0000000..85c7626 --- /dev/null +++ b/uint32_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Uint32 represents an optional value of type uint32. +// It can either hold a valid uint32 (IsSome == true) or be empty (IsZero == true). +type Uint32 struct { + value uint32 + exists bool +} + +// SomeUint32 creates an optional Uint32 with the given uint32 value. +// The returned Uint32 will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeUint32(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeUint32(value uint32) Uint32 { + return Uint32{ + value: value, + exists: true, + } +} + +// NoneUint32 creates an empty optional Uint32 value. +// The returned Uint32 will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneUint32() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneUint32() Uint32 { + return Uint32{ + exists: false, + value: zero[uint32](), + } +} + +// IsSome returns true if the Uint32 contains a value. +// This indicates the value is explicitly set (not None). +func (o Uint32) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Uint32 does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Uint32) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Uint32) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of uint32, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Uint32) Get() (uint32, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Uint32) MustGet() uint32 { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for uint32. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Uint32) Unwrap() uint32 { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneUint32() +// v := o.UnwrapOr(someDefaultUint32) +func (o Uint32) UnwrapOr(defaultValue uint32) uint32 { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneUint32() +// v := o.UnwrapOrElse(func() uint32 { return computeDefault() }) +func (o Uint32) UnwrapOrElse(defaultValue func() uint32) uint32 { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Uint32 value using MessagePack format. +// - If the value is present, it is encoded as uint32. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Uint32) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Uint32", encodeUint32(encoder, o.value)) + } + + return newEncodeError("Uint32", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Uint32 value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneUint32) +// - uint32: interpreted as a present value (SomeUint32) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on uint32: exists = true, value = decoded value +func (o *Uint32) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Uint32", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Uint32", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeUint32(decoder) + if err != nil { + return newDecodeError("Uint32", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Uint32", code) + } +} diff --git a/uint32_gen_test.go b/uint32_gen_test.go new file mode 100644 index 0000000..1acb84d --- /dev/null +++ b/uint32_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestUint32_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint32 := option.SomeUint32(12) + assert.True(t, someUint32.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint32 := option.NoneUint32() + assert.False(t, emptyUint32.IsSome()) + }) +} + +func TestUint32_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint32 := option.SomeUint32(12) + assert.False(t, someUint32.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint32 := option.NoneUint32() + assert.True(t, emptyUint32.IsZero()) + }) +} + +func TestUint32_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint32 := option.SomeUint32(12) + assert.False(t, someUint32.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint32 := option.NoneUint32() + assert.True(t, emptyUint32.IsNil()) + }) +} + +func TestUint32_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint32 := option.SomeUint32(12) + val, ok := someUint32.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint32 := option.NoneUint32() + _, ok := emptyUint32.Get() + require.False(t, ok) + }) +} + +func TestUint32_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint32 := option.SomeUint32(12) + assert.EqualValues(t, 12, someUint32.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint32 := option.NoneUint32() + assert.Panics(t, func() { + emptyUint32.MustGet() + }) + }) +} + +func TestUint32_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint32 := option.SomeUint32(12) + assert.EqualValues(t, 12, someUint32.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint32 := option.NoneUint32() + assert.NotPanics(t, func() { + emptyUint32.Unwrap() + }) + }) +} + +func TestUint32_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint32 := option.SomeUint32(12) + assert.EqualValues(t, 12, someUint32.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint32 := option.NoneUint32() + assert.EqualValues(t, 13, emptyUint32.UnwrapOr(13)) + }) +} + +func TestUint32_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint32 := option.SomeUint32(12) + assert.EqualValues(t, 12, someUint32.UnwrapOrElse(func() uint32 { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint32 := option.NoneUint32() + assert.EqualValues(t, 13, emptyUint32.UnwrapOrElse(func() uint32 { + return 13 + })) + }) +} + +func TestUint32_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someUint32 := option.SomeUint32(12) + err := someUint32.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Uint32 + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyUint32 := option.NoneUint32() + err := emptyUint32.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Uint32 + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/uint64_gen.go b/uint64_gen.go new file mode 100644 index 0000000..093ff6d --- /dev/null +++ b/uint64_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Uint64 represents an optional value of type uint64. +// It can either hold a valid uint64 (IsSome == true) or be empty (IsZero == true). +type Uint64 struct { + value uint64 + exists bool +} + +// SomeUint64 creates an optional Uint64 with the given uint64 value. +// The returned Uint64 will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeUint64(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeUint64(value uint64) Uint64 { + return Uint64{ + value: value, + exists: true, + } +} + +// NoneUint64 creates an empty optional Uint64 value. +// The returned Uint64 will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneUint64() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneUint64() Uint64 { + return Uint64{ + exists: false, + value: zero[uint64](), + } +} + +// IsSome returns true if the Uint64 contains a value. +// This indicates the value is explicitly set (not None). +func (o Uint64) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Uint64 does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Uint64) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Uint64) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of uint64, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Uint64) Get() (uint64, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Uint64) MustGet() uint64 { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for uint64. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Uint64) Unwrap() uint64 { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneUint64() +// v := o.UnwrapOr(someDefaultUint64) +func (o Uint64) UnwrapOr(defaultValue uint64) uint64 { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneUint64() +// v := o.UnwrapOrElse(func() uint64 { return computeDefault() }) +func (o Uint64) UnwrapOrElse(defaultValue func() uint64) uint64 { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Uint64 value using MessagePack format. +// - If the value is present, it is encoded as uint64. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Uint64) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Uint64", encodeUint64(encoder, o.value)) + } + + return newEncodeError("Uint64", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Uint64 value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneUint64) +// - uint64: interpreted as a present value (SomeUint64) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on uint64: exists = true, value = decoded value +func (o *Uint64) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Uint64", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Uint64", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeUint64(decoder) + if err != nil { + return newDecodeError("Uint64", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Uint64", code) + } +} diff --git a/uint64_gen_test.go b/uint64_gen_test.go new file mode 100644 index 0000000..484c842 --- /dev/null +++ b/uint64_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestUint64_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint64 := option.SomeUint64(12) + assert.True(t, someUint64.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint64 := option.NoneUint64() + assert.False(t, emptyUint64.IsSome()) + }) +} + +func TestUint64_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint64 := option.SomeUint64(12) + assert.False(t, someUint64.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint64 := option.NoneUint64() + assert.True(t, emptyUint64.IsZero()) + }) +} + +func TestUint64_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint64 := option.SomeUint64(12) + assert.False(t, someUint64.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint64 := option.NoneUint64() + assert.True(t, emptyUint64.IsNil()) + }) +} + +func TestUint64_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint64 := option.SomeUint64(12) + val, ok := someUint64.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint64 := option.NoneUint64() + _, ok := emptyUint64.Get() + require.False(t, ok) + }) +} + +func TestUint64_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint64 := option.SomeUint64(12) + assert.EqualValues(t, 12, someUint64.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint64 := option.NoneUint64() + assert.Panics(t, func() { + emptyUint64.MustGet() + }) + }) +} + +func TestUint64_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint64 := option.SomeUint64(12) + assert.EqualValues(t, 12, someUint64.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint64 := option.NoneUint64() + assert.NotPanics(t, func() { + emptyUint64.Unwrap() + }) + }) +} + +func TestUint64_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint64 := option.SomeUint64(12) + assert.EqualValues(t, 12, someUint64.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint64 := option.NoneUint64() + assert.EqualValues(t, 13, emptyUint64.UnwrapOr(13)) + }) +} + +func TestUint64_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint64 := option.SomeUint64(12) + assert.EqualValues(t, 12, someUint64.UnwrapOrElse(func() uint64 { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint64 := option.NoneUint64() + assert.EqualValues(t, 13, emptyUint64.UnwrapOrElse(func() uint64 { + return 13 + })) + }) +} + +func TestUint64_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someUint64 := option.SomeUint64(12) + err := someUint64.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Uint64 + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyUint64 := option.NoneUint64() + err := emptyUint64.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Uint64 + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/uint8_gen.go b/uint8_gen.go new file mode 100644 index 0000000..2d5882c --- /dev/null +++ b/uint8_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Uint8 represents an optional value of type uint8. +// It can either hold a valid uint8 (IsSome == true) or be empty (IsZero == true). +type Uint8 struct { + value uint8 + exists bool +} + +// SomeUint8 creates an optional Uint8 with the given uint8 value. +// The returned Uint8 will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeUint8(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeUint8(value uint8) Uint8 { + return Uint8{ + value: value, + exists: true, + } +} + +// NoneUint8 creates an empty optional Uint8 value. +// The returned Uint8 will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneUint8() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneUint8() Uint8 { + return Uint8{ + exists: false, + value: zero[uint8](), + } +} + +// IsSome returns true if the Uint8 contains a value. +// This indicates the value is explicitly set (not None). +func (o Uint8) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Uint8 does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Uint8) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Uint8) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of uint8, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Uint8) Get() (uint8, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Uint8) MustGet() uint8 { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for uint8. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Uint8) Unwrap() uint8 { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneUint8() +// v := o.UnwrapOr(someDefaultUint8) +func (o Uint8) UnwrapOr(defaultValue uint8) uint8 { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneUint8() +// v := o.UnwrapOrElse(func() uint8 { return computeDefault() }) +func (o Uint8) UnwrapOrElse(defaultValue func() uint8) uint8 { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Uint8 value using MessagePack format. +// - If the value is present, it is encoded as uint8. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Uint8) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Uint8", encodeUint8(encoder, o.value)) + } + + return newEncodeError("Uint8", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Uint8 value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneUint8) +// - uint8: interpreted as a present value (SomeUint8) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on uint8: exists = true, value = decoded value +func (o *Uint8) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Uint8", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Uint8", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeUint8(decoder) + if err != nil { + return newDecodeError("Uint8", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Uint8", code) + } +} diff --git a/uint8_gen_test.go b/uint8_gen_test.go new file mode 100644 index 0000000..c909ac1 --- /dev/null +++ b/uint8_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestUint8_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint8 := option.SomeUint8(12) + assert.True(t, someUint8.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint8 := option.NoneUint8() + assert.False(t, emptyUint8.IsSome()) + }) +} + +func TestUint8_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint8 := option.SomeUint8(12) + assert.False(t, someUint8.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint8 := option.NoneUint8() + assert.True(t, emptyUint8.IsZero()) + }) +} + +func TestUint8_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint8 := option.SomeUint8(12) + assert.False(t, someUint8.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint8 := option.NoneUint8() + assert.True(t, emptyUint8.IsNil()) + }) +} + +func TestUint8_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint8 := option.SomeUint8(12) + val, ok := someUint8.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint8 := option.NoneUint8() + _, ok := emptyUint8.Get() + require.False(t, ok) + }) +} + +func TestUint8_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint8 := option.SomeUint8(12) + assert.EqualValues(t, 12, someUint8.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint8 := option.NoneUint8() + assert.Panics(t, func() { + emptyUint8.MustGet() + }) + }) +} + +func TestUint8_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint8 := option.SomeUint8(12) + assert.EqualValues(t, 12, someUint8.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint8 := option.NoneUint8() + assert.NotPanics(t, func() { + emptyUint8.Unwrap() + }) + }) +} + +func TestUint8_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint8 := option.SomeUint8(12) + assert.EqualValues(t, 12, someUint8.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint8 := option.NoneUint8() + assert.EqualValues(t, 13, emptyUint8.UnwrapOr(13)) + }) +} + +func TestUint8_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint8 := option.SomeUint8(12) + assert.EqualValues(t, 12, someUint8.UnwrapOrElse(func() uint8 { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint8 := option.NoneUint8() + assert.EqualValues(t, 13, emptyUint8.UnwrapOrElse(func() uint8 { + return 13 + })) + }) +} + +func TestUint8_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someUint8 := option.SomeUint8(12) + err := someUint8.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Uint8 + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyUint8 := option.NoneUint8() + err := emptyUint8.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Uint8 + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/uint_gen.go b/uint_gen.go new file mode 100644 index 0000000..adcd4c5 --- /dev/null +++ b/uint_gen.go @@ -0,0 +1,181 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option + +import ( + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +// Uint represents an optional value of type uint. +// It can either hold a valid uint (IsSome == true) or be empty (IsZero == true). +type Uint struct { + value uint + exists bool +} + +// SomeUint creates an optional Uint with the given uint value. +// The returned Uint will have IsSome() == true and IsZero() == false. +// +// Example: +// +// o := SomeUint(12) +// if o.IsSome() { +// v := o.Unwrap() // v == 12 +// } +func SomeUint(value uint) Uint { + return Uint{ + value: value, + exists: true, + } +} + +// NoneUint creates an empty optional Uint value. +// The returned Uint will have IsSome() == false and IsZero() == true. +// +// Example: +// +// o := NoneUint() +// if o.IsZero() { +// fmt.Println("value is absent") +// } +func NoneUint() Uint { + return Uint{ + exists: false, + value: zero[uint](), + } +} + +// IsSome returns true if the Uint contains a value. +// This indicates the value is explicitly set (not None). +func (o Uint) IsSome() bool { + return o.exists +} + +// IsZero returns true if the Uint does not contain a value. +// Equivalent to !IsSome(). Useful for consistency with types where +// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished. +func (o Uint) IsZero() bool { + return !o.exists +} + +// IsNil is an alias for IsZero. +// +// This method is provided for compatibility with the msgpack Encoder interface. +func (o Uint) IsNil() bool { + return o.IsZero() +} + +// Get returns the stored value and a boolean flag indicating its presence. +// If the value is present, returns (value, true). +// If the value is absent, returns (zero value of uint, false). +// +// Recommended usage: +// +// if value, ok := o.Get(); ok { +// // use value +// } +func (o Uint) Get() (uint, bool) { + return o.value, o.exists +} + +// MustGet returns the stored value if it is present. +// Panics if the value is absent (i.e., IsZero() == true). +// +// Use with caution — only when you are certain the value exists. +// +// Panics with: "optional value is not set" if no value is set. +func (o Uint) MustGet() uint { + if !o.exists { + panic("optional value is not set") + } + + return o.value +} + +// Unwrap returns the stored value regardless of presence. +// If no value is set, returns the zero value for uint. +// +// Warning: Does not check presence. Use IsSome() before calling if you need +// to distinguish between absent value and explicit zero value. +func (o Uint) Unwrap() uint { + return o.value +} + +// UnwrapOr returns the stored value if present. +// Otherwise, returns the provided default value. +// +// Example: +// +// o := NoneUint() +// v := o.UnwrapOr(someDefaultUint) +func (o Uint) UnwrapOr(defaultValue uint) uint { + if o.exists { + return o.value + } + + return defaultValue +} + +// UnwrapOrElse returns the stored value if present. +// Otherwise, calls the provided function and returns its result. +// Useful when the default value requires computation or side effects. +// +// Example: +// +// o := NoneUint() +// v := o.UnwrapOrElse(func() uint { return computeDefault() }) +func (o Uint) UnwrapOrElse(defaultValue func() uint) uint { + if o.exists { + return o.value + } + + return defaultValue() +} + +// EncodeMsgpack encodes the Uint value using MessagePack format. +// - If the value is present, it is encoded as uint. +// - If the value is absent (None), it is encoded as nil. +// +// Returns an error if encoding fails. +func (o Uint) EncodeMsgpack(encoder *msgpack.Encoder) error { + if o.exists { + return newEncodeError("Uint", encodeUint(encoder, o.value)) + } + + return newEncodeError("Uint", encoder.EncodeNil()) +} + +// DecodeMsgpack decodes a Uint value from MessagePack format. +// Supports two input types: +// - nil: interpreted as no value (NoneUint) +// - uint: interpreted as a present value (SomeUint) +// +// Returns an error if the input type is unsupported or decoding fails. +// +// After successful decoding: +// - on nil: exists = false, value = default zero value +// - on uint: exists = true, value = decoded value +func (o *Uint) DecodeMsgpack(decoder *msgpack.Decoder) error { + code, err := decoder.PeekCode() + if err != nil { + return newDecodeError("Uint", err) + } + + switch { + case code == msgpcode.Nil: + o.exists = false + + return newDecodeError("Uint", decoder.Skip()) + case checkNumber(code): + o.value, err = decodeUint(decoder) + if err != nil { + return newDecodeError("Uint", err) + } + o.exists = true + + return err + default: + return newDecodeWithCodeError("Uint", code) + } +} diff --git a/uint_gen_test.go b/uint_gen_test.go new file mode 100644 index 0000000..0324e67 --- /dev/null +++ b/uint_gen_test.go @@ -0,0 +1,211 @@ +// Code generated by github.com/tarantool/go-option; DO NOT EDIT. + +package option_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-option" +) + +func TestUint_IsSome(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint := option.SomeUint(12) + assert.True(t, someUint.IsSome()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint := option.NoneUint() + assert.False(t, emptyUint.IsSome()) + }) +} + +func TestUint_IsZero(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint := option.SomeUint(12) + assert.False(t, someUint.IsZero()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint := option.NoneUint() + assert.True(t, emptyUint.IsZero()) + }) +} + +func TestUint_IsNil(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint := option.SomeUint(12) + assert.False(t, someUint.IsNil()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint := option.NoneUint() + assert.True(t, emptyUint.IsNil()) + }) +} + +func TestUint_Get(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint := option.SomeUint(12) + val, ok := someUint.Get() + require.True(t, ok) + assert.EqualValues(t, 12, val) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint := option.NoneUint() + _, ok := emptyUint.Get() + require.False(t, ok) + }) +} + +func TestUint_MustGet(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint := option.SomeUint(12) + assert.EqualValues(t, 12, someUint.MustGet()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint := option.NoneUint() + assert.Panics(t, func() { + emptyUint.MustGet() + }) + }) +} + +func TestUint_Unwrap(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint := option.SomeUint(12) + assert.EqualValues(t, 12, someUint.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint := option.NoneUint() + assert.NotPanics(t, func() { + emptyUint.Unwrap() + }) + }) +} + +func TestUint_UnwrapOr(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint := option.SomeUint(12) + assert.EqualValues(t, 12, someUint.UnwrapOr(13)) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint := option.NoneUint() + assert.EqualValues(t, 13, emptyUint.UnwrapOr(13)) + }) +} + +func TestUint_UnwrapOrElse(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + someUint := option.SomeUint(12) + assert.EqualValues(t, 12, someUint.UnwrapOrElse(func() uint { + return 13 + })) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + emptyUint := option.NoneUint() + assert.EqualValues(t, 13, emptyUint.UnwrapOrElse(func() uint { + return 13 + })) + }) +} + +func TestUint_EncodeDecodeMsgpack(t *testing.T) { + t.Parallel() + + t.Run("some", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + someUint := option.SomeUint(12) + err := someUint.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Uint + err = unmarshaled.DecodeMsgpack(dec) + require.NoError(t, err) + assert.True(t, unmarshaled.IsSome()) + assert.EqualValues(t, 12, unmarshaled.Unwrap()) + }) + + t.Run("none", func(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + + enc := msgpack.NewEncoder(&buf) + dec := msgpack.NewDecoder(&buf) + + emptyUint := option.NoneUint() + err := emptyUint.EncodeMsgpack(enc) + require.NoError(t, err) + + var unmarshaled option.Uint + err = unmarshaled.DecodeMsgpack(dec) + + require.NoError(t, err) + assert.False(t, unmarshaled.IsSome()) + }) +} diff --git a/zero.go b/zero.go new file mode 100644 index 0000000..29a4a2b --- /dev/null +++ b/zero.go @@ -0,0 +1,9 @@ +package option + +// zero is a helper function that returns the zero value of a type. +// It simplifies returning abstract zero values in template-generated code. +func zero[T any]() T { + var zero T + + return zero +}