github
https://github.com/dave/jennifer
Jennifer
Jennifer是Go的代码生成器。
package main
import (
"fmt"
. "github.com/dave/jennifer/jen"
)
func main() {
f := NewFile("main")
f.Func().Id("main").Params().Block(
Qual("fmt", "Println").Call(Lit("Hello, world")),
)
fmt.Printf("%#v", f)
}
输出:
package main
import "fmt"
func main() {
fmt.Println("Hello, world")
}
安装
go get -u github.com/dave/jennifer/jen
例子
Jennifer有一套完整的示例——参见godoc的索引。下面是珍妮弗在现实生活中的一些例子:
- genjen (which generates much of jennifer, using data in data.go)
- zerogen
- go-contentful-generator
渲染
对于测试,可以使用%#v动词用fmt包呈现文件或语句。
不建议在生产环境中使用这种方法,因为任何错误都会导致panic。用于生产环境,File.Render 或 File.Save 是被推荐的。c := Id("a").Call(Lit("b"))
fmt.Printf("%#v", c)
// Output:
// a("b")
标识符
Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
Id
Id renders an identifier.
c := If(Id("i").Op("==").Id("j")).Block(
Return(Id("i")),
)
fmt.Printf("%#v", c)
// Output:
// if i == j {
// return i
// }
Dot
Dot 点呈现一个句点,后面跟着一个标识符。用于字段和选择器。
c := Qual("a.b/c", "Foo").Call().Dot("Bar").Index(Lit(0)).Dot("Baz")
fmt.Printf("%#v", c)
// Output:
// c.Foo().Bar[0].Baz
Qual
Qual 呈现一个限定标识符。
c := Qual("encoding/gob", "NewEncoder").Call()
fmt.Printf("%#v", c)
// Output:
// gob.NewEncoder()
当与文件一起使用时,会自动添加导入。如果路径匹配本地路径,包的名字是省略。如果包名称冲突,则会自动重命名它们。
f := NewFilePath("a.b/c")
f.Func().Id("init").Params().Block(
Qual("a.b/c", "Foo").Call().Comment("Local package - name is omitted."),
Qual("d.e/f", "Bar").Call().Comment("Import is automatically added."),
Qual("g.h/f", "Baz").Call().Comment("Colliding package name is renamed."),
)
fmt.Printf("%#v", f)
// Output:
// package c
//
// import (
// f "d.e/f"
// f1 "g.h/f"
// )
//
// func init() {
// Foo() // Local package - name is omitted.
// f.Bar() // Import is automatically added.
// f1.Baz() // Colliding package name is renamed.
// }
注意,给定任意包路径,不可能可靠地确定包名,因此从路径中猜测合理的名称并添加为别名。所有标准库包的名字是已知的这些不需要别名。如果需要对别名进行更多控制,请阅读 File.ImportName 或 File.ImportAlias。
List
list 呈现一个逗号分隔的列表。用于多个返回函数。
c := List(Id("a"), Err()).Op(":=").Id("b").Call()
fmt.Printf("%#v", c)
// Output:
// a, err := b()
关键字
Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
简单的关键字、预先声明的标识符和内置函数是自解释的:
Construct | Name |
---|---|
Keywords | Break, Chan, Const, Continue, Default, Defer, Else, Fallthrough, Func, Go, Goto, Range, Select, Type, Var |
Functions | Append, Cap, Close, Complex, Copy, Delete, Imag, Len, Make, New, Panic, Print, Println, Real, Recover |
Types | Bool, Byte, Complex64, Complex128, Error, Float32, Float64, Int, Int8, Int16, Int32, Int64, Rune, String, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr |
Constants | True, False, Iota, Nil |
Helpers | Err |
内置函数接受参数列表并适当地呈现它们:
c := Id("a").Op("=").Append(Id("a"), Id("b").Op("..."))
fmt.Printf("%#v", c)
// Output:
// a = append(a, b...)
下面解释了If, for, Interface, Struct, Switch, Case, Return和Map的特殊情况。
操作符
Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
Op呈现提供的操作符/令牌。
c := Id("a").Op(":=").Id("b").Call()
fmt.Printf("%#v", c)
// Output:
// a := b()
c := Id("a").Op("=").Op("*").Id("b")
fmt.Printf("%#v", c)
// Output:
// a = *b
c := Id("a").Call(Id("b").Op("..."))
fmt.Printf("%#v", c)
// Output:
// a(b...)
c := If(Parens(Id("a").Op("||").Id("b")).Op("&&").Id("c")).Block()
fmt.Printf("%#v", c)
// Output:
// if (a || b) && c {
// }
花括号
Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
几种方法呈现花括号,总结如下:
Name | Prefix | Separator | Example |
---|---|---|---|
Block | \n |
func a() { ... } or if a { ... } |
|
Interface | interface |
\n |
interface { ... } |
Struct | struct |
\n |
struct { ... } |
Values | , |
[]int{1, 2} or A{B: "c"} |
块
block 呈现一个用大括号括起来的语句列表。用于代码块。
c := Func().Id("foo").Params().String().Block(
Id("a").Op("=").Id("b"),
Id("b").Op("++"),
Return(Id("b")),
)
fmt.Printf("%#v", c)
// Output:
// func foo() string {
// a = b
// b++
// return b
// }
c := If(Id("a").Op(">").Lit(10)).Block(
Id("a").Op("=").Id("a").Op("/").Lit(2),
)
fmt.Printf("%#v", c)
// Output:
// if a > 10 {
// a = a / 2
// }
当直接在case或Default后面使用时,将应用一个特殊的case,其中省略了大括号。这允许在switch和select语句中使用。看到的例子。
接口, 结构体
Interface和struct 呈现关键字,后跟一个用大括号括起来的语句列表。
c := Var().Id("a").Interface()
fmt.Printf("%#v", c)
// Output:
// var a interface{}
c := Type().Id("a").Interface(
Id("b").Params().String(),
)
fmt.Printf("%#v", c)
// Output:
// type a interface {
// b() string
// }
c := Id("c").Op(":=").Make(Chan().Struct())
fmt.Printf("%#v", c)
// Output:
// c := make(chan struct{})
c := Type().Id("foo").Struct(
List(Id("x"), Id("y")).Int(),
Id("u").Float32(),
)
fmt.Printf("%#v", c)
// Output:
// type foo struct {
// x, y int
// u float32
// }
圆括号
Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
输出括号的几种方法,总结如下:
Name | Prefix | Separator | Example |
---|---|---|---|
Call | , |
fmt.Println(b, c) |
|
Params | , |
func (a *A) Foo(i int) { ... } |
|
Defs | \n |
const ( ... ) |
|
Parens | []byte(s) or a / (b + c) |
||
Assert | . |
s, ok := i.(string) |
调用
call 呈现用括号括起来的逗号分隔的列表。用于函数调用。
c := Qual("fmt", "Printf").Call(
Lit("%#v: %T\n"),
Id("a"),
Id("b"),
)
fmt.Printf("%#v", c)
// Output:
// fmt.Printf("%#v: %T\n", a, b)
参数
Params呈现一个用圆括号括起来的逗号分隔的列表。用于函数参数和方法接收器。
c := Func().Params(
Id("a").Id("A"),
).Id("foo").Params(
Id("b"),
Id("c").String(),
).String().Block(
Return(Id("b").Op("+").Id("c")),
)
fmt.Printf("%#v", c)
// Output:
// func (a A) foo(b, c string) string {
// return b + c
// }
定义多个
Defs 呈现一份声明列表包含在括号。用于定义列表。
c := Const().Defs(
Id("a").Op("=").Lit("a"),
Id("b").Op("=").Lit("b"),
)
fmt.Printf("%#v", c)
// Output:
// const (
// a = "a"
// b = "b"
// )
括弧
Parens 在括号中呈现单个项。用于类型转换或指定计算顺序。
c := Id("b").Op(":=").Index().Byte().Parens(Id("s"))
fmt.Printf("%#v", c)
// Output:
// b := []byte(s)
c := Id("a").Op("/").Parens(Id("b").Op("+").Id("c"))
fmt.Printf("%#v", c)
// Output:
// a / (b + c)
断言
Assert 呈现一个句点,后跟一个用圆括号括起来的项。用于类型断言。
c := List(Id("b"), Id("ok")).Op(":=").Id("a").Assert(Bool())
fmt.Printf("%#v", c)
// Output:
// b, ok := a.(bool)
控制流程
Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
If, For
If 和 For 呈现关键字,后跟一个分号分隔的列表。
c := If(
Err().Op(":=").Id("a").Call(),
Err().Op("!=").Nil(),
).Block(
Return(Err()),
)
fmt.Printf("%#v", c)
// Output:
// if err := a(); err != nil {
// return err
// }
c := For(
Id("i").Op(":=").Lit(0),
Id("i").Op("<").Lit(10),
Id("i").Op("++"),
).Block(
Qual("fmt", "Println").Call(Id("i")),
)
fmt.Printf("%#v", c)
// Output:
// for i := 0; i < 10; i++ {
// fmt.Println(i)
// }
Switch, Select
Switch, Select,Case和Block用于构建switch或select语句:
c := Switch(Id("value").Dot("Kind").Call()).Block(
Case(Qual("reflect", "Float32"), Qual("reflect", "Float64")).Block(
Return(Lit("float")),
),
Case(Qual("reflect", "Bool")).Block(
Return(Lit("bool")),
),
Case(Qual("reflect", "Uintptr")).Block(
Fallthrough(),
),
Default().Block(
Return(Lit("none")),
),
)
fmt.Printf("%#v", c)
// Output:
// switch value.Kind() {
// case reflect.Float32, reflect.Float64:
// return "float"
// case reflect.Bool:
// return "bool"
// case reflect.Uintptr:
// fallthrough
// default:
// return "none"
// }
Return
Return 关键字后跟逗号分隔的列表。
c := Return(Id("a"), Id("b"))
fmt.Printf("%#v", c)
// Output:
// return a, b
集合
Map
Map 呈现关键字,后跟一个用方括号括起来的项。用于映射定义。
c := Id("a").Op(":=").Map(String()).String().Values()
fmt.Printf("%#v", c)
// Output:
// a := map[string]string{}
Index
Index 呈现用方括号括起来的冒号分隔的列表。用于数组/片索引和定义。
c := Var().Id("a").Index().String()
fmt.Printf("%#v", c)
// Output:
// var a []string
c := Id("a").Op(":=").Id("b").Index(Lit(0), Lit(1))
fmt.Printf("%#v", c)
// Output:
// a := b[0:1]
c := Id("a").Op(":=").Id("b").Index(Lit(1), Empty())
fmt.Printf("%#v", c)
// Output:
// a := b[1:]
Values
Values 呈现用大括号括起来的逗号分隔的列表。用于切片或复合字面值。
c := Index().String().Values(Lit("a"), Lit("b"))
fmt.Printf("%#v", c)
// Output:
// []string{"a", "b"}
Dict呈现为键/值对。与映射或复合文字的值一起使用。
c := Map(String()).String().Values(Dict{
Lit("a"): Lit("b"),
Lit("c"): Lit("d"),
})
fmt.Printf("%#v", c)
// Output:
// map[string]string{
// "a": "b",
// "c": "d",
// }
c := Op("&").Id("Person").Values(Dict{
Id("Age"): Lit(1),
Id("Name"): Lit("a"),
})
fmt.Printf("%#v", c)
// Output:
// &Person{
// Age: 1,
// Name: "a",
// }
DictFunc执行一个func(Dict)来生成值。
c := Id("a").Op(":=").Map(String()).String().Values(DictFunc(func(d Dict) {
d[Lit("a")] = Lit("b")
d[Lit("c")] = Lit("d")
}))
fmt.Printf("%#v", c)
// Output:
// a := map[string]string{
// "a": "b",
// "c": "d",
// }
注意:项目是关键时候下令呈现,确保可重复的代码。
字面值
Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
Lit
Lit 显示文字。Lit只支持内置类型(bool、string、int、complex128、float64、float32、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64、uintptr和complex64)。传递任何其他类型的信息都会引起恐慌。
c := Id("a").Op(":=").Lit("a")
fmt.Printf("%#v", c)
// Output:
// a := "a"
c := Id("a").Op(":=").Lit(1.5)
fmt.Printf("%#v", c)
// Output:
// a := 1.5
LitFunc 通过执行所提供的函数生成要呈现的值。
c := Id("a").Op(":=").LitFunc(func() interface{} { return 1 + 1 })
fmt.Printf("%#v", c)
// Output:
// a := 2
对于默认的常量类型(bool, int, float64, string, complex128), Lit将呈现无类型常量。
Code | Output |
---|---|
Lit(true) |
true |
Lit(1) |
1 |
Lit(1.0) |
1.0 |
Lit("foo") |
"foo" |
Lit(0 + 1i) |
(0 + 1i) |
对于所有其他内置类型(float32、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64、uintptr、complex64), Lit也会渲染该类型。
Code | Output |
---|---|
Lit(float32(1)) |
float32(1) |
Lit(int16(1)) |
int16(1) |
Lit(uint8(0x1)) |
uint8(0x1) |
Lit(complex64(0 + 1i)) |
complex64(0 + 1i) |
内置的别名类型字节和符文需要一个特殊的情况。LitRune和LitByte呈现rune和字节文字。
Code | Output |
---|---|
LitRune('x') |
'x' |
LitByte(byte(0x1)) |
byte(0x1) |
注释
Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
Comment
添加了一个注释。如果提供的字符串包含换行符,则注释将以多行格式格式化。
f := NewFile("a")
f.Comment("Foo returns the string \"foo\"")
f.Func().Id("Foo").Params().String().Block(
Return(Lit("foo")).Comment("return the string foo"),
)
fmt.Printf("%#v", f)
// Output:
// package a
//
// // Foo returns the string "foo"
// func Foo() string {
// return "foo" // return the string foo
// }
c := Comment("a\nb")
fmt.Printf("%#v", c)
// Output:
// /*
// a
// b
// */
如果注释字符串以”//“或”/*”开头,自动格式化将被禁用,字符串将被直接呈现。
c := Id("foo").Call(Comment("/* inline */")).Comment("//no-space")
fmt.Printf("%#v", c)
// Output:
// foo( /* inline */ ) //no-space
Commentf
使用格式字符串和参数列表添加注释。
name := "foo"
val := "bar"
c := Id(name).Op(":=").Lit(val).Commentf("%s is the string \"%s\"", name, val)
fmt.Printf("%#v", c)
// Output:
// foo := "bar" // foo is the string "bar"
Helpers
Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
Func methods
所有接受可变项列表的构造都与接受func(*Group)的GroupFunc函数配对。用于嵌入逻辑。
c := Id("numbers").Op(":=").Index().Int().ValuesFunc(func(g *Group) {
for i := 0; i <= 5; i++ {
g.Lit(i)
}
})
fmt.Printf("%#v", c)
// Output:
// numbers := []int{0, 1, 2, 3, 4, 5}
increment := true
name := "a"
c := Func().Id("a").Params().BlockFunc(func(g *Group) {
g.Id(name).Op("=").Lit(1)
if increment {
g.Id(name).Op("++")
} else {
g.Id(name).Op("--")
}
})
fmt.Printf("%#v", c)
// Output:
// func a() {
// a = 1
// a++
// }
Add
Add将提供的项追加到语句中。
ptr := Op("*")
c := Id("a").Op("=").Add(ptr).Id("b")
fmt.Printf("%#v", c)
// Output:
// a = *b
a := Id("a")
i := Int()
c := Var().Add(a, i)
fmt.Printf("%#v", c)
// Output:
// var a int
Do
Do将语句作为参数调用所提供的函数。用于嵌入逻辑。
f := func(name string, isMap bool) *Statement {
return Id(name).Op(":=").Do(func(s *Statement) {
if isMap {
s.Map(String()).String()
} else {
s.Index().String()
}
}).Values()
}
fmt.Printf("%#v\n%#v", f("a", true), f("b", false))
// Output:
// a := map[string]string{}
// b := []string{}
Misc
Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
Tag
标记呈现一个结构标记
c := Type().Id("foo").Struct(
Id("A").String().Tag(map[string]string{"json": "a"}),
Id("B").Int().Tag(map[string]string{"json": "b", "bar": "baz"}),
)
fmt.Printf("%#v", c)
// Output:
// type foo struct {
// A string `json:"a"`
// B int `bar:"baz" json:"b"`
// }
注意:这些项在呈现时按键排序,以确保代码可重复。
Null
添加一个空项。空项在列表中不呈现任何内容,并且后面没有分隔符。
在列表中,nil也会产生同样的效果。
c := Func().Id("foo").Params(
nil,
Id("s").String(),
Null(),
Id("i").Int(),
).Block()
fmt.Printf("%#v", c)
// Output:
// func foo(s string, i int) {}
Empty
Empty添加一个空项。空项不呈现任何内容,只是在列表中后跟一个分隔符。
c := Id("a").Op(":=").Id("b").Index(Lit(1), Empty())
fmt.Printf("%#v", c)
// Output:
// a := b[1:]
Line
插入一个空行。
Clone
在传递*语句时要小心。考虑以下…
a := Id("a")
c := Block(
a.Call(),
a.Call(),
)
fmt.Printf("%#v", c)
// Output:
// {
// a()()
// a()()
// }
Id(“a”)返回一个*语句,Call()方法将该语句追加两次。为了避免这种情况,可以使用克隆。Clone复制该语句,因此可以追加更多的令牌,而不会影响原始的令牌。
a := Id("a")
c := Block(
a.Clone().Call(),
a.Clone().Call(),
)
fmt.Printf("%#v", c)
// Output:
// {
// a()
// a()
// }
Cgo
cgo“C”伪包是一种特殊的情况,总是没有包别名呈现。可以使用Qual、Anon或提供序言来添加导入。在文件中添加序言。与注释具有相同语义的CgoPreamble。如果提供了序言,则导入将被分隔,并且在其前面有序言。
f := NewFile("a")
f.CgoPreamble(`#include <stdio.h>
#include <stdlib.h>
void myprint(char* s) {
printf("%s\n", s);
}
`)
f.Func().Id("init").Params().Block(
Id("cs").Op(":=").Qual("C", "CString").Call(Lit("Hello from stdio\n")),
Qual("C", "myprint").Call(Id("cs")),
Qual("C", "free").Call(Qual("unsafe", "Pointer").Parens(Id("cs"))),
)
fmt.Printf("%#v", f)
// Output:
// package a
//
// import "unsafe"
//
// /*
// #include <stdio.h>
// #include <stdlib.h>
//
// void myprint(char* s) {
// printf("%s\n", s);
// }
// */
// import "C"
//
// func init() {
// cs := C.CString("Hello from stdio\n")
// C.myprint(cs)
// C.free(unsafe.Pointer(cs))
// }
File
Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File
**
表示单个源文件。包导入是根据文件自动管理的。
NewFile
用指定的包名创建一个新文件。
NewFilePath
NewFilePath在指定包路径时创建一个新文件—包名从路径推断。
NewFilePathName
用指定的包路径和名称创建一个新文件。
f := NewFilePathName("a.b/c", "main")
f.Func().Id("main").Params().Block(
Qual("a.b/c", "Foo").Call(),
)
fmt.Printf("%#v", f)
// Output:
// package main
//
// func main() {
// Foo()
// }
Save
保存呈现文件并保存到提供的文件名。
Render
Render将文件呈现给所提供的写入器。
f := NewFile("a")
f.Func().Id("main").Params().Block()
buf := &bytes.Buffer{}
err := f.Render(buf)
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(buf.String())
}
// Output:
// package a
//
// func main() {}
Anon
Anon添加了一个匿名导入。
f := NewFile("c")
f.Anon("a")
f.Func().Id("init").Params().Block()
fmt.Printf("%#v", f)
// Output:
// package c
//
// import _ "a"
//
// func init() {}
ImportName
ImportName提供路径的包名。如果指定,别名将从导入块中省略。这是可选的。如果未指定,则根据路径使用合理的包名,并将其作为别名添加到导入块中。
f := NewFile("main")
// package a should use name "a"
f.ImportName("github.com/foo/a", "a")
// package b is not used in the code so will not be included
f.ImportName("github.com/foo/b", "b")
f.Func().Id("main").Params().Block(
Qual("github.com/foo/a", "A").Call(),
)
fmt.Printf("%#v", f)
// Output:
// package main
//
// import "github.com/foo/a"
//
// func main() {
// a.A()
// }
ImportNames
ImportNames允许多个名称作为映射导入。使用gennames命令自动生成一个go文件,该文件包含选择的包名的映射。
ImportAlias
ImportAlias提供了应该在导入块中使用的包路径的别名。可以使用句点强制点导入。
f := NewFile("main")
// package a should be aliased to "b"
f.ImportAlias("github.com/foo/a", "b")
// package c is not used in the code so will not be included
f.ImportAlias("github.com/foo/c", "c")
f.Func().Id("main").Params().Block(
Qual("github.com/foo/a", "A").Call(),
)
fmt.Printf("%#v", f)
// Output:
// package main
//
// import b "github.com/foo/a"
//
// func main() {
// b.A()
// }
Comments
PackageComment在文件的顶部package关键字的上方添加一个注释。
HeaderComment将注释添加到文件的顶部,任何包注释的上方。在头注释下面呈现一个空行,以确保头注释不包含在包文档中。
CanonicalPath将规范导入路径注释添加到包子句中。
f := NewFile("c")
f.CanonicalPath = "d.e/f"
f.HeaderComment("Code generated by...")
f.PackageComment("Package c implements...")
f.Func().Id("init").Params().Block()
fmt.Printf("%#v", f)
// Output:
// // Code generated by...
//
// // Package c implements...
// package c // import "d.e/f"
//
// func init() {}
CgoPreamble添加了一个cgo序言注释,直接呈现在“C”伪包导入之前。
PackagePrefix
如果你担心包生成别名冲突与局部变量的名字,你可以在这里设置一个前缀。包foo变成了{prefix}_foo。
f := NewFile("a")
f.PackagePrefix = "pkg"
f.Func().Id("main").Params().Block(
Qual("b.c/d", "E").Call(),
)
fmt.Printf("%#v", f)
// Output:
// package a
//
// import pkg_d "b.c/d"
//
// func main() {
// pkg_d.E()
// }