bson.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. // BSON library for Go
  2. //
  3. // Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
  4. //
  5. // All rights reserved.
  6. //
  7. // Redistribution and use in source and binary forms, with or without
  8. // modification, are permitted provided that the following conditions are met:
  9. //
  10. // 1. Redistributions of source code must retain the above copyright notice, this
  11. // list of conditions and the following disclaimer.
  12. // 2. Redistributions in binary form must reproduce the above copyright notice,
  13. // this list of conditions and the following disclaimer in the documentation
  14. // and/or other materials provided with the distribution.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17. // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  20. // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. // Package bson is an implementation of the BSON specification for Go:
  27. //
  28. // http://bsonspec.org
  29. //
  30. // It was created as part of the mgo MongoDB driver for Go, but is standalone
  31. // and may be used on its own without the driver.
  32. package bson
  33. import (
  34. "crypto/md5"
  35. "crypto/rand"
  36. "encoding/binary"
  37. "encoding/hex"
  38. "errors"
  39. "fmt"
  40. "io"
  41. "os"
  42. "reflect"
  43. "runtime"
  44. "strings"
  45. "sync"
  46. "sync/atomic"
  47. "time"
  48. )
  49. // --------------------------------------------------------------------------
  50. // The public API.
  51. // A value implementing the bson.Getter interface will have its GetBSON
  52. // method called when the given value has to be marshalled, and the result
  53. // of this method will be marshaled in place of the actual object.
  54. //
  55. // If GetBSON returns return a non-nil error, the marshalling procedure
  56. // will stop and error out with the provided value.
  57. type Getter interface {
  58. GetBSON() (interface{}, error)
  59. }
  60. // A value implementing the bson.Setter interface will receive the BSON
  61. // value via the SetBSON method during unmarshaling, and the object
  62. // itself will not be changed as usual.
  63. //
  64. // If setting the value works, the method should return nil or alternatively
  65. // bson.SetZero to set the respective field to its zero value (nil for
  66. // pointer types). If SetBSON returns a value of type bson.TypeError, the
  67. // BSON value will be omitted from a map or slice being decoded and the
  68. // unmarshalling will continue. If it returns any other non-nil error, the
  69. // unmarshalling procedure will stop and error out with the provided value.
  70. //
  71. // This interface is generally useful in pointer receivers, since the method
  72. // will want to change the receiver. A type field that implements the Setter
  73. // interface doesn't have to be a pointer, though.
  74. //
  75. // Unlike the usual behavior, unmarshalling onto a value that implements a
  76. // Setter interface will NOT reset the value to its zero state. This allows
  77. // the value to decide by itself how to be unmarshalled.
  78. //
  79. // For example:
  80. //
  81. // type MyString string
  82. //
  83. // func (s *MyString) SetBSON(raw bson.Raw) error {
  84. // return raw.Unmarshal(s)
  85. // }
  86. //
  87. type Setter interface {
  88. SetBSON(raw Raw) error
  89. }
  90. // SetZero may be returned from a SetBSON method to have the value set to
  91. // its respective zero value. When used in pointer values, this will set the
  92. // field to nil rather than to the pre-allocated value.
  93. var SetZero = errors.New("set to zero")
  94. // M is a convenient alias for a map[string]interface{} map, useful for
  95. // dealing with BSON in a native way. For instance:
  96. //
  97. // bson.M{"a": 1, "b": true}
  98. //
  99. // There's no special handling for this type in addition to what's done anyway
  100. // for an equivalent map type. Elements in the map will be dumped in an
  101. // undefined ordered. See also the bson.D type for an ordered alternative.
  102. type M map[string]interface{}
  103. // D represents a BSON document containing ordered elements. For example:
  104. //
  105. // bson.D{{"a", 1}, {"b", true}}
  106. //
  107. // In some situations, such as when creating indexes for MongoDB, the order in
  108. // which the elements are defined is important. If the order is not important,
  109. // using a map is generally more comfortable. See bson.M and bson.RawD.
  110. type D []DocElem
  111. // See the D type.
  112. type DocElem struct {
  113. Name string
  114. Value interface{}
  115. }
  116. // Map returns a map out of the ordered element name/value pairs in d.
  117. func (d D) Map() (m M) {
  118. m = make(M, len(d))
  119. for _, item := range d {
  120. m[item.Name] = item.Value
  121. }
  122. return m
  123. }
  124. // The Raw type represents raw unprocessed BSON documents and elements.
  125. // Kind is the kind of element as defined per the BSON specification, and
  126. // Data is the raw unprocessed data for the respective element.
  127. // Using this type it is possible to unmarshal or marshal values partially.
  128. //
  129. // Relevant documentation:
  130. //
  131. // http://bsonspec.org/#/specification
  132. //
  133. type Raw struct {
  134. Kind byte
  135. Data []byte
  136. }
  137. // RawD represents a BSON document containing raw unprocessed elements.
  138. // This low-level representation may be useful when lazily processing
  139. // documents of uncertain content, or when manipulating the raw content
  140. // documents in general.
  141. type RawD []RawDocElem
  142. // See the RawD type.
  143. type RawDocElem struct {
  144. Name string
  145. Value Raw
  146. }
  147. // ObjectId is a unique ID identifying a BSON value. It must be exactly 12 bytes
  148. // long. MongoDB objects by default have such a property set in their "_id"
  149. // property.
  150. //
  151. // http://www.mongodb.org/display/DOCS/Object+IDs
  152. type ObjectId string
  153. // ObjectIdHex returns an ObjectId from the provided hex representation.
  154. // Calling this function with an invalid hex representation will
  155. // cause a runtime panic. See the IsObjectIdHex function.
  156. func ObjectIdHex(s string) ObjectId {
  157. d, err := hex.DecodeString(s)
  158. if err != nil || len(d) != 12 {
  159. panic(fmt.Sprintf("Invalid input to ObjectIdHex: %q", s))
  160. }
  161. return ObjectId(d)
  162. }
  163. // IsObjectIdHex returns whether s is a valid hex representation of
  164. // an ObjectId. See the ObjectIdHex function.
  165. func IsObjectIdHex(s string) bool {
  166. if len(s) != 24 {
  167. return false
  168. }
  169. _, err := hex.DecodeString(s)
  170. return err == nil
  171. }
  172. // objectIdCounter is atomically incremented when generating a new ObjectId
  173. // using NewObjectId() function. It's used as a counter part of an id.
  174. var objectIdCounter uint32 = 0
  175. // machineId stores machine id generated once and used in subsequent calls
  176. // to NewObjectId function.
  177. var machineId = readMachineId()
  178. // readMachineId generates machine id and puts it into the machineId global
  179. // variable. If this function fails to get the hostname, it will cause
  180. // a runtime error.
  181. func readMachineId() []byte {
  182. var sum [3]byte
  183. id := sum[:]
  184. hostname, err1 := os.Hostname()
  185. if err1 != nil {
  186. _, err2 := io.ReadFull(rand.Reader, id)
  187. if err2 != nil {
  188. panic(fmt.Errorf("cannot get hostname: %v; %v", err1, err2))
  189. }
  190. return id
  191. }
  192. hw := md5.New()
  193. hw.Write([]byte(hostname))
  194. copy(id, hw.Sum(nil))
  195. return id
  196. }
  197. // NewObjectId returns a new unique ObjectId.
  198. func NewObjectId() ObjectId {
  199. var b [12]byte
  200. // Timestamp, 4 bytes, big endian
  201. binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix()))
  202. // Machine, first 3 bytes of md5(hostname)
  203. b[4] = machineId[0]
  204. b[5] = machineId[1]
  205. b[6] = machineId[2]
  206. // Pid, 2 bytes, specs don't specify endianness, but we use big endian.
  207. pid := os.Getpid()
  208. b[7] = byte(pid >> 8)
  209. b[8] = byte(pid)
  210. // Increment, 3 bytes, big endian
  211. i := atomic.AddUint32(&objectIdCounter, 1)
  212. b[9] = byte(i >> 16)
  213. b[10] = byte(i >> 8)
  214. b[11] = byte(i)
  215. return ObjectId(b[:])
  216. }
  217. // NewObjectIdWithTime returns a dummy ObjectId with the timestamp part filled
  218. // with the provided number of seconds from epoch UTC, and all other parts
  219. // filled with zeroes. It's not safe to insert a document with an id generated
  220. // by this method, it is useful only for queries to find documents with ids
  221. // generated before or after the specified timestamp.
  222. func NewObjectIdWithTime(t time.Time) ObjectId {
  223. var b [12]byte
  224. binary.BigEndian.PutUint32(b[:4], uint32(t.Unix()))
  225. return ObjectId(string(b[:]))
  226. }
  227. // String returns a hex string representation of the id.
  228. // Example: ObjectIdHex("4d88e15b60f486e428412dc9").
  229. func (id ObjectId) String() string {
  230. return fmt.Sprintf(`ObjectIdHex("%x")`, string(id))
  231. }
  232. // Hex returns a hex representation of the ObjectId.
  233. func (id ObjectId) Hex() string {
  234. return hex.EncodeToString([]byte(id))
  235. }
  236. // MarshalJSON turns a bson.ObjectId into a json.Marshaller.
  237. func (id ObjectId) MarshalJSON() ([]byte, error) {
  238. return []byte(fmt.Sprintf(`"%x"`, string(id))), nil
  239. }
  240. // UnmarshalJSON turns *bson.ObjectId into a json.Unmarshaller.
  241. func (id *ObjectId) UnmarshalJSON(data []byte) error {
  242. if len(data) != 26 || data[0] != '"' || data[25] != '"' {
  243. return errors.New(fmt.Sprintf("Invalid ObjectId in JSON: %s", string(data)))
  244. }
  245. var buf [12]byte
  246. _, err := hex.Decode(buf[:], data[1:25])
  247. if err != nil {
  248. return errors.New(fmt.Sprintf("Invalid ObjectId in JSON: %s (%s)", string(data), err))
  249. }
  250. *id = ObjectId(string(buf[:]))
  251. return nil
  252. }
  253. // Valid returns true if id is valid. A valid id must contain exactly 12 bytes.
  254. func (id ObjectId) Valid() bool {
  255. return len(id) == 12
  256. }
  257. // byteSlice returns byte slice of id from start to end.
  258. // Calling this function with an invalid id will cause a runtime panic.
  259. func (id ObjectId) byteSlice(start, end int) []byte {
  260. if len(id) != 12 {
  261. panic(fmt.Sprintf("Invalid ObjectId: %q", string(id)))
  262. }
  263. return []byte(string(id)[start:end])
  264. }
  265. // Time returns the timestamp part of the id.
  266. // It's a runtime error to call this method with an invalid id.
  267. func (id ObjectId) Time() time.Time {
  268. // First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch.
  269. secs := int64(binary.BigEndian.Uint32(id.byteSlice(0, 4)))
  270. return time.Unix(secs, 0)
  271. }
  272. // Machine returns the 3-byte machine id part of the id.
  273. // It's a runtime error to call this method with an invalid id.
  274. func (id ObjectId) Machine() []byte {
  275. return id.byteSlice(4, 7)
  276. }
  277. // Pid returns the process id part of the id.
  278. // It's a runtime error to call this method with an invalid id.
  279. func (id ObjectId) Pid() uint16 {
  280. return binary.BigEndian.Uint16(id.byteSlice(7, 9))
  281. }
  282. // Counter returns the incrementing value part of the id.
  283. // It's a runtime error to call this method with an invalid id.
  284. func (id ObjectId) Counter() int32 {
  285. b := id.byteSlice(9, 12)
  286. // Counter is stored as big-endian 3-byte value
  287. return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]))
  288. }
  289. // The Symbol type is similar to a string and is used in languages with a
  290. // distinct symbol type.
  291. type Symbol string
  292. // Now returns the current time with millisecond precision. MongoDB stores
  293. // timestamps with the same precision, so a Time returned from this method
  294. // will not change after a roundtrip to the database. That's the only reason
  295. // why this function exists. Using the time.Now function also works fine
  296. // otherwise.
  297. func Now() time.Time {
  298. return time.Unix(0, time.Now().UnixNano()/1e6*1e6)
  299. }
  300. // MongoTimestamp is a special internal type used by MongoDB that for some
  301. // strange reason has its own datatype defined in BSON.
  302. type MongoTimestamp int64
  303. type orderKey int64
  304. // MaxKey is a special value that compares higher than all other possible BSON
  305. // values in a MongoDB database.
  306. var MaxKey = orderKey(1<<63 - 1)
  307. // MinKey is a special value that compares lower than all other possible BSON
  308. // values in a MongoDB database.
  309. var MinKey = orderKey(-1 << 63)
  310. type undefined struct{}
  311. // Undefined represents the undefined BSON value.
  312. var Undefined undefined
  313. // Binary is a representation for non-standard binary values. Any kind should
  314. // work, but the following are known as of this writing:
  315. //
  316. // 0x00 - Generic. This is decoded as []byte(data), not Binary{0x00, data}.
  317. // 0x01 - Function (!?)
  318. // 0x02 - Obsolete generic.
  319. // 0x03 - UUID
  320. // 0x05 - MD5
  321. // 0x80 - User defined.
  322. //
  323. type Binary struct {
  324. Kind byte
  325. Data []byte
  326. }
  327. // RegEx represents a regular expression. The Options field may contain
  328. // individual characters defining the way in which the pattern should be
  329. // applied, and must be sorted. Valid options as of this writing are 'i' for
  330. // case insensitive matching, 'm' for multi-line matching, 'x' for verbose
  331. // mode, 'l' to make \w, \W, and similar be locale-dependent, 's' for dot-all
  332. // mode (a '.' matches everything), and 'u' to make \w, \W, and similar match
  333. // unicode. The value of the Options parameter is not verified before being
  334. // marshaled into the BSON format.
  335. type RegEx struct {
  336. Pattern string
  337. Options string
  338. }
  339. // JavaScript is a type that holds JavaScript code. If Scope is non-nil, it
  340. // will be marshaled as a mapping from identifiers to values that may be
  341. // used when evaluating the provided Code.
  342. type JavaScript struct {
  343. Code string
  344. Scope interface{}
  345. }
  346. const initialBufferSize = 64
  347. func handleErr(err *error) {
  348. if r := recover(); r != nil {
  349. if _, ok := r.(runtime.Error); ok {
  350. panic(r)
  351. } else if _, ok := r.(externalPanic); ok {
  352. panic(r)
  353. } else if s, ok := r.(string); ok {
  354. *err = errors.New(s)
  355. } else if e, ok := r.(error); ok {
  356. *err = e
  357. } else {
  358. panic(r)
  359. }
  360. }
  361. }
  362. // Marshal serializes the in value, which may be a map or a struct value.
  363. // In the case of struct values, only exported fields will be serialized.
  364. // The lowercased field name is used as the key for each exported field,
  365. // but this behavior may be changed using the respective field tag.
  366. // The tag may also contain flags to tweak the marshalling behavior for
  367. // the field. The tag formats accepted are:
  368. //
  369. // "[<key>][,<flag1>[,<flag2>]]"
  370. //
  371. // `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
  372. //
  373. // The following flags are currently supported:
  374. //
  375. // omitempty Only include the field if it's not set to the zero
  376. // value for the type or to empty slices or maps.
  377. //
  378. // minsize Marshal an int64 value as an int32, if that's feasible
  379. // while preserving the numeric value.
  380. //
  381. // inline Inline the field, which must be a struct or a map,
  382. // causing all of its fields or keys to be processed as if
  383. // they were part of the outer struct. For maps, keys must
  384. // not conflict with the bson keys of other struct fields.
  385. //
  386. // Some examples:
  387. //
  388. // type T struct {
  389. // A bool
  390. // B int "myb"
  391. // C string "myc,omitempty"
  392. // D string `bson:",omitempty" json:"jsonkey"`
  393. // E int64 ",minsize"
  394. // F int64 "myf,omitempty,minsize"
  395. // }
  396. //
  397. func Marshal(in interface{}) (out []byte, err error) {
  398. defer handleErr(&err)
  399. e := &encoder{make([]byte, 0, initialBufferSize)}
  400. e.addDoc(reflect.ValueOf(in))
  401. return e.out, nil
  402. }
  403. // Unmarshal deserializes data from in into the out value. The out value
  404. // must be a map, a pointer to a struct, or a pointer to a bson.D value.
  405. // The lowercased field name is used as the key for each exported field,
  406. // but this behavior may be changed using the respective field tag.
  407. // The tag may also contain flags to tweak the marshalling behavior for
  408. // the field. The tag formats accepted are:
  409. //
  410. // "[<key>][,<flag1>[,<flag2>]]"
  411. //
  412. // `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
  413. //
  414. // The following flags are currently supported during unmarshal (see the
  415. // Marshal method for other flags):
  416. //
  417. // inline Inline the field, which must be a struct or a map.
  418. // Inlined structs are handled as if its fields were part
  419. // of the outer struct. An inlined map causes keys that do
  420. // not match any other struct field to be inserted in the
  421. // map rather than being discarded as usual.
  422. //
  423. // The target field or element types of out may not necessarily match
  424. // the BSON values of the provided data. The following conversions are
  425. // made automatically:
  426. //
  427. // - Numeric types are converted if at least the integer part of the
  428. // value would be preserved correctly
  429. // - Bools are converted to numeric types as 1 or 0
  430. // - Numeric types are converted to bools as true if not 0 or false otherwise
  431. // - Binary and string BSON data is converted to a string, array or byte slice
  432. //
  433. // If the value would not fit the type and cannot be converted, it's
  434. // silently skipped.
  435. //
  436. // Pointer values are initialized when necessary.
  437. func Unmarshal(in []byte, out interface{}) (err error) {
  438. defer handleErr(&err)
  439. v := reflect.ValueOf(out)
  440. switch v.Kind() {
  441. case reflect.Map, reflect.Ptr:
  442. d := newDecoder(in)
  443. d.readDocTo(v)
  444. case reflect.Struct:
  445. return errors.New("Unmarshal can't deal with struct values. Use a pointer.")
  446. default:
  447. return errors.New("Unmarshal needs a map or a pointer to a struct.")
  448. }
  449. return nil
  450. }
  451. // Unmarshal deserializes raw into the out value. If the out value type
  452. // is not compatible with raw, a *bson.TypeError is returned.
  453. //
  454. // See the Unmarshal function documentation for more details on the
  455. // unmarshalling process.
  456. func (raw Raw) Unmarshal(out interface{}) (err error) {
  457. defer handleErr(&err)
  458. v := reflect.ValueOf(out)
  459. switch v.Kind() {
  460. case reflect.Ptr:
  461. v = v.Elem()
  462. fallthrough
  463. case reflect.Map:
  464. d := newDecoder(raw.Data)
  465. good := d.readElemTo(v, raw.Kind)
  466. if !good {
  467. return &TypeError{v.Type(), raw.Kind}
  468. }
  469. case reflect.Struct:
  470. return errors.New("Raw Unmarshal can't deal with struct values. Use a pointer.")
  471. default:
  472. return errors.New("Raw Unmarshal needs a map or a valid pointer.")
  473. }
  474. return nil
  475. }
  476. type TypeError struct {
  477. Type reflect.Type
  478. Kind byte
  479. }
  480. func (e *TypeError) Error() string {
  481. return fmt.Sprintf("BSON kind 0x%02x isn't compatible with type %s", e.Kind, e.Type.String())
  482. }
  483. // --------------------------------------------------------------------------
  484. // Maintain a mapping of keys to structure field indexes
  485. type structInfo struct {
  486. FieldsMap map[string]fieldInfo
  487. FieldsList []fieldInfo
  488. InlineMap int
  489. Zero reflect.Value
  490. }
  491. type fieldInfo struct {
  492. Key string
  493. Num int
  494. OmitEmpty bool
  495. MinSize bool
  496. Inline []int
  497. }
  498. var structMap = make(map[reflect.Type]*structInfo)
  499. var structMapMutex sync.RWMutex
  500. type externalPanic string
  501. func (e externalPanic) String() string {
  502. return string(e)
  503. }
  504. func getStructInfo(st reflect.Type) (*structInfo, error) {
  505. structMapMutex.RLock()
  506. sinfo, found := structMap[st]
  507. structMapMutex.RUnlock()
  508. if found {
  509. return sinfo, nil
  510. }
  511. n := st.NumField()
  512. fieldsMap := make(map[string]fieldInfo)
  513. fieldsList := make([]fieldInfo, 0, n)
  514. inlineMap := -1
  515. for i := 0; i != n; i++ {
  516. field := st.Field(i)
  517. if field.PkgPath != "" {
  518. continue // Private field
  519. }
  520. info := fieldInfo{Num: i}
  521. tag := field.Tag.Get("bson")
  522. if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
  523. tag = string(field.Tag)
  524. }
  525. if tag == "-" {
  526. continue
  527. }
  528. // XXX Drop this after a few releases.
  529. if s := strings.Index(tag, "/"); s >= 0 {
  530. recommend := tag[:s]
  531. for _, c := range tag[s+1:] {
  532. switch c {
  533. case 'c':
  534. recommend += ",omitempty"
  535. case 's':
  536. recommend += ",minsize"
  537. default:
  538. msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", string([]byte{uint8(c)}), tag, st)
  539. panic(externalPanic(msg))
  540. }
  541. }
  542. msg := fmt.Sprintf("Replace tag %q in field %s of type %s by %q", tag, field.Name, st, recommend)
  543. panic(externalPanic(msg))
  544. }
  545. inline := false
  546. fields := strings.Split(tag, ",")
  547. if len(fields) > 1 {
  548. for _, flag := range fields[1:] {
  549. switch flag {
  550. case "omitempty":
  551. info.OmitEmpty = true
  552. case "minsize":
  553. info.MinSize = true
  554. case "inline":
  555. inline = true
  556. default:
  557. msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
  558. panic(externalPanic(msg))
  559. }
  560. }
  561. tag = fields[0]
  562. }
  563. if inline {
  564. switch field.Type.Kind() {
  565. case reflect.Map:
  566. if inlineMap >= 0 {
  567. return nil, errors.New("Multiple ,inline maps in struct " + st.String())
  568. }
  569. if field.Type.Key() != reflect.TypeOf("") {
  570. return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
  571. }
  572. inlineMap = info.Num
  573. case reflect.Struct:
  574. sinfo, err := getStructInfo(field.Type)
  575. if err != nil {
  576. return nil, err
  577. }
  578. for _, finfo := range sinfo.FieldsList {
  579. if _, found := fieldsMap[finfo.Key]; found {
  580. msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
  581. return nil, errors.New(msg)
  582. }
  583. if finfo.Inline == nil {
  584. finfo.Inline = []int{i, finfo.Num}
  585. } else {
  586. finfo.Inline = append([]int{i}, finfo.Inline...)
  587. }
  588. fieldsMap[finfo.Key] = finfo
  589. fieldsList = append(fieldsList, finfo)
  590. }
  591. default:
  592. panic("Option ,inline needs a struct value or map field")
  593. }
  594. continue
  595. }
  596. if tag != "" {
  597. info.Key = tag
  598. } else {
  599. info.Key = strings.ToLower(field.Name)
  600. }
  601. if _, found = fieldsMap[info.Key]; found {
  602. msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
  603. return nil, errors.New(msg)
  604. }
  605. fieldsList = append(fieldsList, info)
  606. fieldsMap[info.Key] = info
  607. }
  608. sinfo = &structInfo{
  609. fieldsMap,
  610. fieldsList,
  611. inlineMap,
  612. reflect.New(st).Elem(),
  613. }
  614. structMapMutex.Lock()
  615. structMap[st] = sinfo
  616. structMapMutex.Unlock()
  617. return sinfo, nil
  618. }