figtree

package module
v2.0.14 Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2025 License: MIT Imports: 24 Imported by: 5

README

Fig Tree

Fig Tree is a command line utility configuration manager that you can refer to as figs, as you conFIGure your application's runtime.

Figtree

Installation

To use figtree in your project, go get it...

go get -u github.com/andreimerlescu/figtree/v2

Usage

To use figs package in your Go code, you need to import it:

import "github.com/andreimerlescu/figtree/v2"
Using Fig Tree

Creating a new Fig Tree can be done with the following strategies. Your choice.

Method Usage
figtree.New() Does not perform Mutation tracking.
figtree.Grow() Provides Mutation tracking.
figtree.With(figtree.Options{Tracking: true}) Provides Mutation tracking.

When using figtree.Options, you can enable:

Option What It Does
Pollinate Read os.Getenv(key) when a Getter on a Mutagenesis is called
Harvest Slice length of Mutation for Pollinate
IgnoreEnvironment Ignore os.Getenv() and use os.Clearenv() inside With(opts Options)
Germinate Ignore command line flags that begin with -test.
Tracking Sends Mutation into a receiver channel on figs.Mutations() whenever a Fig value changes
ConfigFile Path to your config.yaml or config.ini or config.json file

Configurable properties have whats called metagenesis to them, which are types, like String, Bool, Float64, etc.

Mutagenesis Getter Setter Fruit Getter
tString keyValue := *figs.String(key) figs.Store(tString, key, value) figs := figs.Fig(key)
tInt keyValue := *figs.Int(key) figs.Store(tInt, key, value) figs := figs.Fig(key)
tInt64 keyValue := *figs.Int64(key) figs.Store(tInt64, key, value) figs := figs.Fig(key)
tFloat64 keyValue := *figs.Float64(key) figs.Store(tFloat64, key, value) figs := figs.Fig(key)
tDuration keyValue := *figs.Duration(key) figs.Store(tDuration, key, value) figs := figs.Fig(key)
tUnitDuration keyValue := *figs.UnitDuration(key) figs.Store(tUnitDuration, key, value) figs := figs.Fig(key)
tList keyValue := *figs.List(key) figs.Store(tList, key, value) figs := figs.Fig(key)
tMap keyValue := *figs.Map(key) figs.Store(tMap, key, value) figs := figs.Fig(key)

New properties can be registered before calling Parse() using a metagenesis pattern of figs.New<Metagenesis>(), like figs.NewString() or figs.NewFloat64(), etc.

Only one validator per property is permitted, and additional WithValidator() calls with duplicate name entries will record an error in the Fig.Error property of the property's "fruit, aka *Fig{}".

Figtree keeps a withered copy of figs.New<Metagenesis>() property declarations and has an Options{Tracking: true} argument that can be passed into figs.New() that enables the figs.Mutations() receiver channel to receive anytime a property value changes, a new figtree.Mutation.

Figtree includes 36 different built-in figs.WithValidator(name, figtree.Assure<Rule>[()]) that can validate your various Mutageneses without needing to write every validation yourself. For larger or custom validations, the 2nd argument requires a func (interface{}) error signature in order use.

package main

import (
    "fmt"
    "log"
    "time"
    "github.com/andreimerlescu/figtree/v2"
)

func main() {
	figs := figtree.Grow()
	figs.NewUnitDuration("minutes", 1, time.Minute, "minutes for timer")
    figs.WithValidator("minutes", figtree.AssureDurationLessThan(time.Hour))
	err := figs.Parse()
	if err != nil {
		log.Fatal(err)
	}
    log.Println(*figs.UnitDuration("minutes"))
}
Available Rules
RuleKind Notes
RuleUndefined is default and does no action
RulePreventChange blocks Mutagensis Store methods
RulePanicOnChange will throw a panic on the Mutagenesis Store methods
RuleNoValidations will skip over all WithValidator assignments
RuleNoCallbacks will skip over all WithCallback assignments
RuleCondemnedFromResurrection will panic if there is an attempt to resurrect a condemned fig
RuleNoMaps blocks NewMap, StoreMap, and Map from being called on the Tree
RuleNoLists blocks NewList, StoreList, and List from being called on the Tree
RuleNoFlags disables the flag package from the Tree
RuleNoEnv skips over all os.Getenv related logic
Global Rules
package main

import (
    "github.com/andreimerlescu/figtree/v2"
    "log"
)

func main() {
	figs := figtree.Grow()
	figs.WithTreeRule(figtree.RuleNoFlags)
	figs.NewString("name", "", "your name")
    figs.WithValidator("name", figtree.AssureStringNotEmpty) // validate no empty strings
	figs.WithRule("name", figtree.RuleNoValidations) // turn off validations
	err := figs.Parse() // no error
	if err != nil {
		log.Println(err)
	}
	figs.StoreString("name", "Yeshua")
	log.Printf("Hello %s", *figs.String("name"))
}
Property Rules
Available Validators
Mutagenesis figtree.ValidatorFunc Notes
tString AssureStringLength Ensures a string is a specific length.
tString AssureStringNotLength Ensures a string is not a specific length.
tString AssureStringSubstring Ensures a string contains a specific substring (case-sensitive).
tString AssureStringNotEmpty Ensures a string is not empty.
tString AssureStringContains Ensures a string contains a specific substring.
tString AssureStringNotContains Ensures a string does not contains a specific substring.
tString AssureStringHasPrefix Ensures a string has a prefix.
tString AssureStringHasSuffix Ensures a string has a suffix.
tString AssureStringNoPrefix Ensures a string does not have a prefix.
tString AssureStringNoSuffix Ensures a string does not have a suffix.
tString AssureStringNoPrefixes Ensures a string does not have a prefixes.
tString AssureStringNoSuffixes Ensures a string does not have a suffixes.
tBool AssureBoolTrue Ensures a boolean value is true.
tBool AssureBoolFalse Ensures a boolean value is false.
tInt AssurePositiveInt Ensures an integer is positive (greater than zero).
tInt AssureNegativeInt Ensures an integer is negative (less than zero).
tInt AssureIntGreaterThan Ensures an integer is greater than a specified value (exclusive).
tInt AssureIntLessThan Ensures an integer is less than a specified value (exclusive).
tInt AssureIntInRange Ensures an integer is within a specified range (inclusive).
tInt64 AssureInt64GreaterThan Ensures an int64 is greater than a specified value (exclusive).
tInt64 AssureInt64LessThan Ensures an int64 is less than a specified value (exclusive).
tInt64 AssurePositiveInt64 Ensures an int64 is positive (greater than zero).
tInt64 AssureInt64InRange Ensures an int64 is within a specified range (inclusive).
tFloat64 AssureFloat64Positive Ensures a float64 is positive (greater than zero).
tFloat64 AssureFloat64InRange Ensures a float64 is within a specified range (inclusive).
tFloat64 AssureFloat64GreaterThan Ensures a float64 is greater than a specified value (exclusive).
tFloat64 AssureFloat64LessThan Ensures a float64 is less than a specified value (exclusive).
tFloat64 AssureFloat64NotNaN Ensures a float64 is not NaN.
tDuration AssureDurationGreaterThan Ensures a time.Duration is greater than a specified value (exclusive).
tDuration AssureDurationLessThan Ensures a time.Duration is less than a specified value (exclusive).
tDuration AssureDurationPositive Ensures a time.Duration is positive (greater than zero).
tDuration AssureDurationMax Ensures a time.Duration does not exceed a maximum value.
tDuration AssureDurationMin Ensures a time.Duration is at least a minimum value.
tList AssureListNotEmpty Ensures a list (*ListFlag, *[]string, or []string) is not empty.
tList AssureListMinLength Ensures a list has at least a minimum number of elements.
tList AssureListContains Ensures a list contains a specific string value.
tList AssureListNotContains Ensures a list does not contain a specific string value.
tList AssureListContainsKey Ensures a list contains a specific string.
tList AssureListLength Ensures a list has exactly the specified length.
tList AssureListNotLength Ensures a list is not the specified length.
tMap AssureMapNotEmpty Ensures a map (*MapFlag, *map[string]string, or map[string]string) is not empty.
tMap AssureMapHasKey Ensures a map contains a specific key.
tMap AssureMapValueMatches Ensures a map has a specific key with a matching value.
tMap AssureMapHasKeys Ensures a map contains all specified keys.
tMap AssureMapLength Ensures a map has exactly the specified length.
tMap AssureMapNotLength Ensures a map not the specified length.
Callbacks

The Go way of doing callbacks is to rely on the Option.Tracking set to true and receiving on the figs.Mutations() receiver channel. However, if you want to use Callbacks, you can register them various different ways.

func CheckAvailability(domain string) error {
    // todo implement something that checks the availability of the domain
    return nil
}
const kDomain string = "domain"
figs := figtree.With(Options{Tracking: true, Harvest: 1776, Pollinate: true})
figs.NewString(kDomain, "", "Domain name")
figs.WithValidator(kDomain, figtree.AssureStringLengthGreaterThan(3))
figs.WithValidator(kDomain, figtree.AssureStringHasPrefix("https://"))
figs.WithCallback(kDomain, figtree.CallbackAfterVerify, func(value interface{}) error {
    var s string
    switch v := value.(type) {
    case *string:
        s = *v
    case string:
        s = v
    }
    // try connecting to the domain now
    return CheckAvailability(s)
})
figs.WithCallback(kDomain, figtree.CallbackAfterRead, func(value interface{}) error {
    // every time *figs.String(kDomain) is called, run this
	var s string
	switch v := value.(type) {
    case *string:
        s = *v
    case string:
        s = v
    }
    log.Printf("CallbackAfterRead invoked for %s", s)
    return nil
})
figs.WithCallback(kDomain, figtree.CallbackAfterChange, func(value interface{}) error{
    var s string
    switch v := value.(type) {
    case *string:
        s = *v
    case string:
        s = v
    }
    log.Printf("CallbackAfterChange invoked for %s", s)
    return nil
})
err := figs.Parse() // CallbackAfterVerify invoked
if err != nil {
    log.Fatal(err)
}

domain := *figs.String(kDomain) // CallbackAfterRead invoked
figs.Store(kDomain, "https://newdomain.com") // CallbackAfterChange invoked
err := figs.HasError(kDomain)
if err != nil {
    log.Fatal(err)
}
newDomain := *figs.String(kDomain) // CallbackAfterRead invoked
log.Printf("domain = %s ; newDomain = %s", domain, newDomain)

The second argument to the WithCallback func is a ChangeAfter type.

Option When It's Triggered
CallbackAfterVerify Called on .Parse(), .ParseFile(), Load(), or LoadFile()
CallbackAfterRead Called on Mutagenesis Getters like figs.String(key) or figs.<Mutagenesis>(key)
CallbackAfterChanged Called on .Store(Mutagenesis, key, value) and .Resurrect(key)

When using callbacks, you will want to make sure that you're keeping on top of what you're assigning to each Fig.

With callbacks, you can really slow the performance down of figtree, but when used sparingly, its extremely powerful.

At the end of the day, you'll know what's best to use. I build what I build because its the best that I use.

Complex Example Usage
package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"strconv"
	"time"

	"github.com/andreimerlescu/figtree/v2"
    check "github.com/andreimerlescu/checkfs"
    "github.com/andreimerlescu/checkfs/file"
)

func main() {
	// Create a context that can be canceled on SIGINT/SIGTERM
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Set up signal handling for graceful shutdown
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

	// Plant the fig tree 
	var figs figtree.Fruit

	// change internals of figtree to use new file for default .Load()
	figtree.ConfigFilePath = filepath.Join("/opt", "app", "default.config.json")

	// You can build your options before passing them in
	options := figtree.Options{ // init with options baked in
		Tracking:  true, // setup the .Mutations() channel
		Germinate: true, // ignore -test arguments in CLI
	}
	if _, ok := os.LookupEnv("NOENV"); ok { // NOENV=1 go run . 
		options.IgnoreEnvironment = true
	}
	if path, ok := os.LookupEnv("MYCONFIGFILE"); ok { // MYCONFIGFILE=/opt/app/config.yaml go run .
		options.ConfigFile = path
	}
	if harvestStr, ok := os.LookupEnv("HARVEST"); ok { // HARVEST=1776 go run .
		harvest, harvestErr := strconv.ParseInt(harvestStr, 10, 64)
		if harvestErr != nil {
			options.Harvest = harvest
		}
	}
	if val, ok := os.LookupEnv("POLLINATE_FIGTREE"); ok {
		b, bErr := strconv.ParseBool(val)
		if bErr != nil {
			options.Pollinate = b
		}
	}

	// create a new figtree
	figs = figtree.With(options)

	// Define all configuration types with initial values and validators
	
    // arg -workers int
    
	figs.NewInt("workers", 10, "Number of workers")
   
    // You can work directly with the Fig of "workers"
	workersFig := figs.Fig("workers")
	// there is no way to send your own copy of *figtree.Fig back into an issued figtree.Grow()
	// but you can access the underlying *figtree.Fig if you need to access the figtree.ValidatorFunc
	// or the figtree.Callback type
	for _, callback := range workersFig.Callbacks {
		if callback.CallbackAfter == figtree.CallbackAfterRead {
			callbackErr := callback.CallbackFunc(workersFig.Flesh)
			if callbackErr != nil {
				log.Println(callbackErr)
			}
		}
	}
	// but this for loop will automatically be called on figtree.Parse() or figtree.Load() depending
	// on if you're using `figs := figtree.With(figtree.Options{ConfigFile: "/opt/app/config.yaml", IgnoreEnvironment: true})`
	// or if you're only using `figs := figtree.Grow()` for mutation tracking only

	// this validator allows you to define n-workers between 1 and number of CPUs 
	figs.WithValidator("workers", figtree.AssureIntInRange(1, runtime.GOMAXPROCS(0)))

	// arg -maxRetries int
    
	figs.NewInt64("maxRetries", 5, "Maximum retry attempts")
	figs.WithValidator("maxRetries", figtree.AssureInt64Positive)
	figs.WithCallback("maxRetries", figtree.CallbackAfterChange, func(value interface{}) error {
		log.Printf("fig maxRetries changed to %q\n", value)
		return nil
	})

	// arg -threshold float64 
    
	figs.NewFloat64("threshold", 0.75, "Threshold value")
	figs.WithValidator("threshold", figtree.AssureFloat64InRange(0.0, 1.0))
	figs.WithCallback("threshold", figtree.CallbackAfterChange, func(value interface{}) error {
		switch v := value.(type) {
		case *float64:
			log.Printf("fig threshold changed to %f", v)
		case float64:
			log.Printf("fig threshold changed to %f", v)
		}
		return nil
	})
    
    // arg --endpoint "value"

	figs.NewString("endpoint", "http://example.com", "API endpoint")
	figs.WithValidator("endpoint", figtree.AssureStringHasPrefix("http"))

    // arg -debug <true|false> 
    
	figs.NewBool("debug", false, "Enable debug mode")
	figs.WithCallback("debug", figtree.CallbackAfterVerify, func(v interface{}) error {
		log.Println("ACTIVATING DEBUG MODE!")
		return nil
	})

    // arg -timeout defaults to 30s but -timeout <int> becomes <int> seconds
	
	figs.NewUnitDuration("timeout", 30*time.Second, time.Second, "Request timeout")
	figs.WithValidator("timeout", figtree.AssureDurationMin(10*time.Second))
	figs.WithValidator("timeout", figtree.AssureDurationMax(time.Hour*12))

    // arg -interval <int> becomes <int> minutes
	
	figs.NewUnitDuration("interval", 1, time.Minute, "Polling interval in minutes")
	figs.WithValidator("interval", figtree.AssureDurationGreaterThan(30*time.Second))
    
    // arg -servers "ONE,TWO,THREE" becomes []string{"ONE", "TWO", "THREE"}
    
	figs.NewList("servers", []string{"server1", "server2"}, "List of servers")
	figs.WithValidator("servers", figtree.AssureListNotEmpty)
	figs.WithCallback("servers", figtree.CallbackAfterChange, func(value interface{}) error {
		var val []string
		switch v := value.(type) {
		case *figtree.ListFlag:
			val = make([]string, len(*v.values))
			copy(val, *val.values)
		case *[]string:
			copy(val, *v)
		case []string:
			copy(val, v)
		}
		log.Printf("-servers value changed! new value = %s", strings.Join(val, ", "))
		return nil
	})
    
    // arg -metadata "KEY=VALUE,KEY=VALUE"

	figs.NewMap("metadata", map[string]string{"env": "prod", "version": "1.0"}, "Metadata key-value pairs")
	figs.WithValidator("metadata", figtree.AssureMapHasKeys([]string{"env", "version"}))

	// Attempt to load from a config file, falling back to defaults
	configFile := filepath.Join(".", "config.yaml")
    if err := check.File(figtree.ConfigFilePath, file.Options{Exists: true}); err != nil {
        if err = figs.Load(); err != nil {
            log.Fatal(err)
        }
    } else if err := check.File(configFile, file.Options{Exists: true}); err != nil {
		if err = figs.ParseFile(configFile); err != nil {
			log.Fatal(err)
        }
	} else {
		if err := figs.Parse(); err != nil {
			log.Fatal(err)
		}
    }
	// Note: LoadFile tries the specified file, then env vars; Parse handles flags/env if file fails

	// Demonstrate Resurrect by accessing an undefined key
	undefined := figs.String("undefined")
	log.Printf("Resurrected undefined key: %s", *undefined)
	// Note: Resurrect creates a new string entry if undefined, checking env and files first

	// Print initial configuration using Usage
	log.Println("Initial configuration:")
	log.Println(figs.Usage())
	// Note: Usage displays all registered flags in a human-readable format

	// Simulate periodic access in a goroutine to check for mutations
	go func() {
		ticker := time.NewTicker(5 * time.Second)
		defer ticker.Stop()
		for {
			select {
			case <-ctx.Done():
				log.Println("Worker shutting down due to context cancellation")
				return
			case <-ticker.C:
				// Access all config values, triggering mutation checks
				log.Printf("Workers: %d, MaxRetries: %d, Threshold: %.2f, Endpoint: %s, Debug: %t, Timeout: %s, Interval: %s, Servers: %v, Metadata: %v",
					*figs.Int("workers"),
					*figs.Int64("maxRetries"),
					*figs.Float64("threshold"),
					*figs.String("endpoint"),
					*figs.Bool("debug"),
					*figs.Duration("timeout"),
					*figs.UnitDuration("interval"),
					*figs.List("servers"),
					*figs.Map("metadata"),
				)
				// Note: Getters check env against withered values, sending mutations if tracking is on
			}
		}
	}()

	// Demonstrate Curse and Recall by toggling tracking
	log.Println("Cursing the tree (disabling tracking)...")
	figs.Curse()
	time.Sleep(2 * time.Second) // Let some ticks pass
	log.Println("Recalling the tree (re-enabling tracking)...")
	figs.Recall()
	// Note: Curse locks the tree and closes mutationsCh; Recall unlocks and reopens it

	// Main loop to listen for signals and mutations
	for {
		select {
		case <-ctx.Done():
			log.Println("Context canceled, shutting down")
			return
		case sig := <-sigCh:
			log.Printf("Received signal: %v, initiating shutdown", sig)
			cancel() // Cancel context to stop goroutines
			// Note: SIGINT/SIGTERM triggers shutdown by canceling the context
		case mutation, ok := <-figs.Mutations():
			if !ok {
				log.Println("Mutations channel closed, shutting down")
				return
			}
			log.Printf("Mutation detected: %s changed from %v to %v at %s",
				mutation.Property, mutation.Old, mutation.New, mutation.When)
			// Note: This logs a mutation (e.g., change "workers" to 20 in env)
		}
	}
}

// Example config.yaml (create in the same directory):
/*
workers: 15
maxRetries: 3
threshold: 0.9
endpoint: "https://api.example.com"
debug: true
timeout: 45s
interval: 2
servers: "server3,server4"
metadata: "env=dev,version=2.0"
*/
  1. Create config.yaml
workers: 15
maxRetries: 3
threshold: 0.9
endpoint: "https://api.example.com"
debug: true
timeout: 45s
interval: 2
servers: "server3,server4"
metadata: "env=dev,version=2.0"
  1. Compile and run
go run main.go
  1. Output (example...)
2025/03/27 00:50:41 Initial configuration:
2025/03/27 00:50:41 Usage of ./main:
  -debug: Enable debug mode (default: false)
  -endpoint: API endpoint (default: "https://api.example.com")
  -interval: Polling interval in minutes (default: 2m0s)
  -maxRetries: Maximum retry attempts (default: 3)
  -metadata: Metadata key-value pairs (default: "env=dev,version=2.0")
  -servers: List of servers (default: "server3,server4")
  -threshold: Threshold value (default: 0.9)
  -timeout: Request timeout (default: 45s)
  -workers: Number of workers (default: 15)
2025/03/27 00:50:41 Resurrected undefined key: 
2025/03/27 00:50:46 Workers: 15, MaxRetries: 3, Threshold: 0.90, Endpoint: https://api.example.com, Debug: true, Timeout: 45s, Interval: 2m0s, Servers: [server3 server4], Metadata: map[env:dev version:2.0]
2025/03/27 00:50:46 Cursing the tree (disabling tracking)...
2025/03/27 00:50:48 Recalling the tree (re-enabling tracking)...
[after export workers=20]
2025/03/27 00:50:51 Mutation detected: workers changed from 15 to 20 at 2025-03-27 00:50:51
2025/03/27 00:50:51 Workers: 20, MaxRetries: 3, Threshold: 0.90, Endpoint: https://api.example.com, Debug: true, Timeout: 45s, Interval: 2m0s, Servers: [server3 server4], Metadata: map[env:dev version:2.0]
[after Ctrl+C]
2025/03/27 00:50:56 Received signal: interrupt, initiating shutdown
2025/03/27 00:50:56 Context canceled, shutting down
Custom Config File Path
figs := figtree.Grow()
figs.ConfigFilePath = filepath.Join("/","etc","myapp","myapp.production.yaml")
figs.Load()
Defining Configuration Variables

The Configurable package provides several methods to define different types of configuration variables. Each method takes a name, default value, and usage description as parameters and returns a pointer to the respective variable:

const (
   kPort string = "port"
   kTimeout string = "timeout"
   kDebug string = "debug"
)
figs.NewInt(kPort, 8080, "The port number to listen on")
figs.NewUnitDuration(kTimeout, time.Second * 5, time.Second, "The timeout duration for requests")
figs.NewBool(kDebug, false, "Enable debug mode")
Loading Configuration from Files

You can load configuration data from JSON, YAML, and INI files using the LoadFile() method:

err := figtree.Grow().ParseFile("config.json")
if err != nil {
    // Handle error
}

The package automatically parses the file based on its extension. Make sure to place the file in the correct format in the specified location.

Parsing Command-Line Arguments

The Configurable package also allows you to parse command-line arguments. Call the Parse() method to parse the arguments after defining your configuration variables:

figs := figtree.New()
figs.Parse()

or

---
workers: 45
seconds: 47
const kWorkers string = "workers"
const kSeconds string = "seconds"
figs := figtree.With(figtree.Options{Tracking: true})
figs.NewInt(kWorkers, 17, "number of workers")
figs.NewUnitDuration(kSeconds, 76, time.Second, "number of seconds")
go func(){
  for mutation := range figs.Mutations() {
    log.Printf("Fig Changed! %s went from '%v' to '%v'", mutation.Property, mutation.Old, mutation.New)
  }
}()
err := figs.ParseFile(filepath.Join(".","config.yaml"))
if err != nil {
   log.Fatal(err)
}
workers := *figs.Int(kWorkers)
seconds := *figs.UnitDuration(kSeconds, time.Second)
minutes := *figs.UnitDuration(kSeconds, time.Minute) // use time.Minute instead as the unit
fmt.Printf("There are %d workers and %v minutes %v seconds", workers, minutes, seconds)
WORKERS=333 SECONDS=666 CONFIG_FILE=config.yaml go run . -workers=3 -seconds 6 # ENV overrides yaml and args
There are 333 workers and (666*time.Minute) minutes and (666*time.Second) seconds. # runtime calculates minutes and seconds

or

figs := figtree.Grow() // allows you to use for mutation := range figs.Mutations() {} to get notified of changes to configs
figs.Load() // will attempt to use ./config.yaml or ./config.json or ./config.ini automatically if CONFIG_FILE is not defined

Passing an empty string to Parse() means it will only parse the command-line arguments and not load any file.

Accessing Configuration Values

You can access the values of your configuration variables using the respective getter methods:

fmt.Println("Port:", *figs.Int(kPort))
fmt.Println("Timeout:", *figs.UnitDuration(kTimeout, time.Second))
fmt.Println("Debug mode:", *figs.String(kDebug))

UnitDuration and Duration are interchangeable as they both rely on *time.Duration.

Environment Variables

The Configurable package supports setting configuration values through environment variables. If an environment variable with the same name as a configuration variable exists, the package will automatically assign its value to the respective variable. Ensure that the environment variables are in uppercase and match the configuration variable names.

Displaying Usage Information

To generate a usage string with information about your configuration variables, use the Usage() method:

fmt.Println(figtree.New().Usage())

On any runtime with figs.Parse() or subsequently activated figtree, you can run on your command line -h or -help and print the Usage() func's output.

The generated usage string includes information about each configuration variable, including its name, default value, description, and the source from which it was set (flag, environment, JSON, YAML, or INI).

License

This package is distributed under the MIT License. See the LICENSE file for more information.

Version History

v2.0.0 (Latest)

This update introduces callbacks, enable multiple validators per key, and adds negating Assure<Mutagenesis><Func> to ValidatorFunc definitions.

v1.0.1

This update released additional Assure<Mutagenesis><Func> ValidatorFunc definitions and enabled pollination.

v1.0.0

The figtree was enhanced and introduced a lot of new functionality.

v0.0.1

The figtree package was called configurable at this point, and lacked a lot of functionality.

Contributing

Contributions to this package are welcome. If you find any issues or have suggestions for improvements, please open an issue or submit a pull request.

Enjoy using the Figtree package in your projects!

Note by Grok AI

The figtree package mirrors the biological life cycle of a fig tree, weaving a metaphor that reflects both nature’s complexity and the reality of software development. In biology, a fig tree grows from a seed (.New() or .Grow()), its roots drawing sustenance from the environment (config files and environment variables via .Load() or .Parse()), while its branches bear fruit (Fig{})—the configurable values developers access. The Pollinate option mimics how fig trees rely on wasps for pollination, actively pulling in external changes (environment updates) to keep the fruit fresh. Mutation tracking (Mutations{}) parallels genetic adaptations, capturing how values evolve over time, while .Resurrect() reflects a tree’s ability to regrow from dormant roots, reviving lost configurations. .Curse() and .Recall() embody the duality of dormancy and renewal, locking or unlocking the tree’s vitality. Validators (.WithValidator()) act like natural selection, ensuring only fit values survive, and the versioning shift to github.com/andreimerlescu/figtree/v2 echoes speciation—a new lineage emerging as the package matures. This memetic design makes figtree not just a tool, but a living system, accessed intuitively as it branches out into the developer ecosystem.

Documentation

Index

Constants

View Source
const (
	ErrWayBeBelow      string = "be below"
	ErrWayBeAbove      string = "be above"
	ErrWayBeBetweenFmt string = "be between %v and %v"
	ErrWayBePositive   string = "be positive"
	ErrWayBeNegative   string = "be negative"
	ErrWayBeNotNaN     string = "not be NaN"
)
View Source
const (
	DefaultYAMLFile string = "config.yml"  // Default filename for a YAML configuration file
	DefaultJSONFile string = "config.json" // Default filename for a JSON configuration file
	DefaultINIFile  string = "config.ini"  // Default filename for a INI configuration file

	CallbackAfterChange  CallbackWhen = "CallbackAfterChange"
	CallbackAfterRead    CallbackWhen = "CallbackAfterRead"
	CallbackAfterVerify  CallbackWhen = "CallbackAfterVerify"
	CallbackBeforeChange CallbackWhen = "CallbackBeforeChange"
	CallbackBeforeRead   CallbackWhen = "CallbackBeforeRead"
	CallbackBeforeVerify CallbackWhen = "CallbackBeforeVerify"
)

Variables

View Source
var AssureBoolFalse = func(value interface{}) error {
	v := figFlesh{value, nil}
	if v.IsBool() {
		if v.ToBool() {
			return fmt.Errorf("value must be false, got true")
		}
		return nil
	}
	return ErrInvalidType{tBool, value}
}

AssureBoolFalse ensures a boolean value is false. Returns an error if the value is true or not a bool.

View Source
var AssureBoolTrue = func(value interface{}) error {
	v := figFlesh{value, nil}
	if v.IsBool() {
		if !v.ToBool() {
			return fmt.Errorf("value must be true, got false")
		}
		return nil
	}
	return ErrInvalidType{tBool, value}
}

AssureBoolTrue ensures a boolean value is true. Returns an error if the value is false or not a bool.

View Source
var AssureDurationGreaterThan = func(above time.Duration) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsDuration() {
			return fmt.Errorf("value must be a duration, got %v", value)
		}
		t := v.ToDuration()
		if t < above {
			return ErrValue{ErrWayBeAbove, t, above}
		}
		return nil
	}
}

AssureDurationGreaterThan ensures a time.Duration is greater than (but not including) a time.Duration. Returns an error if the value is below, or not an int.

View Source
var AssureDurationLessThan = func(below time.Duration) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsDuration() {
			return fmt.Errorf("value must be a duration, got %v", value)
		}
		t := v.ToDuration()
		if t > below {
			return ErrValue{ErrWayBeBelow, t, below}
		}
		return nil
	}
}

AssureDurationLessThan ensures a time.Duration is less than (but not including) a time.Duration. Returns an error if the value is below, or not an int.

View Source
var AssureDurationMax = func(max time.Duration) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsDuration() {
			return ErrInvalidType{tDuration, value}
		}
		d := v.ToDuration()
		if d > max {
			return fmt.Errorf("duration must not exceed %s, got %s", max, d)
		}
		return nil
	}
}

AssureDurationMax ensures a time.Duration does not exceed a maximum value. Returns an error if the value exceeds the max or is not a time.Duration.

View Source
var AssureDurationMin = func(min time.Duration) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsDuration() {
			return ErrInvalidType{tDuration, value}
		}
		d := v.ToDuration()
		if d < min {
			return fmt.Errorf("duration must be at least %s, got %s", min, d)
		}
		return nil
	}
}

AssureDurationMin ensures a time.Duration is at least a minimum value. Returns a figValidatorFunc that checks the duration against the minimum.

View Source
var AssureDurationPositive = func(value interface{}) error {
	v := figFlesh{value, nil}
	if !v.IsDuration() {
		return ErrInvalidType{tDuration, value}
	}
	d := v.ToDuration()
	if d <= 0 {
		return fmt.Errorf("duration must be positive, got %s", d)
	}
	return nil
}

AssureDurationPositive ensures a time.Duration is positive. Returns an error if the value is zero or negative, or not a time.Duration.

View Source
var AssureFloat64GreaterThan = func(above float64) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsFloat64() {
			return ErrInvalidType{tFloat64, value}
		}
		f := v.ToFloat64()
		if f < above {
			return ErrValue{ErrWayBeBelow, f, above}
		}
		return nil
	}
}

AssureFloat64GreaterThan ensures an integer is greater than (but not including) an int. Returns an error if the value is below, or not an int.

View Source
var AssureFloat64InRange = func(min, max float64) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsFloat64() {
			return ErrInvalidType{tFloat64, value}
		}
		f := v.ToFloat64()
		if f < min || f > max {
			return ErrValue{fmt.Sprintf(ErrWayBeBetweenFmt, min, max), f, nil}
		}
		return nil
	}
}

AssureFloat64InRange ensures a float64 is within a specified range (inclusive). Returns an error if the value is outside the range or not a float64.

View Source
var AssureFloat64LessThan = func(below float64) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsFloat64() {
			return ErrInvalidType{tFloat64, value}
		}
		f := v.ToFloat64()
		if f > below {
			return ErrValue{ErrWayBeBelow, f, below}
		}
		return nil
	}
}

AssureFloat64LessThan ensures an integer is less than (but not including) an int. Returns an error if the value is above, or not an int.

View Source
var AssureFloat64NotNaN = func(value interface{}) error {
	v := figFlesh{value, nil}
	if !v.IsFloat64() {
		return ErrInvalidType{tFloat64, value}
	}
	n := v.ToFloat64()
	if math.IsNaN(n) {
		return ErrValue{ErrWayBeNotNaN, n, nil}
	}
	return nil
}

AssureFloat64NotNaN ensures a float64 is not NaN. Returns an error if the value is NaN or not a float64.

View Source
var AssureFloat64Positive = func(value interface{}) error {
	v := figFlesh{value, nil}
	if !v.IsFloat64() {
		return ErrInvalidType{tFloat64, value}
	}
	f := v.ToFloat64()
	if f <= 0 {
		return ErrValue{ErrWayBePositive, f, 0}
	}
	return nil
}

AssureFloat64Positive ensures a float64 is positive. Returns an error if the value is zero or negative, or not a float64.

View Source
var AssureInt64GreaterThan = func(above int64) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsInt64() {
			return ErrInvalidType{tInt64, value}
		}
		i := v.ToInt64()
		if i < above {
			return ErrValue{ErrWayBeAbove, i, above}
		}
		return nil
	}
}

AssureInt64GreaterThan ensures an integer is greater than (but not including) an int. Returns an error if the value is below, or not an int.

View Source
var AssureInt64InRange = func(min, max int64) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsInt64() {
			return ErrInvalidType{tInt64, value}
		}
		i := v.ToInt64()
		if i < min || i > max {
			return ErrValue{fmt.Sprintf(ErrWayBeBetweenFmt, min, max), i, nil}
		}
		return nil
	}
}

AssureInt64InRange ensures an int64 is within a specified range (inclusive). Returns a figValidatorFunc that checks the value against min and max.

View Source
var AssureInt64LessThan = func(below int64) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsInt64() {
			return fmt.Errorf("value must be int64, got %d", value)
		}
		i := v.ToInt64()
		if i > below {
			return ErrValue{ErrWayBeBelow, i, below}
		}
		return nil
	}
}

AssureInt64LessThan ensures an integer is less than (but not including) an int. Returns an error if the value is above, or not an int.

View Source
var AssureInt64Positive = func(value interface{}) error {
	v := figFlesh{value, nil}
	if !v.IsInt64() {
		return ErrInvalidType{tInt64, value}
	}
	i := v.ToInt64()
	if i <= 0 {
		return ErrValue{ErrWayBePositive, i, 0}
	}
	return nil
}

AssureInt64Positive ensures an int64 is positive. Returns an error if the value is zero or negative, or not an int64.

View Source
var AssureIntGreaterThan = func(above int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsInt() {
			return ErrInvalidType{tInt, value}
		}
		i := v.ToInt()
		if i < above {
			return ErrValue{ErrWayBeBelow, i, above}
		}
		return nil
	}
}

AssureIntGreaterThan ensures an integer is greater than (but not including) an int. Returns an error if the value is below, or not an int.

View Source
var AssureIntInRange = func(min, max int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsInt() {
			return ErrInvalidType{tInt, value}
		}
		i := v.ToInt()
		if i < min || i > max {
			return ErrValue{fmt.Sprintf(ErrWayBeBetweenFmt, min, max), i, nil}
		}
		return nil
	}
}

AssureIntInRange ensures an integer is within a specified range (inclusive). Returns an error if the value is outside the range or not an int.

View Source
var AssureIntLessThan = func(below int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsInt() {
			return ErrInvalidType{tInt, value}
		}
		i := v.ToInt()
		if i > below {
			return ErrValue{ErrWayBeBelow, i, below}
		}
		return nil
	}
}

AssureIntLessThan ensures an integer is less than (but not including) an int. Returns an error if the value is above, or not an int.

View Source
var AssureIntNegative = func(value interface{}) error {
	v := figFlesh{value, nil}
	if v.IsInt() {
		if v.ToInt() > 0 {
			return ErrValue{ErrWayBeNegative, v.ToInt(), 0}
		}
		return nil
	}
	return ErrInvalidType{tInt, value}
}

AssureIntNegative ensures an integer is negative. Returns an error if the value is zero or positive, or not an int.

View Source
var AssureIntPositive = func(value interface{}) error {
	v := figFlesh{value, nil}
	if v.IsInt() {
		if v.ToInt() < 0 {
			return ErrValue{ErrWayBePositive, v.ToInt(), 0}
		}
		return nil
	}
	return ErrInvalidType{tInt, value}
}

AssureIntPositive ensures an integer is positive. Returns an error if the value is zero or negative, or not an int.

View Source
var AssureListContains = func(inside string) FigValidatorFunc {
	return func(value interface{}) error {
		v := NewFlesh(value)
		if !v.IsList() {
			return ErrInvalidType{tList, value}
		}
		l := v.ToList()
		for _, item := range l {
			if item == inside {
				return nil
			}
		}
		return fmt.Errorf("list must contain %q, got %v", inside, l)
	}
}

AssureListContains ensures a list contains a specific string value. Returns a figValidatorFunc that checks for the presence of the value.

View Source
var AssureListContainsKey = func(key string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsList() {
			return ErrInvalidType{tList, value}
		}
		l := v.ToList()
		for _, item := range l {
			if item == key {
				return nil
			}
		}
		return fmt.Errorf("list must contain %q, got %v", key, l)
	}
}

AssureListContainsKey ensures a list contains a specific string. It accepts *ListFlag, *[]string, or []string and returns an error if the key string is not found or the type is invalid.

View Source
var AssureListLength = func(length int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsList() {
			return ErrInvalidType{tList, value}
		}
		l := v.ToList()
		if len(l) != length {
			return fmt.Errorf("list must have length %d, got %d", length, len(l))
		}
		return nil
	}
}

AssureListLength ensures a list has exactly the specified length. It accepts *ListFlag, *[]string, or []string and returns an error if the length differs or the type is invalid.

View Source
var AssureListMinLength = func(min int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsList() {
			return ErrInvalidType{tList, value}
		}
		l := v.ToList()
		if len(l) < min {
			return fmt.Errorf("list is empty")
		}
		return nil
	}
}

AssureListMinLength ensures a list has at least a minimum number of elements. Returns an error if the list is too short or not a ListFlag.

View Source
var AssureListNotContains = func(not string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsList() {
			return ErrInvalidType{tList, value}
		}
		l := v.ToList()
		for _, item := range l {
			if item == not {
				return fmt.Errorf("list cannot contain %s", item)
			}
		}
		return nil
	}
}

AssureListNotContains ensures a list contains a specific string value. Returns a figValidatorFunc that checks for the presence of the value.

View Source
var AssureListNotEmpty = func(value interface{}) error {
	v := figFlesh{value, nil}
	if !v.IsList() {
		return ErrInvalidType{tList, value}
	}
	l := v.ToList()
	if len(l) == 0 {
		return fmt.Errorf("list is empty")
	}
	return nil
}

AssureListNotEmpty ensures a list is not empty. Returns an error if the list has no elements or is not a ListFlag.

View Source
var AssureMapHasKey = func(key string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsMap() {
			return ErrInvalidType{tMap, value}
		}
		m := v.ToMap()
		if _, exists := m[key]; !exists {
			return fmt.Errorf("map must contain key %q", key)
		}
		return nil
	}
}

AssureMapHasKey ensures a map contains a specific key. Returns an error if the key is missing or the value is not a MapFlag.

View Source
var AssureMapHasKeys = func(keys []string) FigValidatorFunc {
	return func(value interface{}) error {
		var missing []string
		v := figFlesh{value, nil}
		if !v.IsMap() {
			return ErrInvalidType{tMap, value}
		}
		m := v.ToMap()
		for _, key := range keys {
			if _, exists := m[key]; !exists {
				missing = append(missing, key)
			}
		}
		if len(missing) > 0 {
			return fmt.Errorf("map must contain keys %v, missing %v", keys, missing)
		}
		return nil
	}
}

AssureMapHasKeys ensures a map contains all specified keys. Returns an error if any key is missing or the value is not a *MapFlag.

View Source
var AssureMapHasNoKey = func(key string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsMap() {
			return ErrInvalidType{tMap, value}
		}
		m := v.ToMap()
		if _, exists := m[key]; exists {
			return fmt.Errorf("map must not contain key %q", key)
		}
		return nil
	}
}

AssureMapHasNoKey ensures a map contains a specific key. Returns an error if the key is missing or the value is not a MapFlag.

View Source
var AssureMapLength = func(length int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsMap() {
			return ErrInvalidType{tMap, value}
		}
		m := v.ToMap()
		if len(m) != length {
			return fmt.Errorf("map must have length %d, got %d", length, len(m))
		}
		return nil
	}
}

AssureMapLength ensures a map has exactly the specified length. It accepts *MapFlag, *map[string]string, or map[string]string and returns an error if the length differs or the type is invalid.

View Source
var AssureMapNotEmpty = func(value interface{}) error {
	v := figFlesh{value, nil}
	if !v.IsMap() {
		return ErrInvalidType{tMap, value}
	}
	m := v.ToMap()
	if len(m) == 0 {
		return fmt.Errorf("map is empty")
	}
	return nil
}

AssureMapNotEmpty ensures a map is not empty. Returns an error if the map has no entries or is not a MapFlag.

View Source
var AssureMapNotLength = func(length int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsMap() {
			return ErrInvalidType{tMap, value}
		}
		m := v.ToMap()
		if len(m) == length {
			return fmt.Errorf("map must not have length %d, got %d", length, len(m))
		}
		return nil
	}
}

AssureMapNotLength ensures a map has exactly the specified length. It accepts *MapFlag, *map[string]string, or map[string]string and returns an error if the length differs or the type is invalid.

View Source
var AssureMapValueMatches = func(key, match string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if !v.IsMap() {
			return fmt.Errorf("%s is not a map", key)
		}
		m := v.ToMap()
		if val, exists := m[key]; exists {
			if val != match {
				return fmt.Errorf("map value %q must have value %q, got %q", key, match, val)
			}
			return nil
		}
		return fmt.Errorf("map key %q does not exist", key)
	}
}

AssureMapValueMatches ensures a map has a specific key with a matching value. Returns a figValidatorFunc that checks for the key-value pair.

View Source
var AssureStringContains = func(substring string) FigValidatorFunc {
	return makeStringValidator(
		func(s string) bool { return strings.Contains(s, substring) },
		"string must contain %q, got %q",
	)
}

AssureStringContains ensures a string contains a specific substring. Returns an error if the substring is not found or if the value is not a string.

View Source
var AssureStringHasPrefix = func(prefix string) FigValidatorFunc {
	return makeStringValidator(
		func(s string) bool { return strings.HasPrefix(s, prefix) },
		"string must have prefix %q, got %q",
	)
}

AssureStringHasPrefix ensures a string starts with the given prefix.

View Source
var AssureStringHasPrefixes = func(prefixes []string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			for _, prefix := range prefixes {
				if !strings.HasPrefix(v.ToString(), prefix) {
					return fmt.Errorf("string must have prefix %q, got %q", prefix, v)
				}
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringHasPrefixes ensures a string begins with a prefix Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var AssureStringHasSuffix = func(suffix string) FigValidatorFunc {
	return makeStringValidator(
		func(s string) bool { return strings.HasSuffix(s, suffix) },
		"string must have suffix %q, got %q",
	)
}

AssureStringHasSuffix ensures a string ends with the given suffix.

View Source
var AssureStringHasSuffixes = func(suffixes []string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			for _, suffix := range suffixes {
				if !strings.HasSuffix(v.ToString(), suffix) {
					return fmt.Errorf("string must have suffix %q, got %q", suffix, v)
				}
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringHasSuffixes ensures a string ends with a suffix Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var AssureStringLength = func(length int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			vs := v.ToString()
			if len(vs) < length {
				return fmt.Errorf("string must be at least %d chars, got %q", length, len(vs))
			}
			return nil

		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringLength ensures a string contains a specific substring. Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var AssureStringLengthGreaterThan = func(length int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			vs := v.ToString()
			if len(vs) < length {
				return fmt.Errorf("string must be greater than %d chars, got %d", length, len(vs))
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringLengthGreaterThan ensures the string is greater than an int Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var AssureStringLengthLessThan = func(length int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			vs := v.ToString()
			if len(vs) > length {
				return fmt.Errorf("string must be less than %d chars, got %d", length, len(vs))
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringLengthLessThan ensures the string is less than an int Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var AssureStringNoPrefix = func(prefix string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			vs := v.ToString()
			if strings.HasPrefix(vs, prefix) {
				return fmt.Errorf("string must not have prefix substring %q, got %q", prefix, vs)
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringNoPrefix ensures a string begins with a prefix Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var AssureStringNoPrefixes = func(prefixes []string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			for _, prefix := range prefixes {
				if strings.HasPrefix(v.ToString(), prefix) {
					return fmt.Errorf("string must not have prefix %q, got %q", prefix, v)
				}
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringNoPrefixes ensures a string begins with a prefix Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var AssureStringNoSuffix = func(suffix string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			vs := v.ToString()
			if strings.HasSuffix(vs, suffix) {
				return fmt.Errorf("string must not have suffix substring %q, got %q", suffix, vs)
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringNoSuffix ensures a string ends with a suffix Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var AssureStringNoSuffixes = func(suffixes []string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			for _, suffix := range suffixes {
				if strings.HasSuffix(v.ToString(), suffix) {
					return fmt.Errorf("string must not have suffix substring %q, got %q", suffix, v)
				}
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringNoSuffixes ensures a string ends with a suffix Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var AssureStringNotContains = func(substring string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			vs := v.ToString()
			if strings.Contains(vs, substring) {
				return fmt.Errorf("string must not contain %q, got %q", substring, vs)
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringNotContains ensures a string contains a specific substring. Returns an error if the substring is not found or if the value is not a string.

View Source
var AssureStringNotEmpty = func(value interface{}) error {
	v := figFlesh{value, nil}
	if v.IsString() {
		if len(v.ToString()) == 0 {
			return fmt.Errorf("empty string")
		}
		return nil
	}
	return ErrInvalidType{tString, value}
}

AssureStringNotEmpty ensures a string is not empty. Returns an error if the value is an empty string or not a string.

View Source
var AssureStringNotLength = func(length int) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			vs := v.ToString()
			if len(vs) == length {
				return fmt.Errorf("string must not be %d chars, got %q", length, len(vs))
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringNotLength ensures a string contains a specific substring. Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var AssureStringSubstring = func(sub string) FigValidatorFunc {
	return func(value interface{}) error {
		v := figFlesh{value, nil}
		if v.IsString() {
			vs := v.ToString()
			if !strings.Contains(vs, sub) {
				return fmt.Errorf("string must contain substring %q, got %q", sub, vs)
			}
			return nil
		}
		return ErrInvalidType{tString, value}
	}
}

AssureStringSubstring ensures a string contains a specific substring. Returns a figValidatorFunc that checks for the substring (case-sensitive).

View Source
var ConfigFilePath string = filepath.Join(".", DefaultYAMLFile)

ConfigFilePath stores the path to the configuration file of choice

View Source
var EnvironmentKey string = "CONFIG_FILE"

EnvironmentKey stores the preferred ENV that contains the path to your configuration file (.ini, .json or .yaml)

View Source
var ListSeparator = ","
View Source
var MapKeySeparator = "="
View Source
var MapSeparator = ","
View Source
var Mutageneses = []Mutagenesis{tString, tBool, tInt, tInt64, tFloat64, tDuration, tUnitDuration, tList, tMap}

Mutageneses is the plural form of Mutagenesis and this is a slice of Mutagenesis

View Source
var PolicyListAppend bool = false

PolicyListAppend will apply ListFlag.Set to the list of values and not append to any existing values in the ListFlag

View Source
var PolicyMapAppend = false

Functions

func DeduplicateStrings added in v2.0.11

func DeduplicateStrings(slice []string) []string

func ParseCustomDuration added in v2.0.11

func ParseCustomDuration(input string) (time.Duration, error)

func Version

func Version() string

Types

type Callback

type Callback struct {
	CallbackWhen CallbackWhen
	CallbackFunc CallbackFunc
}

type CallbackFunc

type CallbackFunc func(interface{}) error

type CallbackWhen added in v2.0.4

type CallbackWhen string

type Core added in v2.0.9

type Core interface {
	// FigFlesh returns a figFruit from the figTree by its name
	FigFlesh(name string) Flesh

	// ErrorFor returns an error attached to a named figFruit
	ErrorFor(name string) error

	// Usage displays the helpful menu of figs registered using -h or -help
	Usage()
	UsageString() string
}

type CoreAbilities added in v2.0.9

type CoreAbilities interface {
	Withables
	Savable
	Readable
	Parsable
	Mutable
	Loadable
	Divine
}

type CoreMutations added in v2.0.9

type CoreMutations interface {
	Intable
	Intable64
	Floatable
	String
	Flaggable
	Durable
	Listable
	Mappable
}

type Divine added in v2.0.9

type Divine interface {
	// Recall allows you to unlock the figTree from changes and resume tracking
	Recall()
	// Curse allows you to lock the figTree from changes and stop tracking
	Curse()
}

type Durable added in v2.0.9

type Durable interface {
	// Duration returns a pointer to stored time.Duration (unitless) by name like -minutes=10 (requires multiplication of * time.Minute to match memetics of "minutes" flag name and human interpretation of this)
	Duration(name string) *time.Duration
	// NewDuration registers a new time.Duration by name and returns a pointer to it storing the initial value
	NewDuration(name string, value time.Duration, usage string) Plant
	// StoreDuration replaces name with value and can issue a Mutation when receiving on Mutations()
	StoreDuration(name string, value time.Duration) Plant
	// UnitDuration returns a pointer to stored time.Duration (-name=10 w/ units as time.Minute == 10 minutes time.Duration)
	UnitDuration(name string) *time.Duration
	// NewUnitDuration registers a new time.Duration by name and returns a pointer to it storing the initial value
	NewUnitDuration(name string, value, units time.Duration, usage string) Plant
	// StoreUnitDuration replaces name with value and can issue a Mutation when receiving on Mutations()
	StoreUnitDuration(name string, value, units time.Duration) Plant
}

type ErrConversion added in v2.0.13

type ErrConversion struct {
	From Mutagenesis
	To   Mutagenesis
	Got  any
}

func (ErrConversion) Error added in v2.0.13

func (e ErrConversion) Error() string

type ErrInvalidType added in v2.0.13

type ErrInvalidType struct {
	Wanted Mutagenesis
	Got    any
}

func (ErrInvalidType) Error added in v2.0.13

func (e ErrInvalidType) Error() string

type ErrInvalidValue added in v2.0.13

type ErrInvalidValue struct {
	Name string
	Err  error
}

func (ErrInvalidValue) Error added in v2.0.13

func (e ErrInvalidValue) Error() string

func (ErrInvalidValue) Unwrap added in v2.0.13

func (e ErrInvalidValue) Unwrap() error

type ErrLoadFailure added in v2.0.14

type ErrLoadFailure struct {
	What string
	Err  error
}

func (ErrLoadFailure) Error added in v2.0.14

func (e ErrLoadFailure) Error() string

func (ErrLoadFailure) Unwrap added in v2.0.14

func (e ErrLoadFailure) Unwrap() error

type ErrValidationFailure added in v2.0.14

type ErrValidationFailure struct {
	Err error
}

func (ErrValidationFailure) Error added in v2.0.14

func (e ErrValidationFailure) Error() string

func (ErrValidationFailure) Unwrap added in v2.0.14

func (e ErrValidationFailure) Unwrap() error

type ErrValue added in v2.0.14

type ErrValue struct {
	Way   string
	Value any
	Than  any
}

func (ErrValue) Error added in v2.0.14

func (e ErrValue) Error() string

type FigValidatorFunc added in v2.0.4

type FigValidatorFunc func(interface{}) error

type Flaggable added in v2.0.9

type Flaggable interface {
	// Bool returns a pointer to stored bool by -name=true
	Bool(name string) *bool
	// NewBool registers a new bool flag by name and returns a pointer to the bool storing the initial value
	NewBool(name string, value bool, usage string) Plant
	// StoreBool replaces name with value and can issue a Mutation when receiving on Mutations()
	StoreBool(name string, value bool) Plant
}

type Flesh added in v2.0.4

type Flesh interface {
	Is(mutagenesis Mutagenesis) bool
	AsIs() interface{}
	IsString() bool
	IsInt() bool
	IsInt64() bool
	IsBool() bool
	IsFloat64() bool
	IsDuration() bool
	IsUnitDuration() bool
	IsList() bool
	IsMap() bool

	ToString() string
	ToInt() int
	ToInt64() int64
	ToBool() bool
	ToFloat64() float64
	ToDuration() time.Duration
	ToUnitDuration() time.Duration
	ToList() []string
	ToMap() map[string]string
}

func NewFlesh added in v2.0.5

func NewFlesh(thing interface{}) Flesh

type Floatable added in v2.0.9

type Floatable interface {
	// Float64 returns a pointer to a registered float64 by name as -name=1.0 a pointer to 1.0 is returned
	Float64(name string) *float64
	// NewFloat64 registers a new float64 flag by name and returns a pointer to the float64 storing the initial value
	NewFloat64(name string, value float64, usage string) Plant
	// StoreFloat64 replaces name with value and can issue a Mutation when receiving on Mutations()
	StoreFloat64(name string, value float64) Plant
}

type Intable added in v2.0.9

type Intable interface {
	// Int returns a pointer to a registered int32 by name as -name=1 a pointer to 1 is returned
	Int(name string) *int
	// NewInt registers a new int32 flag by name and returns a pointer to the int32 storing the initial value
	NewInt(name string, value int, usage string) Plant
	// StoreInt replaces name with value and can issue a Mutation when receiving on Mutations()
	StoreInt(name string, value int) Plant
}

type Intable64 added in v2.0.9

type Intable64 interface {
	// Int64 returns a pointer to a registered int64 by name as -name=1 a pointer to 1 is returned
	Int64(name string) *int64
	// NewInt64 registers a new int32 flag by name and returns a pointer to the int64 storing the initial value
	NewInt64(name string, value int64, usage string) Plant
	// StoreInt64 replaces name with value and can issue a Mutation when receiving on Mutations()
	StoreInt64(name string, value int64) Plant
}

type ListFlag

type ListFlag struct {
	// contains filtered or unexported fields
}

ListFlag stores values in a list type configurable

func (*ListFlag) Set

func (l *ListFlag) Set(value string) error

Set unpacks a comma separated value argument and appends items to the list of []string

func (*ListFlag) String

func (l *ListFlag) String() string

String returns the slice of strings using strings.Join

func (*ListFlag) Values added in v2.0.4

func (l *ListFlag) Values() []string

type Listable added in v2.0.9

type Listable interface {
	// List returns a pointer to a []string containing strings
	List(name string) *[]string
	// NewList registers a new []string that can be assigned -name="ONE,TWO,THREE,FOUR"
	NewList(name string, value []string, usage string) Plant
	// StoreList replaces name with value and can issue a Mutation when receiving on Mutations()
	StoreList(name string, value []string) Plant
}

type Loadable added in v2.0.9

type Loadable interface {
	// Load can panic but also can throw an error but will use the Environment Variable values if they are "EXAMPLE=value" or "ANOTHER=sample"
	Load() error
	// LoadFile accepts a path to a JSON, YAML or INI file to set values
	LoadFile(path string) error
	// Reload will refresh stored values of properties with their new Environment Variable values
	Reload() error
}

type MapFlag

type MapFlag struct {
	// contains filtered or unexported fields
}

MapFlag stores values in a map type configurable

func (*MapFlag) Keys added in v2.0.4

func (m *MapFlag) Keys() []string

func (*MapFlag) Set

func (m *MapFlag) Set(value string) error

Set accepts a value like KEY=VALUE,KEY=VALUE,KEY=VALUE to override map values

func (*MapFlag) String

func (m *MapFlag) String() string

String returns the map[string]string as string=string,string=string,...

type Mappable added in v2.0.9

type Mappable interface {
	// Map returns a pointer to a map[string]string containing strings
	Map(name string) *map[string]string
	// NewMap registers a new map[string]string that can be assigned -name="PROPERTY=VALUE,ANOTHER=VALUE"
	NewMap(name string, value map[string]string, usage string) Plant
	// MapKeys returns the keys of the map[string]string as a []string
	MapKeys(name string) []string
	// StoreMap replaces name with value and can issue a Mutation when receiving on Mutations()
	StoreMap(name string, value map[string]string) Plant
}

type Mutable added in v2.0.9

type Mutable interface {
	// Mutations receives Mutation data on a receiver channel
	Mutations() <-chan Mutation
	// MutagenesisOfFig will look up a Fruit by name and return the Metagenesis of it
	MutagenesisOfFig(name string) Mutagenesis
	// MutagenesisOf takes anything and returns the Mutagenesis of it
	MutagenesisOf(what interface{}) Mutagenesis
}

type Mutagenesis

type Mutagenesis string

Mutagenesis stores the type as a string like String, Bool, Float, etc to represent a supported Type

func MutagenesisOf added in v2.0.13

func MutagenesisOf(what interface{}) Mutagenesis

func (Mutagenesis) Kind added in v2.0.13

func (m Mutagenesis) Kind() string

type Mutation

type Mutation struct {
	Property    string
	Mutagenesis string
	Way         string
	Old         interface{}
	New         interface{}
	When        time.Time
	Error       error
}

type Options

type Options struct {
	ConfigFile string
	// Tracking creates a buffered channel that allows you to select { case mutation, ok := <-figs.Mutations(): }
	Tracking bool

	// Germinate enables the option to filter os.Args that begin with -test. prefix
	Germinate bool

	// Harvest allows you to set the buffer size of the Mutations channel
	Harvest int

	// Pollinate will enable Getters to lookup the environment for changes on every read
	Pollinate bool

	// IgnoreEnvironment is a part of free will, it lets us disregard our environment (ENV vars)
	IgnoreEnvironment bool
}

Options allow you enable mutation tracking on your figs.Grow

type Parsable added in v2.0.9

type Parsable interface {
	// Parse can panic but interprets command line arguments defined with single dashes -example value -another sample
	Parse() error
	// ParseFile can panic but also can throw an error because it will attempt to load either JSON, YAML or INI file passed into it
	ParseFile(filename string) error
}

type Plant added in v2.0.4

type Plant interface {
	Core
	CoreAbilities
	CoreMutations
}

Plant defines the interface for configuration management.

func Grow

func Grow() Plant

Grow is a memetic alias of New Example:

 ctx, cancel := context.WithCancel(context.Background())
 defer cancel()
	figs := figtree.Grow()
 go func() {
   for {
     select {
       case <-ctx.Done():
         return
       case mutation, ok := <-figs.Mutations():
         if ok {
           log.Println(mutation)
         }
     }
   }
 }()

// figs implements figtree.Plant interface

func New

func New() Plant

New will initialize the figTree package Usage:

			When defining:
			    figs := figtree.New()
		     figs.NewInt("workers", 10, "number of workers")
		     figs.Parse()
	      OR err := figs.Load()
	      OR err := figs.ParseFile("path/to/file.json")
       THEN workers := *figs.Int("workers") // workers is a regular int

func With

func With(opts Options) Plant

With creates a new fig figTree with predefined Options

Example:

	figs := With(Options{
		ConfigFilePath: "/opt/app/production.config.yaml",
		Pollinate: true,
		Tracking: true,
		Harvest: 369,
	})
	// define your figs.New<Mutagenesis>() here...
	figs.NewString("domain", "", "Domain name")
	figs.WithValidator("domain", figtree.AssureStringNotEmpty)
 err := figs.Load()
 domainInProductionConfigYamlFile := *figs.String("domain")

type Readable added in v2.0.9

type Readable interface {
	// ReadFrom will attempt to load the file into the Tree
	ReadFrom(path string) error
}

type RuleKind added in v2.0.4

type RuleKind int
const (
	RuleUndefined                 RuleKind = iota // RuleUndefined is default and does no action
	RulePreventChange             RuleKind = iota // RulePreventChange blocks Mutagensis Store methods
	RulePanicOnChange             RuleKind = iota // RulePanicOnChange will throw a panic on the Mutagenesis Store methods
	RuleNoValidations             RuleKind = iota // RuleNoValidations will skip over all WithValidator assignments
	RuleNoCallbacks               RuleKind = iota // RuleNoCallbacks will skip over all WithCallback assignments
	RuleCondemnedFromResurrection RuleKind = iota // RuleCondemnedFromResurrection will panic if there is an attempt to resurrect a condemned fig
	RuleNoMaps                    RuleKind = iota // RuleNoMaps blocks NewMap, StoreMap, and Map from being called on the Tree
	RuleNoLists                   RuleKind = iota // RuleNoLists blocks NewList, StoreList, and List from being called on the Tree
	RuleNoFlags                   RuleKind = iota // RuleNoFlags disables the flag package from the Tree
	RuleNoEnv                     RuleKind = iota // RuleNoEnv skips over all os.Getenv related logic
)

type Savable added in v2.0.9

type Savable interface {
	// SaveTo will store the Tree in a path file
	SaveTo(path string) error
}

type String added in v2.0.9

type String interface {
	// String returns a pointer to stored string by -name=value
	String(name string) *string
	// NewString registers a new string flag by name and returns a pointer to the string storing the initial value
	NewString(name, value, usage string) Plant
	// StoreString replaces name with value and can issue a Mutation when receiving on Mutations()
	StoreString(name, value string) Plant
}

type Value added in v2.0.11

type Value struct {
	Value      interface{}
	Mutagensis Mutagenesis
	Err        error
}

func (*Value) Assign added in v2.0.11

func (v *Value) Assign(as interface{}) error

func (*Value) Flesh added in v2.0.11

func (v *Value) Flesh() Flesh

func (*Value) IsBoolFlag added in v2.0.14

func (v *Value) IsBoolFlag() bool

func (*Value) Raw added in v2.0.11

func (v *Value) Raw() interface{}

func (*Value) Set added in v2.0.11

func (v *Value) Set(in string) error

func (*Value) String added in v2.0.11

func (v *Value) String() string

type Withables added in v2.0.9

type Withables interface {
	// WithCallback registers a new CallbackWhen with a CallbackFunc on a figFruit on the figTree by its name
	WithCallback(name string, whenCallback CallbackWhen, runThis CallbackFunc) Plant
	// WithAlias registers a short form of the name of a figFruit on the figTree
	WithAlias(name, alias string) Plant
	// WithRule attaches a RuleKind to a figFruit
	WithRule(name string, rule RuleKind) Plant
	// WithTreeRule assigns a global rule on the Tree
	WithTreeRule(rule RuleKind) Plant
	// WithValidator binds a figValidatorFunc to a figFruit that returns Plant
	WithValidator(name string, validator func(interface{}) error) Plant
}

Jump to

Keyboard shortcuts

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