/* Tool for generating the frontend definition of the role model It generates a typescript file containing the full role, but without functions */ package main import ( "flag" "fmt" "io" "os" "regexp" "strings" "unicode" "gitlab.com/mstarongitlab/goutils/sliceutils" ) var findRoleStructRegex = regexp.MustCompile(`type Role struct \{([\s\S]+)\}\n\n/\*`) var ( flagInputFile = flag.String("input", "", "Specify the input file. If empty, read from stdin") flagOutputFile = flag.String( "output", "", "Specify the output file. If empty, writes to stdout", ) ) func main() { flag.Parse() var input io.Reader var output io.Writer if *flagInputFile == "" { input = os.Stdin } else { file, err := os.Open(*flagInputFile) if err != nil { panic(err) } defer file.Close() input = file } if *flagOutputFile == "" { output = os.Stdout } else { file, err := os.Create(*flagOutputFile) if err != nil { panic(err) } defer file.Close() output = file } data, err := io.ReadAll(input) if err != nil { panic(err) } if !findRoleStructRegex.Match(data) { panic("Input doesn't contain role struct") } content := findRoleStructRegex.FindStringSubmatch(string(data))[1] lines := strings.Split(content, "\n") lines = sliceutils.Map(lines, func(t string) string { return strings.TrimSpace(t) }) importantLines := sliceutils.Filter(lines, func(t string) bool { if strings.HasPrefix(t, "//") { return false } if strings.Contains(t, "gorm.Model") { return false } data := sliceutils.Filter(strings.Split(t, " "), func(t string) bool { return t != "" }) if len(data) < 2 { return false } if !strings.HasPrefix(data[1], "*") && !strings.HasPrefix(data[1], "[]") { return false } return true }) nameTypeMap := map[string]string{} for _, line := range importantLines { parts := sliceutils.Filter(strings.Split(line, " "), func(t string) bool { return t != "" }) nameTypeMap[parts[0]] = parts[1] } outBuilder := strings.Builder{} outBuilder.WriteString(`// Code generated by cmd/RolesApiTypeGenerator DO NOT EDIT. // If you need to refresh the content, run go generate again `) outBuilder.WriteString("import Model, { attr } from '@ember-data/model';\n\n") outBuilder.WriteString("export default class Role extends Model {\n") outBuilder.WriteString(` @attr() declare createdAt: Date @attr() declare updatedAt: Date @attr() declare name: string @attr() declare priority: number @attr() declare isUserRole: boolean @attr() declare isBuiltIn: boolean `) for n, t := range nameTypeMap { outBuilder.WriteString(fNT(n, t)) } outBuilder.WriteString("}\n\n") outBuilder.WriteString(`declare module 'ember-data/types/registries/model' { export default interface ModelRegistry { role: Role } }`) fmt.Fprint(output, outBuilder.String()) } func convertGoTypeToTS(t string) string { switch t { case "string", "*string": return "string" case "time.Time": return "Date" case "uint", "uint8", "uint16", "uint32", "uint64", "int", "int8", "int16", "int32", "int64", "float32", "float64", "byte", "*uint", "*uint8", "*uint16", "*uint32", "*uint64", "*int", "*int8", "*int16", "*int32", "*int64", "*float32", "*float64", "*byte": return "number" case "bool", "*bool": return "boolean" case "[]string": return "Array" default: panic(fmt.Sprintf("Unknown type: %s", t)) } } func convertGoNameToTS(name string) string { buffer := []rune{} isFirst := true for _, char := range name { if isFirst { buffer = append(buffer, unicode.ToLower(char)) isFirst = false } else { buffer = append(buffer, char) } } return string(buffer) } func fNT(name, valType string) string { return fmt.Sprintf( " @attr() declare %s?: %s\n", convertGoNameToTS(name), convertGoTypeToTS(valType), ) }