stackage

package module
v1.0.3 Latest Latest
Warning

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

Go to latest
Published: Mar 31, 2024 License: MIT Imports: 12 Imported by: 2

README

go-stackage

Go Report Card Go Reference CodeQL Software License codecov contributions welcome Experimental GitHub Workflow Status (with event) Author GitHub release (with filter) Help Animals

stacks03

Summary

The stackage package implements flexible Stack and Condition types with many useful features. It can be used to create object-based Boolean statements, abstract mathematical constructs, simple lists and much more: the possibilities are endless!

Mission

The main goal of this package is provide an extremely reliable and accommodating stack/condition solution that is suitable for use in virtually any conceivable Go-based scenario in which objects of these types are needed. While extremely extensible and flexible, it should always be possible to use this package with no need for additional (non main) code while operating in extremely simple scenarios.

Features

  • Stack instances are either LIFO (stack based, default) or FIFO (queue based)
    • FIFO is First-In/First-Out (like a line at your favorite deli: first come, first serve)
    • LIFO is Last-In/First-Out (like those plate-stacking apparatuses found in restaurant kitchens, in which the first plate inserted shall be the last plate removed)
  • Flexible Stack configuration controls, allowing custom stringer presentation, push controls and validity-checking policies to be imposed
  • Recursive design - Stacks can reside in Stacks. Conditions can reside in Stacks. Conditions can contain other Stacks. Whatever!
    • Eligible values are easily navigated using the Stack.Traverse method using an ordered sequence of indices, or slice index numbers
    • Conversely, recursion capabilities can also be easily disabled per instance!
  • Observable - flexible logging facilities, using the log package are available globally, or on a per-instance basis
  • Interrogable - Stacks and Conditions extend many interrogation features, allowing the many facets and "states" of an instance to be queried simply
  • Resilient - Stack writeability can be toggled easily, allowing safe (albeit naïve) read-only operation without the need for mutexing
  • Fluent-style - Types which offer methods for cumulative configuration are written in fluent-form, allowing certain commands to be optionally "chained" together
  • Extensible
    • Logical operator framework allows custom operators to be added for specialized expressions, instead of the package-provided ComparisonOperator constants
    • Users can add their own Evaluator function to perform computational tasks, value interrogation, matching procedures ... pretty much anything you could imagine
  • Stack instances are (independently) MuTeX capable, thanks to the sync package
    • Recursive locking mechanisms are NOT supported due to my aversion to insanity
  • Adopters may create a type alias of the Condition and/or Stack types
  • Fast, reliable, useful, albeit very niche

Status

Although fairly well-tested, this package is in its early stages and is undergoing active development. It should only be used in production environments while under heavy scrutiny and with great care.

License

The stackage package, from go-stackage, is released under the terms of the MIT license. See the LICENSE file in the repository root, or click the License badge above, for complete details.

Type Aliasing

When needed, users may opt to create their own derivative alias types of either the Stack or Condition types for more customized use in their application.

The caveat, naturally, is that users will be expected to wrap all of the package-provided methods (e.g.: String, Push, Pop, etc) they intend to use.

However, the upside is that the user may now write (extend) wholly new methods that are unique to their own application, and without having to resort to potentially awkward measures, such as embedding.

To create a derivative type based on the Stack type, simply do something similar to the following example in your code:

type MyStack stackage.Stack

// Here we extend a wholly new function. The input and output signatures
// are entirely defined at the discretion of the author and are shown in
// "pseudo code context" here.
func (r MyStack) NewMethodName([input signature]) [<output signature>] {
	// your custom code, do whatever!
}

// Here we wrap a pre-existing package-provided function, String, that
// one would probably intend to use.
// 
// To run the actual String method, we need to first CAST the custom
// type (r, MyStack) to a bonafide stackage.Stack instance as shown
// here. Unlike the above example, this is NOT "pseudo code" and will
// compile just fine.
//
// Repeat as needed for other methods that may be used.
func (r MyStack) String() string {
 	// return the result from a "TYPE CAST -> EXEC" call
	return stackage.Stack(r).String()
}

The procedure would be identical for a Condition alias -- just change the name and the derived stackage type from the first example line and modify as desired.

If you'd like to see a more complex working example of this concept in the wild, have a look at the go-aci package, which makes heavy use of derivative stackage types.

Documentation

Overview

Package stackage implements a flexible stack type optimized for use in creating and presenting conditional Boolean statements, abstract mathematical constructs, or simple lists: the possibilities are endless!

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultConditionLogLevel

func DefaultConditionLogLevel() int
Example
fmt.Printf("%d", DefaultConditionLogLevel())
Output:

0

func DefaultStackLogLevel

func DefaultStackLogLevel() int
Example
fmt.Printf("%d", DefaultStackLogLevel())
Output:

0

func SetDefaultConditionLogLevel

func SetDefaultConditionLogLevel(lvl any)

SetDefaultConditionLogLevel sets the instance of LogLevel (lvl) as a LITERAL value, as the verbosity indicator. When set with appropriate level identifiers, this will increase or decrease log verbosity accordingly. This value shall be used for logging verbosity (or lack thereof) for any newly created (and qualified) instances.

Note that the input value(s) are NOT shifted. Users are expected to either sum the values and cast the product as a LogLevel, OR settle for one of the predefined LogLevel constants.

The default is NoLogLevels, which implies a loglevel of zero (0).

Example
// define a custom loglevel cfg
SetDefaultConditionLogLevel(
	LogLevel3 + // 4
		UserLogLevel1 + // 64
		UserLogLevel7, // 4096
)
custom := DefaultConditionLogLevel()

// turn loglevel to none
SetDefaultConditionLogLevel(NoLogLevels)
off := DefaultConditionLogLevel()

fmt.Printf("%d (custom), %d (off)", custom, off)
Output:

4164 (custom), 0 (off)

func SetDefaultConditionLogger

func SetDefaultConditionLogger(logger any)

SetDefaultConditionLogger is a package-level function that will define which logging facility new instances of Condition or equivalent type alias shall be assigned during initialization procedures.

Logging is available but is set to discard all events by default. Note that enabling this will have no effect on instances already created.

An active logging subsystem within any given Condition shall supercede this default package logger.

The following types/values are permitted:

  • string: `none`, `off`, `null`, `discard` will turn logging off
  • string: `stdout` will set basic STDOUT logging
  • string: `stderr` will set basic STDERR logging
  • int: 0 will turn logging off
  • int: 1 will set basic STDOUT logging
  • int: 2 will set basic STDERR logging
  • *log.Logger: user-defined *log.Logger instance will be set

Case is not significant in the string matching process.

Logging may also be set for individual Condition instances using the SetLogger method. Similar semantics apply.

func SetDefaultStackLogLevel

func SetDefaultStackLogLevel(lvl any)

SetDefaultStackLogLevel sets the instance of LogLevel (lvl) as a LITERAL value, as the verbosity indicator. When set with appropriate level identifiers, this will increase or decrease log verbosity accordingly. This value shall be used for logging verbosity (or lack thereof) for any newly created (and qualified) instances.

Note that the input value(s) are NOT shifted. Users are expected to either sum the values and cast the product as a LogLevel, OR settle for one of the predefined LogLevel constants.

The default is NoLogLevels, which implies a loglevel of zero (0).

Example
SetDefaultStackLogLevel(
	LogLevel1 + // 1
		LogLevel4 + // 8
		UserLogLevel2 + // 128
		UserLogLevel5, // 1024
)
custom := DefaultStackLogLevel()

// turn loglevel to none
SetDefaultStackLogLevel(NoLogLevels)
off := DefaultStackLogLevel()

fmt.Printf("%d (custom), %d (off)", custom, off)
Output:

1161 (custom), 0 (off)

func SetDefaultStackLogger

func SetDefaultStackLogger(logger any)

SetDefaultStackLogger is a package-level function that will define which logging facility new instances of Stack or equivalent type alias shall be assigned during initialization procedures.

Logging is available but is set to discard all events by default. Note that enabling this will have no effect on instances already created.

An active logging subsystem within any given Stack shall supercede this default package logger.

The following types/values are permitted:

  • string: `none`, `off`, `null`, `discard` will turn logging off
  • string: `stdout` will set basic STDOUT logging
  • string: `stderr` will set basic STDERR logging
  • int: 0 will turn logging off
  • int: 1 will set basic STDOUT logging
  • int: 2 will set basic STDERR logging
  • *log.Logger: user-defined *log.Logger instance will be set

Case is not significant in the string matching process.

Logging may also be set for individual Stack instances using the SetLogger method. Similar semantics apply.

Types

type Auxiliary

type Auxiliary map[string]any

Auxiliary is a map[string]any type alias extended by this package. It can be created within any Stack instance when [re]initialized using the SetAuxiliary method extended through instances of the Stack type, and can be accessed using the Auxiliary() method in similar fashion.

The Auxiliary type extends four (4) methods: Get, Set, Len and Unset. These are purely for convenience. Given that instances of this type can easily be cast to standard map[string]any by the user, the use of these methods is entirely optional.

The Auxiliary map instance is available to be leveraged in virtually any way deemed appropriate by the user. Its primary purpose is for storage of any instance(s) pertaining to the *administration of the stack*, as opposed to the storage of content normally submitted *into* said stack.

Examples of suitable instance types for storage within the Auxiliary map include, but are certainly not limited to:

  • HTTP server listener / mux
  • HTTP client, agent
  • Graphviz node data
  • Go Metrics meters, gauges, etc.
  • Redis cache
  • bytes.Buffer
  • ANTLR parser
  • text/template instances
  • channels

Which instances are considered suitable for storage within Auxiliary map instances is entirely up to the user. This package shall not impose ANY controls or restrictions regarding the content within this instances of this type, nor its behavior.

func (Auxiliary) Get

func (r Auxiliary) Get(key string) (value any, ok bool)

Get returns the value associated with key, alongside a presence-indicative Boolean value (ok).

Even if found within the receiver instance, if value is nil, ok shall be explicitly set to false prior to return.

Case is significant in the matching process.

Example
var aux Auxiliary = make(Auxiliary, 0)
aux.Set(`value`, 18)
val, ok := aux.Get(`value`)
if ok {
	fmt.Printf("%d", val)
}
Output:

18

func (Auxiliary) Len

func (r Auxiliary) Len() int

Len returns the integer length of the receiver, defining the number of key/value pairs present.

Example
aux := Auxiliary{
	`value`: 18,
}
fmt.Printf("Len: %d", aux.Len())
Output:

Len: 1

func (Auxiliary) Set

func (r Auxiliary) Set(key string, value any) Auxiliary

Set associates key with value, and assigns to receiver instance. See also the Unset method.

If the receiver is not initialized, a new allocation is made.

Example
var aux Auxiliary = make(Auxiliary, 0)
aux.Set(`value`, 18)
aux.Set(`color`, `red`)
aux.Set(`max`, 200)
fmt.Printf("Len: %d", aux.Len())
Output:

Len: 3

func (Auxiliary) Unset

func (r Auxiliary) Unset(key string) Auxiliary

Unset removes the key/value pair, identified by key, from the receiver instance, if found. See also the Set method.

This method internally calls the following builtin:

delete(*rcvr,key)

Case is significant in the matching process.

Example
var aux Auxiliary = make(Auxiliary, 0)
aux.Set(`value`, 18)
aux.Set(`color`, `red`)
aux.Set(`max`, 200)
aux.Unset(`max`)

fmt.Printf("Len: %d", aux.Len())
Output:

Len: 2

type ComparisonOperator

type ComparisonOperator uint8

ComparisonOperator is a uint8 enumerated type used for abstract representation of the following well-known and package-provided operators:

  • Equal (=)
  • Not Equal (!=)
  • Less Than (<)
  • Greater Than (>)
  • Less Than Or Equal (<=)
  • Greater Than Or Equal (>=)

Instances of this type should be passed to Cond as the 'op' value.

const (
	Eq ComparisonOperator // 1 (=)
	Ne                    // 2 (!=)
	Lt                    // 3 (<)
	Gt                    // 4 (>)
	Le                    // 5 (<=)
	Ge                    // 6 (>=)
)

ComparisonOperator constants are intended to be used in singular form when evaluating two (2) particular values.

func (ComparisonOperator) Context

func (r ComparisonOperator) Context() string

Context returns the contextual label associated with instances of this type as a string value.

Example
var cop ComparisonOperator = Ge
fmt.Printf("Context: %s", cop.Context())
Output:

Context: comparison

func (ComparisonOperator) String

func (r ComparisonOperator) String() (op string)

String is a stringer method that returns the string representation of the receiver instance.

Example
var cop ComparisonOperator = Ge
fmt.Printf("Operator: %s", cop)
Output:

Operator: >=

type Condition

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

Condition describes a single evaluative statement, i.e.:

       op
       |
       v
person = "Jesse"
   ^         ^
   |         |
   kw        ex

The keyword (kw) shall always represent an abstract user-defined string construct against which the expression value (ex) is to be evaluated in some manner.

The disposition of the evaluation is expressed through one (1) of several ComparisonOperator (op) instances made available through this package:

  • Eq, or "equal to" (=)
  • Ne, or "not equal to" (!=)
  • Lt, or "less than" (<)
  • Le, or "less than or equal" (<=)
  • Gt, or "greater than" (>)
  • Ge, or "greater than or equal" (>=)

... OR through a user-defined operator that conforms to the package defined Operator interface.

By default, permitted expression (ex) values must honor these guidelines:

  • Must be a non-zero string, OR ...
  • Must be a valid instance of Stack (or an *alias* of Stack that is convertible back to the Stack type), OR ...
  • Must be a valid instance of any type that exports a stringer method (String()) intended to produce the Condition's final expression string representation

However when a PushPolicy function or method is added to an instance of this type, greater control is afforded to the user in terms of what values will be accepted, as well as the quality or state of such values.

Instances of this type -- similar to Stack instances -- MUST be initialized before use. Initialization can occur as a result of executing the Cond package-level function, or using the Init method extended through instances of this type. Initialization state may be checked using the IsInit method.

Example (OneShot)

This example demonstrates the act of a one-shot creation of an instance of Condition using the Cond package-level function. All values must be non-zero/non-nil. This is useful in cases where users have all of the information they need and simply want to fashion a Condition quickly.

Note: line-breaks added for readability; no impact on functionality, so long as dot sequence "connectors" (.) are honored where required.

c := Cond(`person`, Eq, `Jesse`).
	Paren().
	Encap(`"`).
	NoPadding()

fmt.Printf("%s", c)
Output:

(person="Jesse")
Example (Procedural)

This example demonstrates the act of procedural assembly of an instance of Condition. The variable c is defined and not initialized. Its values are not set until "later" in the users code, and are set independently of one another.

Note that with this technique a manual call of the Init method is always required as the first action following variable declaration. When using the "one-shot" technique by way of the Cond package-level function, the process of initialization is handled automatically for the user.

var c Condition
c.Init() // always init
c.Paren()
c.SetKeyword(`myKeyword`)
c.SetOperator(Eq)
c.SetExpression(`value123`)

fmt.Printf("%s", c)
Output:

( myKeyword = value123 )
Example (TypeAlias)

This example offers a naïve demonstration of a user-authored type alias for the Condition type.

In a practical scenario, a user would likely want to write custom methods to perform new tasks and/or wrap any existing Condition methods desired in order to avoid the frequent need to cast a custom type to the native stackage.Condition type.

As this is an example, we'll perform a simple cast merely to demonstrate the type was cast successfully.

type customCondition Condition
var c Condition = Cond(`keyword`, Ne, `Value`)
fmt.Printf("%s (%T)",
	Condition(customCondition(c)),
	customCondition(c),
)
Output:

keyword != Value (stackage.customCondition)

func Cond

func Cond(kw any, op Operator, ex any) (c Condition)

Cond returns an instance of Condition bearing the provided component values. This is intended to be used in situations where a Condition instance can be created in one shot.

Example
fmt.Printf("%s", Cond(`π`, Eq, float64(3.141592653589793)))
Output:

π = 3.141592653589793

func (Condition) Addr

func (r Condition) Addr() (addr string)

Addr returns the string representation of the pointer address for the receiver. This may be useful for logging or debugging operations.

Note: this method calls fmt.Sprintf.

Example

This example demonstrates use of the Addr method to obtain the string representation of the memory address for the underlying embedded receiver instance.

NOTE: Since the address will usually be something different each runtime, we can't reliably test this in literal fashion, so we do a simple prefix check as an alternative.

var c Condition
c.Init()

fmt.Printf("Address prefix valid: %t", c.Addr()[:2] == `0x`)
// Address prefix valid: true
Output:

func (Condition) Auxiliary

func (r Condition) Auxiliary() (aux Auxiliary)

Auxiliary returns the instance of Auxiliary from within the receiver.

Example
var c Condition
c.Init()

// one can put anything they wish into this map,
// so we'll do a bytes.Buffer since it is simple
// and commonplace.
var buf *bytes.Buffer = &bytes.Buffer{}
_, _ = buf.WriteString(`some .... data .....`)

// Create our map (one could also use make
// and populate it piecemeal as opposed to
// in-line, as we do below).
c.SetAuxiliary(map[string]any{
	`buffer`: buf,
})

//  Call our map and call its 'Get' method in one-shot
if val, ok := c.Auxiliary().Get(`buffer`); ok {
	fmt.Printf("%s", val)
}
Output:

some .... data .....
Example (ByTypeCast)

This example demonstrates building of the Auxiliary map in its generic form (map[string]any) before being type cast to Auxiliary.

var c Condition = Cond(`keyword`, Eq, `value`)
proto := make(map[string]any, 0)
proto[`value1`] = []int{1, 2, 3, 4, 5}
proto[`value2`] = [2]any{float64(7.014), rune('#')}
c.SetAuxiliary(Auxiliary(proto)) // cast proto and assign to stack
aux := c.Auxiliary()             // call map to variable
fmt.Printf("%T length:%d", aux, aux.Len())
Output:

stackage.Auxiliary length:2
Example (NoInit)

This example demonstrates the call and (failed) set of an uninitialized Auxiliary instance. While no panic ensues, the map instance is not writable.

The user must instead follow the procedures in the WithInit, UserInit or ByTypeCast examples.

var c Condition = Cond(`keyword`, Eq, `value`)
aux := c.Auxiliary()
fmt.Printf("%T found, length:%d",
	aux, aux.Set(`testing`, `123`).Len())
Output:

stackage.Auxiliary found, length:0
Example (UserInit)

This example demonstrates a scenario similar to that of the WithInit example, except in this case the map instance is entirely created and populated by the user in a traditional fashion.

var c Condition = Cond(`keyword`, Eq, `value`)
aux := make(Auxiliary, 0)

// user opts to just use standard map
// key/val set procedure, and avoids
// use of the convenience methods.
// This is totally fine.
aux[`value1`] = []int{1, 2, 3, 4, 5}
aux[`value2`] = [2]any{float64(7.014), rune('#')}

c.SetAuxiliary(aux)
fmt.Printf("%T length:%d", aux, len(aux))
Output:

stackage.Auxiliary length:2
Example (WithInit)

This example continues the concepts within the NoInit example, except in this case proper initialization occurs and a desirable outcome is achieved.

var c Condition = Cond(`keyword`, Eq, `value`)
c.SetAuxiliary() // auto-init
aux := c.Auxiliary()
fmt.Printf("%T found, length was:%d, is now:%d",
	aux,
	aux.Len(),                       // check initial (pre-set) length
	aux.Set(`testing`, `123`).Len()) // fluent Set/Len in one shot
Output:

stackage.Auxiliary found, length was:0, is now:1

func (Condition) CanNest

func (r Condition) CanNest() (can bool)

CanNest returns a Boolean value indicative of whether the no-nesting bit is unset, thereby allowing a Stack or Stack type alias instance to be set as the value.

See also the IsNesting method.

Example
c := Cond(`π`, Eq, float64(3.14159265358979323))
c.NoNesting(true)
fmt.Printf("Can nest: %t", c.CanNest())
Output:

Can nest: false

func (Condition) Category

func (r Condition) Category() (cat string)

Category returns the categorical label string value assigned to the receiver, if set, else a zero string.

Example
c := Cond(`π`, Eq, float64(3.14159265358979323))
c.SetCategory(`mathematical constant`)
fmt.Printf("Category: %s", c.Category())
Output:

Category: mathematical constant

func (Condition) Encap

func (r Condition) Encap(x ...any) Condition

Encap accepts input characters for use in controlled condition value encapsulation. Acceptable input types are:

A single string value will be used for both L and R encapsulation.

An instance of []string with two (2) values will be used for L and R encapsulation using the first and second slice values respectively.

An instance of []string with only one (1) value is identical to the act of providing a single string value, in that both L and R will use one value.

Example (DoubleQuote)
var c Condition
c.Init()
c.SetKeyword(`key`)
c.SetOperator(Eq)
c.SetExpression(`value`)
c.Encap(`"`)
fmt.Printf("%s", c)
Output:

key = "value"
Example (Tag)
var c Condition
c.Init()
c.SetKeyword(`key`)
c.SetOperator(Eq)
c.SetExpression(`value`)
c.Encap([]string{`<`, `>`})
fmt.Printf("%s", c)
Output:

key = <value>

func (Condition) Err

func (r Condition) Err() (err error)

Err returns the error residing within the receiver, or nil if no error condition has been declared.

This method will be particularly useful for users who do not care for fluent-style operations and would instead prefer to operate in a more procedural fashion.

Note that a chained sequence of method calls of this type shall potentially obscure error conditions along the way, as each successive method may happily overwrite any error instance already present.

Example
var c Condition = Cond(``, ComparisonOperator(7), `ThisIsBogus`)
fmt.Printf("%v", c.Err())
Output:

keyword value is zero

func (Condition) Evaluate

func (r Condition) Evaluate(x ...any) (ev any, err error)

Evaluate uses the Evaluator closure function to apply the value (x) to the receiver in order to conduct a matching/assertion test or analysis for some reason. This is entirely up to the user.

A Boolean value returned indicative of the result. Note that if an instance of Evaluator was not assigned to the Condition prior to execution of this method, the return value shall always be false.

Example

This example demonstrates the use of the Evaluate method to execute an Evaluator instance assigned using the Set Evaluator method.

degrees := float64(83.1)
c := Cond(`degrees`, Eq, degrees)

// we don't really need input values for
// this one, since we read directly from
// the Condition instance c. And we don't
// need an error value since this is just
// an example, therefore we use shadowing
// (_) as needed.
c.SetEvaluator(func(_ ...any) (R any, _ error) {
	expr := c.Expression()
	D, _ := expr.(float64) // Don't shadow 'ok' in real-life.

	// I could have imported "math",
	// but this is all we need.
	pi := 3.14159265358979323846264338327950288419716939937510582097494459
	R = float64(D*pi) / 180
	return
})

radians, _ := c.Evaluate()
fmt.Printf("%.02f° x π/180° = %.02frad", degrees, radians.(float64))
Output:

83.10° x π/180° = 1.45rad

func (Condition) Expression

func (r Condition) Expression() (ex any)

Expression returns the expression value(s) stored within the receiver, or nil if unset. A valid receiver instance MUST always possess a non-nil expression value.

Example
var c Condition
c.Init()
c.SetKeyword(`key`)
c.SetOperator(Eq)
c.SetExpression(`value`)
fmt.Printf("%s", c.Expression())
Output:

value

func (Condition) ID

func (r Condition) ID() (id string)

Name returns the name of the receiver instance, if set, else a zero string will be returned. The presence or lack of a name has no effect on any of the receiver's mechanics, and is strictly for convenience.

Example
c := Cond(`π`, Eq, float64(3.14159265358979323))
c.SetID(`pi`)
fmt.Printf("ID: %s", c.ID())
Output:

ID: pi

func (*Condition) Init

func (r *Condition) Init() Condition

Init will [re-]initialize the receiver's contents and return them to an unset, but assignable, state. This is a destructive method: the embedded pointer within the receiver instance shall be totally annihilated.

This method is niladic and fluent in nature. No input is required, and the only element returned is the receiver itself.

This method may be useful in situations where a Condition will be assembled in a "piecemeal" fashion (i.e.: incrementally), or if a Condition instance is slated to be repurposed for use elsewhere (possibly in a repetative manner).

Example
var c Condition
c.Init()
fmt.Printf("%T is initialized: %t", c, c.IsInit())
Output:

stackage.Condition is initialized: true

func (Condition) IsEncap

func (r Condition) IsEncap() (is bool)

IsEncap returns a Boolean value indicative of whether value encapsulation characters have been set within the receiver.

Example
var c Condition = Cond(`key`, Eq, `value`)
c.Encap([]string{`<`, `>`})
fmt.Printf("%T expression is encapsulated: %t", c, c.IsEncap())
Output:

stackage.Condition expression is encapsulated: true

func (Condition) IsFIFO

func (r Condition) IsFIFO() (is bool)

IsFIFO returns a Boolean value indicative of whether the underlying receiver instance's Expression value represents a Stack (or Stack type alias) instance which exhibits First-In-First-Out behavior as it pertains to the act of appending and truncating the receiver's slices.

A value of false implies that no such Stack instance is set as the expression, OR that the Stack exhibits Last-In-Last-Out behavior, which is the default ingress/egress scheme imposed upon instances of this type.

Example
valueStack := And().SetFIFO(true).Push(`this`, `that`, `other`)
c := Cond(`stack`, Eq, valueStack)
fmt.Printf("%T is First-In/First-Out: %t", c, c.IsFIFO())
Output:

stackage.Condition is First-In/First-Out: true

func (Condition) IsInit

func (r Condition) IsInit() (is bool)

IsInit will verify that the internal pointer instance of the receiver has been properly initialized. This method executes a preemptive execution of the IsZero method.

Example
var c Condition
fmt.Printf("%T is initialized: %t", c, c.IsInit())
Output:

stackage.Condition is initialized: false

func (Condition) IsNesting

func (r Condition) IsNesting() (is bool)

IsNesting returns a Boolean value indicative of whether the underlying expression value is either a Stack or Stack type alias. If true, this indicates the expression value descends into another hierarchical (nested) context.

Example
valueStack := And().Push(`this`, `that`, `other`)
c := Cond(`stack`, Eq, valueStack)
fmt.Printf("%T is nesting: %t", c, c.IsNesting())
Output:

stackage.Condition is nesting: true

func (Condition) IsPadded

func (r Condition) IsPadded() (is bool)

IsPadded returns a Boolean value indicative of whether the receiver pads its contents with a SPACE char (ASCII #32).

Example
c := Cond(`keyword`, Eq, `value`)
fmt.Printf("%T is padded: %t", c, c.IsPadded())
Output:

stackage.Condition is padded: true

func (Condition) IsParen

func (r Condition) IsParen() bool

IsParen returns a Boolean value indicative of whether the receiver is parenthetical.

Example
var c Condition
c.Init()
c.Paren() // toggle to true from false default
c.SetKeyword(`keyword`)
c.SetOperator(Ge)
c.SetExpression(1.456)
fmt.Printf("Is parenthetical: %t", c.IsParen())
Output:

Is parenthetical: true

func (Condition) IsZero

func (r Condition) IsZero() bool

IsZero returns a Boolean value indicative of whether the receiver is nil, or unset.

Example
var c Condition
fmt.Printf("Zero: %t", c.IsZero())
Output:

Zero: true

func (Condition) Keyword

func (r Condition) Keyword() (kw string)

Keyword returns the Keyword interface type instance found within the receiver.

Example
var c Condition
c.Init()
c.SetKeyword(`my_keyword`)
fmt.Printf("Keyword: %s", c.Keyword())
Output:

Keyword: my_keyword

func (Condition) Len

func (r Condition) Len() int

Len returns a "perceived" abstract length relating to the content (or lack thereof) assigned to the receiver instance:

  • An uninitialized or zero instance returns zero (0)
  • An initialized instance with no Expression assigned (nil) returns zero (0)
  • A Stack or Stack type alias assigned as the Expression shall impose its own stack length as the return value (even if zero (0))

All other type instances assigned as an Expression shall result in a return of one (1); this includes slice types, maps, arrays and any other type that supports multiple values.

This capability was added to this type to mirror that of the Stack type in order to allow additional functionality to be added to the Interface interface.

Example
var c Condition
c.Init()
c.Paren() // toggle to true from false default
c.SetKeyword(`keyword`)
c.SetOperator(Ge)
noLen := c.Len()

c.SetExpression(`just_a_string`)
strLen := c.Len()

// Overwrite the above string
// with a stack containing
// three (3) strings.
S := And().Push(`this`, `won't`, `work`)
c.SetExpression(S)
stkLen := c.Len()

fmt.Printf("length with nothing: %d\nlength with string: %d\nlength with %T: %d", noLen, strLen, S, stkLen)
Output:

length with nothing: 0
length with string: 1
length with stackage.Stack: 3

func (Condition) LogLevels

func (r Condition) LogLevels() (l string)

LogLevels returns the string representation of a comma-delimited list of all active LogLevel values within the receiver.

Example
var buf *bytes.Buffer = &bytes.Buffer{}
var customLogger *log.Logger = log.New(buf, ``, 0)
var c Condition
c.Init()
c.SetLogger(customLogger)
c.SetLogLevel(LogLevel1, LogLevel3)
fmt.Printf("Loglevels: %s", c.LogLevels())
Output:

Loglevels: CALLS,STATE

func (Condition) Logger

func (r Condition) Logger() (l *log.Logger)

Logger returns the *log.Logger instance. This can be used for quick access to the log.Logger type's methods in a manner such as:

r.Logger().Fatalf("We died")

It is not recommended to modify the return instance for the purpose of disabling logging outright (see Stack.SetLogger method as well as the SetDefaultConditionLogger package-level function for ways of doing this easily).

Example
var buf *bytes.Buffer = &bytes.Buffer{}
var customLogger *log.Logger = log.New(buf, ``, 0)
var c Condition
c.Init()
c.SetLogger(customLogger)
fmt.Printf("%T", c.Logger())
Output:

*log.Logger

func (Condition) NoNesting

func (r Condition) NoNesting(state ...bool) Condition

NoNesting sets the no-nesting bit within the receiver. If set to true, the receiver shall ignore any Stack or Stack type alias instance when assigned using the SetExpression method. In such a case, only primitives, etc., shall be honored during the SetExpression operation.

A Boolean input value explicitly sets the bit as intended. Execution without a Boolean input value will *TOGGLE* the current state of the nesting bit (i.e.: true->false and false->true)

Example
var c Condition
c.Init()
c.Paren() // toggle to true from false default
c.SetKeyword(`keyword`)
c.SetOperator(Ge)
c.NoNesting(true)

c.SetExpression(
	And().Push(`this`, `won't`, `work`),
)

fmt.Printf("%v", c.Expression())
Output:

<nil>

func (Condition) NoPadding

func (r Condition) NoPadding(state ...bool) Condition

NoPadding sets the no-space-padding bit within the receiver. String values within the receiver shall not be padded using a single space character (ASCII #32).

A Boolean input value explicitly sets the bit as intended. Execution without a Boolean input value will *TOGGLE* the current state of the quotation bit (i.e.: true->false and false->true)

Example
var c Condition
c.Init()
c.NoPadding(true)
c.SetKeyword(`keyword`)
c.SetOperator(Ge)
c.SetExpression(`expression`)

fmt.Printf("%s", c)
Output:

keyword>=expression

func (Condition) Operator

func (r Condition) Operator() (op Operator)

Operator returns the Operator interface type instance found within the receiver.

Example
var c Condition
c.Init()
c.SetKeyword(`keyword`)
c.SetOperator(Ge)
c.SetExpression(1.456)
fmt.Printf("Operator: %s", c.Operator())
Output:

Operator: >=

func (Condition) Paren

func (r Condition) Paren(state ...bool) Condition

Paren sets the string-encapsulation bit for parenthetical expression within the receiver. The receiver shall undergo parenthetical encapsulation ( (...) ) during the string representation process. Individual string values shall not be encapsulated in parenthesis, only the whole (current) stack.

A Boolean input value explicitly sets the bit as intended. Execution without a Boolean input value will *TOGGLE* the current state of the encapsulation bit (i.e.: true->false and false->true)

Example
var c Condition
c.Init()
c.Paren() // toggle to true from false default
c.SetKeyword(`keyword`)
c.SetOperator(Ge)
c.SetExpression(1.456)
fmt.Printf("%s", c)
Output:

( keyword >= 1.456 )

func (Condition) SetAuxiliary

func (r Condition) SetAuxiliary(aux ...Auxiliary) Condition

SetAuxiliary assigns aux, as initialized and optionally populated as needed by the user, to the receiver instance. The aux input value may be nil.

If no variadic input is provided, the default Auxiliary allocation shall occur.

Note that this method shall obliterate any instance that may already be present, regardless of the state of the input value aux.

Example
var c Condition
c.Init()

// alloc map
aux := make(Auxiliary, 0)

// populate map
aux.Set(`somethingWeNeed`, struct {
	Type  string
	Value []string
}{
	Type: `L`,
	Value: []string{
		`abc`,
		`def`,
	},
})

// assign map to condition rcvr
c.SetAuxiliary(aux)

// verify presence
call := c.Auxiliary()
fmt.Printf("%T found, length:%d", call, call.Len())
Output:

stackage.Auxiliary found, length:1

func (Condition) SetCategory

func (r Condition) SetCategory(cat string) Condition

SetCategory assigns the provided string to the receiver internal category value. This allows for a means of identifying a particular kind of Condition in the midst of many.

Example
c := Cond(`π`, Eq, float64(3.14159265358979323))
c.SetCategory(`mathematical constant`)
fmt.Printf("Category: %s", c.Category())
Output:

Category: mathematical constant

func (Condition) SetErr

func (r Condition) SetErr(err error) Condition

SetErr sets the underlying error value within the receiver to the assigned input value err, whether nil or not.

This method may be most valuable to users who have chosen to extend this type by aliasing, and wish to control the handling of error conditions in another manner.

Example

This example demonstrates use of the Condition.SetErr method to assign a custom error to the receiver. This is useful in cases where custom processing is being conducted, and a custom error needs to be preserved and accessible to the caller.

var c Condition = Cond(`mySupposedly`, Eq, `validCondition`)
c.SetErr(fmt.Errorf("Hold on there buddy, this is garbage"))

fmt.Printf("%v", c.Err())
Output:

Hold on there buddy, this is garbage
Example (Clear)

This example demonstrates use of the Condition.SetErr method to clear an error instance from the receiver. This may be useful in the event that a problem has potentially been solved and a re-run of testing is needed.

var c Condition = Cond(`mySupposedly`, Eq, `validCondition`)
c.SetErr(nil)

fmt.Printf("Nil: %t", c.Err() == nil)
Output:

Nil: true

func (Condition) SetEvaluator

func (r Condition) SetEvaluator(x Evaluator) Condition

SetEvaluator assigns the instance of Evaluator to the receiver. This will allow the Evaluate method to return a more meaningful result.

Specifying nil shall disable this capability if enabled.

Example

This example demonstrates the use of the SetEvaluator method to assign an instance of the Evaluator closure signature to the receiver instance, allowing the creation and return of a value based upon some procedure defined by the user.

Specifically, the code below demonstrates the conversion a degree value to a radian value using the formula shown in the example below:

degrees := float64(83.1)
c := Cond(`degrees`, Eq, degrees)

// we don't really need input values for
// this one, since we read directly from
// the Condition instance c. And we don't
// need an error value since this is just
// an example, therefore we use shadowing
// (_) as needed.
c.SetEvaluator(func(_ ...any) (R any, _ error) {
	expr := c.Expression()
	D, _ := expr.(float64) // Don't shadow 'ok' in real-life.

	// I could have imported "math",
	// but this is all we need.
	pi := 3.14159265358979323846264338327950288419716939937510582097494459
	R = float64(D*pi) / 180
	return
})

radians, _ := c.Evaluate()
fmt.Printf("%.02f° x π/180° = %.02frad", degrees, radians.(float64))
Output:

83.10° x π/180° = 1.45rad

func (Condition) SetExpression

func (r Condition) SetExpression(ex any) Condition

SetExpression sets the receiver's expression value(s) using the specified ex input argument.

Example
var c Condition
c.Init()
c.SetKeyword(`keyword`)
c.SetOperator(Ge)
c.SetExpression(1.456)
fmt.Printf("Expr type: %T", c.Expression())
Output:

Expr type: float64

func (Condition) SetID

func (r Condition) SetID(id string) Condition

SetID assigns the provided string value (or lack thereof) to the receiver. This is optional, and is usually only needed in complex Condition structures in which "labeling" certain components may be advantageous. It has no effect on an evaluation, nor should a name ever cause a validity check to fail.

If the string `_random` is provided, a 24-character alphanumeric string is randomly generated using math/rand and assigned as the ID.

Example
c := Cond(`π`, Eq, float64(3.14159265358979323))
c.SetID(`pi`)
fmt.Printf("ID: %s", c.ID())
Output:

ID: pi
Example (PointerAddress)
var c Condition = Cond(`keyword`, Ne, `bogus`)

// can't predict what ID will be,
// so we'll check the prefix to
// be certain it begins with '0x'.
c.SetID(`_addr`)
fmt.Printf("Address ID has '0x' prefix: %t", c.ID()[:2] == `0x`)
Output:

Address ID has '0x' prefix: true
Example (Random)
var c Condition = Cond(`keyword`, Ne, `bogus`)

// can't predict what ID will
// be, so we'll check length
// which should always be 24.
c.SetID(`_random`)
fmt.Printf("Random ID len: %d", len(c.ID()))
Output:

Random ID len: 24

func (Condition) SetKeyword

func (r Condition) SetKeyword(kw any) Condition

SetKeyword sets the receiver's keyword using the specified kw input argument.

Example
var c Condition
c.Init()
c.SetKeyword(`my_keyword`)
fmt.Printf("Keyword: %s", c.Keyword())
Output:

Keyword: my_keyword

func (Condition) SetLogLevel

func (r Condition) SetLogLevel(l ...any) Condition

SetLogLevel enables the specified LogLevel instance(s), thereby instructing the logging subsystem to accept events for submission and transcription to the underlying logger.

Users may also sum the desired bit values manually, and cast the product as a LogLevel. For example, if STATE (4), DEBUG (8) and TRACE (32) logging were desired, entering LogLevel(44) would be the same as specifying LogLevel3, LogLevel4 and LogLevel6 in variadic fashion.

Example
var buf *bytes.Buffer = &bytes.Buffer{}
var customLogger *log.Logger = log.New(buf, ``, 0)
var c Condition
c.Init()
c.SetLogger(customLogger)
c.SetLogLevel(LogLevel1, LogLevel3) // calls (1) + state(4)
fmt.Printf("LogLevels active: %s", c.LogLevels())
Output:

LogLevels active: CALLS,STATE

func (Condition) SetLogger

func (r Condition) SetLogger(logger any) Condition

SetLogger assigns the specified logging facility to the receiver instance.

Logging is available but is set to discard all events by default.

An active logging subsystem within the receiver supercedes the default package logger.

The following types/values are permitted:

  • string: `none`, `off`, `null`, `discard` will turn logging off
  • string: `stdout` will set basic STDOUT logging
  • string: `stderr` will set basic STDERR logging
  • int: 0 will turn logging off
  • int: 1 will set basic STDOUT logging
  • int: 2 will set basic STDERR logging
  • *log.Logger: user-defined *log.Logger instance will be set; it should not be nil

Case is not significant in the string matching process.

Logging may also be set globally using the SetDefaultLogger package level function. Similar semantics apply.

func (Condition) SetOperator

func (r Condition) SetOperator(op Operator) Condition

SetOperator sets the receiver's comparison operator using the specified Operator-qualifying input argument (op).

Example
var c Condition
c.Init()
c.SetKeyword(`keyword`)
c.SetOperator(Ge)
c.SetExpression(1.456)
fmt.Printf("Operator: %s", c.Operator())
Output:

Operator: >=

func (Condition) SetPresentationPolicy

func (r Condition) SetPresentationPolicy(x PresentationPolicy) Condition

SetPresentationPolicy assigns the instance of PresentationPolicy to the receiver. This will allow the user to leverage their own "stringer" method for automatic use when this type's String method is called.

Specifying nil shall disable this capability if enabled.

Example

This example demonstrates a custom user-authored stringer that assumes full responsibility for the string representation of a Condition instance.

Here, we import "encoding/base64" to decode a string into the true underlying value prior to calling fmt.Sprintf to present and return the preferred string value.

// create a Condition and set a custom
// ppolicy to handle base64 encoded
// values.
myCondition := Cond(`keyword`, Eq, `MTM4NDk5`)

myCondition.SetPresentationPolicy(func(x ...any) string {
	val := myCondition.Expression().(string)
	decoded, err := base64.StdEncoding.DecodeString(val)
	if err != nil {
		myCondition.SetErr(err)
		//fmt.Printf("%v", err) // optional
		return `invalid_condition`
	}

	// optional
	//myCondition.SetExpression(decoded)

	// return new string value
	return fmt.Sprintf("%v %v %v",
		myCondition.Keyword(),
		myCondition.Operator(),
		string(decoded),
	)
})

fmt.Printf("%s", myCondition)
Output:

keyword = 138499

func (Condition) SetValidityPolicy

func (r Condition) SetValidityPolicy(x ValidityPolicy) Condition

SetValidityPolicy assigns the instance of ValidityPolicy to the receiver. This will allow the Valid method to return a more meaningful result.

Specifying nil shall disable this capability if enabled.

Example
myCondition := Cond(`keyword`, Eq, int(3))
myCondition.SetValidityPolicy(func(_ ...any) (err error) {
	assert, _ := myCondition.Expression().(int)
	if assert%2 != 0 {
		err = fmt.Errorf("%T is not even (%d)", assert, assert)
	}
	return
})
fmt.Printf("%v", myCondition.Valid())
Output:

int is not even (3)

func (Condition) String

func (r Condition) String() (s string)

String is a stringer method that returns the string representation of the receiver instance. It will only function if the receiver is in good standing, and passes validity checks.

Note that if the underlying expression value is not a known type, such as a Stack or a Go primitive, this method may be uncertain as to what it should do. A bogus string may be returned.

In such a case, it may be necessary to subvert the default string representation behavior demonstrated by instances of this type in favor of a custom instance of the PresentationPolicy closure type for maximum control.

Example
c := Cond(`person`, Eq, `Jesse`).
	Paren().
	Encap(`"`).
	NoPadding()

fmt.Printf("%s", c)
Output:

(person="Jesse")

func (Condition) UnsetLogLevel

func (r Condition) UnsetLogLevel(l ...any) Condition

UnsetLogLevel disables the specified LogLevel instance(s), thereby instructing the logging subsystem to discard events submitted for transcription to the underlying logger.

Example
var buf *bytes.Buffer = &bytes.Buffer{}
var customLogger *log.Logger = log.New(buf, ``, 0)
var c Condition
c.Init()
c.SetLogger(customLogger)
c.SetLogLevel(LogLevel1, LogLevel3) // calls (1) + state(4)
c.UnsetLogLevel(LogLevel1)          // -1
fmt.Printf("LogLevels active: %s", c.LogLevels())
Output:

LogLevels active: STATE

func (Condition) Valid

func (r Condition) Valid() (err error)

Valid returns an instance of error, identifying any serious issues perceived with the state of the receiver.

Non-serious (interim) errors such as denied pushes, capacity violations, etc., are not shown by this method.

If a ValidityPolicy was set within the receiver, it shall be executed here. If no ValidityPolicy was specified, only elements pertaining to basic viability are checked.

Example
c := Cond(`person`, Eq, `Jesse`).
	Paren().
	Encap(`"`).
	NoPadding()

fmt.Printf("Valid: %t", c.Valid() == nil)
Output:

Valid: true

type Evaluator

type Evaluator func(...any) (any, error)

Evaluator is a first-class function signature type which may be leveraged by users in order to compose matching functions by which a given Condition shall be measured/compared, etc.

In other words, this type takes this package one step forward: it no longer merely creates various expressions in the abstract sense -- now it will actually *apply* them to real values, or to gauge their verisimilitude in some other manner. The catch is that the user will need to author the needed functions in order to make such determinations practical.

Use of this feature is totally optional, and may be overkill for most users.

The signature allows for variadic input of any type(s), and shall require the return of a single any instance alongside an error. The contents of the return 'any' instance (if not nil) is entirely up to the user.

type Interface

type Interface interface {
	// Stack: Len returns an integer that represents the number of
	// slices present within.
	//
	// Condition: Len returns a one (1) if properly initialized
	// and set with a value that is not a Stack. Len returns a
	// zero (0) if invalid or otherwise not properly initialized.
	// Len returns the value of Stack.Len in deference, should the
	// expression itself be a Stack.
	Len() int

	// IsInit returns a Boolean value indicative of whether the
	// receiver instance is considered 'properly initialized'
	IsInit() bool

	// Stack: IsFIFO returns a Boolean value indicative of whether
	// the Stack instance exhibits First-In-First-Out behavior as it
	// pertains to the ingress and egress order of slices. See the
	// Stack.SetFIFO method for details on setting this behavior.
	//
	// Condition: IsFIFO returns a Boolean value indicative of whether
	// the receiver's Expression value contains a Stack instance AND
	// that Stack exhibits First-In-First-Out behavior as it pertains
	// to the ingress and egress order of slices. If no Stack value is
	// present within the Condition, false is always returned.
	IsFIFO() bool

	// IsZero returns a Boolean value indicative of whether the receiver
	// instance is considered nil, or unset.
	IsZero() bool

	// Stack: CanNest returns a Boolean value indicative of whether the
	// receiver is allowed to append (Push) additional Stack instances
	// into its collection of slices.
	//
	// Condition: CanNest returns a Boolean value indicative of whether
	// the receiver is allowed to set a Stack instance as its Expression
	// value.
	//
	// See also the NoNesting and IsNesting methods for either of these
	// types.
	CanNest() bool

	// IsParen returns a Boolean value indicative of whether the receiver
	// instance, when represented as a string value, shall encapsulate in
	// parenthetical L [(] and R [)] characters.
	//
	// See also the Paren method for Condition and Stack instances.
	IsParen() bool

	// IsEncap returns a Boolean value indicative of whether the receiver
	// instance, when represented as a string value, shall encapsulate the
	// effective value within user-defined quotation characters.
	//
	// See also the Encap method for Condition and Stack instances.
	IsEncap() bool

	// Stack: IsPadded returns a Boolean value indicative of whether WHSP
	// (ASCII #32) padding shall be applied to the outermost ends of the
	// string representation of the receiver (but within parentheticals).
	//
	// Condition: IsPadded returns a Boolean value indicative of whether
	// WHSP (ASCII #32) padding shall be applied to the outermost ends of
	// the string representation of the receiver, as well as around it's
	// comparison operator, when applicable.
	//
	// See also the NoPadding method for either of these types.
	IsPadded() bool

	// Stack: IsNesting returns a Boolean value indicative of whether the
	// receiver contains one (1) or more slices that are Stack instances.
	//
	// Condition: IsNesting returns a Boolean value indicative of whether
	// the Expression value set within the receiver is a Stack instance.
	//
	// See also the NoNesting and CanNest method for either of these types.
	IsNesting() bool

	// ID returns the identifier assigned to the receiver, if set. This
	// may be anything the user chooses to set, or it may be auto-assigned
	// using the `_addr` or `_random` input keywords.
	//
	// See also the SetID method for Condition and Stack instances.
	ID() string

	// Addr returns the string representation of the pointer address of
	// the embedded receiver instance. This is mainly useful in cases
	// where debugging/troubleshooting may be underway, and the ability
	// to distinguish unnamed instances would be beneficial. It may also
	// be used as an alternative to the tedium of manually naming objects.
	Addr() string

	// String returns the string representation of the receiver. Note that
	// if the receiver is a BASIC Stack, string representation shall not be
	// possible.
	String() string

	// Stack: Category returns the categorical string label assigned to the
	// receiver instance during initialization. This will be one of AND, OR,
	// NOT, LIST and BASIC (these values may be folded case-wise depending
	// on the state of the Fold bit, as set by the user).
	//
	// Condition: Category returns the categorical string label assigned to
	// the receiver instance by the user at any time. Condition categorical
	// labels are entirely user-controlled.
	//
	// See also the SetCategory method for either of these types.
	Category() string

	// Err returns the most recently set instance of error within the receiver.
	// This is particularly useful for users who dislike fluent-style method
	// execution and would prefer to operate in the traditional "if err != nil ..."
	// style.
	//
	// See also the SetErr method for Condition and Stack instances.
	Err() error

	// Valid returns an instance of error resulting from a cursory review of the
	// receiver without any special context. Nilness, quality-of-initialization
	// and other rudiments are checked.
	//
	// This method is most useful when users apply a ValidityPolicy method or
	// function to the receiver instance, allowing full control over what they
	// deem "valid". See the SetValidityPolicy method for Condition and Stack
	// instances for details.
	Valid() error

	// Logger returns the underlying instance of *log.Logger, which may be set by
	// the package by defaults, or supplied by the user in a piecemeal manner.
	//
	// See also the SetLogger method for Condition and Stack instances.
	Logger() *log.Logger
}

Interface is an interface type qualified through instances of the following types (whether native, or type-aliased):

  • Stack
  • Condition

This interface type offers users an alternative to the tedium of repeated type assertion for every Stack and Condition instance they encounter. This may be particularly useful in situations where the act of traversal is conducted upon a Stack instance that contains myriad hierarchies and nested contexts of varying types.

This is not a complete "replacement" for the explicit use of package defined types nor their aliased counterparts. The Interface interface only extends methods that are read-only in nature AND common to both of the above categories.

To access the entire breadth of available methods for the underlying type instance, manual type assertion shall be necessary.

Users SHOULD adopt this interface signature for use in their solutions if and when needed, though it is not a requirement.

type LogLevel

type LogLevel uint16

LogLevel is a uint16 type alias used to define compound logging verbosity configuration values.

LogLevel consts zero (0) through four (4) are as follows:

  • NoLogLevels defines the lack of any logging level
  • LogLevel1 defines basic function and method call event logging
  • LogLevel2 defines the logging of events relating to configuration state changes
  • LogLevel3
  • LogLevel4
const (
	LogLevel1      LogLevel = 1 << iota //     1 :: builtin: calls
	LogLevel2                           //     2 :: builtin: policy
	LogLevel3                           //     4 :: builtin: state
	LogLevel4                           //     8 :: builtin: debug
	LogLevel5                           //    16 :: builtin: errors
	LogLevel6                           //    32 :: builtin: trace
	UserLogLevel1                       //    64 :: user-defined
	UserLogLevel2                       //   128 :: user-defined
	UserLogLevel3                       //   256 :: user-defined
	UserLogLevel4                       //   512 :: user-defined
	UserLogLevel5                       //  1024 :: user-defined
	UserLogLevel6                       //  2048 :: user-defined
	UserLogLevel7                       //  4096 :: user-defined
	UserLogLevel8                       //  8192 :: user-defined
	UserLogLevel9                       // 16384 :: user-defined
	UserLogLevel10                      // 32768 :: user-defined

	AllLogLevels LogLevel = ^LogLevel(0) // 65535 :: log all of the above unconditionally (!!)
)
const NoLogLevels LogLevel = 0 // silent

type Operator

type Operator interface {
	// String should return the preferred string
	// representation of the Operator instance.
	// This is ultimately the value that shall be
	// used during the string representation of
	// the instance of Condition to which the
	// Operator is assigned. Generally, this will
	// be something short and succinct (e.g.: `~=`)
	// but can conceivably be anything you want.
	String() string

	// Context returns the string representation
	// of the context behind the operator. As an
	// example, the Context for an instance of
	// ComparisonOperator is `comparison`. Users
	// should choose intuitive, helpful context
	// names for custom types when defining them.
	Context() string
}

Operator is an interface type that allows user-defined operators to be used within instances of Condition. In rare cases, users may wish to utilize operators that go beyond the package provided ComparisonOperator definitions (or just represent the same operators in a different way). Defining types that conform to the signature of this interface type allows just that.

type PresentationPolicy

type PresentationPolicy func(...any) string

PresentationPolicy is a first-class (closure) function signature that may be leveraged by users in order to better control value presentation during the string representation process. Essentially, one may write their own "stringer" (String()) function or method and use it to override the default behavior of the package based String method(s).

Note that basic Stack instances are ineligible for the process of string representation, thus no PresentationPolicy may be set.

type PushPolicy

type PushPolicy func(...any) error

PushPolicy is a first-class (closure) function signature that may be leveraged by users in order to control what types instances may be pushed into a Stack instance when using its 'Push' method.

When authoring functions or methods that conform to this signature, the idea is to return true for any value that should be pushed, and false for all others. This allows for an opportunity to interdict potentially undesirable Stack additions, unsupported types, etc.

A PushPolicy function or method is executed for each element being added to a Stack via its Push method.

type Stack

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

Stack embeds slices of any ([]any) in pointer form and extends methods allowing convenient interaction with stack structures.

func And

func And(capacity ...int) Stack

And initializes and returns a new instance of Stack configured as a Boolean ANDed stack.

Example (SymbolicAnd)

This example demonstrates ANDed stack values using the double ampersand (&&) symbol.

and := And().
	Paren().                                       // Add parenthesis
	Symbol(`&&`).                                  // Use double pipes for OR
	Push(`condition1`, `condition2`, `condition3`) // Push these values now

fmt.Printf("%s\n", and)
Output:

( condition1 && condition2 && condition3 )

func Basic

func Basic(capacity ...int) Stack

Basic initializes and returns a new instance of Stack, set for basic operation only.

Please note that instances of this design are not eligible for string representation, value encaps, delimitation, and other presentation-related string methods. As such, a zero string (“) shall be returned should String() be executed.

PresentationPolicy instances cannot be assigned to Stack instances of this design.

Example

This example demonstrates the creation of a basic stack and a call to its first (0th) index. Type assertion is performed to reveal a float64 instance.

b := Basic()
b.Push(
	float64(3.14159),
	float64(-9.378),
)
idx, _ := b.Index(0)       // call index
assert, _ := idx.(float64) // type assert to float64
fmt.Printf("%.02f", assert)
Output:

3.14
Example (SetAsReadOnly)

This example demonstrates the creation of a basic stack with (and without) read-only controls enabled.

b := Basic()
b.Push(
	float64(3.14159),
	float64(-9.378),
)
b.ReadOnly() // set readonly
b.Remove(1)  // this ought to fail ...
//b.Pop()	 // alternative to b.Remove(1) in this case
first := b.Len() // record len

b.ReadOnly()      // unset readonly
b.Remove(1)       // retry removal
second := b.Len() // record len again

fmt.Printf("first try: %d vs. second try: %d", first, second)
Output:

first try: 2 vs. second try: 1
Example (WithCapacity)

This example demonstrates the creation of a basic stack and an enforced capacity constraint.

b := Basic(2)
b.Push(
	float64(3.14159),
	float64(-9.378),
	float64(139.104),
)
fmt.Printf("%d", b.Len())
Output:

2

func List

func List(capacity ...int) Stack

List initializes and returns a new instance of Stack configured as a simple list. Stack instances of this design can be delimited using the SetDelimiter method.

Example

This example demonstrates the creation of a List stack, a stack type suitable for general use.

l := List().Push(
	1.234,
	1+3i,
	`hello mr thompson`,
)

l.SetDelimiter('?')

// alternatives ...
//l.JoinDelim(`,`) 	//strings ok too!
//l.JoinDelim(`delim`)

fmt.Printf("%s", l)
Output:

1.234 ? (1+3i) ? hello mr thompson

func Not

func Not(capacity ...int) Stack

Not initializes and returns a new instance of Stack configured as a Boolean NOTed stack.

func Or

func Or(capacity ...int) Stack

Or initializes and returns a new instance of Stack configured as a Boolean ORed stack.

Example (SymbolicOr)

This example demonstrates ORed stack values using the double pipe (||) symbol and custom value encapsulation.

or := Or().
	Paren().                                            // Add parenthesis
	Symbol(`||`).                                       // Use double pipes for OR
	Encap(` `, `"`).                                    // Encapsulate individual vals with double-quotes surrounded by spaces
	Push(`cn`, `sn`, `givenName`, `objectClass`, `uid`) // Push these values now

fmt.Printf("%s\n", or)
Output:

( "cn" || "sn" || "givenName" || "objectClass" || "uid" )

func (Stack) Addr

func (r Stack) Addr() string

Addr returns the string representation of the pointer address for the receiver. This may be useful for logging or debugging operations.

Note: this call uses fmt.Sprintf.

Example
var c Stack = List().Push(`this`, `and`, `that`)
fmt.Printf("Address ID has '0x' prefix: %t", c.Addr()[:2] == `0x`)
Output:

Address ID has '0x' prefix: true

func (Stack) Auxiliary

func (r Stack) Auxiliary() (aux Auxiliary)

Auxiliary returns the instance of Auxiliary from within the receiver.

Example
// make a stack ... any type would do
l := List().Push(`this`, `that`, `other`)

// one can put anything they wish into this map,
// so we'll do a bytes.Buffer since it is simple
// and commonplace.
var buf *bytes.Buffer = &bytes.Buffer{}
_, _ = buf.WriteString(`some .... data .....`)

// Create our map (one could also use make
// and populate it piecemeal as opposed to
// in-line, as we do below).
l.SetAuxiliary(map[string]any{
	`buffer`: buf,
})

//  Call our map and call its 'Get' method in one-shot
if val, ok := l.Auxiliary().Get(`buffer`); ok {
	fmt.Printf("%s", val)
}
Output:

some .... data .....
Example (ByTypeCast)

This example demonstrates building of the Auxiliary map in its generic form (map[string]any) before being type cast to Auxiliary.

var list Stack = List()
proto := make(map[string]any, 0)
proto[`value1`] = []int{1, 2, 3, 4, 5}
proto[`value2`] = [2]any{float64(7.014), rune('#')}
list.SetAuxiliary(Auxiliary(proto)) // cast proto and assign to stack
aux := list.Auxiliary()             // call map to variable
fmt.Printf("%T length:%d", aux, aux.Len())
Output:

stackage.Auxiliary length:2
Example (NoInit)

This example demonstrates the call and (failed) set of an uninitialized Auxiliary instance. While no panic ensues, the map instance is not writable.

The user must instead follow the procedures in the WithInit, UserInit or ByTypeCast examples.

var list Stack = List()
aux := list.Auxiliary()
fmt.Printf("%T found, length:%d",
	aux, aux.Set(`testing`, `123`).Len())
Output:

stackage.Auxiliary found, length:0
Example (UserInit)

This example demonstrates a scenario similar to that of the WithInit example, except in this case the map instance is entirely created and populated by the user in a traditional fashion.

var list Stack = List()
aux := make(Auxiliary, 0)

// user opts to just use standard map
// key/val set procedure, and avoids
// use of the convenience methods.
// This is totally fine.
aux[`value1`] = []int{1, 2, 3, 4, 5}
aux[`value2`] = [2]any{float64(7.014), rune('#')}

list.SetAuxiliary(aux)
fmt.Printf("%T length:%d", aux, len(aux))
Output:

stackage.Auxiliary length:2
Example (WithInit)

This example continues the concepts within the NoInit example, except in this case proper initialization occurs and a desirable outcome is achieved.

var list Stack = List().SetAuxiliary() // no args triggers auto-init
aux := list.Auxiliary()
fmt.Printf("%T found, length was:%d, is now:%d",
	aux,
	aux.Len(),                       // check initial (pre-set) length
	aux.Set(`testing`, `123`).Len()) // fluent Set/Len in one shot
Output:

stackage.Auxiliary found, length was:0, is now:1

func (Stack) Avail

func (r Stack) Avail() (avail int)

Avail returns the available number of slices as an integer value by subtracting the current length from a non-zero capacity.

If no capacity is set, this method returns minus one (-1), meaning infinite capacity is available.

If the receiver (r) is uninitialized, zero (0) is returned.

func (Stack) CanMutex

func (r Stack) CanMutex() (can bool)

CanMutex returns a Boolean value indicating whether the receiver instance has been equipped with mutual exclusion locking features.

This does NOT indicate whether the receiver is actually locked.

func (Stack) CanNest

func (r Stack) CanNest() bool

CanNest returns a Boolean value indicative of whether the no-nesting bit is unset, thereby allowing the Push of Stack and/or Stack type alias instances.

See also the IsNesting method.

func (Stack) Cap

func (r Stack) Cap() (c int)

Cap returns the integer representation of a capacity limit imposed upon the receiver. The return values shall be interpreted as follows:

  • A zero (0) value indicates that the receiver has NO capacity, as the instance is not properly initialized
  • A positive non-zero value (e.g.: >=1) reflects the capacity limit imposed upon the receiver instance
  • A minus one (-1) value indicates infinite capacity is available; no limit is imposed

func (Stack) CapReached

func (r Stack) CapReached() (cr bool)

CapReached returns a Boolean value indicative of whether the receiver has reached the maximum configured capacity.

func (Stack) Category

func (r Stack) Category() (cat string)

Category returns the categorical label string value assigned to the receiver, if set, else a zero string.

Example
var stk Stack = Basic().Push(1, 2, 3, 4)
stk.SetCategory(`basic_stuff`)
fmt.Printf("Category: %s", stk.Category())
Output:

Category: basic_stuff

func (Stack) Defrag

func (r Stack) Defrag(max ...int) Stack

Defrag scans the receiver for breaks in the contiguity of slices and will collapse their formation so that they become contiguous. The effective ordering of repositioned slices is preserved.

For example, this:

"value1", nil, "value2", "value3", nil, nil, nil, "value4"

... would become ...

"value1", "value2", "value3", "value4"

Such fragmentation is rare and may only occur if the receiver were initially assembled with explicit nil instances specified as slices. This may also happen if the values were pointer references that have since been annihilated through some external means. This method exists for these reasons, as well as other corner-cases currently inconceivable.

The max integer, which defaults to fifty (50) when unset, shall result in the scan being terminated when the number of nil slices encountered consecutively reaches the maximum. This is to prevent the process from looping into eternity.

If run on a Stack or Stack type-alias that is currently in possession of one (1) or more nested Stack or Stack type-alias instances, Defrag shall hierarchically traverse the structure and process it no differently than the top-level instance. This applies to such Stack values nested with an instance of Condition or Condition type-alias as well.

This is potentially a destructive method and is still very much considered EXPERIMENTAL. While all tests yield expected results, those who use this method are advised to exercise extreme caution. The most obvious note of caution pertains to the volatility of index numbers, which shall shift according to the defragmentation's influence on the instance in question. By necessity, Len return values shall also change accordingly.

func (Stack) Delimiter

func (r Stack) Delimiter() string

Delimiter returns the delimiter string value currently set within the receiver instance.

Example

This example demonstrates the creation of a list stack using comma delimitation and the retrieval of the same delimiter value.

// note: one could also use a rune
// e.g: ',' or rune(44) for comma.
L := List().SetDelimiter(`,`).Push(
	`item1`,
	`item2`,
)
fmt.Printf("%s", L.Delimiter())
Output:

,

func (Stack) Encap

func (r Stack) Encap(x ...any) Stack

Encap accepts input characters for use in controlled stack value encapsulation.

A single string value will be used for both L and R encapsulation.

An instance of []string with two (2) values will be used for L and R encapsulation using the first and second slice values respectively.

An instance of []string with only one (1) value is identical to the act of providing a single string value, in that both L and R will use one (1) value.

func (Stack) Err

func (r Stack) Err() (err error)

Err returns the error residing within the receiver, or nil if no error condition has been declared.

This method will be particularly useful for users who do not care for fluent-style operations and would instead prefer to operate in a more procedural fashion.

Note that a chained sequence of method calls of this type shall potentially obscure error conditions along the way, as each successive method may happily overwrite any error instance already present.

func (Stack) Fold

func (r Stack) Fold(state ...bool) Stack

Stack will fold the case of logical Boolean operators which are not represented through symbols. For example, `AND` becomes `and`, or vice versa. This won't have any effect on List-based receivers, or if symbols are used in place of said Boolean words.

A Boolean input value explicitly sets the bit as intended. Execution without a Boolean input value will *TOGGLE* the current state of the case-folding bit (i.e.: true->false and false->true)

func (Stack) ForwardIndices

func (r Stack) ForwardIndices(state ...bool) Stack

ForwardIndices will enable forward index support when using the Index method extended by this type. See the method documentation for further details.

A Boolean input value explicitly sets the bit as intended. Execution without a Boolean input value will *TOGGLE* the current state of the forward indices bit (i.e.: true->false and false->true)

Example
var stk Stack = Basic().Push(1, 2, 3, 4)
stk.ForwardIndices(true)
slice, _ := stk.Index(1000)
fmt.Printf("%d", slice.(int))
Output:

4

func (Stack) ID

func (r Stack) ID() (id string)

ID returns the assigned identifier string, if set, from within the underlying stack configuration.

func (Stack) Index

func (r Stack) Index(idx int) (slice any, ok bool)

Index returns the Nth slice within the given receiver alongside the true index number and a Boolean value indicative of a successful call of a non-nil value.

This method supports the use of the following index values depending on the configuration of the receiver.

Negatives: When negative index support is enabled, a negative index will not panic, rather the index number will be increased such that it becomes positive and within the bounds of the stack length, and (perhaps most importantly) aligned with the relative (intended) slice. To offer an example, -2 would return the second-to-last slice. When negative index support is NOT enabled, nil is returned for any index out of bounds along with a Boolean value of false, although no panic will occur.

Positives: When forward index support is enabled, an index greater than the length of the stack shall be reduced to the highest valid slice index. For example, if an index of twenty (20) were used on a stack instance of a length of ten (10), the index would transform to nine (9). When forward index support is NOT enabled, nil is returned for any index out of bounds along with a Boolean value of false, although no panic will occur.

In any scenario, a valid index within the bounds of the stack's length returns the intended slice along with Boolean value of true.

func (Stack) Insert

func (r Stack) Insert(x any, left int) (ok bool)

Insert will insert value x to become the left index. For example, using zero (0) as left shall result in value x becoming the first slice within the receiver.

This method returns a Boolean value indicative of success. A value of true indicates the receiver length became longer by one (1).

This method does not currently respond to forward/negative index support. An integer value less than or equal to zero (0) shall become zero (0). An integer value that exceeds the length of the receiver shall become index len-1. A value that falls within the bounds of the receiver's current length is inserted as intended.

Use of the Insert method shall not result in fragmentation of the receiver instance, as any nil x value shall be discarded and not considered for insertion into the stack.

Example
var stk Stack = Basic().Push(1, 2, 3, 5)
add := 4
idx := 3
stk.Insert(add, idx)
slice, _ := stk.Index(idx)
fmt.Printf("%d", slice.(int))
Output:

4

func (Stack) IsEncap

func (r Stack) IsEncap() (is bool)

IsEncap returns a Boolean value indicative of whether value encapsulation characters have been set within the receiver.

func (Stack) IsFIFO

func (r Stack) IsFIFO() (is bool)

IsFIFO returns a Boolean value indicative of whether the underlying receiver instance exhibits First-In-First-Out behavior as it pertains to the appending and truncation order of the receiver instance.

A value of false implies Last-In-Last-Out behavior, which is the default ordering scheme imposed upon instances of this type.

func (Stack) IsInit

func (r Stack) IsInit() (is bool)

IsInit returns a Boolean value indicative of whether the receiver has been initialized using any of the following package-level functions:

  • And
  • Or
  • Not
  • List
  • Basic

This method does not take into account the presence (or absence) of any user-provided values (e.g.: a length of zero (0)) can still return true.

func (Stack) IsNesting

func (r Stack) IsNesting() (is bool)

IsNesting returns a Boolean value indicative of whether at least one (1) slice member is either a Stack or Stack type alias. If true, this indicates the relevant slice descends into another hierarchical (nested) context.

func (Stack) IsPadded

func (r Stack) IsPadded() bool

IsPadded returns a Boolean value indicative of whether the receiver pads its contents with a SPACE char (ASCII #32).

func (Stack) IsParen

func (r Stack) IsParen() bool

IsParen returns a Boolean value indicative of whether the receiver is parenthetical.

func (Stack) IsReadOnly

func (r Stack) IsReadOnly() bool

IsReadOnly returns a Boolean value indicative of whether the receiver is set as read-only.

func (Stack) IsZero

func (r Stack) IsZero() bool

IsZero returns a Boolean value indicative of whether the receiver is nil, or uninitialized.

func (Stack) Kind

func (r Stack) Kind() (k string)

Kind returns the string name of the type of receiver configuration.

Example
var myStack Stack = And()
fmt.Printf("Kind: '%s'", myStack.Kind())
Output:

Kind: 'AND'

func (Stack) LeadOnce

func (r Stack) LeadOnce(state ...bool) Stack

LeadOnce sets the lead-once bit within the receiver. This causes two (2) things to happen:

  • Only use the configured operator once in a stack, and ...
  • Only use said operator at the very beginning of the stack string value

Execution without a Boolean input value will *TOGGLE* the current state of the lead-once bit (i.e.: true->false and false->true)

Example

This example demonstrates the LeadOnce feature, which limits a given Stack's logical operator usage to once-per, and only at the beginning (i.e.: the form of an LDAP Search Filter's operators when conditions are nested).

maker := func(r Stack) Stack {
	return r.Paren().LeadOnce().NoPadding()
}

Ands := maker(And().Symbol('&'))
Ors := maker(Or().Symbol('|')).Push(
	Cond(`objectClass`, Eq, `engineeringLead`).NoPadding().Paren(), // OR condition #1
	Cond(`objectClass`, Eq, `shareholder`).NoPadding().Paren(),     // OR condition #1
)

// Begin filter at AND
filter := Ands.Push(
	Cond(`objectClass`, Eq, `employee`).NoPadding().Paren(), // AND condition #1
	Ors, // Begin OR (which is AND condition #2)
)

fmt.Printf("%s", filter)
Output:

(&(objectClass=employee)(|(objectClass=engineeringLead)(objectClass=shareholder)))

func (Stack) Len

func (r Stack) Len() (i int)

Len returns the integer length of the receiver.

func (Stack) LogLevels

func (r Stack) LogLevels() string

LogLevels returns the string representation of a comma-delimited list of all active LogLevel values within the receiver.

func (Stack) Logger

func (r Stack) Logger() (l *log.Logger)

Logger returns the *log.Logger instance. This can be used for quick access to the log.Logger type's methods in a manner such as:

r.Logger().Fatalf("We died")

It is not recommended to modify the return instance for the purpose of disabling logging outright (see Stack.SetLogger method as well as the SetDefaultStackLogger package-level function for ways of doing this easily).

func (Stack) Mutex

func (r Stack) Mutex() Stack

Mutex enables the receiver's mutual exclusion locking capabilities.

Subsequent calls of write-related methods, such as Push, Pop, Remove and others, shall invoke MuTeX locking at the latest possible state of processing, thereby minimizing the duration of a lock as much as possible.

func (Stack) NegativeIndices

func (r Stack) NegativeIndices(state ...bool) Stack

NegativeIndices will enable negative index support when using the Index method extended by this type. See the method documentation for further details.

A Boolean input value explicitly sets the bit as intended. Execution without a Boolean input value will *TOGGLE* the current state of the negative indices bit (i.e.: true->false and false->true)

Example
var stk Stack = Basic().Push(1, 2, 3, 4)
stk.NegativeIndices(true)
slice, _ := stk.Index(-1)
fmt.Printf("%d", slice.(int))
Output:

4

func (Stack) NoNesting

func (r Stack) NoNesting(state ...bool) Stack

NoNesting sets the no-nesting bit within the receiver. If set to true, the receiver shall ignore any Stack or Stack type alias instance when pushed using the Push method. In such a case, only primitives, Conditions, etc., shall be honored during the Push operation.

Note this will only have an effect when not using a custom PushPolicy. When using a custom PushPolicy, the user has total control -- and full responsibility -- in deciding what may or may not be pushed.

Also note that setting or unsetting this bit shall not, in any way, have an impact on pre-existing Stack or Stack type alias instances within the receiver. This bit only has an influence on the Push method and only when set to true.

A Boolean input value explicitly sets the bit as intended. Execution without a Boolean input value will *TOGGLE* the current state of the nesting bit (i.e.: true->false and false->true)

func (Stack) NoPadding

func (r Stack) NoPadding(state ...bool) Stack

NoPadding sets the no-space-padding bit within the receiver. String values within the receiver shall not be padded using a single space character (ASCII #32).

A Boolean input value explicitly sets the bit as intended. Execution without a Boolean input value will *TOGGLE* the current state of the padding bit (i.e.: true->false and false->true)

func (Stack) Paren

func (r Stack) Paren(state ...bool) Stack

Paren sets the string-encapsulation bit for parenthetical expression within the receiver. The receiver shall undergo parenthetical encapsulation ( (...) ) during the string representation process. Individual string values shall not be encapsulated in parenthesis, only the whole (current) stack.

A Boolean input value explicitly sets the bit as intended. Execution without a Boolean input value will *TOGGLE* the current state of the encapsulation bit (i.e.: true->false and false->true)

func (Stack) Pop

func (r Stack) Pop() (popped any, ok bool)

Pop removes and returns the requisite slice value from the receiver instance. A Boolean value is returned alongside, indicative of whether an actual slice value was found.

The requisite slice shall be one (1) of the following, depending on ordering mode in effect:

  • In the default mode -- LIFO -- this shall be the final slice (index "Stack.Len() - 1", or the "far right" element)
  • In the alternative mode -- FIFO -- this shall be the first slice (index 0, or the "far left" element)

Note that if the receiver is in an invalid state, or has a zero length, nothing will be removed, and a meaningless value of true will be returned alongside a nil slice value.

Example (FIFO)
b := Basic()
b.SetFIFO(true)
b.Push(
	float64(3.14159),
	float32(-9.378),
	-1,
	`banana`,
)

popped, _ := b.Pop()
fmt.Printf("%T, length now: %d", popped, b.Len())
Output:

float64, length now: 3
Example (LIFO)
b := Basic()
b.Push(
	float64(3.14159),
	float32(-9.378),
	-1,
	`banana`,
)

popped, _ := b.Pop()
fmt.Printf("%T, length now: %d", popped, b.Len())
Output:

string, length now: 3

func (Stack) Push

func (r Stack) Push(y ...any) Stack

Push appends the provided value(s) to the receiver, and returns the receiver in fluent form.

Note that if the receiver is in an invalid state, or if maximum capacity has been set and reached, each of the values intended for append shall be ignored.

func (Stack) ReadOnly

func (r Stack) ReadOnly(state ...bool) Stack

ReadOnly sets the receiver bit 'ronly' to a positive state. This will prevent any writes to the receiver or its underlying configuration.

func (Stack) Remove

func (r Stack) Remove(idx int) (slice any, ok bool)

Remove will remove and return the Nth slice from the index, along with a success-indicative Boolean value. A value of true indicates the receiver length became shorter by one (1).

Use of the Remove method shall not result in fragmentation of the stack: gaps resulting from the removal of slice instances shall immediately be "collapsed" using the subsequent slices available.

func (Stack) Replace

func (r Stack) Replace(x any, idx int) bool

Replace will overwrite slice idx using value x and returns a Boolean value indicative of success.

If slice i does not exist (e.g.: idx > receiver len), then nothing is altered and a false Boolean value is returned.

Use of the Replace method shall not result in fragmentation of the receiver instance; this method does not honor any attempt to replace any receiver slice value with nil.

func (Stack) Reset

func (r Stack) Reset()

Reset will silently iterate and delete each slice found within the receiver, leaving it unpopulated but still retaining its active configuration. Nothing is returned.

func (Stack) Reveal

func (r Stack) Reveal() Stack

Reveal processes the receiver instance and disenvelops needlessly enveloped Stack slices.

func (Stack) Reverse added in v1.0.1

func (r Stack) Reverse() Stack

Reverse shall re-order the receiver's current slices in a sequence that is the polar opposite of the original.

Example
var c Stack = List().Push(0, 1, 2, 3, 4)
fmt.Printf("%s", c.Reverse())
Output:

4 3 2 1 0

func (Stack) SetAuxiliary

func (r Stack) SetAuxiliary(aux ...Auxiliary) Stack

SetAuxiliary assigns aux, as initialized and optionally populated as needed by the user, to the receiver instance. The aux input value may be nil.

If no variadic input is provided, the default Auxiliary allocation shall occur.

Note that this method shall obliterate any instance that may already be present, regardless of the state of the input value aux.

Example
// Always alloc stack somehow, in this
// case just use List because its simple
// and (unlike Basic) it is feature-rich.
var list Stack = List()

// alloc map
aux := make(Auxiliary, 0)

// populate map
aux.Set(`somethingWeNeed`, struct {
	Type  string
	Value []string
}{
	Type: `L`,
	Value: []string{
		`abc`,
		`def`,
	},
})

// assign map to stack rcvr
list.SetAuxiliary(aux)

// verify presence
call := list.Auxiliary()
fmt.Printf("%T found, length:%d", call, call.Len())
Output:

stackage.Auxiliary found, length:1

func (Stack) SetCategory

func (r Stack) SetCategory(cat string) Stack

SetCategory assigns the provided string to the stack's internal category value. This allows for a means of identifying a particular kind of stack in the midst of many.

Example
var stk Stack = Basic().Push(1, 2, 3, 4)
stk.SetCategory(`basic_stuff`)
fmt.Printf("Category: %s", stk.Category())
Output:

Category: basic_stuff

func (Stack) SetDelimiter

func (r Stack) SetDelimiter(x any) Stack

SetDelimiter accepts input characters (as string, or a single rune) for use in controlled stack value joining when the underlying stack type is a LIST. In such a case, the input value shall be used for delimitation of all slice values during the string representation process.

A zero string, the NTBS (NULL) character -- ASCII #0 -- or nil, shall unset this value within the receiver.

If this method is executed using any other stack type, the operation has no effect. If using Boolean AND, OR or NOT stacks and a character delimiter is preferred over a Boolean WORD, see the Stack.Symbol method.

Example

This example demonstrates the creation of a list stack using comma delimitation.

// note: one could also use a rune
// e.g: ',' or rune(44) for comma.
L := List().SetDelimiter(`+`).Push(
	`item1`,
	`item2`,
)
fmt.Printf("%s", L)
Output:

item1 + item2

func (Stack) SetErr

func (r Stack) SetErr(err error) Stack

SetErr sets the underlying error value within the receiver to the assigned input value err, whether nil or not.

This method may be most valuable to users who have chosen to extend this type by aliasing, and wish to control the handling of error conditions in another manner.

func (Stack) SetFIFO

func (r Stack) SetFIFO(fifo bool) Stack

SetFIFO shall assign the bool instance to the underlying receiver configuration, declaring the nature of the append/truncate scheme to be honored.

  • A value of true shall impose First-In-First-Out behavior
  • A value of false (the default) shall impose Last-In-First-Out behavior

This setting shall impose no influence on any methods other than the Pop method. In other words, Push, Defrag, Remove, Replace, et al., will all operate in the same manner regardless.

Once set to the non-default value of true, this setting cannot be changed nor toggled ever again for this instance and shall not be subject to any override controls.

In short, once you go FIFO, you cannot go back.

func (Stack) SetID

func (r Stack) SetID(id string) Stack

SetID assigns the provided string value (or lack thereof) to the receiver. This is optional, and is usually only needed in complex structures in which "labeling" certain components may be advantageous. It has no effect on an evaluation, nor should a name ever cause a validity check to fail.

If the string `_random` is provided, a 24-character alphanumeric string is randomly generated using math/rand and assigned as the ID.

Example (PointerAddress)
var stk Stack = Basic().Push(1, 2, 3, 4)

// can't predict what ID will be,
// so we'll check the prefix to
// be certain it begins with '0x'.
stk.SetID(`_addr`)
fmt.Printf("Address ID has '0x' prefix: %t", stk.ID()[:2] == `0x`)
Output:

Address ID has '0x' prefix: true
Example (Random)
var stk Stack = Basic().Push(1, 2, 3, 4)

// can't predict what ID will
// be, so we'll check length
// which should always be 24.
stk.SetID(`_random`)
fmt.Printf("Random ID len: %d", len(stk.ID()))
Output:

Random ID len: 24

func (Stack) SetLogLevel

func (r Stack) SetLogLevel(l ...any) Stack

SetLogLevel enables the specified LogLevel instance(s), thereby instructing the logging subsystem to accept events for submission and transcription to the underlying logger.

Users may also sum the desired bit values manually, and cast the product as a LogLevel. For example, if STATE (4), DEBUG (8) and TRACE (32) logging were desired, entering LogLevel(44) would be the same as specifying LogLevel3, LogLevel4 and LogLevel6 in variadic fashion.

func (Stack) SetLogger

func (r Stack) SetLogger(logger any) Stack

SetLogger assigns the specified logging facility to the receiver instance.

Logging is available but is set to discard all events by default.

An active logging subsystem within the receiver supercedes the default package logger.

The following types/values are permitted:

  • string: `none`, `off`, `null`, `discard` will turn logging off
  • string: `stdout` will set basic STDOUT logging
  • string: `stderr` will set basic STDERR logging
  • int: 0 will turn logging off
  • int: 1 will set basic STDOUT logging
  • int: 2 will set basic STDERR logging
  • *log.Logger: user-defined *log.Logger instance will be set; it should not be nil

Case is not significant in the string matching process.

Logging may also be set globally using the SetDefaultLogger package level function. Similar semantics apply.

func (Stack) SetPresentationPolicy

func (r Stack) SetPresentationPolicy(ppol PresentationPolicy) Stack

SetPresentationPolicy assigns the provided PresentationPolicy closure function to the receiver, thereby enabling full control over the stringification of the receiver. Execution of this type's String() method will execute the provided policy instead of the package-provided routine.

Example

This example demonstrates a custom user-authored stringer that assumes full responsibility for the string representation of a Stack instance.

// slices we intend to push
// into our stack.
slices := []any{
	`safe_text`,
	`sensitive_text`,
	1.261,
	`safe_text`,
	`sensitive_text`,
	[]string{`health`, `your`, `for`, `bad`, `is`, `smoking`, `ziggurat`},
	`irrelevant_text`,
	`sensitive_text`,
	`safe_text`,
	[]string{`homes.`, `feelings,`, `your`, `about`, `talk`, `to`, `need`, `You`},
}

// create a List stack, push the above slices
// in variadic form and create/set a ppolicy.
myStack := List()
myStack.Push(slices...).SetPresentationPolicy(func(x ...any) string {
	var retval []string
	for i := 0; i < myStack.Len(); i++ {
		slice, _ := myStack.Index(i)
		switch tv := slice.(type) {
		case string:
			if tv == `safe_text` {
				retval = append(retval, tv+` (vetted)`) // declare safe
			} else if tv == `sensitive_text` {
				retval = append(retval, `[REDACTED]`) // redact
			} else {
				retval = append(retval, tv) // as-is
			}
		case []string:
			var message []string
			for j := len(tv); j > 0; j-- {
				message = append(message, tv[j-1])
			}
			retval = append(retval, strings.Join(message, string(rune(32))))
		default:
			retval = append(retval, fmt.Sprintf("%v (%T)", tv, tv))
		}
	}

	// Since we're doing our own stringer, we can't rely on the
	// SetDelimiter method (nor any method used in the string
	// representation process).
	return strings.Join(retval, ` || `)
})

fmt.Printf("%s", myStack)
Output:

safe_text (vetted) || [REDACTED] || 1.261 (float64) || safe_text (vetted) || [REDACTED] || ziggurat smoking is bad for your health || irrelevant_text || [REDACTED] || safe_text (vetted) || You need to talk about your feelings, homes.

func (Stack) SetPushPolicy

func (r Stack) SetPushPolicy(ppol PushPolicy) Stack

SetPushPolicy assigns the provided PushPolicy closure function to the receiver, thereby enabling protection against undesired appends to the Stack. The provided function shall be executed by the Push method for each individual item being added.

Example

This example demonstrates the use of a custom PushPolicy instance assigned to the receiver instance to limit valid push candidates to only integers, and only those that are positive and are powers of two (n²).

Generally speaking, even though the input signature for instances of the PushPolicy type is variadic, it is recommended that it be used in a unary manner, as we will demonstrate by calling slice #0 explicitly and exclusively.

myStack := List()
myStack.SetPushPolicy(func(x ...any) (err error) {
	if len(x) == 0 {
		return // no error because nothing was pushed
	}

	switch tv := x[0].(type) {
	case int:
		// We allow int values, but only certain
		// ones will be accepted for push.
		if tv <= 0 {
			// value cannot be negative nor can it be zero
			err = fmt.Errorf("%T->%T push denied: unsupported integer value: %d <= 0", tv, myStack, tv)
		} else if !isPowerOfTwo(tv) {
			// value must be power of two
			err = fmt.Errorf("%T->%T push denied: unsupported integer value: %d != n²", tv, myStack, tv)
		}
	default:
		// If not an integer, its most definitely bogus.
		err = fmt.Errorf("%T->%T push denied: unsupported type %T", tv, myStack, tv)
	}

	return
})

// prepare some values to be pushed
// into the myStack instance.
values := []any{
	3.14159,
	2,
	`2`,
	14,
	nil,
	-81,
	uint8(3),
	128,
	[]int{1, 2, 3, 4},
	1023,
	1,
	`peter`,
	nil,
}

// Push each slice individually and allow for the
// opportunity to check for errors at each loop
// iteration. Though this particular technique is
// not required, it can be useful when dealing with
// unvetted or unscrubbed data.
var errct int
for i := 0; i < len(values); i++ {
	myStack.Push(values[i])

	// For the sake of a simple example,
	// let's just count the errors. Push
	// what we can, don't quit.
	if err := myStack.Err(); err != nil {
		errct++
	}

	/*
		// Other error handling options for
		// the sake of a helpful example. One
		// or more of the actions below may
		// be appropriate, given the context
		// of the importing app ...
		if err := myStack.Err(); err != nil {
			//fmt.Println(err)
			//err = fmt.Errorf("Error with additional info prepended: %v", err)
			//panic(err)
			//return
			//continue or break
			//some other action
		}
	*/tion
		}
	*/
}

fmt.Printf("Successful pushes: %d/%d", myStack.Len(), len(values))
Output:

Successful pushes: 3/13

func (Stack) SetValidityPolicy

func (r Stack) SetValidityPolicy(vpol ValidityPolicy) Stack

SetValidityPolicy assigns the provided ValidityPolicy closure function instance to the receiver, thereby allowing users to introduce inline verification checks of a Stack to better gauge its validity. The provided function shall be executed by the Valid method.

Example

This example demonstrates the analytical capabilities made possible through use of the ValidityPolicy type.

Setting a scanning policy, as demonstrates below, can allow a more in-depth analysis of the receiver instance to occur.

The Stack.Valid method shall abandon its "vanilla checks" of the receiver and will, instead, report solely on the result of calling the specified user-authored ValidityPolicy.

// a custom type made by the user,
// because why not?
type demoStruct struct {
	Type  string
	Value any
}

// Initialize our stack, and
// push into it immediately.
myStack := Basic().Push(
	`healthy stuff`,
	`healthy stuff`,
	`healthy stuff`,
	`healthy stuff`,
	demoStruct{
		Type:  `text`,
		Value: `complete_garbage`,
	},
)

// Set a ValidityPolicy. Users can handle
// errors in any manner they wish, such as
// using one the following actions:
//
//   - wrap multiple errors in one
//   - encode error as JSON or some other encoding
//   - error filtering (e.g.: ignore all except X, Y and Z)
//   - craft preliminary error (e.g.: Found N errors; see logs for details)
//
// For the purpose of this simple example, we
// are only scanning for demoStruct instances
// and are only throwing an error should the
// Value field not contain the literal string
// of 'healthy stuff'.
myStack.SetValidityPolicy(func(_ ...any) (err error) {

	// Iterate stack slices per length
	for i := 0; i < myStack.Len(); i++ {
		// Grab each stack index (slice)
		slice, _ := myStack.Index(i)

		// Perform type switch on slice
		switch tv := slice.(type) {
		case demoStruct:
			if tv.Type == `text` {
				if assert, _ := tv.Value.(string); assert != `healthy stuff` {
					// What a piece of junk!
					err = errorf("embedded instance is invalid")
				}
			}
		}

		// We choose not to go any
		// further when errors appear.
		if err != nil {
			break
		}
	}

	return
})

// Execute Stack.Valid, which returns an error instance,
// directly into a fmt.Printf call.
fmt.Printf("%v", myStack.Valid())
Output:

embedded instance is invalid

func (Stack) String

func (r Stack) String() (s string)

String is a stringer method that returns the string representation of the receiver.

Note that invalid Stack instances, as well as basic Stacks, are not eligible for string representation.

func (Stack) Symbol

func (r Stack) Symbol(c ...any) Stack

Symbol sets the provided symbol expression, which will be a sequence of any characters desired, to represent various Boolean operators without relying on words such as "AND". If a non-zero sequence of characters is set, they will be used to supplant the default word-based operators within the given stack in which the symbol is configured.

Acceptable input types are string and rune.

Execution of this method with no arguments empty the symbol store within the receiver, thereby returning to the default word-based behavior.

This method has no effect on list-style stacks.

func (Stack) Transfer

func (r Stack) Transfer(dest Stack) (ok bool)

Transfer will iterate the receiver (r) and add all slices contained therein to the destination instance (dest).

The following circumstances will result in a false return:

  • Capacity constraints are in-force within the destination instance, and the transfer request (if larger than the sum number of available slices) cannot proceed as a result
  • The destination instance is nil, or has not been properly initialized
  • The receiver instance (r) contains no slices to transfer

The receiver instance (r) is not modified in any way as a result of calling this method. If the receiver (source) should undergo a call to its Reset() method following a call to the Transfer method, only the source will be emptied, and of the slices that have since been transferred instance shall remain in the destination instance.

Example
var source Stack = Basic().Push(1, 2, 3, 4)
var dest Stack = Basic()
source.Transfer(dest)
slice, _ := dest.Index(2)
fmt.Printf("%d", slice.(int))
Output:

3

func (Stack) Traverse

func (r Stack) Traverse(indices ...int) (slice any, ok bool)

Traverse will "walk" a structure of stack elements using the path indices provided. It returns the slice found at the final index, or nil, along with a success-indicative Boolean value.

The semantics of "traversability" are as follows:

  • Any "nesting" instance must be a Stack or Stack type alias
  • Condition instances must either be the final requested element, OR must contain a Stack or Stack type alias instance through which the traversal process may continue
  • All other value types are returned as-is

If the traversal ended at any given value, it will be returned along with a positive ok value letting the user know they arrived at the coordinates they defined and that "something" was found.

If, however, any path elements remained and further traversal was NOT possible, the last slice is returned as nil.

As the return type is any, the slice value must be manually type asserted.

Example

This example demonstrates traversing a Stack instance containing Condition slice instances. We use a path sequence of []int{1, 1} to target slice #1 on the first level, and value #1 of the second level.

// An optional Stack "maker" for configuring
// a specific kind of Stack. Just looks neater
// than doing fluent execs over and over ...
sMaker := func(r Stack) Stack {
	// encapsulate in parens, use symbol rune
	// for logical operators only once, and
	// use no padding between kw, op and value
	return r.Paren().LeadOnce().NoPadding()
}

// An optional Condition "maker", same logic
// as above ...
cMaker := func(r Condition) Condition {
	// encapsulate in parens, no padding between
	// kw, op and value ...
	return r.Paren().NoPadding()
}

// Let's make a faux LDAP Search Filter ...
//
// This will be our top level, which is an AND
Ands := sMaker(And().Symbol('&'))

// This will be our second level Stack, which
// is an OR containing a couple of Condition
// instances in Equality form.
Ors := sMaker(Or().Symbol('|')).Push(
	cMaker(Cond(`objectClass`, Eq, `engineeringLead`)), // OR condition #1
	cMaker(Cond(`objectClass`, Eq, `shareholder`)),     // OR condition #2 // **our traversal target**
)

// Begin filter at AND, and push our
// desired elements into the Stack.
filter := Ands.Push(
	cMaker(Cond(`objectClass`, Eq, `employee`)), // AND condition #1
	Ors, // Begin OR (which is AND's condition #2)
)

// Bool returns shadowed only for brevity.
// Generally you should not do that ...
slice, _ := filter.Traverse(1, 1)   // Enter coordinates
condAssert, ok := slice.(Condition) // The return is any, so assert to what we expect
if !ok {
	fmt.Printf("Type Assertion failed: %T is not expected value\n", slice)
	return
}

fmt.Printf("%s", condAssert) // use its String method automagically
Output:

(objectClass=shareholder)

func (Stack) UnsetLogLevel

func (r Stack) UnsetLogLevel(l ...any) Stack

UnsetLogLevel disables the specified LogLevel instance(s), thereby instructing the logging subsystem to discard events submitted for transcription to the underlying logger.

func (Stack) Valid

func (r Stack) Valid() (err error)

Valid returns an error if the receiver lacks a configuration value, or is unset as a whole. This method does not check to see whether the receiver is in an error condition regarding user content operations (see the E method).

type ValidityPolicy

type ValidityPolicy func(...any) error

ValidityPolicy is a first-class (closure) function signature that may be leveraged by users in order to better gauge the validity of a stack based on its configuration and/or values.

A ValidityPolicy function or method is executed via the Stack method Valid.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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