{"id":33180946,"url":"https://github.com/igorrius/go-fsm","last_synced_at":"2026-01-11T03:39:37.691Z","repository":{"id":57498809,"uuid":"211266852","full_name":"igorrius/go-fsm","owner":"igorrius","description":"Finite State Machine in Go (with Blackjack and... using context)","archived":false,"fork":false,"pushed_at":"2023-02-17T06:38:01.000Z","size":17,"stargazers_count":10,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-13T12:25:38.857Z","etag":null,"topics":["context","finite-state-machine","fsm","go","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/igorrius.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-09-27T07:59:03.000Z","updated_at":"2025-09-29T18:38:42.000Z","dependencies_parsed_at":"2024-01-07T01:19:54.327Z","dependency_job_id":null,"html_url":"https://github.com/igorrius/go-fsm","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/igorrius/go-fsm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorrius%2Fgo-fsm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorrius%2Fgo-fsm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorrius%2Fgo-fsm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorrius%2Fgo-fsm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/igorrius","download_url":"https://codeload.github.com/igorrius/go-fsm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorrius%2Fgo-fsm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":285512086,"owners_count":27184300,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-20T02:00:05.334Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["context","finite-state-machine","fsm","go","golang"],"created_at":"2025-11-16T04:00:23.294Z","updated_at":"2025-11-20T21:04:42.296Z","avatar_url":"https://github.com/igorrius.png","language":"Go","funding_links":[],"categories":["Libraries"],"sub_categories":["Go"],"readme":"[![Go Report Card](https://goreportcard.com/badge/github.com/igorrius/go-fsm)](https://goreportcard.com/report/github.com/igorrius/go-fsm)\n\n# go-fsm\nFinite State Machine in Go (with Blackjack and... using context)\n\nOverview\n----------\nFinite State Machine is designed with alignment to\n[Erlang Finite State Machine](http://erlang.org/documentation/doc-4.8.2/doc/design_principles/fsm.html) principles \nand inspired by https://github.com/dyrkin/fsm solution. But has a bit different approach which based on `context.Context`\nGO package. \n\nA FSM can be described as a set of relations of the form:\n```\nState(S) x Event(E) -\u003e Actions (A), State(S')\n```\nIf we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S'.\n\nInstall\n-------\n```\ngo get -u github.com/igorrius/go-fsm\n```\n\nExample\n--------\n```go\npackage main\n\nimport (\n\t\"context\"\n\tgo_fsm \"github.com/igorrius/go-fsm\"\n\t\"log\"\n)\n\ntype Logger struct {\n}\n\nfunc (l Logger) Log(v ...interface{}) {\n\tlog.Print(v...)\n}\n\nfunc (l Logger) Logf(format string, v ...interface{}) {\n\tlog.Printf(format, v...)\n}\n\nfunc main() {\n\tconst (\n\t\t// define states\n\t\tstateIdle     = \"idle\"\n\t\tstateInAction = \"inAction\"\n\t)\n\n\t// create logger\n\tlogger := \u0026Logger{}\n\n\t// create a new instance of FSM,\n\t// if a logger is not needed use constructor without a logger option e.g. fsm, err := go_fsm.NewFsm().\n\tfsm, err := go_fsm.NewFsm(go_fsm.LoggerOption(logger)).\n\t\tWhen(\n\t\t\tstateIdle,\n\t\t\tfunc(eventCtx go_fsm.EventContext, fsmCtx go_fsm.FsmContext) (next go_fsm.State, nextFsmCtx go_fsm.FsmContext, err error) {\n\t\t\t\tvar state go_fsm.State\n\t\t\t\tvar event go_fsm.Event\n\t\t\t\t// get current state from eventCtx\n\t\t\t\tif event, err = go_fsm.EventFromCtx(eventCtx); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// get current state from fsmContext\n\t\t\t\tif state, err = go_fsm.StateFromCtx(fsmCtx); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tswitch event {\n\t\t\t\tcase \"moveRight\":\n\t\t\t\t\tlog.Println(\"Action: move right\")\n\t\t\t\t\tnextFsmCtx = context.WithValue(fsmCtx, \"degrees\", \"30\")\n\t\t\t\t\tnext = stateInAction\n\t\t\t\tcase \"moveLeft\":\n\t\t\t\t\tlog.Println(\"Action: move left\")\n\t\t\t\t\tnextFsmCtx = context.WithValue(fsmCtx, \"degrees\", \"50\")\n\t\t\t\t\tnext = stateInAction\n\t\t\t\tcase \"letsError\":\n\t\t\t\t\tlog.Println(\"Action: letsError\")\n\t\t\t\t\tnext = \"unknownState\"\n\t\t\t\tdefault:\n\t\t\t\t\t// FSM must stay in current state\n\t\t\t\t\tlog.Println(\"Unknown event: \", event)\n\t\t\t\t\tnext = state\n\t\t\t\t}\n\n\t\t\t\treturn\n\t\t\t},\n\t\t).\n\t\tWhen(\n\t\t\tstateInAction,\n\t\t\tfunc(eventCtx go_fsm.EventContext, fsmCtx go_fsm.FsmContext) (next go_fsm.State, nextFsmCtx go_fsm.FsmContext, err error) {\n\t\t\t\tvar state go_fsm.State\n\t\t\t\tvar event go_fsm.Event\n\t\t\t\t// get current state from eventCtx\n\t\t\t\tif event, err = go_fsm.EventFromCtx(eventCtx); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// get current state from fsmContext\n\t\t\t\tif state, err = go_fsm.StateFromCtx(fsmCtx); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tswitch event {\n\t\t\t\tcase \"stop\":\n\t\t\t\t\tlog.Println(\"Action: stop\")\n\t\t\t\t\tlog.Println(\"Degrees: \", fsmCtx.Value(\"degrees\").(string))\n\t\t\t\t\tnext = stateIdle\n\t\t\t\tdefault:\n\t\t\t\t\t// FSM must stay in current state\n\t\t\t\t\tlog.Println(\"Unknown event: \", event)\n\t\t\t\t\tnext = state\n\t\t\t\t}\n\n\t\t\t\treturn\n\t\t\t},\n\t\t).\n\t\tRegisterPostTransitionFunc(\"*\", \"*\",\n\t\t\tfunc(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {\n\t\t\t\tlog.Println(\"Transition Function [ANY to ANY]\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tRegisterPostTransitionFunc(stateIdle, \"*\",\n\t\t\tfunc(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {\n\t\t\t\tlog.Println(\"Transition Function [stateIdle to ANY]\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tRegisterPostTransitionFunc(stateInAction, \"*\",\n\t\t\tfunc(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {\n\t\t\t\tlog.Println(\"Transition Function [stateInAction to ANY]\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tRegisterPostTransitionFunc(\"*\", stateInAction,\n\t\t\tfunc(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {\n\t\t\t\tlog.Println(\"Transition Function [ANY to stateInAction]\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tRegisterPostTransitionFunc(\"*\", stateIdle,\n\t\t\tfunc(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {\n\t\t\t\tlog.Println(\"Transition Function [ANY to stateIdle]\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tRegisterPostTransitionFunc(stateInAction, stateIdle,\n\t\t\tfunc(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {\n\t\t\t\tlog.Println(\"Transition Function [stateIdle to stateInAction]\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tRegisterPostTransitionFunc(stateIdle, stateInAction,\n\t\t\tfunc(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {\n\t\t\t\tlog.Println(\"Transition Function [stateIdle to stateInAction]\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tInitWithState(stateIdle)\n\n\tif err != nil {\n\t\tlog.Fatalln(\"FSM init error:\", err)\n\t}\n\tdefer fsm.Close()\n\n\t// call stop - wrong event\n\tif err = fsm.ProcessEvent(\"stop\", context.TODO()); err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\t// call letsError - error next state\n\tif err = fsm.ProcessEvent(\"letsError\", context.TODO()); err != nil {\n\t\tlog.Println(\"This will be an error:\", err)\n\t}\n\n\t// call move right event\n\tif err = fsm.ProcessEvent(\"moveRight\", context.TODO()); err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\t// call move stop\n\tif err = fsm.ProcessEvent(\"stop\", context.TODO()); err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\t// call move right event\n\tif err = fsm.ProcessEvent(\"moveLeft\", context.TODO()); err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\t// call move stop - wrong event\n\tif err = fsm.ProcessEvent(\"stop\", context.TODO()); err != nil {\n\t\tlog.Println(\"This will be an error:\", err)\n\t}\n}\n\n```\n\nBenchmark\n---------\n```\nTransition_Permitted-8                                   \t   10000\t    129269 ns/op\t     176 B/op\t       7 allocs/op\nTransition_Denied-8                                        \t 2450037\t       470 ns/op\t     192 B/op\t       8 allocs/op\nTransition_with_post_transition_functions_run-8         \t   10000\t    131176 ns/op\t     176 B/op\t       7 allocs/op\n```\n-------------\nmade with love in GO :)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorrius%2Fgo-fsm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figorrius%2Fgo-fsm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorrius%2Fgo-fsm/lists"}