jennifer

repository·master·Indexed 25 days ago

https://github.com/dave/jennifer

A code generator for Go that provides a fluent API to construct Go source code programmatically. It includes features for managing imports, rendering identifiers, functions, and blocks, and supports Go 1.18+ generics. The package also provides the gennames CLI to generate package name indices for commonly used packages.

Tokens
5.5K
Snippets
27
Records
39
Agent score
36%

What's inside jennifer

  1. Quickstart: Generate a basic Go file

    master

    Use NewFile to create a new Go file and chain methods to build its structure. Jennifer uses a fluent API to construct identifiers, functions, and blocks.

    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)
    }
  2. Avoid side effects when reusing Statements with Clone()

    master

    In Jennifer, methods like Call() append to the existing *Statement. If you reuse the same *Statement variable multiple times, it will accumulate those calls. To use a base statement multiple times without affecting the original, use Clone() to create a copy.

    a := Id("a")
    // Incorrect: a is modified by each call
    c := Block(
    	a.Call(),
    	a.Call(),
    )
    // Output: { a()() a()() }
    
    // Correct: Clone() creates a fresh copy
    c := Block(
    	a.Clone().Call(),
    	a.Clone().Call(),
    )
    // Output: { a() a() }
  3. Use the gennames CLI to generate a package name index

    master

    The gennames command generates an index of package names for commonly used packages. This index can be added to generated files using File.ImportNames. You can use the -standard flag to specifically target standard library packages.

    gennames -filter "foo|bar"
  4. Render File or Statement for testing

    master

    For testing purposes, you can render a File or Statement using the fmt package with the %#v verb.

    Warning: This is not recommended for production because any error will cause a panic. For production, use File.Render or File.Save instead.

    c := Id("a").Call(Lit("b"))
    fmt.Printf("%#v", c)
    // Output:
    a("b")
  5. Generate a package name index for vendored packages

    master

    To create a file specifically for packages vendored inside a specific directory, use the -output, -package, and -path flags. For example, to create foo/names.go with package foo listing packages vendored inside github.com/foo/bar:

    gennames -output "foo/names.go" -package "foo" -path "github.com/foo/bar/vendor/..."
  6. Generate Switch and Select statements

    master

    Use Switch(), Select(), Case(), and Default() to build switch or select statements. Each case or default must be followed by a .Block().

    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")),
    	),
    )
    // Output: switch value.Kind() {
    // case reflect.Float32, reflect.Float64: return "float"
    // case reflect.Bool: return "bool"
    // case reflect.Uintptr: fallthrough
    // default: return "none"
    // }
  7. Generate Slice and Composite Literals with Values and Dict

    master

    Use .Values() to render a comma-separated list in curly braces (for slices or composite literals). Use Dict or DictFunc within .Values() to render key/value pairs for maps or composite literals.

    // Slice literal: []string{"a", "b"}
    c := Index().String().Values(Lit("a"), Lit("b"))
    
    // Map literal: map[string]string{"a": "b", "c": "d"}
    c := Map(String()).String().Values(Dict{
    	Lit("a"): Lit("b"),
    	Lit("c"): Lit("d"),
    })
    
    // Composite literal: &Person{Age: 1, Name: "a"}
    c := Op("&").Id("Person").Values(Dict{
    	Id("Age"): Lit(1),
    	Id("Name"): Lit("a"),
    })
    
    // Map literal using DictFunc for dynamic generation
    c := Id("a").Op(":=").Map(String()).String().Values(DictFunc(func(d Dict) {
    	d[Lit("a")] = Lit("b")
    	d[Lit("c")] = Lit("d")
    }))
  8. Create and render a new File

    master

    Use NewFile(packageName) to create a new source file. You can render the file to a io.Writer using Render(w) or save it directly to disk using Save(filename).

    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() {}
  9. Generate If and For statements

    master

    Use If() and For() to generate Go control flow statements. If and For render the keyword followed by a semicolon-separated list and a .Block() containing the body.

    c := If(
    	Err().Op(":=").Id("a").Call(),
    	Err().Op("!=").Nil(),
    ).Block(
    	Return(Err()),
    )
    // Output: if err := a(); err != nil { return err }
    
    // And For:
    c := For(
    	Id("i").Op("::=").Lit(0),
    	Id("i").Op("<").Lit(10),
    	Id("i").Op("++"),
    ).Block(
    	Qual("fmt", "Println").Call(Id("i")),
    )
    // Output: for i := 0; i < 10; i++ { fmt.Println(i) }
  10. Add comments and metadata to a File

    master

    Use the following methods to add documentation and metadata to the top of your generated file:

    • HeaderComment(text): Adds a comment at the very top of the file. A blank line is inserted after it.
    • PackageComment(text): Adds a comment above the package keyword.
    • CanonicalPath = "path": Adds a canonical import path annotation to the package clause (e.g., // import "d.e/f").
    f := NewFile("c")
    f.CanonicalPath = "d.e/f"
    f.HeaderComment("Code generated by...")
    f.PackageComment("Package c implements...")
    fmt.Printf("%#v", f)
    // Output:
    // // Code generated by...
    //
    // // Package c implements...
    // package c // import "d.e/f"
    //
    // func init() {}