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  }