diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-22 17:35:49 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-22 17:35:49 -0600 |
| commit | 20ef0d92694465ac86b550df139e8366a0a2b4fa (patch) | |
| tree | 3f14589e1ce6eb9306a3af31c3a1f9e1af5ed637 /vendor/github.com/ccoveille | |
| parent | 44e0d272c040cdc53a98b9f1dc58ae7da67752e6 (diff) | |
feat: connect to spicedb
Diffstat (limited to 'vendor/github.com/ccoveille')
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/.editorconfig | 25 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/.gitattributes | 4 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/.golangci.yml | 178 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/.ls-lint.yml | 17 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/.markdownlint.yml | 14 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/.yamllint.yml | 14 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/LICENSE | 21 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/README.md | 165 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/asserters.go | 78 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/conversion.go | 293 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/doc.go | 5 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/errors.go | 63 | ||||
| -rw-r--r-- | vendor/github.com/ccoveille/go-safecast/types.go | 36 |
13 files changed, 913 insertions, 0 deletions
diff --git a/vendor/github.com/ccoveille/go-safecast/.editorconfig b/vendor/github.com/ccoveille/go-safecast/.editorconfig new file mode 100644 index 0000000..f32456b --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.editorconfig @@ -0,0 +1,25 @@ +# More information about this file +# https://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf + +[*.md] +tab_width = 2 +trim_trailing_whitespace = false # can be used for indentation with double spaces +# Not enforced, markdownlint already reports it +#insert_final_newline = true + +[*.{yml,yaml}] +tab_width = 2 +trim_trailing_whitespace = true +# Not enforced, markdownlint already reports it +#insert_final_newline = true + +[*.toml,.ini] +tab_width = 2 +trim_trailing_whitespace = true +insert_final_newline = true + diff --git a/vendor/github.com/ccoveille/go-safecast/.gitattributes b/vendor/github.com/ccoveille/go-safecast/.gitattributes new file mode 100644 index 0000000..a681ce3 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.gitattributes @@ -0,0 +1,4 @@ +# ensure that line endings for Windows builds are properly formatted +# see https://github.com/golangci/golangci-lint-action?tab=readme-ov-file#how-to-use +# at "Multiple OS Example" section +*.go text eol=lf diff --git a/vendor/github.com/ccoveille/go-safecast/.golangci.yml b/vendor/github.com/ccoveille/go-safecast/.golangci.yml new file mode 100644 index 0000000..680f6b6 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.golangci.yml @@ -0,0 +1,178 @@ +--- +# golangci-lint configuration file made by @ccoVeille +# Source: https://github.com/ccoVeille/golangci-lint-config-examples/ +# Author: @ccoVeille +# License: MIT +# Variant: 03-safe +# +issues: + include: + # restore some rules ignored by default by golangci-lint + - EXC0011 # stylecheck:ST1000|ST1020|ST1021|ST1022 about exported functions, structs, and methods should have comments + - EXC0012 # revive:exported about exported functions, structs, and methods should have comment + - EXC0013 # revive:exported about package comment should contain the name of the package + - EXC0014 # revive:exported about comment should contain the name of the function, type, variable, constant, or method. + - EXC0015 # revive:exported about package should have a comment + +linters: + # some linters are enabled by default + # https://golangci-lint.run/usage/linters/ + # + # enable some extra linters + enable: + # Errcheck is a program for checking for unchecked errors in Go code. + - errcheck + + # Linter for Go source code that specializes in simplifying code. + - gosimple + + # Vet examines Go source code and reports suspicious constructs. + - govet + + # gosec is a security linter for Go code. + - gosec + + # Detects when assignments to existing variables are not used. + - ineffassign + + # It's a set of rules from staticcheck. See https://staticcheck.io/ + - staticcheck + + # stylecheck is a linter that applies the official Go style guide. + - stylecheck + + # Fast, configurable, extensible, flexible, and beautiful linter for Go. + # Drop-in replacement of golint. + - revive + + # check imports order and makes it always deterministic. + - gci + + # make sure to use t.Helper() when needed + - thelper + + # checks if package imports are in a list of acceptable packages. + - depguard + + # mirror suggests rewrites to avoid unnecessary []byte/string conversion + - mirror + + # detect the possibility to use variables/constants from the Go standard library. + - usestdlibvars + + # Finds commonly misspelled English words. + - misspell + + # Checks for duplicate words in the source code. + - dupword + +linters-settings: + gci: # define the section orders for imports + sections: + # Standard section: captures all standard packages. + - standard + # Default section: catchall that is not standard or custom + - default + # linters that related to local tool, so they should be separated + - localmodule + + depguard: + rules: + # enforce the library has no dependencies except the standard library + code: + files: + - "!$test" # depguard alias for all files except the test ones + allow: + - "$gostd" # depguard alias for all standard libraries + # enforce the test files have no dependencies except the standard library and the library itself + test: + files: + - "$test" # depguard alias for all the test files + allow: + - "$gostd" # depguard alias for all standard libraries + - "github.com/ccoveille/go-safecast" + + revive: + rules: + # these are the default revive rules + # you can remove the whole "rules" node if you want + # BUT + # ! /!\ they all need to be present when you want to add more rules than the default ones + # otherwise, you won't have the default rules, but only the ones you define in the "rules" node + + # Blank import should be only in a main or test package, or have a comment justifying it. + - name: blank-imports + + # context.Context() should be the first parameter of a function when provided as argument. + - name: context-as-argument + arguments: + - allowTypesBefore: "*testing.T" + + # Basic types should not be used as a key in `context.WithValue` + - name: context-keys-type + + # Importing with `.` makes the programs much harder to understand + - name: dot-imports + + # Empty blocks make code less readable and could be a symptom of a bug or unfinished refactoring. + - name: empty-block + + # for better readability, variables of type `error` must be named with the prefix `err`. + - name: error-naming + + # for better readability, the errors should be last in the list of returned values by a function. + - name: error-return + + # for better readability, error messages should not be capitalized or end with punctuation or a newline. + - name: error-strings + + # report when replacing `errors.New(fmt.Sprintf())` with `fmt.Errorf()` is possible + - name: errorf + + # exported functions, structs, and methods should have comments. + - name: exported + + # enforces conventions on source file names. + - name: filename-format + + # incrementing an integer variable by 1 is recommended to be done using the `++` operator + - name: increment-decrement + + # highlights redundant else-blocks that can be eliminated from the code + - name: indent-error-flow + + # This rule suggests a shorter way of writing ranges that do not use the second value. + - name: range + + # receiver names in a method should reflect the struct name (p for Person, for example) + - name: receiver-naming + + # redefining built in names (true, false, append, make) can lead to bugs very difficult to detect. + - name: redefines-builtin-id + + # redundant else-blocks that can be eliminated from the code. + - name: superfluous-else + + # prevent confusing name for variables when using `time` package + - name: time-naming + + # warns when an exported function or method returns a value of an un-exported type. + - name: unexported-return + + # spots and proposes to remove unreachable code. also helps to spot errors + - name: unreachable-code + + # Functions or methods with unused parameters can be a symptom of an unfinished refactoring or a bug. + - name: unused-parameter + + # report when a variable declaration can be simplified + - name: var-declaration + + # warns when initialism, variable or package naming conventions are not followed. + - name: var-naming + + misspell: + # Correct spellings using locale preferences for US or UK. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + # Default ("") is to use a neutral variety of English. + locale: US diff --git a/vendor/github.com/ccoveille/go-safecast/.ls-lint.yml b/vendor/github.com/ccoveille/go-safecast/.ls-lint.yml new file mode 100644 index 0000000..9e34b8a --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.ls-lint.yml @@ -0,0 +1,17 @@ +--- +# ls-lint configuration file. More information on the file format can be found on https://ls-lint.org/ +ls: + .md: screamingsnakecase # README.md or CODE_OF_CONDUCT.md + + # DEACTIVATED: Go files should are linted via golangci-lint revive filename-format rule + # .go: snakecase + +ignore: + # .git folder cannot be linted + - .git + # .github folder contains configuration files with specific name, and should not be linted + - .github + # dot files are usually configuration files with specific name + - .ls-lint.yml + - .markdownlint.yml + - .yamllint.yml diff --git a/vendor/github.com/ccoveille/go-safecast/.markdownlint.yml b/vendor/github.com/ccoveille/go-safecast/.markdownlint.yml new file mode 100644 index 0000000..fe687e0 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.markdownlint.yml @@ -0,0 +1,14 @@ +--- + +# Default state for all rules +default: true + +MD013: + # Overload the default value (120) + line_length: 200 + +MD033: + allowed_elements: + # <details><summary></summary></details> are useful for spoilers + - summary + - details diff --git a/vendor/github.com/ccoveille/go-safecast/.yamllint.yml b/vendor/github.com/ccoveille/go-safecast/.yamllint.yml new file mode 100644 index 0000000..f67627c --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.yamllint.yml @@ -0,0 +1,14 @@ +--- +extends: default +ignore: + # These files are imported by vale from an external repository + .vale/styles/RedHat/ +rules: + line-length: + max: 200 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true + truthy: + # the node "on:" present in each GitHub Actions workflow file + ignore: | + .github/ diff --git a/vendor/github.com/ccoveille/go-safecast/LICENSE b/vendor/github.com/ccoveille/go-safecast/LICENSE new file mode 100644 index 0000000..7f8b751 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 ccoVeille + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/ccoveille/go-safecast/README.md b/vendor/github.com/ccoveille/go-safecast/README.md new file mode 100644 index 0000000..171a5bb --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/README.md @@ -0,0 +1,165 @@ +# 🪄 go-safecast: safe numbers conversion + +[](https://goreportcard.com/report/github.com/ccoveille/go-safecast) +[](https://godoc.org/github.com/ccoVeille/go-safecast) +[](https://codecov.io/gh/ccoVeille/go-safecast) +[](https://codeclimate.com/github/ccoVeille/go-safecast) +[](https://github.com/search?q=%22%5C%22github.com%2Fccoveille%2Fgo-safecast%5C%22%22+language%3Ago++-is%3Afork+-is%3Aarchived+&type=code) + + +go-safecast solves the type conversion issues in Go + +In Go, integer type conversion can lead to a silent and unexpected behavior and errors if not handled carefully. + +This package helps to convert any number to another, and report an error when if there would be a [loss or overflow in the conversion](#conversion-issues) + +## Usage + +```go +package main + +import ( + "fmt" + "math" + + "github.com/ccoveille/go-safecast" +) + +func main() { + var a int + + a = 42 + b, err := safecast.ToUint8(a) // everything is fine + if err != nil { + fmt.Println(err) + } + fmt.Println(b) + // Output: 42 + + a = 255 + 1 + _, err = safecast.ToUint8(a) // 256 is greater than uint8 maximum value + if err != nil { + fmt.Println(err) + // Output: conversion issue: 256 (int) is greater than 255 (uint8): maximum value for this type exceeded + } + + a = -1 + _, err = safecast.ToUint8(a) // -1 cannot fit in uint8 + if err != nil { + fmt.Println(err) + // Output: conversion issue: -1 (int) is less than 0 (uint8): minimum value for this type exceeded + } + + str := "\x99" // ASCII code 153 for Trademark symbol + e := str[0] + _, err = safecast.ToInt8(e) + if err != nil { + fmt.Println(err) + // Output: conversion issue: 153 (uint8) is greater than 127 (int8): maximum value for this type exceeded + } +} +``` + +[Go Playground](https://go.dev/play/p/nelJshulOnj) + +## Conversion issues + +Issues can happen when converting between signed and unsigned integers, or when converting to a smaller integer type. + +```go +package main + +import "fmt" + +func main() { + var a int64 + a = 42 + b := uint8(a) + fmt.Println(b) // 42 + + a = 255 // this is the math.MaxUint8 + b = uint8(a) + fmt.Println(b) // 255 + + a = 255 + 1 + b = uint8(a) + fmt.Println(b) // 0 conversion overflow + + a = -1 + b = uint8(a) + fmt.Println(b) // 255 conversion overflow +} +``` + +[Go Playground](https://go.dev/play/p/DHfNUcZBvVn) + +So you need to adapt your code to write something like this. + +```go +package main + +import "fmt" + +func main() { + var a int64 + a = 42 + if a < 0 || a > math.MaxUint8 { + log.Println("overflow") // Output: overflow + } + fmt.Println(b) // 42 + + a = 255 // this is the math.MaxUint8 + b = uint8(a) + fmt.Println(b) // 255 + + a = 255 + 1 + b = uint8(a) + if a < 0 || a > math.MaxUint8 { + log.Println("overflow") // Output: overflow + } + fmt.Println(b) // Output: 0 + + a = -1 + b = uint8(a) + if a < 0 || a > math.MaxUint8 { + log.Println("overflow") // Output: overflow + } + fmt.Println(b) // Output:255 +} +``` + +[Go Playground](https://go.dev/play/p/qAHGyy4NCLP) + +`go-safecast` is there to avoid boilerplate copy pasta. + +## Motivation + +The gosec project raised this to my attention when the gosec [G115 rule was added](https://github.com/securego/gosec/pull/1149) + +> G115: Potential overflow when converting between integer types. + +This issue was way more complex than expected, and required multiple fixes. + +[CWE-190](https://cwe.mitre.org/data/definitions/190.html) explains in detail. + +But to sum it up, you can face: + +- infinite loop +- access to wrong resource by id +- grant access to someone who exhausted their quota + +The gosec G115 will now report issues in a lot of project. + +## Alternatives + +Some libraries existed, but they were not able to cover all the use cases. + +- [github.com/rung/go-safecast](https://github.com/rung/go-safecast): + Unmaintained, not architecture agnostic, do not support `uint` -> `int` conversion + +- [github.com/cybergarage/go-safecast](https://github.com/cybergarage/go-safecast) + Work with pointer like `json.Marshall` + +## Stargazers over time + +[](https://starchart.cc/ccoVeille/go-safecast) diff --git a/vendor/github.com/ccoveille/go-safecast/asserters.go b/vendor/github.com/ccoveille/go-safecast/asserters.go new file mode 100644 index 0000000..13cf3c3 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/asserters.go @@ -0,0 +1,78 @@ +package safecast + +import "math" + +func negative[T Number](t T) bool { + return t < 0 +} + +func sameSign[T1, T2 Number](a T1, b T2) bool { + return negative(a) == negative(b) +} + +func getUpperBoundary(value any) any { + var upper any = math.Inf(1) + switch value.(type) { + case int8: + upper = int8(math.MaxInt8) + case int16: + upper = int16(math.MaxInt16) + case int32: + upper = int32(math.MaxInt32) + case int64: + upper = int64(math.MaxInt64) + case int: + upper = int(math.MaxInt) + case uint8: + upper = uint8(math.MaxUint8) + case uint32: + upper = uint32(math.MaxUint32) + case uint16: + upper = uint16(math.MaxUint16) + case uint64: + upper = uint64(math.MaxUint64) + case uint: + upper = uint(math.MaxUint) + + // Note: there is no float64 boundary + // because float64 cannot overflow + case float32: + upper = float32(math.MaxFloat32) + } + + return upper +} + +func getLowerBoundary(value any) any { + var lower any = math.Inf(-1) + switch value.(type) { + case int64: + lower = int64(math.MinInt64) + case int32: + lower = int32(math.MinInt32) + case int16: + lower = int16(math.MinInt16) + case int8: + lower = int8(math.MinInt8) + case int: + lower = int(math.MinInt) + case uint: + lower = uint(0) + case uint8: + lower = uint8(0) + case uint16: + lower = uint16(0) + case uint32: + lower = uint32(0) + case uint64: + lower = uint64(0) + + // Note: there is no float64 boundary + // because float64 cannot overflow + case float32: + lower = float32(-math.MaxFloat32) + + } + + return lower +} diff --git a/vendor/github.com/ccoveille/go-safecast/conversion.go b/vendor/github.com/ccoveille/go-safecast/conversion.go new file mode 100644 index 0000000..de207d8 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/conversion.go @@ -0,0 +1,293 @@ +package safecast + +import ( + "errors" + "fmt" + "math" + "reflect" + "strconv" + "strings" +) + +// Convert attempts to convert any value to the desired type +// - If the conversion is possible, the converted value is returned. +// - If the conversion results in a value outside the range of the desired type, an [ErrRangeOverflow] error is wrapped in the returned error. +// - If the conversion exceeds the maximum value of the desired type, an [ErrExceedMaximumValue] error is wrapped in the returned error. +// - If the conversion exceeds the minimum value of the desired type, an [ErrExceedMinimumValue] error is wrapped in the returned error. +// - If the conversion is not possible for the desired type, an [ErrUnsupportedConversion] error is wrapped in the returned error. +// - If the conversion fails from string, an [ErrStringConversion] error is wrapped in the returned error. +// - If the conversion results in an error, an [ErrConversionIssue] error is wrapped in the returned error. +func Convert[NumOut Number, NumIn Input](orig NumIn) (converted NumOut, err error) { + v := reflect.ValueOf(orig) + switch v.Kind() { + case reflect.Int: + return convertFromNumber[NumOut](int(v.Int())) + case reflect.Uint: + return convertFromNumber[NumOut](uint(v.Uint())) + case reflect.Int8: + //nolint:gosec // the int8 is confirmed + return convertFromNumber[NumOut](int8(v.Int())) + case reflect.Uint8: + //nolint:gosec // the uint8 is confirmed + return convertFromNumber[NumOut](uint8(v.Uint())) + case reflect.Int16: + //nolint:gosec // the int16 is confirmed + return convertFromNumber[NumOut](int16(v.Int())) + case reflect.Uint16: + //nolint:gosec // the uint16 is confirmed + return convertFromNumber[NumOut](uint16(v.Uint())) + case reflect.Int32: + //nolint:gosec // the int32 is confirmed + return convertFromNumber[NumOut](int32(v.Int())) + case reflect.Uint32: + //nolint:gosec // the uint32 is confirmed + return convertFromNumber[NumOut](uint32(v.Uint())) + case reflect.Int64: + return convertFromNumber[NumOut](int64(v.Int())) + case reflect.Uint64: + return convertFromNumber[NumOut](uint64(v.Uint())) + case reflect.Float32: + return convertFromNumber[NumOut](float32(v.Float())) + case reflect.Float64: + return convertFromNumber[NumOut](float64(v.Float())) + case reflect.Bool: + o := 0 + if v.Bool() { + o = 1 + } + return NumOut(o), nil + case reflect.String: + return convertFromString[NumOut](v.String()) + } + + return 0, errorHelper{ + err: fmt.Errorf("%w from %T", ErrUnsupportedConversion, orig), + } +} + +// MustConvert calls [Convert] to convert the value to the desired type, and panics if the conversion fails. +func MustConvert[NumOut Number, NumIn Input](orig NumIn) NumOut { + converted, err := Convert[NumOut](orig) + if err != nil { + panic(err) + } + return converted +} + +func convertFromNumber[NumOut Number, NumIn Number](orig NumIn) (converted NumOut, err error) { + converted = NumOut(orig) + + // floats could be compared directly + switch any(converted).(type) { + case float64: + // float64 cannot overflow, so we don't have to worry about it + return converted, nil + case float32: + origFloat64, isFloat64 := any(orig).(float64) + if !isFloat64 { + // only float64 can overflow float32 + // everything else can be safely converted + return converted, nil + } + + // check boundary + if math.Abs(origFloat64) < math.MaxFloat32 { + // the value is within float32 range, there is no overflow + return converted, nil + } + + // TODO: check for numbers close to math.MaxFloat32 + + boundary := getUpperBoundary(converted) + errBoundary := ErrExceedMaximumValue + if negative(orig) { + boundary = getLowerBoundary(converted) + errBoundary = ErrExceedMinimumValue + } + + return 0, errorHelper{ + value: orig, + err: errBoundary, + boundary: boundary, + } + } + + errBoundary := ErrExceedMaximumValue + boundary := getUpperBoundary(converted) + if negative(orig) { + errBoundary = ErrExceedMinimumValue + boundary = getLowerBoundary(converted) + } + + if !sameSign(orig, converted) { + return 0, errorHelper{ + value: orig, + err: errBoundary, + boundary: boundary, + } + } + + // convert back to the original type + cast := NumIn(converted) + // and compare + base := orig + switch f := any(orig).(type) { + case float64: + base = NumIn(math.Trunc(f)) + case float32: + base = NumIn(math.Trunc(float64(f))) + } + + // exact match + if cast == base { + return converted, nil + } + + return 0, errorHelper{ + value: orig, + err: errBoundary, + boundary: boundary, + } +} + +func convertFromString[NumOut Number](s string) (converted NumOut, err error) { + s = strings.TrimSpace(s) + + if b, err := strconv.ParseBool(s); err == nil { + if b { + return NumOut(1), nil + } + return NumOut(0), nil + } + + if strings.Contains(s, ".") { + o, err := strconv.ParseFloat(s, 64) + if err != nil { + return 0, errorHelper{ + value: s, + err: fmt.Errorf("%w %v to %T", ErrStringConversion, s, converted), + } + } + return convertFromNumber[NumOut](o) + } + + if strings.HasPrefix(s, "-") { + o, err := strconv.ParseInt(s, 0, 64) + if err != nil { + if errors.Is(err, strconv.ErrRange) { + return 0, errorHelper{ + value: s, + err: ErrExceedMinimumValue, + boundary: math.MinInt, + } + } + return 0, errorHelper{ + value: s, + err: fmt.Errorf("%w %v to %T", ErrStringConversion, s, converted), + } + } + + return convertFromNumber[NumOut](o) + } + + o, err := strconv.ParseUint(s, 0, 64) + if err != nil { + if errors.Is(err, strconv.ErrRange) { + return 0, errorHelper{ + value: s, + err: ErrExceedMaximumValue, + boundary: uint(math.MaxUint), + } + } + + return 0, errorHelper{ + value: s, + err: fmt.Errorf("%w %v to %T", ErrStringConversion, s, converted), + } + } + return convertFromNumber[NumOut](o) +} + +// ToInt attempts to convert any [Type] value to an int. +// If the conversion results in a value outside the range of an int, +// an [ErrConversionIssue] error is returned. +func ToInt[T Number](i T) (int, error) { + return convertFromNumber[int](i) +} + +// ToUint attempts to convert any [Number] value to an uint. +// If the conversion results in a value outside the range of an uint, +// an [ErrConversionIssue] error is returned. +func ToUint[T Number](i T) (uint, error) { + return convertFromNumber[uint](i) +} + +// ToInt8 attempts to convert any [Number] value to an int8. +// If the conversion results in a value outside the range of an int8, +// an [ErrConversionIssue] error is returned. +func ToInt8[T Number](i T) (int8, error) { + return convertFromNumber[int8](i) +} + +// ToUint8 attempts to convert any [Number] value to an uint8. +// If the conversion results in a value outside the range of an uint8, +// an [ErrConversionIssue] error is returned. +func ToUint8[T Number](i T) (uint8, error) { + return convertFromNumber[uint8](i) +} + +// ToInt16 attempts to convert any [Number] value to an int16. +// If the conversion results in a value outside the range of an int16, +// an [ErrConversionIssue] error is returned. +func ToInt16[T Number](i T) (int16, error) { + return convertFromNumber[int16](i) +} + +// ToUint16 attempts to convert any [Number] value to an uint16. +// If the conversion results in a value outside the range of an uint16, +// an [ErrConversionIssue] error is returned. +func ToUint16[T Number](i T) (uint16, error) { + return convertFromNumber[uint16](i) +} + +// ToInt32 attempts to convert any [Number] value to an int32. +// If the conversion results in a value outside the range of an int32, +// an [ErrConversionIssue] error is returned. +func ToInt32[T Number](i T) (int32, error) { + return convertFromNumber[int32](i) +} + +// ToUint32 attempts to convert any [Number] value to an uint32. +// If the conversion results in a value outside the range of an uint32, +// an [ErrConversionIssue] error is returned. +func ToUint32[T Number](i T) (uint32, error) { + return convertFromNumber[uint32](i) +} + +// ToInt64 attempts to convert any [Number] value to an int64. +// If the conversion results in a value outside the range of an int64, +// an [ErrConversionIssue] error is returned. +func ToInt64[T Number](i T) (int64, error) { + return convertFromNumber[int64](i) +} + +// ToUint64 attempts to convert any [Number] value to an uint64. +// If the conversion results in a value outside the range of an uint64, +// an [ErrConversionIssue] error is returned. +func ToUint64[T Number](i T) (uint64, error) { + return convertFromNumber[uint64](i) +} + +// ToFloat32 attempts to convert any [Number] value to a float32. +// If the conversion results in a value outside the range of a float32, +// an [ErrConversionIssue] error is returned. +func ToFloat32[T Number](i T) (float32, error) { + return convertFromNumber[float32](i) +} + +// ToFloat64 attempts to convert any [Number] value to a float64. +// If the conversion results in a value outside the range of a float64, +// an [ErrConversionIssue] error is returned. +func ToFloat64[T Number](i T) (float64, error) { + return convertFromNumber[float64](i) +} diff --git a/vendor/github.com/ccoveille/go-safecast/doc.go b/vendor/github.com/ccoveille/go-safecast/doc.go new file mode 100644 index 0000000..b236245 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/doc.go @@ -0,0 +1,5 @@ +// Package safecast solves the type conversion issues in Go +// +// In Go, integer type conversion can lead to unexpected behavior and errors if not handled carefully. +// Issues can happen when converting between signed and unsigned integers, or when converting to a smaller integer type. +package safecast diff --git a/vendor/github.com/ccoveille/go-safecast/errors.go b/vendor/github.com/ccoveille/go-safecast/errors.go new file mode 100644 index 0000000..254a54d --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/errors.go @@ -0,0 +1,63 @@ +package safecast + +import ( + "errors" + "fmt" +) + +// ErrConversionIssue is a generic error for type conversion issues +// It is used to wrap other errors +var ErrConversionIssue = errors.New("conversion issue") + +// ErrRangeOverflow is an error for when the value is outside the range of the desired type +var ErrRangeOverflow = errors.New("range overflow") + +// ErrExceedMaximumValue is an error for when the value is greater than the maximum value of the desired type. +var ErrExceedMaximumValue = errors.New("maximum value for this type exceeded") + +// ErrExceedMinimumValue is an error for when the value is less than the minimum value of the desired type. +var ErrExceedMinimumValue = errors.New("minimum value for this type exceeded") + +// ErrUnsupportedConversion is an error for when the conversion is not supported from the provided type. +var ErrUnsupportedConversion = errors.New("unsupported type") + +// ErrStringConversion is an error for when the conversion fails from string. +var ErrStringConversion = errors.New("cannot convert from string") + +// errorHelper is a helper struct for error messages +// It is used to wrap other errors, and provides additional information +type errorHelper struct { + value any + boundary any + err error +} + +func (e errorHelper) Error() string { + errMessage := ErrConversionIssue.Error() + + switch { + case errors.Is(e.err, ErrExceedMaximumValue): + errMessage = fmt.Sprintf("%s: %v (%T) is greater than %v (%T)", errMessage, e.value, e.value, e.boundary, e.boundary) + case errors.Is(e.err, ErrExceedMinimumValue): + errMessage = fmt.Sprintf("%s: %v (%T) is less than %v (%T)", errMessage, e.value, e.value, e.boundary, e.boundary) + } + + if e.err != nil { + errMessage = fmt.Sprintf("%s: %s", errMessage, e.err.Error()) + } + return errMessage +} + +func (e errorHelper) Unwrap() []error { + errs := []error{ErrConversionIssue} + if e.err != nil { + switch { + case + errors.Is(e.err, ErrExceedMaximumValue), + errors.Is(e.err, ErrExceedMinimumValue): + errs = append(errs, ErrRangeOverflow) + } + errs = append(errs, e.err) + } + return errs +} diff --git a/vendor/github.com/ccoveille/go-safecast/types.go b/vendor/github.com/ccoveille/go-safecast/types.go new file mode 100644 index 0000000..7d992c8 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/types.go @@ -0,0 +1,36 @@ +package safecast + +// This files is highly inspired from https://pkg.go.dev/golang.org/x/exp/constraints +// I didn't import it as it would have added an unnecessary dependency + +// Signed is a constraint for all signed integers: int, int8, int16, int32, and int64 types. +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +// Unsigned is a constraint for all unsigned integers: uint, uint8, uint16, uint32, and uint64 types. +// TODO: support uintpr +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 +} + +// Integer is a constraint for the all unsigned and signed integers +type Integer interface { + Signed | Unsigned +} + +// Float is a constraint for the float32 and float64 types. +type Float interface { + ~float32 | ~float64 +} + +// Number is a constraint for all integers and floats +// TODO: consider using complex, but not sure there is a need +type Number interface { + Integer | Float +} + +// Input is a constraint for all types that can be used as input for [Convert], and [MustConvert] +type Input interface { + Number | ~string | ~bool +} |
