github.com/lastbackend/toolkit@v0.0.0-20241020043710-cafa37b95aad/pkg/server/http/marshaler/formpb/formpb.go (about) 1 /* 2 Copyright [2014] - [2023] The Last.Backend authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package formpb 18 19 import ( 20 "github.com/lastbackend/toolkit/pkg/server/http/marshaler" 21 "github.com/lastbackend/toolkit/pkg/util/converter" 22 "google.golang.org/protobuf/encoding/protojson" 23 "google.golang.org/protobuf/proto" 24 25 "bytes" 26 "encoding/json" 27 "fmt" 28 "io" 29 "net/url" 30 "reflect" 31 ) 32 33 var ( 34 protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem() 35 ) 36 37 type protoEnum interface { 38 fmt.Stringer 39 EnumDescriptor() ([]byte, []int) 40 } 41 42 type FORMPb struct { 43 protojson.MarshalOptions 44 protojson.UnmarshalOptions 45 } 46 47 // ContentType always returns "application/x-www-form-urlencoded". 48 func (*FORMPb) ContentType() string { 49 return "application/x-www-form-urlencoded" 50 } 51 52 // Marshal marshals "v" into JSON 53 // Currently it can marshal only proto.Message. 54 func (j *FORMPb) Marshal(v interface{}) ([]byte, error) { 55 if _, ok := v.(proto.Message); !ok { 56 return j.marshalNonProtoField(v) 57 } 58 59 var buf bytes.Buffer 60 if err := j.marshalTo(&buf, v); err != nil { 61 return nil, err 62 } 63 64 return buf.Bytes(), nil 65 } 66 67 // Unmarshal not realized for the moment 68 func (j *FORMPb) Unmarshal(data []byte, v interface{}) error { 69 return nil 70 } 71 72 // NewDecoder returns a Decoder which reads form data stream from "r". 73 func (j *FORMPb) NewDecoder(r io.Reader) marshaler.Decoder { 74 return marshaler.DecoderFunc(func(v interface{}) error { return decodeFORMPb(r, v) }) 75 } 76 77 // NewEncoder returns an Encoder which writes JSON stream into "w". 78 func (j *FORMPb) NewEncoder(w io.Writer) marshaler.Encoder { 79 return marshaler.EncoderFunc(func(v interface{}) error { return j.marshalTo(w, v) }) 80 } 81 82 func decodeFORMPb(d io.Reader, v interface{}) error { 83 msg, ok := v.(proto.Message) 84 if !ok { 85 return fmt.Errorf("not proto message") 86 } 87 88 formData, err := io.ReadAll(d) 89 if err != nil { 90 return err 91 } 92 93 values, err := url.ParseQuery(string(formData)) 94 if err != nil { 95 return err 96 } 97 98 if err := converter.ParseRequestQueryParametersToProto(msg, values); err != nil { 99 return err 100 } 101 102 return nil 103 } 104 105 func (j *FORMPb) marshalTo(w io.Writer, v interface{}) error { 106 p, ok := v.(proto.Message) 107 if !ok { 108 buf, err := j.marshalNonProtoField(v) 109 if err != nil { 110 return err 111 } 112 _, err = w.Write(buf) 113 return err 114 } 115 116 b, err := j.MarshalOptions.Marshal(p) 117 if err != nil { 118 return err 119 } 120 121 _, err = w.Write(b) 122 123 return err 124 } 125 126 func (j *FORMPb) marshalNonProtoField(v interface{}) ([]byte, error) { 127 if v == nil { 128 return []byte("null"), nil 129 } 130 131 rv := reflect.ValueOf(v) 132 133 for rv.Kind() == reflect.Ptr { 134 if rv.IsNil() { 135 return []byte("null"), nil 136 } 137 rv = rv.Elem() 138 } 139 140 if rv.Kind() == reflect.Slice { 141 if rv.IsNil() { 142 if j.EmitUnpopulated { 143 return []byte("[]"), nil 144 } 145 return []byte("null"), nil 146 } 147 148 if rv.Type().Elem().Implements(protoMessageType) { 149 var buf bytes.Buffer 150 err := buf.WriteByte('[') 151 if err != nil { 152 return nil, err 153 } 154 for i := 0; i < rv.Len(); i++ { 155 if i != 0 { 156 err = buf.WriteByte(',') 157 if err != nil { 158 return nil, err 159 } 160 } 161 if err = j.marshalTo(&buf, rv.Index(i).Interface().(proto.Message)); err != nil { 162 return nil, err 163 } 164 } 165 err = buf.WriteByte(']') 166 if err != nil { 167 return nil, err 168 } 169 170 return buf.Bytes(), nil 171 } 172 } 173 174 if rv.Kind() == reflect.Map { 175 m := make(map[string]*json.RawMessage) 176 for _, k := range rv.MapKeys() { 177 buf, err := j.Marshal(rv.MapIndex(k).Interface()) 178 if err != nil { 179 return nil, err 180 } 181 m[fmt.Sprintf("%v", k.Interface())] = (*json.RawMessage)(&buf) 182 } 183 if j.Indent != "" { 184 return json.MarshalIndent(m, "", j.Indent) 185 } 186 return json.Marshal(m) 187 } 188 189 if enum, ok := rv.Interface().(protoEnum); ok && !j.UseEnumNumbers { 190 return json.Marshal(enum.String()) 191 } 192 193 return json.Marshal(rv.Interface()) 194 }