Go library for creating state machines

Stateless logo. Fire gopher designed by https://www.deviantart.com/quasilyte

Build Status Code Coverage Go Report Card go.dev Licenses Mentioned in Awesome Go

Stateless

Create state machines and lightweight state machine-based workflows directly in Go code:

phoneCall := stateless.NewStateMachine(stateOffHook)

phoneCall.Configure(stateOffHook).Permit(triggerCallDialed, stateRinging)

phoneCall.Configure(stateRinging).
  OnEntryFrom(triggerCallDialed, func(_ context.Context, args ...interface{}) error {
    onDialed(args[0].(string))
    return nil
  }).
  Permit(triggerCallConnected, stateConnected)

phoneCall.Configure(stateConnected).
  OnEntry(func(_ context.Context, _ ...interface{}) error {
    startCallTimer()
    return nil
  }).
  OnExit(func(_ context.Context, _ ...interface{}) error {
    stopCallTimer()
    return nil
  }).
  Permit(triggerLeftMessage, stateOffHook).
  Permit(triggerPlacedOnHold, stateOnHold)

// ...

phoneCall.Fire(triggerCallDialed, "qmuntal")

This project, as well as the example above, is almost a direct, yet idiomatic, port of dotnet-state-machine/stateless, which is written in C#.

The state machine implemented in this library is based on the theory of UML statechart. The concepts behind it are about organizing the way a device, computer program, or other (often technical) process works such that an entity or each of its sub-entities is always in exactly one of a number of possible states and where there are well-defined conditional transitions between these states.

Features

Most standard state machine constructs are supported:

  • Support for states and triggers of any comparable type (int, strings, boolean, structs, etc.)
  • Hierarchical states
  • Entry/exit events for states
  • Guard clauses to support conditional transitions
  • Introspection

Some useful extensions are also provided:

  • Ability to store state externally (for example, in a property tracked by an ORM)
  • Parameterised triggers
  • Reentrant states
  • Thread-safe
  • Export to DOT graph

Hierarchical States

In the example below, the OnHold state is a substate of the Connected state. This means that an OnHold call is still connected.

phoneCall.Configure(stateOnHold).
  SubstateOf(stateConnected).
  Permit(triggerTakenOffHold, stateConnected).
  Permit(triggerPhoneHurledAgainstWall, statePhoneDestroyed)

In addition to the StateMachine.State property, which will report the precise current state, an IsInState(State) method is provided. IsInState(State) will take substates into account, so that if the example above was in the OnHold state, IsInState(State.Connected) would also evaluate to true.

Entry/Exit Events

In the example, the StartCallTimer() method will be executed when a call is connected. The StopCallTimer() will be executed when call completes (by either hanging up or hurling the phone against the wall.)

The call can move between the Connected and OnHold states without the StartCallTimer() and StopCallTimer() methods being called repeatedly because the OnHold state is a substate of the Connected state.

Entry/Exit event handlers can be supplied with a parameter of type Transition that describes the trigger, source and destination states.

Initial state transitions

A substate can be marked as initial state. When the state machine enters the super state it will also automatically enter the substate. This can be configured like this:

sm.Configure(State.B)
  .InitialTransition(State.C);

sm.Configure(State.C)
  .SubstateOf(State.B);

External State Storage

Stateless is designed to be embedded in various application models. For example, some ORMs place requirements upon where mapped data may be stored, and UI frameworks often require state to be stored in special "bindable" properties. To this end, the StateMachine constructor can accept function arguments that will be used to read and write the state values:

machine := stateless.NewStateMachineWithExternalStorage(func(_ context.Context) (stateless.State, error) {
  return myState.Value, nil
}, func(_ context.Context, state stateless.State) error {
  myState.Value  = state
  return nil
}, stateless.FiringQueued)

In this example the state machine will use the myState object for state storage.

Activation / Deactivation

It might be necessary to perform some code before storing the object state, and likewise when restoring the object state. Use Deactivate and Activate for this. Activation should only be called once before normal operation starts, and once before state storage.

Introspection

The state machine can provide a list of the triggers that can be successfully fired within the current state via the StateMachine.PermittedTriggers property.

Guard Clauses

The state machine will choose between multiple transitions based on guard clauses, e.g.:

phoneCall.Configure(stateOffHook).
  Permit(triggerCallDialled, stateRinging, func(_ context.Context, _ ...interface{}) bool {
    return IsValidNumber()
  }).
  Permit(triggerCallDialled, stateBeeping, func(_ context.Context, _ ...interface{}) bool {
    return !IsValidNumber()
  })

Guard clauses within a state must be mutually exclusive (multiple guard clauses cannot be valid at the same time). Substates can override transitions by respecifying them, however substates cannot disallow transitions that are allowed by the superstate.

The guard clauses will be evaluated whenever a trigger is fired. Guards should therefor be made side effect free.

Parameterised Triggers

Strongly-typed parameters can be assigned to triggers:

stateMachine.SetTriggerParameters(triggerCallDialed, reflect.TypeOf(""))

stateMachine.Configure(stateRinging).
  OnEntryFrom(triggerCallDialed, func(_ context.Context, args ...interface{}) error {
    fmt.Println(args[0].(string))
    return nil
  })

stateMachine.Fire(triggerCallDialed, "qmuntal")

It is runtime safe to cast parameters to the ones specified in SetTriggerParameters. If the parameters passed in Fire do not match the ones specified it will panic.

Trigger parameters can be used to dynamically select the destination state using the PermitDynamic() configuration method.

Ignored Transitions and Reentrant States

Firing a trigger that does not have an allowed transition associated with it will cause a panic to be thrown.

To ignore triggers within certain states, use the Ignore(Trigger) directive:

phoneCall.Configure(stateConnected).
  Ignore(triggerCallDialled)

Alternatively, a state can be marked reentrant so its entry and exit events will fire even when transitioning from/to itself:

stateMachine.Configure(stateAssigned).
  PermitReentry(triggerAssigned).
  OnEntry(func(_ context.Context, _ ...interface{}) error {
    startCallTimer()
    return nil
  })

By default, triggers must be ignored explicitly. To override Stateless's default behaviour of throwing a panic when an unhandled trigger is fired, configure the state machine using the OnUnhandledTrigger method:

stateMachine.OnUnhandledTrigger( func (_ context.Context, state State, _ Trigger, _ []string) {})

Export to DOT graph

It can be useful to visualize state machines on runtime. With this approach the code is the authoritative source and state diagrams are by-products which are always up to date.

sm := stateMachine.Configure(stateOffHook).
  Permit(triggerCallDialed, stateRinging, isValidNumber)
graph := sm.ToGraph()

The StateMachine.ToGraph() method returns a string representation of the state machine in the DOT graph language, e.g.:

digraph {
  OffHook -> Ringing [label="CallDialled [isValidNumber]"];
}

This can then be rendered by tools that support the DOT graph language, such as the dot command line tool from graphviz.org or viz.js. See webgraphviz.com for instant gratification. Command line example: dot -T pdf -o phoneCall.pdf phoneCall.dot to generate a PDF file.

This is the complete Phone Call graph as builded in example_test.go.

Phone Call graph

Project Goals

This page is an almost-complete description of Stateless, and its explicit aim is to remain minimal.

Please use the issue tracker or the if you'd like to report problems or discuss features.

(Why the name? Stateless implements the set of rules regarding state transitions, but, at least when the delegate version of the constructor is used, doesn't maintain any internal state itself.)

Owner
Quim Muntal
Eager traveler, passionate engineer and polyglot programmer.
Quim Muntal
Comments
  • rewrite ToGraph with gographviz

    rewrite ToGraph with gographviz

    close #21 I use gographviz to refactor ToGraph function features:

    • multi level sub state
    • InitialState
    • escape string

    example code

    package main
    
    import (
    	"context"
    	"fmt"
    
    	"github.com/qmuntal/stateless"
    )
    
    func main() {
    	sm := stateless.NewStateMachine("a")
    	sm.Configure("a").Permit("toB", "b").OnEntry(func(ctx context.Context, i ...interface{}) error {
    		return nil
    	})
    	sm.Configure("b").InitialTransition("b_1").OnExit(func(ctx context.Context, i ...interface{}) error {
    		return nil
    	}).Permit("toC", "c")
    	sm.Configure("b_1").SubstateOf("b").InitialTransition("b_1_1").Permit("toB2", "b_2")
    	sm.Configure("b_2").SubstateOf("b")
    	sm.Configure("b_1_1").SubstateOf("b_1")
    	fmt.Println(sm.ToGraph())
    }
    
    

    exmaple output

    digraph stateless {
    	compound=true;
    	rankdir=LR;
    	"cluster_b_1-init"->b_1_1;
    	"cluster_b-init"->"cluster_b_1-init"[ lhead=cluster_b_1 ];
    	a->b_1_1[ label=toB, lhead=cluster_b ];
    	b_1_1->c[ label=toC, ltail=cluster_b ];
    	b_1_1->b_2[ label=toB2, ltail=cluster_b_1 ];
    	"stateless-init"->a;
    	subgraph cluster_b {
    	label="b
    ----------
    exit / func2";
    	"cluster_b-init" [ label=init, shape=point ];
    	b_2 [ label=b_2, shape=Mrecord ];
    	subgraph cluster_b_1 {
    	label=b_1;
    	"cluster_b_1-init" [ label=init, shape=point ];
    	b_1_1 [ label=b_1_1, shape=Mrecord ];
    
    }
    ;
    
    }
    ;
    	"stateless-init" [ label=init, shape=point ];
    	a [ label="a|entry / func1", shape=Mrecord ];
    	c;
    
    }
    

    image

  • accessing current state inside stateMutator

    accessing current state inside stateMutator

    Halo, thank you very much for creating this amazing project!

    So, I've been experimenting on this for this past few days and I wonder if there's a possibility to access the current state inside the stateMutator?

    I've tried doing it but I had an issue, coz when stateMutator gets executed (i.e. setting the next state), it acquires the lock from stateMutex, then when I try to get the current state (i.e. StateMachine.State) inside stateMutator, it calls the stateAccessor but the lock is already acquired by stateMutator, hence, deadlock.

    This is specially useful coz I'm storing the state from an external storage (e.g. in-memory or db).

  • SetTriggerParameters enhancment: validate interfaces

    SetTriggerParameters enhancment: validate interfaces

    Currently SetTriggerParameters only matches types exactly. It would be great if instead of requiring the exact type, you could pass SetTriggerParameters and validateParameters used reflect.Value#Implements to validate the parameter.

  • How to handle State timers or counters?

    How to handle State timers or counters?

    I am trying to figure out how I might implement something along the lines of "stay in state for X duration" or "stay in state for X consecutive events". I do not have a lot of experience with state machines, so I'm wondering if:

    1. What I am trying to do is no longer a finite state machine (so this library is probably not what I want to use)
    2. It is a FSM, but this library isn't good for this particular task
    3. This library is good, you might do it like …?

    Simplified Example:

    Events: eGood and eBad. A scheduler is running, sending either a good/bad event at mostly regular intervals. These events are the input to the state machine.

    States: sGood, sBad, sPending.

    My confusion is around the sPending state. The idea of the pending state is that it is a hold for a time duration where consecutive sBad events have been received, before going into the sBad state and performing some action.

    sPending Transitions:

    • Transition into the pending state sPending when eBad event is received and the current state is sGood.
    • (When in sPending, eGood event would set the state to sGood - basically a reset)

    What I am not clear on how a transition from sPending to sBad could be done after either:

    • A certain number of consecutive eBad events have been received
    • A certain amount of time has passed

    Things Considered:

    A) If using the count method, I could create Pending1, Pending2, Pending3, etc state constants, but this feels cumbersome and wouldn't work as well with time durations I don't think.

    B) I can imagine that on receiving a eBad while in the pending state I could start a timer, or create a counter. However, my concern is that I am creating a piece (the timer) of information that is detached from State and StateMachine objects, but does impact the transition behavior of the state machine - and this will get me into trouble.

  • Model vs Machine

    Model vs Machine

    This is a question, not an issue. I am trying to gather the intended method of usage for this package.

    I am wondering if there is a way with this package to define the valid triggers/states one time (maybe in func main(). Then afterwards, create a single StateMachine instance per incoming HTTP request using the previously created configuration (triggers/states)?

    I am hoping to use this package in a way where I would define the triggers/states on application start up then I would use a new StateMachine instance for each new incoming HTTP request because my app is stateless. So for each incoming request I would do the following:

    1. retrieve current state from an external durable storage
    2. create a new StateMachine with the current state and the pre-defined valid triggers/states
    3. fire triggers according to the particular HTTP endpoint
    4. store end state into external durable storage and return data to HTTP endpoint caller
  • What are internal transitions?

    What are internal transitions?

    In the code there are mentions of "internal" transitions as well as of an "initial" transition which supposedly adds an internal transition to the machine. What is the purpose of this and how and when to use them? The README just tells how to define them in code without any examples on how to actually use them.

  • Passing arguments in state accessor and mutator

    Passing arguments in state accessor and mutator

    Hi @qmuntal,

    We're implementing a state machine that uses Postgres as a backend for external storage. As a solution for the concurrent writes issue, we needed to use row-level locks in Postgres. For this to work, we have to make use of the same transaction object on both state accessor and mutator.

    We currently have no way to explicitly pass this via state accessor and mutator so we're injecting the transaction object in the context.

    // inject tx to context
    tx, _ := db.BeginTx()
    ctx := context.WithValue(ctx, "ctxKey", tx)
    
    // access tx from context
    func stateAccessor(ctx context.Context) (interface{}, error) {
        tx, ok := ctx.Value("ctxKey").(Tx)
        // read the current state using the tx object
    }
    
    // access the same  tx from context
    func stateMutator(ctx context.Context, nextState interface{}) error {
        tx, ok := ctx.Value("ctxKey").(Tx)
        // write the next state and commit using the tx object
    }
    

    The above solution can work but is not ideal since we're injecting a complex object into the context.

    What I propose is to extend the signature of state accessor and mutator to also accept the variadic arguments from the StateMachine.FireCtx method.

    func stateMutator(ctx context.context, args ...interface{}) (interface{}, error) { }
    
    func stateAccessor(ctx context.Context, nextState interface{}, args ...interface{}) error { }
    

    With this change, the expected behavior is, when I call StateMachine.FireCtx with argument tx, I should be able to access that argument from both state accessor and mutator.

    // Fire an event
    stateMachine.Fire(ctx, someTrigger, tx)
    
    // access tx from from args
    func stateAccessor(ctx context.Context, args ...interface{}) (interface{}, error) {
        tx, ok := args[0].(Tx)
        // read the current state
    }
    
    // access the same  tx from args
    func stateMutator(ctx context.Context, nextState interface{}, args ...interface{}) error {
        tx, ok := args[0].(Tx)
        // write the next state and commit using the tx object
    }
    

    I'm not sure how this would affect the existing API, but I'm willing to help with the change if needed. Please let me know if you need more clarification regarding this.

    Thanks a lot in advance!

  • Panic on 32bit ARM processor

    Panic on 32bit ARM processor

    The following Unaligned panic occurs on a 32 bit ARM processor

    runtime/internal/atomic.panicUnaligned()
    	/usr/lib/go/src/runtime/internal/atomic/unaligned.go:8 +0x24
    runtime/internal/atomic.Load64(0xf482b4)
    	/usr/lib/go/src/runtime/internal/atomic/atomic_arm.s:286 +0x14
    github.com/qmuntal/stateless.(*StateMachine).Firing(...)
    	/home/andy/Documents/werk/software/products/xte/xte_dev/vendor/github.com/qmuntal/stateless/statemachine.go:268
    	
    

    A simple workaround is to re-arrange the StateMachine struct to place the 'ops' field first where it has the correct byte alignment.

    type StateMachine struct {
    	ops                    uint64
    	stateConfig            map[State]*stateRepresentation
    	triggerConfig          map[Trigger]triggerWithParameters
    	stateAccessor          func(context.Context) (State, error)
    	stateMutator           func(context.Context, State) error
    	unhandledTriggerAction UnhandledTriggerActionFunc
    	onTransitioningEvents  onTransitionEvents
    	onTransitionedEvents   onTransitionEvents
    	eventQueue             *list.List
    	firingMode             FiringMode
    	firingMutex            sync.Mutex
    }
    
    
  • Error control flow poorly documented

    Error control flow poorly documented

    After Ctrl+F'ing the go reference page for error handling documentation I can't be sure what happens if an Entry handler returns an error... does the program panic? Does it prevent the state transition and return to previous state (behaviour I would infer)? Some documentation on OnEntryFrom and OnEntry, among others would be nice. Below is an example of what I mean by returning error

    SM.Configure(state.TestSystems).OnEntryFrom(trigger.TestSystems, 
            func(c context.Context, i ...interface{}) error {
    		act := drivers.FromContext(c)
    		if act == nil || act[0] == nil {
    			return errors.New("got nil actuator slice in state machine") // what happen?
    		}
    		setZero(act)
    		return nil
    	})
    
  • External state storage breaking change

    External state storage breaking change

    I've been using version 1.1.6 of stateless and i noticed that my tests fail after upgrading to 1.1.7. It seems that the callback for writing the state value is no longer being executed after OnEntry.

    machine := stateless.NewStateMachineWithExternalStorage(func(_ context.Context) (stateless.State, error) {
      return myState.Value, nil
    }, func(_ context.Context, state stateless.State) error {
      // this callback is not being executed anymore after onEntry so that i'm able to persist the state to db
      db.Save(myState)
      return nil
    }, stateless.FiringQueued)
    
    
    .Configure(entity.StateCompleted).OnEntry(func(c context.Context, args ...interface{}) error {
      myState.Value = "newValue"
    })
    

    Sequence of events with 1.1.6:

    • Fire custom event
    • read state value
    • write state value
    • OnEntry hook executed
    • write state value

    Sequence of events with 1.1.7:

    • Fire custom event
    • read state value
    • write state value
    • OnEntry hook executed

    I was wondering if this is an expected breaking change.

  • Panic when firing trigger

    Panic when firing trigger

    We're seeing an intermittent problem with a panic when a trigger is fired from a callback (in a separate goroutine) The call stack is as follows:

    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x6c78a1]
    
    goroutine 711 [running]:
    container/list.(*List).insert(...)
            /usr/local/go/src/container/list/list.go:96
    container/list.(*List).insertValue(...)
            /usr/local/go/src/container/list/list.go:104
    container/list.(*List).PushBack(...)
            /usr/local/go/src/container/list/list.go:155
    github.com/qmuntal/stateless.(*StateMachine).internalFireQueued(0xc00004c4e0, 0xebfb40, 0xc00019e010, 0xc589e0, 0xe9cd30, 0x0, 0x0, 0x0, 0x0, 0x0)
            /go/pkg/mod/github.com/qmuntal/[email protected]/statemachine.go:296 +0x4c1
    github.com/qmuntal/stateless.(*StateMachine).internalFire(0xc00004c4e0, 0xebfb40, 0xc00019e010, 0xc589e0, 0xe9cd30, 0x0, 0x0, 0x0, 0x0, 0x0)
            /go/pkg/mod/github.com/qmuntal/[email protected]/statemachine.go:289 +0x89
    github.com/qmuntal/stateless.(*StateMachine).FireCtx(...)
            /go/pkg/mod/github.com/qmuntal/[email protected]/statemachine.go:227
    github.com/qmuntal/stateless.(*StateMachine).Fire(...)
            /go/pkg/mod/github.com/qmuntal/[email protected]/statemachine.go:220
    
    ... our code omitted...
    

    We've been seeing this on and off when we run our unit tests so I decided to investigate. I believe theres a concurrency issue in the internalFireQueued function.

    Here's my analysis: Our code is running some code in the OnEntry func as it has just transitioned to a new state. The callback mentioned above fires a new trigger, which is adding the next trigger to the sm.eventQueue in https://github.com/qmuntal/stateless/blob/3ead642f6c11905eb551a5076e4b40169fba3626/statemachine.go#L296

    At the same time as the new trigger is being added, the OnEntry func returns, and the internalFireQueued resumes execution on https://github.com/qmuntal/stateless/blob/3ead642f6c11905eb551a5076e4b40169fba3626/statemachine.go#L308

    The problematic lines of code at this point are:

    https://github.com/qmuntal/stateless/blob/3ead642f6c11905eb551a5076e4b40169fba3626/statemachine.go#L311 https://github.com/qmuntal/stateless/blob/3ead642f6c11905eb551a5076e4b40169fba3626/statemachine.go#L312 https://github.com/qmuntal/stateless/blob/3ead642f6c11905eb551a5076e4b40169fba3626/statemachine.go#L318

    All of these is accessing the sm.eventQueue which is being mutated by the callback on line https://github.com/qmuntal/stateless/blob/3ead642f6c11905eb551a5076e4b40169fba3626/statemachine.go#L296

    Any access to the sm.eventQueue must be done within the scope of sm.firingMutex to ensure "thread" safety.

  • A better way to pass transition arguments

    A better way to pass transition arguments

    Currently it is possible to pass arguments using variable number of arguments since Fire() function is variadic. This works, however this sort of defeats the purpose of static type checking that Go provides while also bloating user code with type checks.

    While I have no definite solution in mind, I would like to discuss a better solution for this. Namely, what if we introduced an argument wrapper structure that handled most of typical use cases that users could need.

    In my mind I have two specific use-cases:

    1. User always passes a single argument, typically a pointer to a complex struct (i.e. chat bot message struct)
    2. User passes multiple primitive arguments akin to program start arguments (strings, integers, floats)

    We could introduce a stateless.TransitionArguments struct which would be a wrapper for the underlying []interface{}. Then we could tackle both use cases using such wrapper.

    First use case

    For the first use case we could introduce First() and Empty() methods:

    // Check to see if there are any arguments at all
    func (ta *stateless.TransitionArguments) Empty() bool
    // Return the first argument from the list
    func (ta *stateless.TransitionArguments) First() interface{}
    

    Then we could use it like so:

    fsm.Configure("state_a").Permit("step", "state_b").
        OnEntry(func(ctx context.Context, args stateless.TransitionArguments) error {
            var arg = args.First() // returns interface{}
            if arg == nil {
                return errors.New("argument must not be nil")
            }
    
            var message = arg.(*Message) // Which you could later cast (risking segfault if not careful)
    
    	fmt.Printf("Recieved: %s", message)
    
            return nil
        })
    

    We could further simply it like this:

    fsm.Configure("state_a").Permit("step", "state_b").
        OnEntry(func(ctx context.Context, args stateless.TransitionArguments) error {
            if args.Empty() {
                return errors.New("argument must not be nil")
            }
    
            var message = args.First().(*Message)
    
    	fmt.Printf("Recieved: %s", message)
    
            return nil
        })
    

    I am not sure, but maybe there is a way to use reflection to enforce static contract for a value to be either of specified type or nil? This could help avoid segfault during runtime.

    Second use case

    For the second use case we could define a fluent API similar to what genv uses.

    fsm.Configure("state_a").Permit("step", "state_b").
        OnEntry(func(ctx context.Context, args stateless.TransitionArguments) error {
            if args.Has(0) != true {
                return errors.New("argument 0 is required")
            }
    
            var text string = args.At(0).String()
            var count int = args.At(1).Default(1).Integer()
    
    	fmt.Printf("Recieved: %s, %d", text, count)
    
            return nil
        })
    

    Of course, this will be a breaking change, but I am interested in what is your opinion on this. There still persists a problem that there is no way to enforce contract for Fire() method, but I feel like input validation is much more error-prone and useful in the end.

  • Test passing state and trigger as generic types

    Test passing state and trigger as generic types

    This PR is just to view which are the changes required to adopt Go 1.18 type parameters. I'm still not sure if it is worth making State and Trigger generic compared to the overhead of having type parameters polluting almost all internal functions.

  • How to handle triggers that are allowed in all states

    How to handle triggers that are allowed in all states

    Hi, thanks for this great state machine library and I meent a problem when using it. I have a trigger named "shutdown machine", and it can be fired at any state, which will result in back to the very original state. How to implement this? Adding the trigger to every state?

Package fsm allows you to add finite-state machines to your Go code.

fsm Package fsm allows you to add finite-state machines to your Go code. States and Events are defined as int consts: const ( StateFoo fsm.State =

Dec 9, 2022
libFFM-gp: Pure Golang implemented library for FM (factorization machines)

libFFM-gp: Pure Golang implemented library for FM (factorization machines)

Oct 10, 2022
Lima launches Linux virtual machines on macOS, with automatic file sharing, port forwarding, and containerd.

Lima: Linux-on-Mac ("macOS subsystem for Linux", "containerd for Mac")

Jan 8, 2023
Auto-magic file organisation for your machines. :open_file_folder:
Auto-magic file organisation for your machines. :open_file_folder:

Switchboard Description Do you ever get annoyed that your Downloads folder gets cluttered with all types of files? Do you wish you could automatically

Jan 2, 2023
Plinko - a Fluent State Machine for Go
 Plinko - a Fluent State Machine for Go

Plinko - a Fluent State Machine for Go Build Status Create state machines and lightweight state machine-based workflows directly in golang code The pr

Jan 3, 2023
Finite State Machine for Go
Finite State Machine for Go

FSM for Go Finite State Machine for Go It is heavily inspired from looplab/fsm library but with more abstractions and optimizations License FSM is lic

Nov 30, 2021
Converts NFAs (and DFAs) to a regular expressions using the state removal method

nfa2regex: convert NFAs (and DFAs) to regular expressions An implementation of the state removal technique for converting an NFA to a regular expressi

Apr 29, 2022
An ease to use finit state machine golang implementation.Turn any struct to a fsm with graphviz visualization supported.

go-fsm An ease to use finit state machine golang implementation.Turn any struct to a fsm with graphviz visualization supported. usage import github.co

Dec 26, 2021
Finite-state machine with processors

FSM Finite-state machine with processors. Builder Register state processors type state1Processor struct { // clients } func NewState1Processor(..

Jan 7, 2022
Get notifications about unexpected system state from your local Gesundheitsdienst.

Get notifications about unexpected system state from your local Gesundheitsdienst.

Nov 1, 2022
The template repository is used for creating secondary sources written in Golang.
The template repository is used for creating secondary sources written in Golang.

Source Template The template repository is used for creating secondary sources written in Golang. Getting Started Follow this getting started guide to

Dec 28, 2021
Library to work with MimeHeaders and another mime types. Library support wildcards and parameters.

Mime header Motivation This library created to help people to parse media type data, like headers, and store and match it. The main features of the li

Nov 9, 2022
Evolutionary optimization library for Go (genetic algorithm, partical swarm optimization, differential evolution)
Evolutionary optimization library for Go (genetic algorithm, partical swarm optimization, differential evolution)

eaopt is an evolutionary optimization library Table of Contents Changelog Example Background Features Usage General advice Genetic algorithms Overview

Dec 30, 2022
cross-platform, normalized battery information library

battery Cross-platform, normalized battery information library. Gives access to a system independent, typed battery state, capacity, charge and voltag

Dec 22, 2022
GoLang Library for Browser Capabilities Project

Browser Capabilities GoLang Project PHP has get_browser() function which tells what the user's browser is capable of. You can check original documenta

Sep 27, 2022
Go bindings for unarr (decompression library for RAR, TAR, ZIP and 7z archives)

go-unarr Golang bindings for the unarr library from sumatrapdf. unarr is a decompression library and CLI for RAR, TAR, ZIP and 7z archives. GoDoc See

Dec 29, 2022
Type-safe Prometheus metrics builder library for golang

gotoprom A Prometheus metrics builder gotoprom offers an easy to use declarative API with type-safe labels for building and using Prometheus metrics.

Dec 5, 2022
An easy to use, extensible health check library for Go applications.

Try browsing the code on Sourcegraph! Go Health Check An easy to use, extensible health check library for Go applications. Table of Contents Example M

Dec 30, 2022
An simple, easily extensible and concurrent health-check library for Go services
An simple, easily extensible and concurrent health-check library for Go services

Healthcheck A simple and extensible RESTful Healthcheck API implementation for Go services. Health provides an http.Handlefunc for use as a healthchec

Dec 30, 2022