github.com/letsencrypt/boulder@v0.20251208.0/config/duration.go (about)

     1  package config
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"reflect"
     7  	"time"
     8  )
     9  
    10  // Duration is custom type embedding a time.Duration which allows defining
    11  // methods such as serialization to YAML or JSON.
    12  type Duration struct {
    13  	time.Duration `validate:"required"`
    14  }
    15  
    16  // DurationCustomTypeFunc enables registration of our custom config.Duration
    17  // type as a time.Duration and performing validation on the configured value
    18  // using the standard suite of validation functions.
    19  func DurationCustomTypeFunc(field reflect.Value) any {
    20  	if c, ok := field.Interface().(Duration); ok {
    21  		return c.Duration
    22  	}
    23  
    24  	return reflect.Invalid
    25  }
    26  
    27  // ErrDurationMustBeString is returned when a non-string value is
    28  // presented to be deserialized as a ConfigDuration
    29  var ErrDurationMustBeString = errors.New("cannot JSON unmarshal something other than a string into a ConfigDuration")
    30  
    31  // UnmarshalJSON parses a string into a ConfigDuration using
    32  // time.ParseDuration.  If the input does not unmarshal as a
    33  // string, then UnmarshalJSON returns ErrDurationMustBeString.
    34  func (d *Duration) UnmarshalJSON(b []byte) error {
    35  	s := ""
    36  	err := json.Unmarshal(b, &s)
    37  	if err != nil {
    38  		var jsonUnmarshalTypeErr *json.UnmarshalTypeError
    39  		if errors.As(err, &jsonUnmarshalTypeErr) {
    40  			return ErrDurationMustBeString
    41  		}
    42  		return err
    43  	}
    44  	dd, err := time.ParseDuration(s)
    45  	d.Duration = dd
    46  	return err
    47  }
    48  
    49  // MarshalJSON returns the string form of the duration, as a byte array.
    50  func (d Duration) MarshalJSON() ([]byte, error) {
    51  	return []byte(d.Duration.String()), nil
    52  }
    53  
    54  // UnmarshalYAML uses the same format as JSON, but is called by the YAML
    55  // parser (vs. the JSON parser).
    56  func (d *Duration) UnmarshalYAML(unmarshal func(any) error) error {
    57  	var s string
    58  	err := unmarshal(&s)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	dur, err := time.ParseDuration(s)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	d.Duration = dur
    68  	return nil
    69  }
    70  
    71  // MarshalYAML returns the string form of the duration, as a string.
    72  func (d Duration) MarshalYAML() (any, error) {
    73  	return d.Duration.String(), nil
    74  }