summaryrefslogtreecommitdiff
path: root/vendor/github.com/ccoveille
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-22 17:35:49 -0600
committermo khan <mo@mokhan.ca>2025-07-22 17:35:49 -0600
commit20ef0d92694465ac86b550df139e8366a0a2b4fa (patch)
tree3f14589e1ce6eb9306a3af31c3a1f9e1af5ed637 /vendor/github.com/ccoveille
parent44e0d272c040cdc53a98b9f1dc58ae7da67752e6 (diff)
feat: connect to spicedb
Diffstat (limited to 'vendor/github.com/ccoveille')
-rw-r--r--vendor/github.com/ccoveille/go-safecast/.editorconfig25
-rw-r--r--vendor/github.com/ccoveille/go-safecast/.gitattributes4
-rw-r--r--vendor/github.com/ccoveille/go-safecast/.golangci.yml178
-rw-r--r--vendor/github.com/ccoveille/go-safecast/.ls-lint.yml17
-rw-r--r--vendor/github.com/ccoveille/go-safecast/.markdownlint.yml14
-rw-r--r--vendor/github.com/ccoveille/go-safecast/.yamllint.yml14
-rw-r--r--vendor/github.com/ccoveille/go-safecast/LICENSE21
-rw-r--r--vendor/github.com/ccoveille/go-safecast/README.md165
-rw-r--r--vendor/github.com/ccoveille/go-safecast/asserters.go78
-rw-r--r--vendor/github.com/ccoveille/go-safecast/conversion.go293
-rw-r--r--vendor/github.com/ccoveille/go-safecast/doc.go5
-rw-r--r--vendor/github.com/ccoveille/go-safecast/errors.go63
-rw-r--r--vendor/github.com/ccoveille/go-safecast/types.go36
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
+
+[![Go Report Card](https://goreportcard.com/badge/github.com/ccoveille/go-safecast)](https://goreportcard.com/report/github.com/ccoveille/go-safecast)
+[![GoDoc](https://godoc.org/github.com/ccoVeille/go-safecast?status.svg)](https://godoc.org/github.com/ccoVeille/go-safecast)
+[![codecov](https://codecov.io/gh/ccoVeille/go-safecast/graph/badge.svg?token=VW0VO503U6)](https://codecov.io/gh/ccoVeille/go-safecast)
+[![Code Climate](https://codeclimate.com/github/ccoVeille/go-safecast.png)](https://codeclimate.com/github/ccoVeille/go-safecast)
+[![Go Imports](https://img.shields.io/github/search?query=%22%5C%22github.com%2Fccoveille%2Fgo-safecast%5C%22%22%20language%3Ago%20%20-is%3Afork%20-is%3Aarchived%20&label=Go%20imports)](https://github.com/search?q=%22%5C%22github.com%2Fccoveille%2Fgo-safecast%5C%22%22+language%3Ago++-is%3Afork+-is%3Aarchived+&type=code)
+![GitHub Repo stars](https://img.shields.io/github/stars/ccoveille/go-safecast)
+
+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
+
+[![Stargazers over time](https://starchart.cc/ccoVeille/go-safecast.svg?variant=adaptive)](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
+}