Skip to content

Add Branch Support #18

@andreimerlescu

Description

@andreimerlescu

I need to be able to bind branches to my figtree that can contain their own figs themselves.

Add NewBranch support to figtree allowing a figTree to contain child figTree instances in a map[string]*figTree data structure, with a configurable maximum depth enforced at runtime.

Developer Experience

When using branches in figtree, they should provide the same essence of the figtree, in a map[string]*figTree data structure within the figTree itself.

package main

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

func main(){

figs := figtree.Grow()
figs.NewString("name", "", "your name")
figsdb := figs.NewBranch("db")
figsdb.NewString("host", "", "database host")
figsdb.NewString("user", "", "database user")
figsdb.NewString("pass", "", "database pass")
figsdb.NewString("name", "", "name of the database")
figsdb.WithValidator("host", figtree.AssureStringNotEmpty)
figsdb.WithValidator("user", figtree.AssureStringNotEmpty)
figsdb.WithValidator("pass", figtree.AssureStringNotEmpty)
figsdb.WithValidator("name", figtree.AssureStringNotEmpty)

err := figs.Parse()
if err != nil {
   panic(err)
}

name := figs.String("name")
dbHost := figs.Branch("db").String("host")
dbUser := figs.Branch("db").String("user")
dbPass := figs.Branch("db").String("pass")
dbName := figs.Branch("db").String("name")
}

JSON Files

{
   "name": "andrei",
   "db": {
      "host": "localhost",
      "user": "username",
      "pass": "",
      "database": "dev_db_appname"
   }
}

This should result in a Validation error due to the JSON present has a bad value in the pass field due to "", which is an empty string that is explicitly not allowed by figtree.AssureStringNotEmpty.

YAML Files

---
name: andrei
db:
  host: localhost
  user: username
  pass: notpass
  database: dev_db_appname

Ini Files

The root level / global level of the figtree is the [default] section.

CLI .ini Universal App: goini

[default]
name=andrei
[db]
host=localhost
user=username
pass=notpass
database=dev_db_appname

Advanced Usages of Branches

  • Branches get their own rules and validators attached to their own properties.
  • Branches are separate figtree's reference by parent-child relationship.

Branch Behavior

  • Each branch is its own figTree held in map[string]*figTree on the parent
  • Branches inherit the parent’s shared mutationsCh — all mutations funnel to the root channel with Mutation.Branch []string carrying the path
  • Branches get their own rules, validators, and callbacks scoped to their own figs
  • figs.Branch("db") returns a Plant interface — all figtree methods apply

Accessors

type Fig interface {
  // MODIFY ALL TYPES
  String(key string) string
  Int(key string) int
  Int64(key string) int64
  Float(key string) Float64
  // ... etc. 
}

GetValue would accept a key that has figtree.SeparatorGetValue string char (like "," or "|") that can be "db.host" and then it would expand into .Branch("db").String("host").

Limitations

Intentionally program a limit of 3-deep permitted. This means that if you try to do:

// Permitted
figs := figtree.New()

figsdbs := figs.NewBranch("databases")
figsmysql := figsdbs.NewBranch("mysql")
figspgsql := figsdbs.NewBranch("pgsql")
figsmongo := figsdbs.NewBranch("mongo")
figscockroach := figsdbs.NewBranch("cockroach")

// Not Permitted
figs := figtree.New()

figsdbs := figs.NewBranch("databases") // 1st level okay
figsmysql := figsdbs.NewBranch("mysql") // 2nd level okay
mysqlproperties := figsmysql.NewBranch("properties") // 3rd level okay
globalproperties := mysqlproperties.NewBranch("globalproperties") // panic here

Fig Tree Limitations

Design in this test a scenario where the figtree being accessed and tested has the following conditions:

  1. 100 root level properties that are a blend of the different types provided
  2. 100 branches // 1st level okay
    1. 50 branches // 2nd level okay
      1. 25 branches // 3rd level okay

This represents an absolutely MASSIVE application. 125,100 configurable properties is excessive, but in the scope of the test, the goal is to measure how figtree performs over its memory footprint, validating the tree, its benchmark, various callback scenarios, and more.

In this test, the developer should be able to read through the source code and see the figtree built out with 125K properties in a manner that is programmatically sound and not excessively verbose as its testing performance of the tree, accuracy of the random strings, and 100% success rates in their test coverage.

Closing Notes

Ultimately, this shouldn't be too complex. You're basically taking that which is figtree and recursively allowing it to be assigned via branches with a 3-degree rule applied. The "3" of the "3-degree" rule itself can be a configurable property. But its not recommended to go above 3 because poorly designed applications can mushroom out of control quickly if you have too many tested properties. In my professional experience, I've demanded as deep as 7 degrees, and for that figtree.MaxBranchDepth = 3 will default as a variable package level - but can be expanded. It's recursive. So if its set to 7, then even the sub-trees will have up to 7.

Another thing to consider is in the performance testing establishing a ceiling on the number of figs that the tree can harvest. Effectively, when the figtree was created, it had a predetermined number of figs that it could harvest in its entire lifetime. The Harvest: property in the Config struct ultimately offers opportunity to programmatically determine recommended values of this. So if the default or value is too low, a warning is provided to the user informing them of the misconfiguration of the runtime.

Metadata

Metadata

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions