github.com/mitranim/gg@v0.1.17/text_decode.go (about)

     1  package gg
     2  
     3  import (
     4  	"encoding"
     5  	r "reflect"
     6  	"strconv"
     7  )
     8  
     9  /*
    10  Decodes arbitrary text into a value of the given type, using `ParseCatch`.
    11  Panics on errors.
    12  */
    13  func ParseTo[Out any, Src Text](src Src) (out Out) {
    14  	Try(ParseCatch(src, &out))
    15  	return
    16  }
    17  
    18  /*
    19  Decodes arbitrary text into a value of the given type, using `ParseCatch`.
    20  Panics on errors.
    21  */
    22  func Parse[Out any, Src Text](src Src, out *Out) {
    23  	Try(ParseCatch(src, out))
    24  }
    25  
    26  /*
    27  Missing feature of the standard library. Decodes arbitrary text into a value of
    28  an arbitrary given type. The output must either implement `Parser`, or
    29  implement `encoding.TextUnmarshaler`, or be a pointer to any of the types
    30  described by the constraint `Textable` defined by this package. If the output
    31  is not decodable, this returns an error.
    32  */
    33  func ParseCatch[Out any, Src Text](src Src, out *Out) error {
    34  	if out == nil {
    35  		return nil
    36  	}
    37  
    38  	parser, _ := AnyNoEscUnsafe(out).(Parser)
    39  	if parser != nil {
    40  		return parser.Parse(ToString(src))
    41  	}
    42  
    43  	unmarshaler, _ := AnyNoEscUnsafe(out).(encoding.TextUnmarshaler)
    44  	if unmarshaler != nil {
    45  		return unmarshaler.UnmarshalText(ToBytes(src))
    46  	}
    47  
    48  	return ParseReflectCatch(src, r.ValueOf(AnyNoEscUnsafe(out)).Elem())
    49  }
    50  
    51  /*
    52  Reflection-based component of `ParseCatch`.
    53  Mostly for internal use.
    54  */
    55  func ParseReflectCatch[A Text](src A, out r.Value) error {
    56  	typ := out.Type()
    57  	kind := typ.Kind()
    58  
    59  	switch kind {
    60  	case r.Int8, r.Int16, r.Int32, r.Int64, r.Int:
    61  		val, err := strconv.ParseInt(ToString(src), 10, typeBitSize(typ))
    62  		out.SetInt(val)
    63  		return ErrParse(err, src, typ)
    64  
    65  	case r.Uint8, r.Uint16, r.Uint32, r.Uint64, r.Uint:
    66  		val, err := strconv.ParseUint(ToString(src), 10, typeBitSize(typ))
    67  		out.SetUint(val)
    68  		return ErrParse(err, src, typ)
    69  
    70  	case r.Float32, r.Float64:
    71  		val, err := strconv.ParseFloat(ToString(src), typeBitSize(typ))
    72  		out.SetFloat(val)
    73  		return ErrParse(err, src, typ)
    74  
    75  	case r.Bool:
    76  		return parseBool(ToString(src), out)
    77  
    78  	case r.String:
    79  		out.SetString(string(src))
    80  		return nil
    81  
    82  	case r.Pointer:
    83  		if out.IsNil() {
    84  			out.Set(r.New(typ.Elem()))
    85  		}
    86  
    87  		ptr := out.Interface()
    88  
    89  		parser, _ := ptr.(Parser)
    90  		if parser != nil {
    91  			return parser.Parse(ToString(src))
    92  		}
    93  
    94  		unmarshaler, _ := ptr.(encoding.TextUnmarshaler)
    95  		if unmarshaler != nil {
    96  			return unmarshaler.UnmarshalText(ToBytes(src))
    97  		}
    98  
    99  		return ParseReflectCatch[A](src, out.Elem())
   100  
   101  	default:
   102  		if IsTypeBytes(typ) {
   103  			out.SetBytes([]byte(src))
   104  			return nil
   105  		}
   106  		return Errf(`unable to convert string to %v: unsupported kind %v`, typ, kind)
   107  	}
   108  }
   109  
   110  /*
   111  Shortcut for implementing text decoding of types that wrap other types, such as
   112  `Opt`. Mostly for internal use.
   113  */
   114  func ParseClearCatch[Out any, Tar ClearerPtrGetter[Out], Src Text](src Src, tar Tar) error {
   115  	if len(src) <= 0 {
   116  		tar.Clear()
   117  		return nil
   118  	}
   119  	return ParseCatch(src, tar.Ptr())
   120  }
   121  
   122  /*
   123  Shortcut for implementing `sql.Scanner` on types that wrap other types, such as
   124  `Opt`. Mostly for internal use.
   125  */
   126  func ScanCatch[Inner any, Outer Ptrer[Inner]](src any, tar Outer) error {
   127  	if src == nil {
   128  		return nil
   129  	}
   130  
   131  	ptr := tar.Ptr()
   132  
   133  	impl, _ := AnyNoEscUnsafe(ptr).(Scanner)
   134  	if impl != nil {
   135  		return impl.Scan(src)
   136  	}
   137  
   138  	str, ok := AnyToText[string](src)
   139  	if ok {
   140  		return ParseCatch(str, ptr)
   141  	}
   142  
   143  	return ErrConv(src, Type[Outer]())
   144  }