// Package generator generates state-machine schemas and grafana dashboards.
package generator // TODO rewrite: // - repeated cli params // - AST // - embed pkg/states/states_utils.go // - optional with pkg/states/global import ( am ssam ) var ( ssG = states.GeneratorStates sgG = states.GeneratorGroups ) type SchemaGenerator struct { Mach *am.Machine Name string // N is the first letter of Name N string States []string StatesAuto []string StatesMulti []string Groups []string // State1 -> Rel -> State2,State3 Relations [][3]string } // TODO return err func ( *SchemaGenerator) ( cli.SFParams) { if .Inherit != "" { for , := range strings.Split(.Inherit, ",") { switch { case "basic": .Mach.Add1(ssG.InheritBasic, nil) case "connected": .Mach.Add1(ssG.InheritConnected, nil) case "disposed": .Mach.Add1(ssG.InheritDisposed, nil) case "rpc/netsrc": .Mach.Add1(ssG.InheritRpcNetSource, nil) case "node/worker": .Mach.Add1(ssG.InheritNodeWorker, nil) default: // TODO err panic(fmt.Sprintf("unknown inherit: %s", )) } } } // states for , := range strings.Split(.States, ",") { if == "" { continue } // multi, auto, relations := strings.Split(, ":") := capitalizeFirstLetter([0]) .States = append(.States, ) if len() < 2 { continue } = [1:] for , := range { switch { case "auto": .StatesAuto = append(.StatesAuto, ) case "multi": .StatesMulti = append(.StatesMulti, ) default: // Require( if !strings.Contains(, "(") { fmt.Printf("wrong format") os.Exit(1) } := strings.Split(strings.TrimRight(, ")"), "(") if len([0]) == 0 || len([1]) == 0 { fmt.Printf("wrong format") os.Exit(1) } := capitalizeFirstLetter([0]) := [1] .Relations = append(.Relations, [3]string{, , }) } } } // groups for , := range strings.Split(.Groups, ",") { if == "" { continue } .Groups = append(.Groups, capitalizeFirstLetter()) .Mach.Add1(ssG.GroupsLocal, nil) } .Name = capitalizeFirstLetter(.Name) .N = string(.Name[0]) } func ( *SchemaGenerator) ( *am.Event) bool { return .Mach.Any1(sgG.Inherit...) } func ( *SchemaGenerator) ( *am.Event) bool { return .Mach.Any1(ssG.GroupsInherited, ssG.GroupsLocal) } func ( *SchemaGenerator) () string { // TODO switch to github.com/dave/jennifer var string if .Mach.Any1(ssG.InheritBasic, ssG.InheritConnected) { = "\n\tss \"github.com/pancsta/asyncmachine-go/pkg/states\"" } // imports := utils.Sp(` package states import ( "context" am "github.com/pancsta/asyncmachine-go/pkg/machine"%s `, ) if .Mach.Is1(ssG.InheritRpcNetSource) { += "\tssrpc \"github.com/pancsta/asyncmachine-go/pkg/rpc/states\"\n" } if .Mach.Is1(ssG.InheritNodeWorker) { += "\tssnode \"github.com/pancsta/asyncmachine-go/pkg/node/states\"\n" } // struct def += utils.Sp(` ) // %sStatesDef contains all the states of the %s state-machine. type %sStatesDef struct { *am.StatesBase `, .Name, .Name, .Name) // state names for , := range .States { += fmt.Sprintf("\t%s string\n", ) } += "\n" // inherits if .Mach.Is1(ssG.InheritBasic) { += "\t// inherit from BasicStatesDef\n\t*ss.BasicStatesDef\n" } if .Mach.Is1(ssG.InheritConnected) { += "\t// inherit from ConnectedStatesDef\n" + "\t*ss.ConnectedStatesDef\n" } if .Mach.Is1(ssG.InheritRpcNetSource) { += "\t// inherit from rpc/NetSourceStatesDef\n" + "\t*ssrpc.NetSourceStatesDef\n" } if .Mach.Is1(ssG.InheritNodeWorker) { += "\t// inherit from node/NetSourceStatesDef\n" + "\t*ssnode.NetSourceStatesDef\n" } += "}\n\n" // groups def += utils.Sp(` // %sGroupsDef contains all the state groups %s state-machine. type %sGroupsDef struct { `, .Name, .Name, .Name) if .Mach.Is1(ssG.InheritConnected) { += "\t*ss.ConnectedGroupsDef\n" } if .Mach.Is1(ssG.InheritNodeWorker) { += "\t*ssnode.WorkerGroupsDef\n" } for , := range .Groups { += fmt.Sprintf("\t%s S\n", strings.Split(, "(")[0]) } += "}\n\n" // struct += fmt.Sprintf( "// %sSchema represents all relations and properties of %sStates.\n", .Name, .Name) var string if .Mach.Is1(ssG.Inherit) { = "\t" += fmt.Sprintf("var %sSchema = SchemaMerge(\n", .Name) } else { += fmt.Sprintf("var %sSchema = am.Schema{\n", .Name) } // struct inherit if .Mach.Is1(ssG.InheritBasic) { += "\t// inherit from BasicSchema\n\tss.BasicSchema,\n" } if .Mach.Is1(ssG.InheritConnected) { += fmt.Sprintf("\t// inherit from ConnectedSchema\n" + "\tss.ConnectedSchema,\n") } if .Mach.Is1(ssG.InheritRpcNetSource) { += fmt.Sprintf("\t// inherit from rpc/WorkerSchema\n" + "\tssrpc.WorkerSchema,\n") } if .Mach.Is1(ssG.InheritNodeWorker) { += fmt.Sprintf("\t// inherit from node/WorkerSchema\n" + "\tssnode.WorkerSchema,\n") } if .Mach.Is1(ssG.Inherit) { += "\tam.Schema{\n" } // struct states += "\n" for , := range .States { // open state += fmt.Sprintf("\t%sss%s.%s: {", , .N, ) var bool if slices.Contains(.StatesAuto, ) { += fmt.Sprintf("\n\t\t%sAuto: true,", ) = true } if slices.Contains(.StatesMulti, ) { += fmt.Sprintf("\n\t\t%sMulti: true,", ) = true } // relations for , := range .Relations { if [0] != { continue } = true := strings.Split([2], ";") // relation to a group TODO >1 if strings.HasPrefix([0], "_") { += fmt.Sprintf("\n\t\t%s%s: SAdd(sg%s.%s", , [1], .N, [0][1:]) if len() > 1 { += ",S{" for , := range [1:] { += fmt.Sprintf("ss%s.%s", .N, +",") } = strings.TrimRight(, ",") + "})," } else { += ")," } continue } else { // relation to states only var []string for , := range { = append(, "ss"+.N+"."+) } += fmt.Sprintf("\n\t\t%s%s: S{%s},", , [1], strings.Join(, ", ")) } } // close state if { += fmt.Sprintf("\n\t%s},\n", ) } else { += "},\n" } } // close states def if .Mach.Is1(ssG.Inherit) { += "})\n" } else { += "}\n" } += "\n" + utils.Sp(` // EXPORTS AND GROUPS var ( ss%s = am.NewStates(%sStatesDef{}) sg%s = am.NewStateGroups(%sGroupsDef{`, .N, .Name, .N, .Name) for , := range .Groups { if strings.Contains(, "(") { := strings.Split(strings.TrimRight(, ")"), "(") var []string for , := range strings.Split([1], ";") { = append(, fmt.Sprintf("ss%s.%s", .N, )) } += fmt.Sprintf("\n\t\t%s: S{%s},", [0], strings.Join(, ", ")) } else { += fmt.Sprintf("\n\t\t%s: S{},", ) } } if .Mach.Is1(ssG.GroupsLocal) { += "\n\t" } += "}" if .Mach.Is1(ssG.InheritConnected) { += ", ss.ConnectedGroups" } if .Mach.Is1(ssG.InheritNodeWorker) { += ", ssnode.WorkerGroups" } += utils.Sp(` ) // %sStates contains all the states for the %s state-machine. %sStates = ss%s // %sGroups contains all the state groups for the %s state-machine. %sGroups = sg%s ) // New%s creates a new %s state-machine in the most basic form. func New%s(ctx context.Context) *am.Machine { return am.New(ctx, %sSchema, nil) } `, .Name, .Name, .Name, .N, .Name, .Name, .Name, .N, .Name, .Name, .Name, .Name) return } func ( context.Context, cli.SFParams, ) (*SchemaGenerator, error) { := &SchemaGenerator{} , := am.NewCommon(, "gen", states.GeneratorSchema, ssG.Names(), , nil, nil) if != nil { return nil, } // TODO env var? // amhelp.MachDebugEnv(mach) .Mach = .parseParams() return , nil } func () string { return ssam.StatesUtilsFile } func capitalizeFirstLetter( string) string { if len() == 0 { return } return string(unicode.ToUpper(rune([0]))) + [1:] }