Giter Club home page Giter Club logo

jennifer's Introduction

Build Status Go Report Card codecov

Jennifer

Jennifer is a code generator for 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)
}

Output:

package main

import fmt "fmt"

func main() {
	fmt.Println("Hello, world")
}

Install

go get -u github.com/dave/jennifer/jen

Examples

Jennifer has a comprehensive suite of examples - see godoc for an index. The code that powers jennifer is generated by jennifer itself, see the genjen package - it uses data in data.go, which is processed by render.go to create generated.go. More examples of jennifer being used in the real-world:

Rendering

For testing, a File or Statement can be rendered with the fmt package using the %#v verb.

c := Id("a").Call(Lit("b"))
fmt.Printf("%#v", c)
// Output:
// a("b")

This is not recommended for use in production because any error will cause a panic. For production use, File.Render or File.Save are preferred.

Identifiers

Identifiers 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 renders a period followed by an identifier. Use for fields and selectors.

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 renders a qualified identifier.

c := Qual("encoding/gob", "NewEncoder").Call()
fmt.Printf("%#v", c)
// Output:
// gob.NewEncoder()

Imports are automatically added when used with a File. If the path matches the local path, the package name is omitted. If package names conflict they are automatically renamed.

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.
// }

List

List renders a comma separated list. Use for multiple return functions.

c := List(Id("a"), Err()).Op(":=").Id("b").Call()
fmt.Printf("%#v", c)
// Output:
// a, err := b()

Keywords

Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File

Simple keywords, predeclared identifiers and built-in functions are self explanatory:

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

Built-in functions take a list of parameters and render them appropriately:

c := Id("a").Op("=").Append(Id("a"), Id("b").Op("..."))
fmt.Printf("%#v", c)
// Output:
// a = append(a, b...)

Special cases for If, For, Interface, Struct, Switch, Case, Return and Map are explained below.

Operators

Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File

Op renders the provided operator / token.

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...)

Braces

Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File

Several methods render curly braces, summarized below:

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

Block renders a statement list enclosed by curly braces. Use for code blocks.

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
// }

A special case applies when used directly after Case or Default, where the braces are omitted. This allows use in switch and select statements. See example.

Interface, Struct

Interface and Struct render the keyword followed by a statement list enclosed by curly braces.

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
// }

Parentheses

Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File

Several methods output parenthesis, summarized below:

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

Call renders a comma separated list enclosed by parenthesis. Use for function calls.

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

Params renders a comma separated list enclosed by parenthesis. Use for function parameters and method receivers.

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

Defs renders a statement list enclosed in parenthesis. Use for definition lists.

c := Const().Defs(
	Id("a").Op("=").Lit("a"),
	Id("b").Op("=").Lit("b"),
)
fmt.Printf("%#v", c)
// Output:
// const (
// 	a = "a"
// 	b = "b"
// )

Parens

Parens renders a single item in parenthesis. Use for type conversion or to specify evaluation order.

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

Assert renders a period followed by a single item enclosed by parenthesis. Use for type assertions.

c := List(Id("b"), Id("ok")).Op(":=").Id("a").Assert(Bool())
fmt.Printf("%#v", c)
// Output:
// b, ok := a.(bool)

Control flow

Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File

If, For

If and For render the keyword followed by a semicolon separated list.

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 and Block are used to build switch or select statements:

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 renders the keyword followed by a comma separated list.

c := Return(Id("a"), Id("b"))
fmt.Printf("%#v", c)
// Output:
// return a, b

Collections

Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File

Map

Map renders the keyword followed by a single item enclosed by square brackets. Use for map definitions.

c := Id("a").Op(":=").Map(String()).String().Values()
fmt.Printf("%#v", c)
// Output:
// a := map[string]string{}

Index

Index renders a colon separated list enclosed by square brackets. Use for array / slice indexes and definitions.

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 renders a comma separated list enclosed by curly braces. Use for slice or composite literals.

c := Index().String().Values(Lit("a"), Lit("b"))
fmt.Printf("%#v", c)
// Output:
// []string{"a", "b"}

Dict renders as key/value pairs. Use with Values for map or composite literals.

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 executes a func(Dict) to generate the value.

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",
// }

Note: the items are ordered by key when rendered to ensure repeatable code.

Literals

Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File

Lit

Lit renders a literal, using the format provided by the fmt package %#v verb.

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 generates the value to render by executing the provided function.

c := Id("a").Op(":=").LitFunc(func() interface{} { return 1 + 1 })
fmt.Printf("%#v", c)
// Output:
// a := 2

Comments

Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File

Comment

Comment adds a comment. If the provided string contains a newline, the comment is formatted in multiline style.

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
// */

If the comment string starts with "//" or "/*", the automatic formatting is disabled and the string is rendered directly.

c := Id("foo").Call(Comment("/* inline */")).Comment("//no-space")
fmt.Printf("%#v", c)
// Output:
// foo( /* inline */ ) //no-space

Commentf

Commentf adds a comment, using a format string and a list of parameters.

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

All constructs that accept a variadic list of items are paired with GroupFunc functions that accept a func(*Group). Use for embedding logic.

increment := true
c := Func().Id("a").Params().BlockFunc(func(g *Group) {
	if increment {
		g.Id("a").Op("++")
	} else {
		g.Id("a").Op("--")
	}
})
fmt.Printf("%#v", c)
// Output:
// func a() {
// 	a++
// }

Add

Add appends the provided items to the statement.

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 calls the provided function with the statement as a parameter. Use for embedding logic.

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

Tag renders a struct 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"`
// }

Note: the items are ordered by key when rendered to ensure repeatable code.

Null

Null adds a null item. Null items render nothing and are not followed by a separator in lists.

In lists, nil will produce the same effect.

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 adds an empty item. Empty items render nothing but are followed by a separator in lists.

c := Id("a").Op(":=").Id("b").Index(Lit(1), Empty())
fmt.Printf("%#v", c)
// Output:
// a := b[1:]

Line

Line inserts a blank line.

Clone

Be careful when passing *Statement. Consider the following...

a := Id("a")
c := Block(
	a.Call(),
	a.Call(),
)
fmt.Printf("%#v", c)
// Output:
// {
// 	a()()
// 	a()()
// }

Id("a") returns a *Statement, which the Call() method appends to twice. To avoid this, use Clone. Clone makes a copy of the Statement, so further tokens can be appended without affecting the original.

a := Id("a")
c := Block(
	a.Clone().Call(),
	a.Clone().Call(),
)
fmt.Printf("%#v", c)
// Output:
// {
// 	a()
// 	a()
// }

File

Identifiers Keywords Operators Braces Parentheses Control flow Collections Literals Comments Helpers Misc File

File represents a single source file. Package imports are managed automaticaly by File.

NewFile

NewFile Creates a new file, with the specified package name.

NewFilePath

NewFilePath creates a new file while specifying the package path - the package name is inferred from the path.

NewFilePathName

NewFilePathName creates a new file with the specified package path and name.

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

Save renders the file and saves to the filename provided.

Render

Render renders the file to the provided writer.

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 adds an anonymous import:

f := NewFile("c")
f.Anon("a")
f.Func().Id("init").Params().Block()
fmt.Printf("%#v", f)
// Output:
// package c
//
// import _ "a"
//
// func init() {}

PackageComment

PackageComment adds a comment to the top of the file, above the package keyword.

f := NewFile("c")
f.PackageComment("a")
f.PackageComment("b")
f.Func().Id("init").Params().Block()
fmt.Printf("%#v", f)
// Output:
// // a
// // b
// package c
//
// func init() {}

PackagePrefix

If you're worried about package aliases conflicting with local variable names, you can set a prefix here. Package foo becomes {prefix}_foo.

f := NewFile("c")
f.PackagePrefix = "pkg"
f.Func().Id("main").Params().Block(
	Qual("fmt", "Println").Call(),
)
fmt.Printf("%#v", f)
// Output:
// package c
//
// import pkg_fmt "fmt"
//
// func main() {
// 	pkg_fmt.Println()
// }

jennifer's People

Contributors

dave avatar zellyn avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.