Files
go-utils/parser/string.go
2025-10-11 19:53:28 +04:00

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
}