gosettings

package module
v0.3.0-rc4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 28, 2023 License: MIT Imports: 3 Imported by: 18

README

Gosettings

gosettings is a Go package providing helper functions for working with settings.

Add it to your Go project with:

go get github.com/qdm12/gosettings

⚠️ Only compatible with Go 1.18+ since it now uses generics.

Features:

Philosophy

After having worked with Go and settings from different sources for years, I have come with a design I am happy with.

Settings struct

Each component has a settings struct, where the zero value of a field should be meaningless. For example, if the value 0 is allowed for a field, then it must be an *int field. On the contrary, you could have an int field if the zero value 0 is not valid. The reasoning behind this is that you want the zero Go value to be considered as 'unset field' so that the field value can be defaulted, merged with or overridden by another settings struct.

Next, each of your settings struct should implement the following interface:

type Settings interface {
 // SetDefaults sets default values for all unset fields.
 // All nilable fields must be set to something not nil.
 // Usage:
 // - Once on the base settings at the start of the program.
 // - If the user requests a reset of the settings, on a zeroed settings struct.
 SetDefaults()
 // Validate validates all the settings and return an error if any field value is invalid.
 // It should only be called after SetDefaults() is called.
 // Usage:
 // - Validate settings early at program start
 // - Validate new settings given, after calling .Copy() + .OverrideWith(newSettings)
 Validate() (err error)
 // Copy deep copies all the settings to a new Settings object.
 // Usage:
 // - Copy settings before modifying them with OverrideWith() or MergeWith(), to validate them with Validate().
 Copy() Settings
 // MergeWith sets all the unset fields of the receiver to the values of the given settings.
 // Usage:
 // - Read from different settings source with an order of precedence
 MergeWith(other Settings)
 // OverrideWith sets all the set values of the other settings to the fields of the receiver settings.
 // Usage:
 // - Write to different settings destination with an order of precedence
 OverrideWith(other Settings)
 // ToLinesNode returns a (tree) node with the settings as lines, for displaying settings
 // in a formatted tree, where you can nest settings node to display a full settings tree.
 ToLinesNode() *gotree.Node
 // String returns the string representation of the settings.
 // It should return `s.ToLinesNode().String()`.
 String() string
}

💁 you don't need to define this interface

💁 you don't need to have all these methods exported

💁 you don't need to define ToLinesNode with gotree if you don't want to

➡️ Example settings implementation

Settings methods usage

In the following Go examples, we use the example settings implementation.

Read settings from multiple sources

🚧 To be completed 🚧

You can check these 10 lines from Gluetun for a concrete example.

Updating settings at runtime

🚧 To be completed 🚧

FAQ

  • Why the github.com/qdm12/govalid dependency? It is used in the environment variables helpers sources/envhelpers.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BoolToYesNo added in v0.3.0

func BoolToYesNo(b *bool) string

BoolToYesNo returns "yes" if the given boolean is true, "no" if the given boolean is false, and an empty string if the given boolean pointer is nil.

func CopyPointer

func CopyPointer[T any](original *T) (copied *T)

CopyPointer returns a new pointer to the copied value of the original argument value.

func CopySlice

func CopySlice[T any](original []T) (copied []T)

CopySlice returns a new slice with each element of the original slice copied.

func DefaultInterface

func DefaultInterface[T any](existing T, defaultValue any) (
	result T)

DefaultInterface returns the existing argument if it is not nil, otherwise it returns the defaultValue argument. Note you should NOT use this function with concrete pointers such as *int, and only use this for interfaces. This function is not type safe nor mutation safe, be careful. If `defaultValue` does not implement the interface of `existing`, this will panic.

func DefaultNumber

func DefaultNumber[T Number](existing, other T) (result T)

DefaultNumber returns the existing argument if it is not zero, otherwise it returns the other argument.

func DefaultPointer

func DefaultPointer[T any](existing *T, defaultValue T) (result *T)

DefaultPointer returns the existing argument if it is not nil. Otherwise it returns a new pointer to the defaultValue argument. For interfaces where the underlying type is not known, use DefaultInterface instead.

func DefaultSlice

func DefaultSlice[T any](existing, other []T) (result []T)

DefaultSlice returns the existing slice argument if is not nil. Otherwise it returns a new slice with the copied values of the defaultValue slice argument. Note it is preferrable to use this function for added mutation safety on the result, but one can use DefaultSliceRaw if performance matters.

func DefaultSliceRaw

func DefaultSliceRaw[T any](existing, defaultValue []T) (result []T)

DefaultSliceRaw returns the existing slice argument if it is not nil, otherwise it returns the defaultValue slice argument.

func DefaultString

func DefaultString(existing, defaultValue string) (result string)

DefaultString returns the existing string argument if it is not empty, otherwise it returns the defaultValue string argument.

func DefaultValidator

func DefaultValidator[T SelfValidator](existing, defaultValue T) (
	result T)

DefaultValidator returns the existing argument if it is valid, otherwise it returns the defaultValue argument.

func MergeWithInterface

func MergeWithInterface[T any](existing any, other T) (
	result T)

MergeWithInterface returns the existing argument if it is not nil, otherwise it returns the other argument. Note you should NOT use this function with concrete pointers such as *int, and only use this for interfaces. This function is not type safe nor mutation safe, be careful. If `existing` does not implement the interface of `other`, this will panic.

func MergeWithNumber

func MergeWithNumber[T Number](existing, other T) (result T)

MergeWithNumber returns the existing argument if it is not zero, otherwise it returns the other argument.

func MergeWithPointer

func MergeWithPointer[T any](existing, other *T) (result *T)

MergeWithPointer returns the existing argument if it is not nil. Otherwise it returns a new pointer to the copied value of the other argument value, for added mutation safety. For interfaces where the underlying type is not known, use MergeWithInterface instead.

func MergeWithSlice

func MergeWithSlice[T any](existing, other []T) (result []T)

MergeWithSlice returns the existing slice argument if is not nil. Otherwise it returns a new slice with the copied values of the other slice argument. Note it is preferrable to use this function for added mutation safety on the result, but one can use MergeWithSliceRaw if performance matters.

func MergeWithSliceRaw

func MergeWithSliceRaw[T any](existing, other []T) (result []T)

MergeWithSliceRaw returns the existing slice argument if it is not nil, otherwise it returns the other slice argument.

func MergeWithString

func MergeWithString(existing, other string) (result string)

MergeWithString returns the existing string argument if it is not empty, otherwise it returns the other string argument.

func MergeWithValidator

func MergeWithValidator[T SelfValidator](existing, defaultValue T) (
	result T)

MergeWithValidator returns the existing argument if it is valid, otherwise it returns the defaultValue argument.

func ObfuscateKey added in v0.3.0

func ObfuscateKey(key string) (obfuscatedKey string)

ObfuscateKey returns an obfuscated key for logging purposes. If the key is empty, `[not set]` is returned. If the key has up to 128 bits of security with 2 characters removed, it will be obfuscated as `[set]`. If the key has at least 128 bits of security if at least 2 characters are removed from it, it will be obfuscated as `start_of_key...end_of_key`, where the start and end parts are each at least 1 character long, and up to 3 characters long. Note the security bits are calculated by assuming each unique character in the given key is a symbol in the alphabet, which gives a worst case scenario regarding the alphabet size. This will likely produce lower security bits estimated compared to the actual security bits of the key, but it's better this way to avoid divulging information about the key when it should not be. Finally, the 128 bits security is chosen because this function is to be used for logging purposes, which should not be exposed publicly either.

func OverrideWithInterface

func OverrideWithInterface[T any](existing T, other any) (
	result T)

OverrideWithInterface returns the other argument if it is not nil, otherwise it returns the existing argument. Note you should NOT use this function with concrete pointers such as *int, and only use this for interfaces. This function is not type safe nor mutation safe, be careful. If `other` does not implement the interface of `existing`, this will panic.

func OverrideWithNumber

func OverrideWithNumber[T Number](existing, other T) (result T)

OverrideWithNumber returns the other argument if it is not zero, otherwise it returns the existing argument.

func OverrideWithPointer

func OverrideWithPointer[T any](existing, other *T) (result *T)

OverrideWithPointer returns the existing argument if the other argument is nil. Otherwise it returns a new pointer to the copied value of the other argument value, for added mutation safety. For interfaces where the underlying type is not known, use OverrideWithInterface instead.

func OverrideWithSlice

func OverrideWithSlice[T any](existing, other []T) (result []T)

OverrideWithSlice returns the existing slice argument if the other slice argument is nil. Otherwise it returns a new slice with the copied values of the other slice argument. Note it is preferrable to use this function for added mutation safety on the result, but one can use OverrideWithSliceRaw if performance matters.

func OverrideWithSliceRaw

func OverrideWithSliceRaw[T any](existing, other []T) (result []T)

OverrideWithSliceRaw returns the other slice argument if it is not nil, otherwise it returns the existing slice argument.

func OverrideWithString

func OverrideWithString(existing, other string) (result string)

OverrideWithString returns the other string argument if it is not empty, otherwise it returns the existing string argument.

func OverrideWithValidator

func OverrideWithValidator[T SelfValidator](existing, other T) (
	result T)

OverrideWithValidator returns the existing argument if other is not valid, otherwise it returns the other argument.

Types

type Number

type Number interface {
	constraints.Integer | constraints.Float
}

Number represents a number type, it can notably be an integer, a float, and any type aliases of them such as `time.Duration`.

type SelfValidator

type SelfValidator interface {
	// IsValid returns true if the value is valid, false otherwise.
	IsValid() bool
}

SelfValidator is an interface for a type that can validate itself. This is notably the case of netip.IP and netip.Prefix, and can be implemented by the user of this library as well.

Directories

Path Synopsis
examples
sources
env

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL