blob: 69d0ae4d0617a1da8c697930da3cdd404e28e649 [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001// Copyright (C) MongoDB, Inc. 2017-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package bsoncodec
8
9import (
10 "reflect"
11 "strings"
12)
13
14// StructTagParser returns the struct tags for a given struct field.
15type StructTagParser interface {
16 ParseStructTags(reflect.StructField) (StructTags, error)
17}
18
19// StructTagParserFunc is an adapter that allows a generic function to be used
20// as a StructTagParser.
21type StructTagParserFunc func(reflect.StructField) (StructTags, error)
22
23// ParseStructTags implements the StructTagParser interface.
24func (stpf StructTagParserFunc) ParseStructTags(sf reflect.StructField) (StructTags, error) {
25 return stpf(sf)
26}
27
28// StructTags represents the struct tag fields that the StructCodec uses during
29// the encoding and decoding process.
30//
31// In the case of a struct, the lowercased field name is used as the key for each exported
32// field but this behavior may be changed using a struct tag. The tag may also contain flags to
33// adjust the marshalling behavior for the field.
34//
35// The properties are defined below:
36//
37// OmitEmpty Only include the field if it's not set to the zero value for the type or to
38// empty slices or maps.
39//
40// MinSize Marshal an integer of a type larger than 32 bits value as an int32, if that's
41// feasible while preserving the numeric value.
42//
43// Truncate When unmarshaling a BSON double, it is permitted to lose precision to fit within
44// a float32.
45//
46// Inline Inline the field, which must be a struct or a map, causing all of its fields
47// or keys to be processed as if they were part of the outer struct. For maps,
48// keys must not conflict with the bson keys of other struct fields.
49//
50// Skip This struct field should be skipped. This is usually denoted by parsing a "-"
51// for the name.
52//
53// TODO(skriptble): Add tags for undefined as nil and for null as nil.
54type StructTags struct {
55 Name string
56 OmitEmpty bool
57 MinSize bool
58 Truncate bool
59 Inline bool
60 Skip bool
61}
62
63// DefaultStructTagParser is the StructTagParser used by the StructCodec by default.
64// It will handle the bson struct tag. See the documentation for StructTags to see
65// what each of the returned fields means.
66//
67// If there is no name in the struct tag fields, the struct field name is lowercased.
68// The tag formats accepted are:
69//
70// "[<key>][,<flag1>[,<flag2>]]"
71//
72// `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
73//
74// An example:
75//
76// type T struct {
77// A bool
78// B int "myb"
79// C string "myc,omitempty"
80// D string `bson:",omitempty" json:"jsonkey"`
81// E int64 ",minsize"
82// F int64 "myf,omitempty,minsize"
83// }
84//
85// A struct tag either consisting entirely of '-' or with a bson key with a
86// value consisting entirely of '-' will return a StructTags with Skip true and
87// the remaining fields will be their default values.
88var DefaultStructTagParser StructTagParserFunc = func(sf reflect.StructField) (StructTags, error) {
89 key := strings.ToLower(sf.Name)
90 tag, ok := sf.Tag.Lookup("bson")
91 if !ok && !strings.Contains(string(sf.Tag), ":") && len(sf.Tag) > 0 {
92 tag = string(sf.Tag)
93 }
94 var st StructTags
95 if tag == "-" {
96 st.Skip = true
97 return st, nil
98 }
99
100 for idx, str := range strings.Split(tag, ",") {
101 if idx == 0 && str != "" {
102 key = str
103 }
104 switch str {
105 case "omitempty":
106 st.OmitEmpty = true
107 case "minsize":
108 st.MinSize = true
109 case "truncate":
110 st.Truncate = true
111 case "inline":
112 st.Inline = true
113 }
114 }
115
116 st.Name = key
117
118 return st, nil
119}