k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/validation/strfmt/bson/objectid.go (about)

     1  // 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  //
     7  // Based on gopkg.in/mgo.v2/bson by Gustavo Niemeyer
     8  // See THIRD-PARTY-NOTICES for original license terms.
     9  
    10  package bson
    11  
    12  import (
    13  	"bytes"
    14  	"encoding/hex"
    15  	"encoding/json"
    16  	"errors"
    17  	"fmt"
    18  )
    19  
    20  // ErrInvalidHex indicates that a hex string cannot be converted to an ObjectID.
    21  var ErrInvalidHex = errors.New("the provided hex string is not a valid ObjectID")
    22  
    23  // ObjectID is the BSON ObjectID type.
    24  type ObjectID [12]byte
    25  
    26  // NilObjectID is the zero value for ObjectID.
    27  var NilObjectID ObjectID
    28  
    29  // Hex returns the hex encoding of the ObjectID as a string.
    30  func (id ObjectID) Hex() string {
    31  	return hex.EncodeToString(id[:])
    32  }
    33  
    34  func (id ObjectID) String() string {
    35  	return fmt.Sprintf("ObjectID(%q)", id.Hex())
    36  }
    37  
    38  // IsZero returns true if id is the empty ObjectID.
    39  func (id ObjectID) IsZero() bool {
    40  	return bytes.Equal(id[:], NilObjectID[:])
    41  }
    42  
    43  // ObjectIDFromHex creates a new ObjectID from a hex string. It returns an error if the hex string is not a
    44  // valid ObjectID.
    45  func ObjectIDFromHex(s string) (ObjectID, error) {
    46  	b, err := hex.DecodeString(s)
    47  	if err != nil {
    48  		return NilObjectID, err
    49  	}
    50  
    51  	if len(b) != 12 {
    52  		return NilObjectID, ErrInvalidHex
    53  	}
    54  
    55  	var oid [12]byte
    56  	copy(oid[:], b[:])
    57  
    58  	return oid, nil
    59  }
    60  
    61  // MarshalJSON returns the ObjectID as a string
    62  func (id ObjectID) MarshalJSON() ([]byte, error) {
    63  	return json.Marshal(id.Hex())
    64  }
    65  
    66  // UnmarshalJSON populates the byte slice with the ObjectID. If the byte slice is 24 bytes long, it
    67  // will be populated with the hex representation of the ObjectID. If the byte slice is twelve bytes
    68  // long, it will be populated with the BSON representation of the ObjectID. This method also accepts empty strings and
    69  // decodes them as NilObjectID. For any other inputs, an error will be returned.
    70  func (id *ObjectID) UnmarshalJSON(b []byte) error {
    71  	// Ignore "null" to keep parity with the standard library. Decoding a JSON null into a non-pointer ObjectID field
    72  	// will leave the field unchanged. For pointer values, encoding/json will set the pointer to nil and will not
    73  	// enter the UnmarshalJSON hook.
    74  	if string(b) == "null" {
    75  		return nil
    76  	}
    77  
    78  	var err error
    79  	switch len(b) {
    80  	case 12:
    81  		copy(id[:], b)
    82  	default:
    83  		// Extended JSON
    84  		var res interface{}
    85  		err := json.Unmarshal(b, &res)
    86  		if err != nil {
    87  			return err
    88  		}
    89  		str, ok := res.(string)
    90  		if !ok {
    91  			m, ok := res.(map[string]interface{})
    92  			if !ok {
    93  				return errors.New("not an extended JSON ObjectID")
    94  			}
    95  			oid, ok := m["$oid"]
    96  			if !ok {
    97  				return errors.New("not an extended JSON ObjectID")
    98  			}
    99  			str, ok = oid.(string)
   100  			if !ok {
   101  				return errors.New("not an extended JSON ObjectID")
   102  			}
   103  		}
   104  
   105  		// An empty string is not a valid ObjectID, but we treat it as a special value that decodes as NilObjectID.
   106  		if len(str) == 0 {
   107  			copy(id[:], NilObjectID[:])
   108  			return nil
   109  		}
   110  
   111  		if len(str) != 24 {
   112  			return fmt.Errorf("cannot unmarshal into an ObjectID, the length must be 24 but it is %d", len(str))
   113  		}
   114  
   115  		_, err = hex.Decode(id[:], []byte(str))
   116  		if err != nil {
   117  			return err
   118  		}
   119  	}
   120  
   121  	return err
   122  }