package main import ( "bytes" "encoding/json" "fmt" "go/format" "io/ioutil" "log" "os" "path/filepath" "strings" ) type TypeProperty struct { Name string Type string Setter bool } func (p TypeProperty) GetterName() string { return "Get" + strings.Title(p.Name) } func (p TypeProperty) SetterName() string { return "Set" + strings.Title(p.Name) } type FunctionArgument struct { Name string Type string } type TypeFunction struct { Name string Arguments []FunctionArgument ReturnType string } func (f TypeFunction) GoName() string { return strings.Title(f.Name) } type TypeStructure struct { Type string ImportJS bool Interface bool Inherits []string Implements []string Properties []TypeProperty Functions []TypeFunction } func (t TypeStructure) Filename() string { return fmt.Sprintf("%s.go", strings.ToLower(t.Type)) } func (t TypeStructure) ShortName() string { return strings.ToLower(string(t.Type[0])) } func (t TypeStructure) writeArguments(out *bytes.Buffer, args []FunctionArgument) { for i, arg := range args { if i > 0 { out.WriteString(",") } out.WriteString(fmt.Sprintf("%s %s", arg.Name, arg.Type)) } } func (t TypeStructure) writeFunctions(out *bytes.Buffer, funcs []TypeFunction) error { for _, f := range funcs { out.WriteString(fmt.Sprintf("func (%s *%s) %s(", t.ShortName(), t.Type, f.GoName())) for i, arg := range f.Arguments { if i > 0 { out.WriteString(",") } out.WriteString(fmt.Sprintf("%s %s", arg.Name, arg.Type)) } out.WriteString(fmt.Sprintf(") %s {\r\n", f.ReturnType)) if f.ReturnType != "" { out.WriteString("val := ") } out.WriteString(fmt.Sprintf("%s.Call(\"%s\"", t.ShortName(), f.Name)) for _, arg := range f.Arguments { out.WriteString(",") out.WriteString(fmt.Sprintf("ToValue(%s)", arg.Name)) } out.WriteString(")\r\n") switch f.ReturnType { case "string": out.WriteString("return val.String()\r\n") case "*Element": out.WriteString("return &Element{ Value: val }\r\n") case "[]*Element": out.WriteString("elms := make([]*Element, 0)\r\n") out.WriteString("for i := 0; i < val.Length(); i += 1 {\r\n") out.WriteString("\telms = append(elms, &Element{Value: val.Index(i)})\r\n") out.WriteString("}\r\n") out.WriteString("return elms\r\n") case "": // skip default: return fmt.Errorf("Unknown function return type %s", f.ReturnType) } out.WriteString("}\r\n") } return nil } func (t TypeStructure) writeProperties(out *bytes.Buffer, props []TypeProperty) error { for _, p := range props { out.WriteString(fmt.Sprintf("func (%s *%s) %s() %s {\r\n", t.ShortName(), t.Type, p.GetterName(), p.Type)) out.WriteString(fmt.Sprintf("\tval := %s.Get(\"%s\")\r\n", t.ShortName(), p.Name)) switch p.Type { case "string": out.WriteString("return val.String()\r\n") case "*Element": out.WriteString("return &Element{ Value: val }\r\n") case "[]*Element": out.WriteString("elms := make([]*Element, 0)\r\n") out.WriteString("for i := 0; i < val.Length(); i += 1 {\r\n") out.WriteString("\telms = append(elms, &Element{Value: val.Index(i)})\r\n") out.WriteString("}\r\n") out.WriteString("return elms\r\n") case "": // skip default: return fmt.Errorf("Unknown property return type %s", p.Type) } out.WriteString(fmt.Sprintf("}\r\n")) if p.Setter { out.WriteString(fmt.Sprintf("func (%s *%s) %s(v %s){\r\n", t.ShortName(), t.Type, p.SetterName(), p.Type)) out.WriteString(fmt.Sprintf("\t%s.Set(\"%s\", v)\r\n", t.ShortName(), p.Name)) out.WriteString("}\r\n") } } return nil } func (t TypeStructure) generateInterface(out *bytes.Buffer, types map[string]TypeStructure) error { out.WriteString(fmt.Sprintf("type %s interface {\r\n", t.Type)) for _, f := range t.Functions { out.WriteString(fmt.Sprintf("\t%s(", f.GoName())) t.writeArguments(out, f.Arguments) out.WriteString(fmt.Sprintf(") %s\r\n", f.ReturnType)) } out.WriteString("}\r\n") return nil } func (t TypeStructure) generateStruct(out *bytes.Buffer, types map[string]TypeStructure) (err error) { out.WriteString(fmt.Sprintf("type %s struct {\r\n", t.Type)) out.WriteString("\tjs.Value\r\n") for _, name := range t.Inherits { out.WriteString(fmt.Sprintf("\t%s\r\n", name)) } out.WriteString("}\r\n") // Properties err = t.writeProperties(out, t.Properties) if err != nil { return } // Functions err = t.writeFunctions(out, t.Functions) if err != nil { return } // Interfaces for _, it := range t.Implements { i, ok := types[it] if !ok { return fmt.Errorf("%q cannot implement unknown type %q", t.Type, it) } // Functions err = t.writeFunctions(out, i.Functions) if err != nil { return } // Properties err = t.writeProperties(out, i.Properties) if err != nil { return } } return } func (t TypeStructure) Generate(types map[string]TypeStructure) ([]byte, error) { out := bytes.NewBuffer(make([]byte, 0)) out.WriteString("// DO NOT EDIT - generated file\r\n") out.WriteString("package dom\r\n") if t.Interface == false || t.ImportJS == true { out.WriteString("import \"syscall/js\"\r\n") } var err error if t.Interface { err = t.generateInterface(out, types) } else { err = t.generateStruct(out, types) } if err != nil { return nil, err } return format.Source(out.Bytes()) } func loadFile(fname string) (t TypeStructure, err error) { fp, err := os.Open(fname) if err != nil { return } defer fp.Close() val, err := ioutil.ReadAll(fp) if err != nil { return } err = json.Unmarshal(val, &t) return } func processType(t TypeStructure, types map[string]TypeStructure) error { contents, err := t.Generate(types) if err != nil { return err } out, err := os.OpenFile(t.Filename(), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { return err } defer out.Close() _, err = out.Write(contents) return err } func main() { matches, err := filepath.Glob("generate/*.json") if err != nil { log.Panic(err) } types := make(map[string]TypeStructure) for _, fname := range matches { typeStructure, err := loadFile(fname) if err != nil { log.Panic(err) } types[typeStructure.Type] = typeStructure } for _, t := range types { log.Printf("Generating %s\r\n", t.Type) err = processType(t, types) if err != nil { log.Panic(err) } } }