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 }