// Copyright (C) MongoDB, Inc. 2024-present. // // Licensed under the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 package mongoutil import ( "context" "reflect" "time" "go.mongodb.org/mongo-driver/v2/mongo/options" ) // NewOptions will functionally merge a slice of mongo.Options in a // "last-one-wins" manner, where nil options are ignored. func NewOptions[T any](opts ...options.Lister[T]) (*T, error) { args := new(T) for _, opt := range opts { if opt == nil || reflect.ValueOf(opt).IsNil() { // Do nothing if the option is nil or if opt is nil but implicitly cast as // an Options interface by the NewArgsFromOptions function. The latter // case would look something like this: continue } for _, setArgs := range opt.List() { if setArgs == nil { continue } if err := setArgs(args); err != nil { return nil, err } } } return args, nil } // OptionsLister implements an options.SetterLister object for an arbitrary // options type. type OptionsLister[T any] struct { Options *T // Arguments to set on the option type Callback func(*T) error // A callback for further modification } // List will re-assign the entire argument option to the Args field // defined on opts. If a callback exists, that function will be executed to // further modify the arguments. func (opts *OptionsLister[T]) List() []func(*T) error { return []func(*T) error{ func(args *T) error { if opts.Options != nil { *args = *opts.Options } if opts.Callback != nil { return opts.Callback(args) } return nil }, } } // NewOptionsLister will construct a SetterLister from the provided Options // object. func NewOptionsLister[T any](args *T, callback func(*T) error) *OptionsLister[T] { return &OptionsLister[T]{Options: args, Callback: callback} } // AuthFromURI will create a Credentials object given the provided URI. func AuthFromURI(uri string) (*options.Credential, error) { opts := options.Client().ApplyURI(uri) return opts.Auth, nil } // HostsFromURI will parse the hosts in the URI and return them as a slice of // strings. func HostsFromURI(uri string) ([]string, error) { opts := options.Client().ApplyURI(uri) return opts.Hosts, nil } // TimeoutWithinContext will return true if the provided timeout is nil or if // it is less than the context deadline. If the context does not have a // deadline, it will return true. func TimeoutWithinContext(ctx context.Context, timeout time.Duration) bool { deadline, ok := ctx.Deadline() if !ok { return true } ctxTimeout := time.Until(deadline) return ctxTimeout <= 0 || timeout < ctxTimeout }