Code Monkey home page Code Monkey logo

nxs-go-fsm's Introduction

nxs-go-fsm

Golang library nxs-go-fsm implements Finite-state machine principles for the data flow.

Introduction

An io.Reader (fsm.Reader further in the text) you will get after initialize the library represents a finite-state machine. The fsm.Reader it's a wrap on specified io.Reader (it may be any io.Reader, e.g. os.Stdin, os.File or even other fsm.Reader) with described states. Using fsm.Reader you may read the data from some source with the required substitutions on the flow.

At any point of time fsm.Reader is in one of specified states. Every state has a set of next states. When trigger (token) for one of the next state is encountered in the data flow and all conditions (described in switch) are match, data handler for this next state will be called and machine will be switched to.

Quickstart

Import

import fsm "github.com/nixys/nxs-go-fsm"

Initialize

You need to initialize this library via Init(r io.Reader, desc fsm.Description) function before using. After that you will get a fsm.Reader reader. Via this reader you'll get a modified data in accordance with described states. To describe a nxs-go-fsm you need to determine following options:

  • r: any io.Reader you wish to read data from
  • desc: struct to describe a states and contexts. See below for details.

Type fsm.StateName

Data type to specify state name. Based on data type string.

Struct fsm.Description

Field Type Description
Ctx context.Context Go context
UserCtx any User context. Used in data handlers to operate with any user's data
States map[fsm.StateName]fsm.State Machine states description
InitState fsm.StateName State used by the machine as an initial state

Struct fsm.State

Field Type Description
NextStates []fsm.NextState Set of next states. May be empty, in this case machine will never change its state

Struct fsm.NextState

Field Type Description
Name fsm.StateName Name of the next state
Switch fsm.Switch Conditions to select the next state
DataHandler func(userCtx any, data, trigger []byte) ([]byte, error) Function to be called (if not nil) if switch conditions are match. Within the function you are able to use user context specified at init, data (all bytes read from the stream since the machine was put into this state) and trigger (token from the data flow that switched machine to next state). Function returns a bytes to be write into output buffer

Struct fsm.Switch

Field Type Description
Trigger []byte Bytes sequence in a data flow that switches machine in the specified state. To be trigger is considered matched a conditions described in lines below in this table must be met
Delimiters fsm.Delimiters Right and left trigger delimiters. The trigger is considered matched if the specified delimiters are present around the trigger. Delimiters can be empty, in this case trigger may has any bytes arounded
Escape bool Set trigger sensitive to escape character ''. If true a trigger is considered matched only if preceding character is not a '' (or even number of this character). Works in conjunction with delimiters

Struct fsm.Delimiters

Field Type Description
L []byte Left delimiters of trigger. It's a set of bytes. When left delimiters are set, trigger considered matched if one preceding byte before trigger present in a left delimiter set. Works in conjunction with right delimiters and escape
R []byte Right delimiters of trigger. It's a set of bytes. When right delimiters are set, trigger considered matched if one following byte after trigger present in a right delimiter set. Works in conjunction with left delimiters and escape

Example

In the example below string somePgSQLDummyPlainDump used as an input data flow. In this flow all columns excluding last will changed to the 000 bytes sequence and the last one to abc.

package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"strings"

	fsm "github.com/nixys/nxs-go-fsm"
)

const somePgSQLDummyPlainDump = `_Some previous data_

---
--- Columns separated by the space!
---

COPY public.names (id, name) FROM stdin;
12 alice
34 bob
\.

_Some following data_

COPY public.comments (id, comment) FROM stdin;
45 foo
78 bar
\.

_Some other following data_
`

var (
	stateCopySearch  = fsm.StateName("copy search")
	stateCopyTail    = fsm.StateName("copy tail")
	stateTableValues = fsm.StateName("table values")
)

func main() {

	r := strings.NewReader(somePgSQLDummyPlainDump)

	fsmR := fsm.Init(
		r,
		fsm.Description{
			Ctx:       context.TODO(),
			UserCtx:   nil,
			InitState: stateCopySearch,
			States: map[fsm.StateName]fsm.State{

				stateCopySearch: {
					NextStates: []fsm.NextState{
						{
							Name: stateCopyTail,
							Switch: fsm.Switch{
								Trigger: []byte("COPY"),
								Delimiters: fsm.Delimiters{
									L: []byte{'\n'},
									R: []byte{' '},
								},
							},
							DataHandler: nil,
						},
					},
				},
				stateCopyTail: {
					NextStates: []fsm.NextState{
						{
							Name: stateTableValues,
							Switch: fsm.Switch{
								Trigger: []byte(";\n"),
							},
							DataHandler: nil,
						},
					},
				},
				stateTableValues: {
					NextStates: []fsm.NextState{
						{
							Name: stateCopySearch,
							Switch: fsm.Switch{
								Trigger: []byte("\\."),
								Delimiters: fsm.Delimiters{
									L: []byte{'\n'},
									R: []byte{'\n'},
								},
							},
							DataHandler: nil,
						},
						{
							Name: stateTableValues,
							Switch: fsm.Switch{
								Trigger: []byte{' '},
							},
							DataHandler: dhTableValueColumn1,
						},
						{
							Name: stateTableValues,
							Switch: fsm.Switch{
								Trigger: []byte{'\n'},
							},
							DataHandler: dhTableValueColumn2,
						},
					},
				},
			},
		},
	)

	_, err := io.Copy(os.Stdout, fsmR)
	if err != nil {
		fmt.Fprintf(os.Stderr, "copy error: %s", err)
	}
}

func dhTableValueColumn1(usrCtx any, data, trigger []byte) ([]byte, error) {
	return append([]byte("000"), trigger...), nil
}

func dhTableValueColumn2(usrCtx any, data, trigger []byte) ([]byte, error) {
	return append([]byte("abc"), trigger...), nil
}

Run:

go run main.go

Output:

_Some previous data_

---
--- Columns separated by the space!
---

COPY public.names (id, name) FROM stdin;
000 abc
000 abc
\.

_Some following data_

COPY public.comments (id, comment) FROM stdin;
000 abc
000 abc
\.

_Some other following data_

For more examples see apps based on this library:

Feedback

For support and feedback please contact me:

License

nxs-go-fsm is released under the MIT License.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.