This is a code generator for Go.
package main
import (
gen "github.com/MyNihongo/codegen"
"fmt"
)
func main() {
f := gen.NewFile("cool", "my-generator").Imports(
gen.Import("fmt"),
gen.ImportAlias("strings", "str"),
)
f.Func("main").Block(
gen.Declare("val").Values(gen.QualFuncCall("str", "Title").Args(gen.String("hello, 世界!"))),
gen.QualFuncCall("fmt", "Println").Args(gen.Identifier("val")),
)
if err := f.Save(`path to the file.go`); err != nil {
fmt.Println(err)
}
}
This code will produce the following output
// Code generated by my-generator. DO NOT EDIT.
package cool
import (
"fmt"
str "strings"
)
func main() {
val := str.Title("hello, 世界!")
fmt.Println(val)
}
I have used Jennifer for code generation (don't get me wrong, the library is awesome 🚀), but I thought that its API is a bit too low-level.
With this library I aim to provide high-level functions with will resemble the actual Go code and will not require to write low-level statements.
gofmt
is used for formatting- auto-generation comment is added automatically
gen.Identifier("a")
// a
gen.Identifier("a").Pointer()
// *a
gen.Identifier("a").Address()
// &(a)
gen.Identifier("a").Equals(gen.Identifier("b"))
// a == b
gen.Identifier("a").NotEquals(gen.Identifier("b"))
// a != b
gen.Err().IsNotNil()
// err != nil
gen.Err().IsNil()
// err == nil
gen.String("my string").IsNotEmpty()
// len("my string") != 0
gen.Declare("val").Values(gen.String("my string"))
// val := "my string"
gen.Assign("val").Values(gen.String("my string"))
// val = "my string"
gen.Declare("val", "err").Values(FuncCall("myFunc").Args(Identifier("a")))
// val, err := myFunc(a)
gen.Assign("val", "err").Values(FuncCall("myFunc").Args(Identifier("a")))
// val, err = myFunc(a)
gen.DeclareVars(
gen.Var("val", "string"),
gen.QualVar("sb", "strings", "Builder"),
)
// var val string
// var sb strings.Builder
gen.Identifier("myVar").Assign(gen.Identifier("val"))
// myVar = val
gen.InitStruct("myStruct").Props(
gen.PropValue("prop", gen.String("string value")),
)
// myStruct{prop:"string value"}
gen.InitStruct("myStruct").Props(
gen.PropValue("prop", gen.FuncCall("myFunc")),
).Address()
// &myStruct{prop:myFunc()}
gen.FuncCall("myFunc")
// myFunc()
gen.FuncCall("myFunc").Args(
gen.Identifier("a"),
gen.FuncCall("anotherFunc"),
)
// myFunc(a, anotherFunc())
gen.QualFuncCall("fmt", "Println").Args(
gen.String("string value")
)
// fmt.Println("string value")
gen.Len(Identifier("str"))
// len(str)
gen.Defer(gen.FuncCall("myFunc"))
// defer myFunc()
gen.Defer(gen.Identifier("a").Call("MyFunc"))
// defer a.MyFunc()
gen.Defer(gen.Lambda().Block(
gen.Identifier("a").Call("MyFunc"),
).Call())
// defer func () {
// a.MyFunc()
// }()
}
gen.Identifier("a").
Field("field").
Call("myFunc").Args(String("str")).
Field("field2")
// a.field.myFunc("str").field2
gen.FuncCall("myFunc").
Call("anotherFunc").Args(Identifier("a")).
Field("field")
// myFunc().anotherFunc(a).field
gen.Identifier("a").Pointer().
Field("field")
// (*a).field
gen.FuncCall("myFunc").Pointer().
Field("field")
// (*myFunc()).field
For methods the first argument is formatted according to Go conventions (first letter of the type in lowercase)
f := gen.NewFile("cool", "my-generator")
f.Func("myFunc")
// func myFunc() {}
f.Func("myFunc").
Params(
gen.Param("val", "string"),
gen.QualParam("sb", "strings", "Builder").Pointer(),
)
// func myFunc(val string, sb *strings.Builder) {
// }
f.Func("myFunc").ReturnTypes(
gen.ReturnType("myType").Pointer(),
gen.ReturnTypeError(),
).Block(
gen.Return(gen.Identifier("a"), gen.Nil()),
)
// func myFunc() (*myType, error) {
// return a, nil
// }
f.Func("myFunc").Block(
gen.Return(),
)
// func myFunc() {
// return
// }
Function API is available for methods as well
f := gen.NewFile("cool", "my-generator")
f.Method(
gen.This("MyTypeName"),
"coolMethod",
).ReturnTypes(
gen.ReturnType("string"),
).Block(
gen.Return(gen.Identifier("m").Field("field")),
)
// func (m MyTypeName) coolMethod() string {
// return m.field
// }
If, else-if and else statements can be chained
gen.If(
gen.Identifier("val").IsNotNil(),
).Block(
gen.Return(Identifier("val")),
).Else(
gen.Return(Nil()),
)
// if val != nil{
// return val
// } else {
// return nil
// }
gen.IfDecl(
gen.Declare("val", "err").Values(gen.QualFuncCall("strconv", "Atoi").Args(gen.QualFuncCall("os", "Getenv").Args(gen.String("ENV_VAR")))),
gen.Err().IsNil(),
).Block(
gen.Identifier("config").Field("myVar").Assign(gen.Identifier("val")),
)
// if val, err := strconv.Atoi(os.Getenv("ENV_VAR")); err == nil {
// config.myVar = val
// }
f := gen.NewFile("cool", "my-generator")
f.Struct("myStruct").Props(
gen.Property("name", "int").Pointer(),
)
// type myStruct struct {
// name *int
// }
f.Interface("myInterface").Funcs(
gen.FuncDecl("myFunc").Params(gen.Param("param", "int")).ReturnTypes(gen.ReturnType("string"))
)
// type myInterface interface {
// myFunc(param int) string
// }
f.Type("myType", "int64")
// type myType int64
f := gen.NewFile("cool", "my-generator").
f.GenerateGetter(gen.This("TypeName").Pointer(), "myField", gen.ReturnType("int"))
// func (t *TypeName) MyField() int {
// return t.myField
// }