package types
import (
"context"
"encoding/gob"
"fmt"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"slices"
"strconv"
"strings"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/coder/websocket"
"github.com/gdamore/tcell/v2"
"github.com/orsinium-labs/enum"
am "github.com/pancsta/asyncmachine-go/pkg/machine"
"github.com/pancsta/asyncmachine-go/pkg/telemetry/dbg"
ssdbg "github.com/pancsta/asyncmachine-go/tools/debugger/states"
"github.com/pancsta/cview"
)
var ss = ssdbg .DebuggerStates
type Params struct {
MachUrl string `arg:"positional" help:"Machine URL to connect to"`
ListenAddr string `arg:"-l,--listen-addr" default:"localhost:6831" help:"Host and port for the debugger to listen on"`
OutputDir string `arg:"-d,--dir" default:"." help:"Output directory for generated files"`
CleanOnConnect bool `arg:"--clean-on-connect" default:"true" help:"Clean up disconnected clients on the 1st connection"`
ImportData string `arg:"-i,--import-data" help:"Import an exported gob.br file"`
FwdData []string `arg:"-f,--fwd-data,separate" help:"Forward incoming data to other instances (repeatable)"`
SelectConnected bool `arg:"-c,--select-connected" help:"Select the newly connected machine, if no other is connected"`
EnableClipboard bool `arg:"--enable-clipboard" default:"true" help:"Enable clipboard support"`
EnableMouse bool `arg:"--enable-mouse" default:"true" help:"Enable mouse support"`
FilterAutoTx bool `arg:"--filter-auto" help:"Filter automatic transitions"`
FilterAutoCanceledTx bool `arg:"--filter-auto-canceled" help:"Filter automatic canceled transitions"`
FilterCanceledTx bool `arg:"--filter-canceled" help:"Filter canceled transitions"`
FilterChecks bool `arg:"--filter-checks" help:"Filter check (read-only) transitions"`
FilterDisconn bool `arg:"--filter-disconn" help:"Filter disconnected machines"`
FilterEmptyTx bool `arg:"--filter-empty" help:"Filter empty transitions"`
FilterGroup bool `arg:"--filter-group" default:"true" help:"Filter transitions by a selected group"`
FilterHealthTx bool `arg:"--filter-health" help:"Filter health-check transitions"`
FilterLogLevel am .LogLevel `arg:"--filter-log-level" default:"2" help:"Filter transitions up to this log level, 0-5 (silent-everything)"`
FilterRpcMachs bool `arg:"--filter-rpc-machs" help:"Filter RPC machines"`
FilterQueuedTx bool `arg:"--filter-queued" help:"Filter queued transitions"`
OutputCallLog bool `arg:"--output-call-log" help:"Write called handlers as Go code into call-log/{mach-id}/{mtime}.go inside --dir (EXPERIMENTAL)"`
OutputClients bool `arg:"--output-clients" help:"Write a detailed client list into clients.txt inside --dir"`
OutputDiagrams ParamsOutputDiagrams `arg:"--output-diagrams" help:"Enable graph diagrams and set the level of detail for machine diagrams in --dir (0 off, 1-3 on) (EXPERIMENTAL)"`
OutputDiagGroup ParamsOutDiagGroup `arg:"--output-diag-group" help:"Only show states from the selected group (valid: hide, skip)" default:"hide"`
OutputDiagTx ParamsOutDiagTx `arg:"--output-diag-tx" help:"Dim states and rels unrelated to a transition (valid: called, changed, touched, relations)" default:"relations"`
OutputGraph bool `arg:"--output-graph" help:"Write the current network graph as graph.(md|mgml) inside --dir (EXPERIMENTAL)"`
OutputLog bool `arg:"--output-log" help:"Write the current log buffer to log.md inside --dir"`
OutputTx bool `arg:"--output-tx" default:"true" help:"Write the current transition with steps into tx.md / d2 / mermaid / txt inside --dir"`
UiMcp bool `arg:"--ui-mcp" help:"Enable MCP server on port --listen-addr +1 (requires --ui-web) (EXPERIMENTAL)" default:"true"`
UiSsh bool `arg:"--ui-ssh" help:"Enable SSH headless mode on port --listen-addr +2 (EXPERIMENTAL)"`
UiWeb bool `arg:"--ui-web" default:"true" help:"Start a web server for --dir and diagrams on --listen-addr +1"`
PrintVersion bool `arg:"--version" help:"Print version and exit"`
StartupView string `arg:"-v,--view" default:"tree-log" help:"Initial view (tree-log, tree-matrix, matrix)"`
ViewExpandLinks bool `arg:"--view-expand-links" help:"Expand all tree links" default:"true"`
ViewLogWrap bool `arg:"--view-log-wrap" help:"Wrap log lines"`
ViewNarrow bool `arg:"--view-narrow" help:"Force a narrow view, independently of the viewport size"`
ViewRain bool `arg:"--view-rain" help:"Show the rain view"`
ViewReader bool `arg:"-r,--view-reader" help:"Show the log reader view" default:"true"`
TailMode bool `arg:"--view-tail" default:"true" help:"Show the most recent transition"`
ViewTheme string `arg:"--view-theme" default:"dark" help:"Color theme (dark, light)"`
ViewTimelines ParamsViewTimelines `arg:"--view-timelines" default:"1" help:"Number of timelines to show (0-2)"`
LogOpsTtl time .Duration `arg:"--log-ops-ttl" default:"1h" help:"Max time to live for logs level LogOps"`
MaxMemMb int `arg:"--max-mem" default:"1000" help:"Max memory usage (in MB) to flush old transitions"`
DebugAddr string `arg:"--dbg-am-dbg-addr" help:"Debug this instance of am-dbg with another one"`
RaceDetector bool `arg:"--dbg-go-race" help:"Go race detector is enabled"`
Id string `arg:"--dbg-id" default:"am-dbg" help:"ID of this instance"`
LogLevel am .LogLevel `arg:"--dbg-log-level" default:"0" help:"Log level produced by this instance, 0-5 (silent-everything)"`
DbgOtel bool `arg:"--dbg-otel" help:"Enable OpenTelemetry tracing for this instance"`
ProfSrv string `arg:"--dbg-prof-srv" help:"Start pprof server"`
Repl bool `arg:"--dbg-repl" help:"Start a REPL server in --dir"`
AddrHttp string `arg:"-"`
AddrRpc string `arg:"-"`
AddrSsh string `arg:"-"`
DbgLogger *log .Logger `arg:"-"`
Filters *Filters `arg:"-"`
Print func (txt string , args ...any ) `arg:"-"`
ProfCpu bool `arg:"-"`
ProfMem bool `arg:"-"`
Screen tcell .Screen `arg:"-"`
Version string `arg:"-"`
}
type ParamsOutputDiagrams enum .Member [int ]
var (
ParamsOutputDiagramsNone = ParamsOutputDiagrams {0 }
ParamsOutputDiagramsOne = ParamsOutputDiagrams {1 }
ParamsOutputDiagramsTwo = ParamsOutputDiagrams {2 }
ParamsOutputDiagramsThree = ParamsOutputDiagrams {3 }
ParamsOutputDiagramsEnum = enum .New (
ParamsOutputDiagramsNone ,
ParamsOutputDiagramsOne ,
ParamsOutputDiagramsTwo ,
ParamsOutputDiagramsThree ,
)
)
func (p *ParamsOutputDiagrams ) UnmarshalText (b []byte ) error {
value , err := strconv .Atoi (string (b ))
if err != nil {
return fmt .Errorf ("invalid value: %s" , b )
}
res := ParamsOutputDiagramsEnum .Parse (value )
if res == nil {
return fmt .Errorf ("invalid value: %s" , b )
}
*p = *res
return nil
}
type ParamsOutDiagTx enum .Member [string ]
var (
ParamsOutDiagTxNone = ParamsOutDiagTx {"" }
ParamsOutDiagTxCalled = ParamsOutDiagTx {"called" }
ParamsOutDiagTxMutated = ParamsOutDiagTx {"mutated" }
ParamsOutDiagTxTouched = ParamsOutDiagTx {"touched" }
ParamsOutDiagTxRelations = ParamsOutDiagTx {"relations" }
ParamsOutDiagTxEnum = enum .New (ParamsOutDiagTxNone , ParamsOutDiagTxCalled ,
ParamsOutDiagTxMutated , ParamsOutDiagTxTouched , ParamsOutDiagTxRelations )
)
func (p *ParamsOutDiagTx ) UnmarshalText (b []byte ) error {
res := ParamsOutDiagTxEnum .Parse (string (b ))
if res == nil {
return fmt .Errorf ("invalid value: %s" , b )
}
*p = *res
return nil
}
type ParamsOutDiagGroup enum .Member [string ]
var (
ParamsOutDiagGroupNone = ParamsOutDiagGroup {"" }
ParamsOutDiagGroupHide = ParamsOutDiagGroup {"hide" }
ParamsOutDiagGroupSkip = ParamsOutDiagGroup {"skip" }
ParamsOutDiagGroupEnum = enum .New (ParamsOutDiagGroupNone ,
ParamsOutDiagGroupHide , ParamsOutDiagGroupSkip )
)
func (p *ParamsOutDiagGroup ) UnmarshalText (b []byte ) error {
res := ParamsOutDiagGroupEnum .Parse (string (b ))
if res == nil {
return fmt .Errorf ("invalid value: %s" , b )
}
*p = *res
return nil
}
type ParamsViewTimelines enum .Member [int ]
var (
ParamsViewTimelinesNone = ParamsViewTimelines {0 }
ParamsViewTimelinesOne = ParamsViewTimelines {1 }
ParamsViewTimelinesTwo = ParamsViewTimelines {2 }
ParamsViewTimelinesEnum = enum .New (
ParamsViewTimelinesNone ,
ParamsViewTimelinesOne ,
ParamsViewTimelinesTwo ,
)
)
func (p *ParamsViewTimelines ) UnmarshalText (b []byte ) error {
value , err := strconv .Atoi (string (b ))
if err != nil {
return fmt .Errorf ("invalid value: %s" , b )
}
res := ParamsViewTimelinesEnum .Parse (value )
if res == nil {
return fmt .Errorf ("invalid value: %s" , b )
}
*p = *res
return nil
}
type Filters struct {
SkipCanceledTx bool
SkipAutoTx bool
SkipAutoCanceledTx bool
SkipEmptyTx bool
SkipHealthTx bool
SkipQueuedTx bool
SkipOutGroup bool
SkipChecks bool
LogLevel am .LogLevel
SkipRpcMach bool
}
func (f *Filters ) Equal (filters *Filters ) bool {
if filters == nil || f == nil {
return false
}
return f .SkipCanceledTx == filters .SkipCanceledTx &&
f .SkipAutoTx == filters .SkipAutoTx &&
f .SkipAutoCanceledTx == filters .SkipAutoCanceledTx &&
f .SkipEmptyTx == filters .SkipEmptyTx &&
f .SkipHealthTx == filters .SkipHealthTx &&
f .SkipQueuedTx == filters .SkipQueuedTx &&
f .SkipOutGroup == filters .SkipOutGroup &&
f .SkipChecks == filters .SkipChecks &&
f .LogLevel == filters .LogLevel &&
f .SkipRpcMach == filters .SkipRpcMach
}
func GetLogger (params *Params , dir string ) *log .Logger {
if params .LogLevel <= 0 {
return log .Default ()
}
name := filepath .Join (dir , "am-dbg.log" )
_ = os .Remove (name )
file , err := os .OpenFile (name , os .O_CREATE |os .O_WRONLY , 0o666 )
if err != nil {
panic (err )
}
return log .New (file , "" , log .LstdFlags )
}
func HandleProfMem (logger *log .Logger , p *Params ) {
if !p .ProfMem {
return
}
f , err := os .Create ("mem.prof" )
if err != nil {
logger .Fatal ("could not create memory profile: " , err )
}
defer f .Close ()
runtime .GC ()
if err := pprof .WriteHeapProfile (f ); err != nil {
logger .Fatal ("could not write memory profile: " , err )
}
}
func StartCpuProfile (logger *log .Logger , p *Params ) func () {
if !p .ProfCpu {
return nil
}
f , err := os .Create ("cpu.prof" )
if err != nil {
logger .Fatal ("could not create CPU profile: " , err )
}
if err := pprof .StartCPUProfile (f ); err != nil {
logger .Fatal ("could not start CPU profile: " , err )
}
return func () {
pprof .StopCPUProfile ()
f .Close ()
}
}
func StartCpuProfileSrv (ctx context .Context , logger *log .Logger , p *Params ) {
if p .ProfSrv == "" {
return
}
go func () {
logger .Println ("Starting pprof server on " + p .ProfSrv )
if err := http .ListenAndServe (p .ProfSrv , nil ); err != nil {
logger .Fatalf ("could not start pprof server: %v" , err )
}
}()
}
type MachAddress struct {
MachId string
TxId string
Step int
MachTime uint64
QueueTick uint64
HumanTime time .Time
Group string
State string
}
type MachTime struct {
Id string
Time uint64
}
func (a *MachAddress ) Clone () *MachAddress {
return &MachAddress {
MachId : a .MachId ,
TxId : a .TxId ,
Step : a .Step ,
MachTime : a .MachTime ,
}
}
func (a *MachAddress ) StringBase () string {
if a == nil || a .MachId == "" {
return ""
}
ret := "mach://" + a .MachId
if a .TxId != "" {
ret += "/" + a .TxId
if a .Step != 0 {
ret += fmt .Sprintf ("/%d" , a .Step )
}
} else if a .MachTime != 0 {
return ret + fmt .Sprintf ("/?t=%d" , a .MachTime )
} else if a .QueueTick != 0 {
return ret + fmt .Sprintf ("/?q=%d" , a .QueueTick )
} else if !a .HumanTime .IsZero () {
return ret + fmt .Sprintf ("/?q=%s" , a .HumanTime )
} else {
return ""
}
return ret
}
func (a *MachAddress ) String () string {
ret := a .StringBase ()
get := []string {}
if a .MachTime != 0 {
get = append (get , fmt .Sprintf ("t=%d" , a .MachTime ))
}
if a .QueueTick != 0 {
get = append (get , fmt .Sprintf ("q=%d" , a .QueueTick ))
}
if !a .HumanTime .IsZero () {
get = append (get , fmt .Sprintf ("ht=%s" , a .HumanTime ))
}
if a .Group != "" {
get = append (get , fmt .Sprintf ("group=%s" , NormalizeGroupName (a .Group )))
}
if a .State != "" {
get = append (get , fmt .Sprintf ("state=%s" , a .State ))
}
if len (get ) == 0 {
return ret
}
slices .Sort (get )
return ret + "?" + strings .Join (get , "&" )
}
func ParseMachUrl (u string ) (*MachAddress , error ) {
parsed , err := url .Parse (u )
if err != nil {
return nil , err
} else if parsed .Host == "" {
return nil , fmt .Errorf ("mach ID missing in: %s" , u )
}
addr := &MachAddress {
MachId : parsed .Host ,
}
p := strings .Split (parsed .Path , "/" )
if len (p ) > 1 {
addr .TxId = p [1 ]
}
if len (p ) > 2 {
if s , err := strconv .Atoi (p [2 ]); err == nil {
addr .Step = s
}
}
q , err := url .ParseQuery (parsed .RawQuery )
if err != nil {
return nil , err
}
if v := q .Get ("t" ); v != "" {
if s , err := strconv .ParseUint (v , 10 , 64 ); err == nil {
addr .MachTime = s
}
}
if v := q .Get ("q" ); v != "" {
if s , err := strconv .ParseUint (v , 10 , 64 ); err == nil {
addr .QueueTick = s
}
}
if v := q .Get ("ht" ); v != "" {
if t , err := time .Parse (time .RFC3339 , v ); err == nil {
addr .HumanTime = t
}
}
addr .State = q .Get ("state" )
addr .Group = q .Get ("group" )
return addr , nil
}
type MsgTxParsed struct {
StatesAdded []int
StatesRemoved []int
StatesTouched []int
TimeSum uint64
TimeDiff uint64
ReaderEntries []*LogReaderEntryPtr
Forks []MachAddress
ForksLabels []string
ResultTick uint64
}
type MsgSchemaParsed struct {
Groups map [string ]am .S
GroupsOrder []string
Hash string
}
type LogReaderEntry struct {
Kind LogReaderKind
States []int
CreatedAt uint64
ClosedAt time .Time
Pipe am .MutationType
Mach string
Ticks am .Time
Args string
QueueTick int
}
type LogReaderEntryPtr struct {
TxId string
EntryIdx int
}
type LogReaderKind int
const (
LogReaderCtx LogReaderKind = iota + 1
LogReaderWhen
LogReaderWhenNot
LogReaderWhenTime
LogReaderWhenArgs
LogReaderWhenQueue
LogReaderPipeIn
LogReaderPipeOut
)
type ToolName enum .Member [string ]
var (
ToolFilterCanceledTx = ToolName {"skip-canceled" }
ToolFilterQueuedTx = ToolName {"skip-queued" }
ToolFilterAutoTx = ToolName {"skip-auto" }
ToolFilterEmptyTx = ToolName {"skip-empty" }
ToolFilterHealth = ToolName {"skip-health" }
ToolFilterOutGroup = ToolName {"skip-outgroup" }
ToolFilterChecks = ToolName {"skip-checks" }
ToolFilterRpcMachs = ToolName {"skip-rpc-machs" }
ToolFilterDisconn = ToolName {"skip-disconn" }
ToolLogTimestamps = ToolName {"hide-timestamps" }
ToolFilterTraces = ToolName {"hide-traces" }
ToolNarrowLayout = ToolName {"narrow-layout" }
ToolLog = ToolName {"log" }
ToolDiagrams = ToolName {"diagrams" }
ToolDiagramsTx = ToolName {"diag-tx" }
ToolDiagramsGroup = ToolName {"diag-group" }
ToolOutputTx = ToolName {"out-tx" }
ToolOutputLog = ToolName {"out-log" }
ToolCallLog = ToolName {"call-log" }
ToolTimelines = ToolName {"timelines" }
ToolReader = ToolName {"reader" }
ToolRain = ToolName {"rain" }
ToolLogWrap = ToolName {"log-wrap" }
ToolWeb = ToolName {"web" }
ToolHelp = ToolName {"help" }
ToolPlay = ToolName {"play" }
ToolTail = ToolName {"tail" }
ToolPrev = ToolName {"prev" }
ToolNext = ToolName {"next" }
ToolJumpNext = ToolName {"jump-next" }
ToolJumpPrev = ToolName {"jump-prev" }
ToolFirst = ToolName {"first" }
ToolLast = ToolName {"last" }
ToolExpand = ToolName {"expand" }
ToolMatrix = ToolName {"matrix" }
ToolExport = ToolName {"export" }
ToolNextStep = ToolName {"next-step" }
ToolPrevStep = ToolName {"prev-step" }
ToolPrevClient = ToolName {"prev-client" }
ToolNextClient = ToolName {"next-client" }
ToolNames = enum .New (
ToolFilterCanceledTx ,
ToolFilterQueuedTx ,
ToolFilterAutoTx ,
ToolFilterEmptyTx ,
ToolFilterHealth ,
ToolFilterOutGroup ,
ToolFilterChecks ,
ToolFilterDisconn ,
ToolLogTimestamps ,
ToolFilterTraces ,
ToolNarrowLayout ,
ToolLog ,
ToolDiagrams ,
ToolDiagramsTx ,
ToolDiagramsGroup ,
ToolCallLog ,
ToolTimelines ,
ToolReader ,
ToolRain ,
ToolLogWrap ,
ToolWeb ,
ToolHelp ,
ToolPlay ,
ToolTail ,
ToolPrev ,
ToolNext ,
ToolJumpNext ,
ToolJumpPrev ,
ToolFirst ,
ToolLast ,
ToolExpand ,
ToolMatrix ,
ToolExport ,
ToolNextStep ,
ToolPrevStep ,
ToolPrevClient ,
ToolNextClient ,
)
)
const APrefix = "dbg"
type Args struct {
am .ArgsBase `json:"-"`
}
func (Args ) ArgsPrefix () string {
return APrefix
}
type A struct {
Args `json:"-"`
ClientId string `log:"client_id"`
TxId string `log:"tx_id"`
ConnId string `log:"conn_id"`
ConnIds []string
Cursor1 int `log:"cursor1" json:",string"`
CursorStep1 int `log:"cursor_step1" json:",string"`
CursorTx1 int `log:"cursor_tx1" json:",string"`
Amount int `log:"amount" json:",string"`
Fwd bool `log:"fwd" json:",string"`
State string `log:"state"`
Group string
ToolName ToolName `log:"tool_name"`
Id string `log:"id"`
LogRebuildEnd int `json:",string"`
LogBuffer string
LogLevel am .LogLevel `json:",string"`
SkipHistory bool
TrimHistory bool
FilterBack bool
FilterTxs bool
BuildClientList bool
Immediate bool
FromConnected bool
FromPlaying bool
MouseFocus bool
Text string `log:"text"`
Uri string `log:"uri"`
Addr string `log:"addr"`
Row int `json:",string"`
Column int `json:",string"`
CurrTxRow int `json:",string"`
FocusPrimitive cview .Primitive
HttpRequest *http .Request
HttpResponseWriter http .ResponseWriter
DoneChan chan struct {}
WebSocketConn *websocket .Conn
MsgStruct *dbg .DbgMsgStruct
MsgsTx []*dbg .DbgMsgTx
DiagDom *goquery .Document
DiagName string
DiagType DiagramType `log:"diag_type"`
}
type ARpc struct {
Args `json:"-"`
ClientId string `log:"client_id"`
TxId string `log:"tx_id"`
ConnId string `log:"conn_id"`
ConnIds []string
Cursor1 int `log:"cursor1" json:",string"`
CursorStep1 int `log:"cursor_step1" json:",string"`
CursorTx1 int `log:"cursor_tx1" json:",string"`
Amount int `log:"amount" json:",string"`
Fwd bool `log:"fwd" json:",string"`
State string `log:"state"`
Group string
ToolName ToolName `log:"tool_name"`
Id string `log:"id"`
LogRebuildEnd int `json:",string"`
LogBuffer string
LogLevel am .LogLevel `json:",string"`
SkipHistory bool
TrimHistory bool
FilterBack bool
FilterTxs bool
BuildClientList bool
Immediate bool
FromConnected bool
FromPlaying bool
MouseFocus bool
Text string `log:"text"`
Uri string `log:"uri"`
Addr string `log:"addr"`
Row int `json:",string"`
Column int `json:",string"`
CurrTxRow int `json:",string"`
}
func init() {
for _ , arg := range ArgsRpc {
gob .Register (arg )
}
}
var ArgsRpc = []am .ArgsApi {ARpc {}}
var StateCalls = []am .CallSignature {
{States : am .S {ss .StateNameSelected }, Needed : []string {"State" }},
{States : am .S {ss .Redraw }, Optional : []string {"Immediate" }},
{States : am .S {ss .Fwd }, Optional : []string {"Amount" }},
{States : am .S {ss .Back }, Optional : []string {"Amount" }},
{States : am .S {ss .ConnectEvent }, Needed : []string {"MsgStruct" , "ConnId" }},
{States : am .S {ss .DisconnectEvent }, Needed : []string {"ConnId" }},
{
States : am .S {ss .ClientMsg },
Needed : []string {"MsgsTx" , "ConnIds" },
},
{
States : am .S {ss .RemoveClient },
Needed : []string {"ClientId" },
},
{
States : am .S {ss .SetGroup },
Needed : []string {"Group" },
},
{
States : am .S {ss .SelectingClient },
Needed : []string {"ClientId" },
Optional : []string {"Group" , "FromConnected" },
},
{
States : am .S {ss .ClientSelected },
Optional : []string {"FromConnected" , "FromPlaying" },
},
{
States : am .S {ss .ScrollToTx },
Optional : []string {"CursorTx1" , "TxId" , "CursorStep1" , "TrimHistory" },
},
{
States : am .S {ss .ScrollToStep },
Needed : []string {"CursorStep1" },
},
{
States : am .S {ss .ToggleTool },
Needed : []string {"ToolName" },
Values : map [string ][]string {
"ToolName" : ToolNames .Values (),
},
},
{
States : am .S {ss .ToolToggled },
Optional : []string {"FilterTxs" , "BuildClientList" },
},
{
States : am .S {ss .SwitchingClientTx },
Desc : "Go to N-th transition of a client." ,
Needed : []string {"ClientId" , "CursorTx1" },
},
{
States : am .S {ss .ScrollToMutTx },
Desc : "ScrollToMutTxState scrolls to a transition which mutated the " +
"passed state, If fwd is true, it scrolls forward, otherwise backwards." ,
Needed : []string {"State" },
Optional : []string {"Fwd" },
},
{
States : am .S {ss .AfterFocus },
Needed : []string {"FocusPrimitive" },
Optional : []string {"MouseFocus" },
},
{
States : am .S {ss .Resized },
Optional : []string {"LogRebuildEnd" },
},
{
States : am .S {ss .WebReqDiag },
Needed : []string {"HttpRequest" , "HttpResponseWriter" , "DoneChan" },
Optional : []string {"Uri" , "Addr" },
},
{
States : am .S {ss .WebSocketDiag },
Needed : []string {"WebSocketConn" , "HttpRequest" , "HttpResponseWriter" ,
"DoneChan" },
Optional : []string {"Addr" },
},
{
States : am .S {ss .MatrixRainSelected },
Needed : []string {"Row" , "Column" , "CurrTxRow" },
},
}
func NormalizeGroupName (name string ) string {
name , _, _ = strings .Cut (name , ":" )
return strings .TrimSuffix (strings .ReplaceAll (strings .ReplaceAll (name ,
"-" , "" ),
" " , "" ),
"StatesDef" )
}
type StateTraceItem struct {
Label string
Source *MachAddress
StateNames am .S
}
type DiagramType enum .Member [string ]
var (
DiagramTypeState = DiagramType {"state" }
DiagramTypeMach = DiagramType {"mach" }
DiagramTypeSteps = DiagramType {"steps" }
DiagramTypeGraph = DiagramType {"graph" }
)
func (d DiagramType ) String () string {
return d .Value
}
type WsDiagMsg struct {
Event string
Id string
Group string
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .