mirror of
https://github.com/aykhans/go-utils.git
synced 2025-10-15 18:25:57 +00:00
187 lines
4.9 KiB
Go
187 lines
4.9 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// ParseStringSupportedTypes defines the type constraint for types that can be
|
|
// parsed from strings using the ParseString family of functions.
|
|
type ParseStringSupportedTypes interface {
|
|
string |
|
|
int | int8 | int16 | int32 | int64 |
|
|
uint | uint8 | uint16 | uint32 | uint64 |
|
|
float64 |
|
|
bool | time.Duration | url.URL
|
|
}
|
|
|
|
// ParseString parses a string value into the specified type T.
|
|
// It uses the appropriate parsing function based on the target type.
|
|
//
|
|
// The function supports all types defined in ParseStringSupportedTypes.
|
|
// For integers, it parses base-10 numbers with appropriate bit sizes.
|
|
// For booleans, it accepts: "1", "t", "T", "TRUE", "true", "True", "0", "f", "F", "FALSE", "false", "False".
|
|
// For durations, it accepts strings like "300ms", "1.5h", "2h45m".
|
|
// For URLs, it parses according to RFC 3986.
|
|
//
|
|
// Returns an error if the string cannot be parsed into the target type.
|
|
//
|
|
// Example:
|
|
//
|
|
// num, err := ParseString[int]("42")
|
|
// duration, err := ParseString[time.Duration]("5s")
|
|
// isValid, err := ParseString[bool]("true")
|
|
//
|
|
//nolint:forcetypeassert
|
|
func ParseString[T ParseStringSupportedTypes](rawValue string) (T, error) { //nolint:forcetypeassert
|
|
var value T
|
|
|
|
switch any(value).(type) {
|
|
case string:
|
|
value = any(rawValue).(T)
|
|
case int:
|
|
i, err := strconv.Atoi(rawValue)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(i).(T)
|
|
case int8:
|
|
i, err := strconv.ParseInt(rawValue, 10, 8)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(int8(i)).(T)
|
|
case int16:
|
|
i, err := strconv.ParseInt(rawValue, 10, 16)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(int16(i)).(T)
|
|
case int32:
|
|
i, err := strconv.ParseInt(rawValue, 10, 32)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(int32(i)).(T)
|
|
case int64:
|
|
i, err := strconv.ParseInt(rawValue, 10, 64)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(i).(T)
|
|
case uint:
|
|
u, err := strconv.ParseUint(rawValue, 10, 0)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(uint(u)).(T)
|
|
case uint8:
|
|
u, err := strconv.ParseUint(rawValue, 10, 8)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(uint8(u)).(T)
|
|
case uint16:
|
|
u, err := strconv.ParseUint(rawValue, 10, 16)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(uint16(u)).(T)
|
|
case uint32:
|
|
u, err := strconv.ParseUint(rawValue, 10, 32)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(uint32(u)).(T)
|
|
case uint64:
|
|
u, err := strconv.ParseUint(rawValue, 10, 64)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(u).(T)
|
|
case float64:
|
|
f, err := strconv.ParseFloat(rawValue, 64)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(f).(T)
|
|
case bool:
|
|
b, err := strconv.ParseBool(rawValue)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(b).(T)
|
|
case time.Duration:
|
|
d, err := time.ParseDuration(rawValue)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(d).(T)
|
|
case url.URL:
|
|
u, err := url.Parse(rawValue)
|
|
if err != nil {
|
|
return value, err
|
|
}
|
|
value = any(*u).(T)
|
|
default:
|
|
return value, fmt.Errorf("unsupported type: %T", value)
|
|
}
|
|
|
|
return value, nil
|
|
}
|
|
|
|
// ParseStringOrZero parses a string value into the specified type T.
|
|
// If parsing fails, it returns the zero value for type T instead of an error.
|
|
//
|
|
// This is useful when you want a simple conversion that falls back to a
|
|
// default zero value on error.
|
|
//
|
|
// Example:
|
|
//
|
|
// num := ParseStringOrZero[int]("invalid") // returns 0
|
|
// num := ParseStringOrZero[int]("42") // returns 42
|
|
func ParseStringOrZero[T ParseStringSupportedTypes](rawValue string) T {
|
|
parsedValue, _ := ParseString[T](rawValue)
|
|
return parsedValue
|
|
}
|
|
|
|
// ParseStringWithDefault parses a string value into the specified type T.
|
|
// If parsing fails, it returns the provided default value along with the error.
|
|
//
|
|
// Unlike ParseString, this ensures a usable value is always returned,
|
|
// but still provides error information for logging or handling.
|
|
//
|
|
// Example:
|
|
//
|
|
// num, err := ParseStringWithDefault("invalid", 10)
|
|
// // returns: 10, error
|
|
// num, err := ParseStringWithDefault("42", 10)
|
|
// // returns: 42, nil
|
|
func ParseStringWithDefault[T ParseStringSupportedTypes](rawValue string, dft T) (T, error) {
|
|
parsedValue, err := ParseString[T](rawValue)
|
|
if err != nil {
|
|
return dft, err
|
|
}
|
|
return parsedValue, nil
|
|
}
|
|
|
|
// ParseStringOrDefault parses a string value into the specified type T.
|
|
// If parsing fails, it returns the provided default value and suppresses the error.
|
|
//
|
|
// This is the most convenient option when you want a fallback value
|
|
// and don't need to handle the error explicitly.
|
|
//
|
|
// Example:
|
|
//
|
|
// num := ParseStringOrDefault("invalid", 10) // returns 10
|
|
// num := ParseStringOrDefault("42", 10) // returns 42
|
|
func ParseStringOrDefault[T ParseStringSupportedTypes](rawValue string, dft T) T {
|
|
parsedValue, err := ParseString[T](rawValue)
|
|
if err != nil {
|
|
return dft
|
|
}
|
|
return parsedValue
|
|
}
|