├── .gitignore ├── LICENSE ├── README.md ├── README.md.tpl ├── genjen ├── data.go ├── main.go ├── render.go └── render_test.go ├── gennames ├── README.md ├── hints.go └── main.go ├── go.mod ├── go.sum ├── jen ├── add.go ├── comments.go ├── custom.go ├── dict.go ├── do.go ├── examples_test.go ├── file.go ├── file_test.go ├── generated.go ├── generated_test.go ├── generics.go ├── group.go ├── group_test.go ├── hints.go ├── jen.go ├── jen_test.go ├── lit.go ├── reserved.go ├── statement.go ├── statement_test.go ├── tag.go └── tokens.go └── jennifer.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | 3 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 4 | *.o 5 | *.a 6 | *.so 7 | 8 | # Folders 9 | _obj 10 | _test 11 | 12 | # Architecture specific extensions/prefixes 13 | *.[568vq] 14 | [568vq].out 15 | 16 | *.cgo1.go 17 | *.cgo2.c 18 | _cgo_defun.c 19 | _cgo_gotypes.go 20 | _cgo_export.* 21 | 22 | _testmain.go 23 | 24 | *.exe 25 | *.test 26 | *.prof 27 | 28 | .DS_Store 29 | .idea/ 30 | coverage.out 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 David Brophy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![docs](https://pkg.go.dev/badge/github.com/dave/jennifer/jen.svg)](https://pkg.go.dev/github.com/dave/jennifer/jen) 2 | ![stability-stable](https://img.shields.io/badge/stability-stable-brightgreen.svg) 3 | 4 | # Jennifer 5 | Jennifer is a code generator for Go. 6 | 7 | ```go 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | 13 | . "github.com/dave/jennifer/jen" 14 | ) 15 | 16 | func main() { 17 | f := NewFile("main") 18 | f.Func().Id("main").Params().Block( 19 | Qual("fmt", "Println").Call(Lit("Hello, world")), 20 | ) 21 | fmt.Printf("%#v", f) 22 | } 23 | ``` 24 | Output: 25 | ```go 26 | package main 27 | 28 | import "fmt" 29 | 30 | func main() { 31 | fmt.Println("Hello, world") 32 | } 33 | ``` 34 | 35 | ### Install 36 | ``` 37 | go get -u github.com/dave/jennifer/jen 38 | ``` 39 | 40 | ### Need help? 41 | If you get stuck, have a question, would like a code review, or just want a 42 | chat: I'm happy to help! Feel free to open an issue, email me or mention @dave 43 | in your PR. 44 | 45 | ### Examples 46 | Jennifer has a comprehensive suite of examples - see [godoc](https://godoc.org/github.com/dave/jennifer/jen#pkg-examples) for an index. Here's some examples of jennifer being used in the real-world: 47 | 48 | * [genjen](genjen/render.go) (which generates much of jennifer, using data in [data.go](genjen/data.go)) 49 | * [zerogen](https://github.com/mrsinham/zerogen/blob/master/generator.go) 50 | * [go-contentful-generator](https://github.com/nicolai86/go-contentful-generator) 51 | 52 | ### Rendering 53 | For testing, a File or Statement can be rendered with the fmt package 54 | using the %#v verb. 55 | 56 | ```go 57 | c := Id("a").Call(Lit("b")) 58 | fmt.Printf("%#v", c) 59 | // Output: 60 | // a("b") 61 | ``` 62 | 63 | This is not recommended for use in production because any error will cause a 64 | panic. For production use, [File.Render](#render) or [File.Save](#save) are 65 | preferred. 66 | 67 | # Identifiers 68 | **Identifiers** [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 69 | 70 | ### Id 71 | Id renders an identifier. 72 | 73 | ```go 74 | c := If(Id("i").Op("==").Id("j")).Block( 75 | Return(Id("i")), 76 | ) 77 | fmt.Printf("%#v", c) 78 | // Output: 79 | // if i == j { 80 | // return i 81 | // } 82 | ``` 83 | 84 | ### Dot 85 | Dot renders a period followed by an identifier. Use for fields and selectors. 86 | 87 | ```go 88 | c := Qual("a.b/c", "Foo").Call().Dot("Bar").Index(Lit(0)).Dot("Baz") 89 | fmt.Printf("%#v", c) 90 | // Output: 91 | // c.Foo().Bar[0].Baz 92 | ``` 93 | 94 | ### Qual 95 | Qual renders a qualified identifier. 96 | 97 | ```go 98 | c := Qual("encoding/gob", "NewEncoder").Call() 99 | fmt.Printf("%#v", c) 100 | // Output: 101 | // gob.NewEncoder() 102 | ``` 103 | 104 | Imports are automatically added when 105 | used with a File. If the path matches the local path, the package name is 106 | omitted. If package names conflict they are automatically renamed. 107 | 108 | ```go 109 | f := NewFilePath("a.b/c") 110 | f.Func().Id("init").Params().Block( 111 | Qual("a.b/c", "Foo").Call().Comment("Local package - name is omitted."), 112 | Qual("d.e/f", "Bar").Call().Comment("Import is automatically added."), 113 | Qual("g.h/f", "Baz").Call().Comment("Colliding package name is renamed."), 114 | ) 115 | fmt.Printf("%#v", f) 116 | // Output: 117 | // package c 118 | // 119 | // import ( 120 | // f "d.e/f" 121 | // f1 "g.h/f" 122 | // ) 123 | // 124 | // func init() { 125 | // Foo() // Local package - name is omitted. 126 | // f.Bar() // Import is automatically added. 127 | // f1.Baz() // Colliding package name is renamed. 128 | // } 129 | ``` 130 | 131 | Note that 132 | it is not possible to reliably determine the package name given an arbitrary 133 | package path, so a sensible name is guessed from the path and added as an 134 | alias. The names of all standard library packages are known so these do not 135 | need to be aliased. If more control is needed of the aliases, see 136 | [File.ImportName](#importname) or [File.ImportAlias](#importalias). 137 | 138 | ### List 139 | List renders a comma separated list. Use for multiple return functions. 140 | 141 | ```go 142 | c := List(Id("a"), Err()).Op(":=").Id("b").Call() 143 | fmt.Printf("%#v", c) 144 | // Output: 145 | // a, err := b() 146 | ``` 147 | 148 | # Keywords 149 | [Identifiers](#identifiers) **Keywords** [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 150 | 151 | Simple keywords, predeclared identifiers and built-in functions are self 152 | explanatory: 153 | 154 | | Construct | Name | 155 | | ---------------- | ---- | 156 | | Keywords | Break, Chan, Const, Continue, Default, Defer, Else, Fallthrough, Func, Go, Goto, Range, Select, Type, Var | 157 | | Functions | Append, Cap, Clear, Close, Complex, Copy, Delete, Imag, Len, Make, Max, Min, New, Panic, Print, Println, Real, Recover | 158 | | Types | Bool, Byte, Complex64, Complex128, Error, Float32, Float64, Int, Int8, Int16, Int32, Int64, Rune, String, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr | 159 | | Constants | True, False, Iota, Nil | 160 | | Helpers | Err | 161 | 162 | Built-in functions take a list of parameters and render them appropriately: 163 | 164 | ```go 165 | c := Id("a").Op("=").Append(Id("a"), Id("b").Op("...")) 166 | fmt.Printf("%#v", c) 167 | // Output: 168 | // a = append(a, b...) 169 | ``` 170 | 171 | Special cases for [If, For](#if-for), [Interface, Struct](#interface-struct), [Switch, Case](#switch-select), [Return](#return) and [Map](#map) are explained below. 172 | 173 | # Operators 174 | [Identifiers](#identifiers) [Keywords](#keywords) **Operators** [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 175 | 176 | Op renders the provided operator / token. 177 | 178 | ```go 179 | c := Id("a").Op(":=").Id("b").Call() 180 | fmt.Printf("%#v", c) 181 | // Output: 182 | // a := b() 183 | ``` 184 | 185 | ```go 186 | c := Id("a").Op("=").Op("*").Id("b") 187 | fmt.Printf("%#v", c) 188 | // Output: 189 | // a = *b 190 | ``` 191 | 192 | ```go 193 | c := Id("a").Call(Id("b").Op("...")) 194 | fmt.Printf("%#v", c) 195 | // Output: 196 | // a(b...) 197 | ``` 198 | 199 | ```go 200 | c := If(Parens(Id("a").Op("||").Id("b")).Op("&&").Id("c")).Block() 201 | fmt.Printf("%#v", c) 202 | // Output: 203 | // if (a || b) && c { 204 | // } 205 | ``` 206 | 207 | # Braces 208 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) **Braces** [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 209 | 210 | Several methods render curly braces, summarized below: 211 | 212 | | Name | Prefix | Separator | Example | 213 | | ------------------------------ | ------------ | --------- | -------------------------------------| 214 | | [Block](#block) | | `\n` | `func a() { ... }` or `if a { ... }` | 215 | | [Interface](#interface-struct) | `interface` | `\n` | `interface { ... }` | 216 | | [Struct](#interface-struct) | `struct` | `\n` | `struct { ... }` | 217 | | [Values](#values) | | `,` | `[]int{1, 2}` or `A{B: "c"}` | 218 | 219 | ### Block 220 | Block renders a statement list enclosed by curly braces. Use for code blocks. 221 | 222 | ```go 223 | c := Func().Id("foo").Params().String().Block( 224 | Id("a").Op("=").Id("b"), 225 | Id("b").Op("++"), 226 | Return(Id("b")), 227 | ) 228 | fmt.Printf("%#v", c) 229 | // Output: 230 | // func foo() string { 231 | // a = b 232 | // b++ 233 | // return b 234 | // } 235 | ``` 236 | 237 | ```go 238 | c := If(Id("a").Op(">").Lit(10)).Block( 239 | Id("a").Op("=").Id("a").Op("/").Lit(2), 240 | ) 241 | fmt.Printf("%#v", c) 242 | // Output: 243 | // if a > 10 { 244 | // a = a / 2 245 | // } 246 | ``` 247 | 248 | 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](#switch-select). 249 | 250 | ### Interface, Struct 251 | Interface and Struct render the keyword followed by a statement list enclosed 252 | by curly braces. 253 | 254 | ```go 255 | c := Var().Id("a").Interface() 256 | fmt.Printf("%#v", c) 257 | // Output: 258 | // var a interface{} 259 | ``` 260 | 261 | ```go 262 | c := Type().Id("a").Interface( 263 | Id("b").Params().String(), 264 | ) 265 | fmt.Printf("%#v", c) 266 | // Output: 267 | // type a interface { 268 | // b() string 269 | // } 270 | ``` 271 | 272 | ```go 273 | c := Id("c").Op(":=").Make(Chan().Struct()) 274 | fmt.Printf("%#v", c) 275 | // Output: 276 | // c := make(chan struct{}) 277 | ``` 278 | 279 | ```go 280 | c := Type().Id("foo").Struct( 281 | List(Id("x"), Id("y")).Int(), 282 | Id("u").Float32(), 283 | ) 284 | fmt.Printf("%#v", c) 285 | // Output: 286 | // type foo struct { 287 | // x, y int 288 | // u float32 289 | // } 290 | ``` 291 | 292 | # Parentheses 293 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) **Parentheses** [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 294 | 295 | Several methods output parenthesis, summarized below: 296 | 297 | | Name | Prefix | Separator | Example | 298 | | ----------------- | ------ | --------- | --------------------------------- | 299 | | [Call](#call) | | `,` | `fmt.Println(b, c)` | 300 | | [Params](#params) | | `,` | `func (a *A) Foo(i int) { ... }` | 301 | | [Defs](#defs) | | `\n` | `const ( ... )` | 302 | | [Parens](#parens) | | | `[]byte(s)` or `a / (b + c)` | 303 | | [Assert](#assert) | `.` | | `s, ok := i.(string)` | 304 | 305 | ### Call 306 | Call renders a comma separated list enclosed by parenthesis. Use for function calls. 307 | 308 | ```go 309 | c := Qual("fmt", "Printf").Call( 310 | Lit("%#v: %T\n"), 311 | Id("a"), 312 | Id("b"), 313 | ) 314 | fmt.Printf("%#v", c) 315 | // Output: 316 | // fmt.Printf("%#v: %T\n", a, b) 317 | ``` 318 | 319 | ### Params 320 | Params renders a comma separated list enclosed by parenthesis. Use for function parameters and method receivers. 321 | 322 | ```go 323 | c := Func().Params( 324 | Id("a").Id("A"), 325 | ).Id("foo").Params( 326 | Id("b"), 327 | Id("c").String(), 328 | ).String().Block( 329 | Return(Id("b").Op("+").Id("c")), 330 | ) 331 | fmt.Printf("%#v", c) 332 | // Output: 333 | // func (a A) foo(b, c string) string { 334 | // return b + c 335 | // } 336 | ``` 337 | 338 | ### Defs 339 | Defs renders a statement list enclosed in parenthesis. Use for definition lists. 340 | 341 | ```go 342 | c := Const().Defs( 343 | Id("a").Op("=").Lit("a"), 344 | Id("b").Op("=").Lit("b"), 345 | ) 346 | fmt.Printf("%#v", c) 347 | // Output: 348 | // const ( 349 | // a = "a" 350 | // b = "b" 351 | // ) 352 | ``` 353 | 354 | ### Parens 355 | Parens renders a single item in parenthesis. Use for type conversion or to specify evaluation order. 356 | 357 | ```go 358 | c := Id("b").Op(":=").Index().Byte().Parens(Id("s")) 359 | fmt.Printf("%#v", c) 360 | // Output: 361 | // b := []byte(s) 362 | ``` 363 | 364 | ```go 365 | c := Id("a").Op("/").Parens(Id("b").Op("+").Id("c")) 366 | fmt.Printf("%#v", c) 367 | // Output: 368 | // a / (b + c) 369 | ``` 370 | 371 | ### Assert 372 | Assert renders a period followed by a single item enclosed by parenthesis. Use for type assertions. 373 | 374 | ```go 375 | c := List(Id("b"), Id("ok")).Op(":=").Id("a").Assert(Bool()) 376 | fmt.Printf("%#v", c) 377 | // Output: 378 | // b, ok := a.(bool) 379 | ``` 380 | 381 | # Control flow 382 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) **Control flow** [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 383 | 384 | ### If, For 385 | If and For render the keyword followed by a semicolon separated list. 386 | 387 | ```go 388 | c := If( 389 | Err().Op(":=").Id("a").Call(), 390 | Err().Op("!=").Nil(), 391 | ).Block( 392 | Return(Err()), 393 | ) 394 | fmt.Printf("%#v", c) 395 | // Output: 396 | // if err := a(); err != nil { 397 | // return err 398 | // } 399 | ``` 400 | 401 | ```go 402 | c := For( 403 | Id("i").Op(":=").Lit(0), 404 | Id("i").Op("<").Lit(10), 405 | Id("i").Op("++"), 406 | ).Block( 407 | Qual("fmt", "Println").Call(Id("i")), 408 | ) 409 | fmt.Printf("%#v", c) 410 | // Output: 411 | // for i := 0; i < 10; i++ { 412 | // fmt.Println(i) 413 | // } 414 | ``` 415 | 416 | ### Switch, Select 417 | Switch, Select, Case and Block are used to build switch or select statements: 418 | 419 | ```go 420 | c := Switch(Id("value").Dot("Kind").Call()).Block( 421 | Case(Qual("reflect", "Float32"), Qual("reflect", "Float64")).Block( 422 | Return(Lit("float")), 423 | ), 424 | Case(Qual("reflect", "Bool")).Block( 425 | Return(Lit("bool")), 426 | ), 427 | Case(Qual("reflect", "Uintptr")).Block( 428 | Fallthrough(), 429 | ), 430 | Default().Block( 431 | Return(Lit("none")), 432 | ), 433 | ) 434 | fmt.Printf("%#v", c) 435 | // Output: 436 | // switch value.Kind() { 437 | // case reflect.Float32, reflect.Float64: 438 | // return "float" 439 | // case reflect.Bool: 440 | // return "bool" 441 | // case reflect.Uintptr: 442 | // fallthrough 443 | // default: 444 | // return "none" 445 | // } 446 | ``` 447 | 448 | ### Return 449 | Return renders the keyword followed by a comma separated list. 450 | 451 | ```go 452 | c := Return(Id("a"), Id("b")) 453 | fmt.Printf("%#v", c) 454 | // Output: 455 | // return a, b 456 | ``` 457 | 458 | # Collections 459 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) **Collections** [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 460 | 461 | ### Map 462 | Map renders the keyword followed by a single item enclosed by square brackets. Use for map definitions. 463 | 464 | ```go 465 | c := Id("a").Op(":=").Map(String()).String().Values() 466 | fmt.Printf("%#v", c) 467 | // Output: 468 | // a := map[string]string{} 469 | ``` 470 | 471 | ### Index 472 | Index renders a colon separated list enclosed by square brackets. Use for array / slice indexes and definitions. 473 | 474 | ```go 475 | c := Var().Id("a").Index().String() 476 | fmt.Printf("%#v", c) 477 | // Output: 478 | // var a []string 479 | ``` 480 | 481 | ```go 482 | c := Id("a").Op(":=").Id("b").Index(Lit(0), Lit(1)) 483 | fmt.Printf("%#v", c) 484 | // Output: 485 | // a := b[0:1] 486 | ``` 487 | 488 | ```go 489 | c := Id("a").Op(":=").Id("b").Index(Lit(1), Empty()) 490 | fmt.Printf("%#v", c) 491 | // Output: 492 | // a := b[1:] 493 | ``` 494 | 495 | ### Values 496 | Values renders a comma separated list enclosed by curly braces. Use for slice or composite literals. 497 | 498 | ```go 499 | c := Index().String().Values(Lit("a"), Lit("b")) 500 | fmt.Printf("%#v", c) 501 | // Output: 502 | // []string{"a", "b"} 503 | ``` 504 | 505 | Dict renders as key/value pairs. Use with Values for map or composite 506 | literals. 507 | 508 | ```go 509 | c := Map(String()).String().Values(Dict{ 510 | Lit("a"): Lit("b"), 511 | Lit("c"): Lit("d"), 512 | }) 513 | fmt.Printf("%#v", c) 514 | // Output: 515 | // map[string]string{ 516 | // "a": "b", 517 | // "c": "d", 518 | // } 519 | ``` 520 | 521 | ```go 522 | c := Op("&").Id("Person").Values(Dict{ 523 | Id("Age"): Lit(1), 524 | Id("Name"): Lit("a"), 525 | }) 526 | fmt.Printf("%#v", c) 527 | // Output: 528 | // &Person{ 529 | // Age: 1, 530 | // Name: "a", 531 | // } 532 | ``` 533 | 534 | DictFunc executes a func(Dict) to generate the value. 535 | 536 | ```go 537 | c := Id("a").Op(":=").Map(String()).String().Values(DictFunc(func(d Dict) { 538 | d[Lit("a")] = Lit("b") 539 | d[Lit("c")] = Lit("d") 540 | })) 541 | fmt.Printf("%#v", c) 542 | // Output: 543 | // a := map[string]string{ 544 | // "a": "b", 545 | // "c": "d", 546 | // } 547 | ``` 548 | 549 | Note: the items are ordered by key when rendered to ensure repeatable code. 550 | 551 | # Literals 552 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) **Literals** [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 553 | 554 | ### Lit 555 | Lit renders a literal. Lit supports only built-in types (bool, string, int, complex128, float64, 556 | float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64). 557 | Passing any other type will panic. 558 | 559 | ```go 560 | c := Id("a").Op(":=").Lit("a") 561 | fmt.Printf("%#v", c) 562 | // Output: 563 | // a := "a" 564 | ``` 565 | 566 | ```go 567 | c := Id("a").Op(":=").Lit(1.5) 568 | fmt.Printf("%#v", c) 569 | // Output: 570 | // a := 1.5 571 | ``` 572 | 573 | LitFunc generates the value to render by executing the provided 574 | function. 575 | 576 | ```go 577 | c := Id("a").Op(":=").LitFunc(func() interface{} { return 1 + 1 }) 578 | fmt.Printf("%#v", c) 579 | // Output: 580 | // a := 2 581 | ``` 582 | 583 | For the default constant types (bool, int, float64, string, complex128), Lit 584 | will render the untyped constant. 585 | 586 | | Code | Output | 587 | | ------------- | ---------- | 588 | | `Lit(true)` | `true` | 589 | | `Lit(1)` | `1` | 590 | | `Lit(1.0)` | `1.0` | 591 | | `Lit("foo")` | `"foo"` | 592 | | `Lit(0 + 1i)` | `(0 + 1i)` | 593 | 594 | For all other built-in types (float32, int8, int16, int32, int64, uint, uint8, 595 | uint16, uint32, uint64, uintptr, complex64), Lit will also render the type. 596 | 597 | | Code | Output | 598 | | ------------------------ | ------------------- | 599 | | `Lit(float32(1))` | `float32(1)` | 600 | | `Lit(int16(1))` | `int16(1)` | 601 | | `Lit(uint8(0x1))` | `uint8(0x1)` | 602 | | `Lit(complex64(0 + 1i))` | `complex64(0 + 1i)` | 603 | 604 | The built-in alias types byte and rune need a special case. LitRune and LitByte 605 | render rune and byte literals. 606 | 607 | | Code | Output | 608 | | ------------------------ | ----------- | 609 | | `LitRune('x')` | `'x'` | 610 | | `LitByte(byte(0x1))` | `byte(0x1)` | 611 | 612 | # Comments 613 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) **Comments** [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 614 | 615 | ### Comment 616 | Comment adds a comment. If the provided string contains a newline, the 617 | comment is formatted in multiline style. 618 | 619 | ```go 620 | f := NewFile("a") 621 | f.Comment("Foo returns the string \"foo\"") 622 | f.Func().Id("Foo").Params().String().Block( 623 | Return(Lit("foo")).Comment("return the string foo"), 624 | ) 625 | fmt.Printf("%#v", f) 626 | // Output: 627 | // package a 628 | // 629 | // // Foo returns the string "foo" 630 | // func Foo() string { 631 | // return "foo" // return the string foo 632 | // } 633 | ``` 634 | 635 | ```go 636 | c := Comment("a\nb") 637 | fmt.Printf("%#v", c) 638 | // Output: 639 | // /* 640 | // a 641 | // b 642 | // */ 643 | ``` 644 | 645 | If the comment string starts 646 | with "//" or "/*", the automatic formatting is disabled and the string is 647 | rendered directly. 648 | 649 | ```go 650 | c := Id("foo").Call(Comment("/* inline */")).Comment("//no-space") 651 | fmt.Printf("%#v", c) 652 | // Output: 653 | // foo( /* inline */ ) //no-space 654 | ``` 655 | 656 | ### Commentf 657 | Commentf adds a comment, using a format string and a list of parameters. 658 | 659 | ```go 660 | name := "foo" 661 | val := "bar" 662 | c := Id(name).Op(":=").Lit(val).Commentf("%s is the string \"%s\"", name, val) 663 | fmt.Printf("%#v", c) 664 | // Output: 665 | // foo := "bar" // foo is the string "bar" 666 | ``` 667 | 668 | # Generics 669 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) **Generics** [Helpers](#helpers) [Misc](#misc) [File](#file) 670 | 671 | It is hoped that with the introduction of generics with Go 1.18, the need to generate code 672 | will be reduced. However, for the sake of completeness, we now support generics including 673 | the `any` and `comparable` predeclared identifiers, and the `Types` and `Union` lists. To 674 | emit the approximation (`~`) token, use `Op("~")`. 675 | 676 | ### Types 677 | 678 | Types renders a comma separated list enclosed by square brackets. Use for type parameters and constraints. 679 | 680 | ### Union 681 | 682 | Union renders a pipe separated list. Use for union type constraints. 683 | 684 | ### Examples 685 | 686 | ```go 687 | c := Func().Id("Keys").Types( 688 | Id("K").Comparable(), 689 | Id("V").Any(), 690 | ).Params( 691 | Id("m").Map(Id("K")).Id("V"), 692 | ).Index().Id("K").Block() 693 | fmt.Printf("%#v", c) 694 | // Output: 695 | // func Keys[K comparable, V any](m map[K]V) []K {} 696 | ``` 697 | ```go 698 | c := Return(Id("Keys").Types(Int(), String()).Call(Id("m"))) 699 | fmt.Printf("%#v", c) 700 | // Output: 701 | // return Keys[int, string](m) 702 | ``` 703 | ```go 704 | c := Type().Id("PredeclaredSignedInteger").Interface( 705 | Union(Int(), Int8(), Int16(), Int32(), Int64()), 706 | ) 707 | fmt.Printf("%#v", c) 708 | // Output: 709 | // type PredeclaredSignedInteger interface { 710 | // int | int8 | int16 | int32 | int64 711 | // } 712 | ``` 713 | ```go 714 | c := Type().Id("AnyString").Interface( 715 | Op("~").String(), 716 | ) 717 | fmt.Printf("%#v", c) 718 | // Output: 719 | // type AnyString interface { 720 | // ~string 721 | // } 722 | ``` 723 | 724 | # Helpers 725 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) **Helpers** [Misc](#misc) [File](#file) 726 | 727 | ### Func methods 728 | All constructs that accept a variadic list of items are paired with GroupFunc 729 | functions that accept a func(*Group). Use for embedding logic. 730 | 731 | ```go 732 | c := Id("numbers").Op(":=").Index().Int().ValuesFunc(func(g *Group) { 733 | for i := 0; i <= 5; i++ { 734 | g.Lit(i) 735 | } 736 | }) 737 | fmt.Printf("%#v", c) 738 | // Output: 739 | // numbers := []int{0, 1, 2, 3, 4, 5} 740 | ``` 741 | 742 | ```go 743 | increment := true 744 | name := "a" 745 | c := Func().Id("a").Params().BlockFunc(func(g *Group) { 746 | g.Id(name).Op("=").Lit(1) 747 | if increment { 748 | g.Id(name).Op("++") 749 | } else { 750 | g.Id(name).Op("--") 751 | } 752 | }) 753 | fmt.Printf("%#v", c) 754 | // Output: 755 | // func a() { 756 | // a = 1 757 | // a++ 758 | // } 759 | ``` 760 | 761 | ### Add 762 | Add appends the provided items to the statement. 763 | 764 | ```go 765 | ptr := Op("*") 766 | c := Id("a").Op("=").Add(ptr).Id("b") 767 | fmt.Printf("%#v", c) 768 | // Output: 769 | // a = *b 770 | ``` 771 | 772 | ```go 773 | a := Id("a") 774 | i := Int() 775 | c := Var().Add(a, i) 776 | fmt.Printf("%#v", c) 777 | // Output: 778 | // var a int 779 | ``` 780 | 781 | ### Do 782 | Do calls the provided function with the statement as a parameter. Use for 783 | embedding logic. 784 | 785 | ```go 786 | f := func(name string, isMap bool) *Statement { 787 | return Id(name).Op(":=").Do(func(s *Statement) { 788 | if isMap { 789 | s.Map(String()).String() 790 | } else { 791 | s.Index().String() 792 | } 793 | }).Values() 794 | } 795 | fmt.Printf("%#v\n%#v", f("a", true), f("b", false)) 796 | // Output: 797 | // a := map[string]string{} 798 | // b := []string{} 799 | ``` 800 | 801 | # Misc 802 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) **Misc** [File](#file) 803 | 804 | ### Tag 805 | Tag renders a struct tag 806 | 807 | ```go 808 | c := Type().Id("foo").Struct( 809 | Id("A").String().Tag(map[string]string{"json": "a"}), 810 | Id("B").Int().Tag(map[string]string{"json": "b", "bar": "baz"}), 811 | ) 812 | fmt.Printf("%#v", c) 813 | // Output: 814 | // type foo struct { 815 | // A string `json:"a"` 816 | // B int `bar:"baz" json:"b"` 817 | // } 818 | ``` 819 | 820 | Note: the items are ordered by key when rendered to ensure repeatable code. 821 | 822 | ### Null 823 | Null adds a null item. Null items render nothing and are not followed by a 824 | separator in lists. 825 | 826 | In lists, nil will produce the same effect. 827 | 828 | ```go 829 | c := Func().Id("foo").Params( 830 | nil, 831 | Id("s").String(), 832 | Null(), 833 | Id("i").Int(), 834 | ).Block() 835 | fmt.Printf("%#v", c) 836 | // Output: 837 | // func foo(s string, i int) {} 838 | ``` 839 | 840 | ### Empty 841 | Empty adds an empty item. Empty items render nothing but are followed by a 842 | separator in lists. 843 | 844 | ```go 845 | c := Id("a").Op(":=").Id("b").Index(Lit(1), Empty()) 846 | fmt.Printf("%#v", c) 847 | // Output: 848 | // a := b[1:] 849 | ``` 850 | 851 | ### Line 852 | Line inserts a blank line. 853 | 854 | ### Clone 855 | Be careful when passing *Statement. Consider the following... 856 | 857 | ```go 858 | a := Id("a") 859 | c := Block( 860 | a.Call(), 861 | a.Call(), 862 | ) 863 | fmt.Printf("%#v", c) 864 | // Output: 865 | // { 866 | // a()() 867 | // a()() 868 | // } 869 | ``` 870 | 871 | Id("a") returns a *Statement, which the Call() method appends to twice. To 872 | avoid this, use Clone. Clone makes a copy of the Statement, so further tokens can be appended 873 | without affecting the original. 874 | 875 | ```go 876 | a := Id("a") 877 | c := Block( 878 | a.Clone().Call(), 879 | a.Clone().Call(), 880 | ) 881 | fmt.Printf("%#v", c) 882 | // Output: 883 | // { 884 | // a() 885 | // a() 886 | // } 887 | ``` 888 | 889 | ### Cgo 890 | The cgo "C" pseudo-package is a special case, and always renders without a package alias. The 891 | import can be added with `Qual`, `Anon` or by supplying a preamble. The preamble is added with 892 | `File.CgoPreamble` which has the same semantics as [Comment](#comments). If a preamble is provided, 893 | the import is separated, and preceded by the preamble. 894 | 895 | ```go 896 | f := NewFile("a") 897 | f.CgoPreamble(`#include 898 | #include 899 | 900 | void myprint(char* s) { 901 | printf("%s\n", s); 902 | } 903 | `) 904 | f.Func().Id("init").Params().Block( 905 | Id("cs").Op(":=").Qual("C", "CString").Call(Lit("Hello from stdio\n")), 906 | Qual("C", "myprint").Call(Id("cs")), 907 | Qual("C", "free").Call(Qual("unsafe", "Pointer").Parens(Id("cs"))), 908 | ) 909 | fmt.Printf("%#v", f) 910 | // Output: 911 | // package a 912 | // 913 | // import "unsafe" 914 | // 915 | // /* 916 | // #include 917 | // #include 918 | // 919 | // void myprint(char* s) { 920 | // printf("%s\n", s); 921 | // } 922 | // */ 923 | // import "C" 924 | // 925 | // func init() { 926 | // cs := C.CString("Hello from stdio\n") 927 | // C.myprint(cs) 928 | // C.free(unsafe.Pointer(cs)) 929 | // } 930 | ``` 931 | 932 | # File 933 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) **File** 934 | 935 | File represents a single source file. Package imports are managed 936 | automatically by File. 937 | 938 | ### NewFile 939 | NewFile Creates a new file, with the specified package name. 940 | 941 | ### NewFilePath 942 | NewFilePath creates a new file while specifying the package path - the 943 | package name is inferred from the path. 944 | 945 | ### NewFilePathName 946 | NewFilePathName creates a new file with the specified package path and name. 947 | 948 | ```go 949 | f := NewFilePathName("a.b/c", "main") 950 | f.Func().Id("main").Params().Block( 951 | Qual("a.b/c", "Foo").Call(), 952 | ) 953 | fmt.Printf("%#v", f) 954 | // Output: 955 | // package main 956 | // 957 | // func main() { 958 | // Foo() 959 | // } 960 | ``` 961 | 962 | ### Save 963 | Save renders the file and saves to the filename provided. 964 | 965 | ### Render 966 | Render renders the file to the provided writer. 967 | 968 | ```go 969 | f := NewFile("a") 970 | f.Func().Id("main").Params().Block() 971 | buf := &bytes.Buffer{} 972 | err := f.Render(buf) 973 | if err != nil { 974 | fmt.Println(err.Error()) 975 | } else { 976 | fmt.Println(buf.String()) 977 | } 978 | // Output: 979 | // package a 980 | // 981 | // func main() {} 982 | ``` 983 | 984 | ### Anon 985 | Anon adds an anonymous import. 986 | 987 | ```go 988 | f := NewFile("c") 989 | f.Anon("a") 990 | f.Func().Id("init").Params().Block() 991 | fmt.Printf("%#v", f) 992 | // Output: 993 | // package c 994 | // 995 | // import _ "a" 996 | // 997 | // func init() {} 998 | ``` 999 | 1000 | ### ImportName 1001 | ImportName provides the package name for a path. If specified, the alias will be omitted from the 1002 | import block. This is optional. If not specified, a sensible package name is used based on the path 1003 | and this is added as an alias in the import block. 1004 | 1005 | ```go 1006 | f := NewFile("main") 1007 | 1008 | // package a should use name "a" 1009 | f.ImportName("github.com/foo/a", "a") 1010 | 1011 | // package b is not used in the code so will not be included 1012 | f.ImportName("github.com/foo/b", "b") 1013 | 1014 | f.Func().Id("main").Params().Block( 1015 | Qual("github.com/foo/a", "A").Call(), 1016 | ) 1017 | fmt.Printf("%#v", f) 1018 | 1019 | // Output: 1020 | // package main 1021 | // 1022 | // import "github.com/foo/a" 1023 | // 1024 | // func main() { 1025 | // a.A() 1026 | // } 1027 | ``` 1028 | 1029 | ### ImportNames 1030 | ImportNames allows multiple names to be imported as a map. Use the [gennames](gennames) command to 1031 | automatically generate a go file containing a map of a selection of package names. 1032 | 1033 | ### ImportAlias 1034 | ImportAlias provides the alias for a package path that should be used in the import block. A 1035 | period can be used to force a dot-import. 1036 | 1037 | ```go 1038 | f := NewFile("main") 1039 | 1040 | // package a should be aliased to "b" 1041 | f.ImportAlias("github.com/foo/a", "b") 1042 | 1043 | // package c is not used in the code so will not be included 1044 | f.ImportAlias("github.com/foo/c", "c") 1045 | 1046 | f.Func().Id("main").Params().Block( 1047 | Qual("github.com/foo/a", "A").Call(), 1048 | ) 1049 | fmt.Printf("%#v", f) 1050 | 1051 | // Output: 1052 | // package main 1053 | // 1054 | // import b "github.com/foo/a" 1055 | // 1056 | // func main() { 1057 | // b.A() 1058 | // } 1059 | ``` 1060 | 1061 | ### Comments 1062 | PackageComment adds a comment to the top of the file, above the package 1063 | keyword. 1064 | 1065 | HeaderComment adds a comment to the top of the file, above any package 1066 | comments. A blank line is rendered below the header comments, ensuring 1067 | header comments are not included in the package doc. 1068 | 1069 | CanonicalPath adds a canonical import path annotation to the package clause. 1070 | 1071 | ```go 1072 | f := NewFile("c") 1073 | f.CanonicalPath = "d.e/f" 1074 | f.HeaderComment("Code generated by...") 1075 | f.PackageComment("Package c implements...") 1076 | f.Func().Id("init").Params().Block() 1077 | fmt.Printf("%#v", f) 1078 | // Output: 1079 | // // Code generated by... 1080 | // 1081 | // // Package c implements... 1082 | // package c // import "d.e/f" 1083 | // 1084 | // func init() {} 1085 | ``` 1086 | 1087 | CgoPreamble adds a cgo preamble comment that is rendered directly before the "C" pseudo-package 1088 | import. 1089 | 1090 | ### PackagePrefix 1091 | If you're worried about generated package aliases conflicting with local variable names, you 1092 | can set a prefix here. Package foo becomes {prefix}_foo. 1093 | 1094 | ```go 1095 | f := NewFile("a") 1096 | f.PackagePrefix = "pkg" 1097 | f.Func().Id("main").Params().Block( 1098 | Qual("b.c/d", "E").Call(), 1099 | ) 1100 | fmt.Printf("%#v", f) 1101 | // Output: 1102 | // package a 1103 | // 1104 | // import pkg_d "b.c/d" 1105 | // 1106 | // func main() { 1107 | // pkg_d.E() 1108 | // } 1109 | ``` 1110 | 1111 | ### NoFormat 1112 | NoFormat can be set to true to disable formatting of the generated source. This may be useful 1113 | when performance is critical, and readable code is not required. -------------------------------------------------------------------------------- /README.md.tpl: -------------------------------------------------------------------------------- 1 | [![docs](https://pkg.go.dev/badge/github.com/dave/jennifer/jen.svg)](https://pkg.go.dev/github.com/dave/jennifer/jen) 2 | ![stability-stable](https://img.shields.io/badge/stability-stable-brightgreen.svg) 3 | 4 | # Jennifer 5 | Jennifer is a code generator for Go. 6 | 7 | ```go 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | 13 | . "github.com/dave/jennifer/jen" 14 | ) 15 | 16 | func main() {{ "ExampleNewFile" | code }} 17 | ``` 18 | Output: 19 | ```go 20 | {{ "ExampleNewFile" | output }} 21 | ``` 22 | 23 | ### Install 24 | ``` 25 | go get -u github.com/dave/jennifer/jen 26 | ``` 27 | 28 | ### Need help? 29 | If you get stuck, have a question, would like a code review, or just want a 30 | chat: I'm happy to help! Feel free to open an issue, email me or mention @dave 31 | in your PR. 32 | 33 | ### Examples 34 | Jennifer has a comprehensive suite of examples - see [godoc](https://godoc.org/github.com/dave/jennifer/jen#pkg-examples) for an index. Here's some examples of jennifer being used in the real-world: 35 | 36 | * [genjen](genjen/render.go) (which generates much of jennifer, using data in [data.go](genjen/data.go)) 37 | * [zerogen](https://github.com/mrsinham/zerogen/blob/master/generator.go) 38 | * [go-contentful-generator](https://github.com/nicolai86/go-contentful-generator) 39 | 40 | ### Rendering 41 | For testing, a File or Statement can be rendered with the fmt package 42 | using the %#v verb. 43 | 44 | {{ "ExampleCall_fmt" | example }} 45 | 46 | This is not recommended for use in production because any error will cause a 47 | panic. For production use, [File.Render](#render) or [File.Save](#save) are 48 | preferred. 49 | 50 | # Identifiers 51 | **Identifiers** [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 52 | 53 | ### Id 54 | {{ "Id" | doc }} 55 | 56 | {{ "ExampleId" | example }} 57 | 58 | ### Dot 59 | {{ "Dot" | doc }} 60 | 61 | {{ "ExampleDot" | example }} 62 | 63 | ### Qual 64 | {{ "Qual[0]" | doc }} 65 | 66 | {{ "ExampleQual" | example }} 67 | 68 | {{ "Qual[1:4]" | doc }} 69 | 70 | {{ "ExampleQual_file" | example }} 71 | 72 | {{ "Qual[4:]" | doc }} 73 | 74 | ### List 75 | {{ "List" | doc }} 76 | 77 | {{ "ExampleList" | example }} 78 | 79 | # Keywords 80 | [Identifiers](#identifiers) **Keywords** [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 81 | 82 | Simple keywords, predeclared identifiers and built-in functions are self 83 | explanatory: 84 | 85 | | Construct | Name | 86 | | ---------------- | ---- | 87 | | Keywords | Break, Chan, Const, Continue, Default, Defer, Else, Fallthrough, Func, Go, Goto, Range, Select, Type, Var | 88 | | Functions | Append, Cap, Clear, Close, Complex, Copy, Delete, Imag, Len, Make, Max, Min, New, Panic, Print, Println, Real, Recover | 89 | | Types | Bool, Byte, Complex64, Complex128, Error, Float32, Float64, Int, Int8, Int16, Int32, Int64, Rune, String, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr | 90 | | Constants | True, False, Iota, Nil | 91 | | Helpers | Err | 92 | 93 | Built-in functions take a list of parameters and render them appropriately: 94 | 95 | {{ "ExampleAppend_more" | example }} 96 | 97 | Special cases for [If, For](#if-for), [Interface, Struct](#interface-struct), [Switch, Case](#switch-select), [Return](#return) and [Map](#map) are explained below. 98 | 99 | # Operators 100 | [Identifiers](#identifiers) [Keywords](#keywords) **Operators** [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 101 | 102 | {{ "Op" | doc }} 103 | 104 | {{ "ExampleOp" | example }} 105 | 106 | {{ "ExampleOp_star" | example }} 107 | 108 | {{ "ExampleOp_variadic" | example }} 109 | 110 | {{ "ExampleOp_complex_conditions" | example }} 111 | 112 | # Braces 113 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) **Braces** [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 114 | 115 | Several methods render curly braces, summarized below: 116 | 117 | | Name | Prefix | Separator | Example | 118 | | ------------------------------ | ------------ | --------- | -------------------------------------| 119 | | [Block](#block) | | `\n` | `func a() { ... }` or `if a { ... }` | 120 | | [Interface](#interface-struct) | `interface` | `\n` | `interface { ... }` | 121 | | [Struct](#interface-struct) | `struct` | `\n` | `struct { ... }` | 122 | | [Values](#values) | | `,` | `[]int{1, 2}` or `A{B: "c"}` | 123 | 124 | ### Block 125 | {{ "Block[:2]" | doc }} 126 | 127 | {{ "ExampleBlock" | example }} 128 | 129 | {{ "ExampleBlock_if" | example }} 130 | 131 | {{ "Block[2:]" | doc }} [See example](#switch-select). 132 | 133 | ### Interface, Struct 134 | Interface and Struct render the keyword followed by a statement list enclosed 135 | by curly braces. 136 | 137 | {{ "ExampleInterface_empty" | example }} 138 | 139 | {{ "ExampleInterface" | example }} 140 | 141 | {{ "ExampleStruct_empty" | example }} 142 | 143 | {{ "ExampleStruct" | example }} 144 | 145 | # Parentheses 146 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) **Parentheses** [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 147 | 148 | Several methods output parenthesis, summarized below: 149 | 150 | | Name | Prefix | Separator | Example | 151 | | ----------------- | ------ | --------- | --------------------------------- | 152 | | [Call](#call) | | `,` | `fmt.Println(b, c)` | 153 | | [Params](#params) | | `,` | `func (a *A) Foo(i int) { ... }` | 154 | | [Defs](#defs) | | `\n` | `const ( ... )` | 155 | | [Parens](#parens) | | | `[]byte(s)` or `a / (b + c)` | 156 | | [Assert](#assert) | `.` | | `s, ok := i.(string)` | 157 | 158 | ### Call 159 | {{ "Call" | doc }} 160 | 161 | {{ "ExampleCall" | example }} 162 | 163 | ### Params 164 | {{ "Params" | doc }} 165 | 166 | {{ "ExampleParams" | example }} 167 | 168 | ### Defs 169 | {{ "Defs" | doc }} 170 | 171 | {{ "ExampleDefs" | example }} 172 | 173 | ### Parens 174 | {{ "Parens" | doc }} 175 | 176 | {{ "ExampleParens" | example }} 177 | 178 | {{ "ExampleParens_order" | example }} 179 | 180 | ### Assert 181 | {{ "Assert" | doc }} 182 | 183 | {{ "ExampleAssert" | example }} 184 | 185 | # Control flow 186 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) **Control flow** [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 187 | 188 | ### If, For 189 | If and For render the keyword followed by a semicolon separated list. 190 | 191 | {{ "ExampleIf" | example }} 192 | 193 | {{ "ExampleFor" | example }} 194 | 195 | ### Switch, Select 196 | Switch, Select, Case and Block are used to build switch or select statements: 197 | 198 | {{ "ExampleSwitch" | example }} 199 | 200 | ### Return 201 | {{ "Return" | doc }} 202 | 203 | {{ "ExampleReturn" | example }} 204 | 205 | # Collections 206 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) **Collections** [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 207 | 208 | ### Map 209 | {{ "Map" | doc }} 210 | 211 | {{ "ExampleMap" | example }} 212 | 213 | ### Index 214 | {{ "Index" | doc }} 215 | 216 | {{ "ExampleIndex" | example }} 217 | 218 | {{ "ExampleIndex_index" | example }} 219 | 220 | {{ "ExampleIndex_empty" | example }} 221 | 222 | ### Values 223 | {{ "Values" | doc }} 224 | 225 | {{ "ExampleValues" | example }} 226 | 227 | {{ "Dict" | doc }} 228 | 229 | {{ "ExampleValues_dict_multiple" | example }} 230 | 231 | {{ "ExampleValues_dict_composite" | example }} 232 | 233 | {{ "DictFunc[0]" | doc }} 234 | 235 | {{ "ExampleDictFunc" | example }} 236 | 237 | Note: the items are ordered by key when rendered to ensure repeatable code. 238 | 239 | # Literals 240 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) **Literals** [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 241 | 242 | ### Lit 243 | {{ "Lit" | doc }} 244 | 245 | {{ "ExampleLit" | example }} 246 | 247 | {{ "ExampleLit_float" | example }} 248 | 249 | {{ "LitFunc[1:2]" | doc }} 250 | 251 | {{ "ExampleLitFunc" | example }} 252 | 253 | For the default constant types (bool, int, float64, string, complex128), Lit 254 | will render the untyped constant. 255 | 256 | | Code | Output | 257 | | ------------- | ---------- | 258 | | `Lit(true)` | `true` | 259 | | `Lit(1)` | `1` | 260 | | `Lit(1.0)` | `1.0` | 261 | | `Lit("foo")` | `"foo"` | 262 | | `Lit(0 + 1i)` | `(0 + 1i)` | 263 | 264 | For all other built-in types (float32, int8, int16, int32, int64, uint, uint8, 265 | uint16, uint32, uint64, uintptr, complex64), Lit will also render the type. 266 | 267 | | Code | Output | 268 | | ------------------------ | ------------------- | 269 | | `Lit(float32(1))` | `float32(1)` | 270 | | `Lit(int16(1))` | `int16(1)` | 271 | | `Lit(uint8(0x1))` | `uint8(0x1)` | 272 | | `Lit(complex64(0 + 1i))` | `complex64(0 + 1i)` | 273 | 274 | The built-in alias types byte and rune need a special case. LitRune and LitByte 275 | render rune and byte literals. 276 | 277 | | Code | Output | 278 | | ------------------------ | ----------- | 279 | | `LitRune('x')` | `'x'` | 280 | | `LitByte(byte(0x1))` | `byte(0x1)` | 281 | 282 | # Comments 283 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) **Comments** [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) 284 | 285 | ### Comment 286 | {{ "Comment[:2]" | doc }} 287 | 288 | {{ "ExampleComment" | example }} 289 | 290 | {{ "ExampleComment_multiline" | example }} 291 | 292 | {{ "Comment[2:]" | doc }} 293 | 294 | {{ "ExampleComment_formatting_disabled" | example }} 295 | 296 | ### Commentf 297 | {{ "Commentf[0]" | doc }} 298 | 299 | {{ "ExampleCommentf" | example }} 300 | 301 | # Generics 302 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) **Generics** [Helpers](#helpers) [Misc](#misc) [File](#file) 303 | 304 | It is hoped that with the introduction of generics with Go 1.18, the need to generate code 305 | will be reduced. However, for the sake of completeness, we now support generics including 306 | the `any` and `comparable` predeclared identifiers, and the `Types` and `Union` lists. To 307 | emit the approximation (`~`) token, use `Op("~")`. 308 | 309 | ### Types 310 | 311 | {{ "Types" | doc }} 312 | 313 | ### Union 314 | 315 | {{ "Union" | doc }} 316 | 317 | ### Examples 318 | 319 | {{ "ExampleGenericsTypesDefinition" | example }} 320 | {{ "ExampleGenericsTypesUsage" | example }} 321 | {{ "ExampleGenericsUnion" | example }} 322 | {{ "ExampleGenericsApproximate" | example }} 323 | 324 | # Helpers 325 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) **Helpers** [Misc](#misc) [File](#file) 326 | 327 | ### Func methods 328 | All constructs that accept a variadic list of items are paired with GroupFunc 329 | functions that accept a func(*Group). Use for embedding logic. 330 | 331 | {{ "ExampleValuesFunc" | example }} 332 | 333 | {{ "ExampleBlockFunc" | example }} 334 | 335 | ### Add 336 | {{ "Add" | doc }} 337 | 338 | {{ "ExampleAdd" | example }} 339 | 340 | {{ "ExampleAdd_var" | example }} 341 | 342 | ### Do 343 | {{ "Do" | doc }} 344 | 345 | {{ "ExampleDo" | example }} 346 | 347 | # Misc 348 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) **Misc** [File](#file) 349 | 350 | ### Tag 351 | {{ "Tag" | doc }} 352 | 353 | {{ "ExampleTag" | example }} 354 | 355 | Note: the items are ordered by key when rendered to ensure repeatable code. 356 | 357 | ### Null 358 | {{ "Null" | doc }} 359 | 360 | In lists, nil will produce the same effect. 361 | 362 | {{ "ExampleNull_and_nil" | example }} 363 | 364 | ### Empty 365 | {{ "Empty" | doc }} 366 | 367 | {{ "ExampleEmpty" | example }} 368 | 369 | ### Line 370 | {{ "Line" | doc }} 371 | 372 | ### Clone 373 | Be careful when passing *Statement. Consider the following... 374 | 375 | {{ "ExampleStatement_Clone_broken" | example }} 376 | 377 | Id("a") returns a *Statement, which the Call() method appends to twice. To 378 | avoid this, use Clone. {{ "Statement.Clone" | doc }} 379 | 380 | {{ "ExampleStatement_Clone_fixed" | example }} 381 | 382 | ### Cgo 383 | The cgo "C" pseudo-package is a special case, and always renders without a package alias. The 384 | import can be added with `Qual`, `Anon` or by supplying a preamble. The preamble is added with 385 | `File.CgoPreamble` which has the same semantics as [Comment](#comments). If a preamble is provided, 386 | the import is separated, and preceded by the preamble. 387 | 388 | {{ "ExampleFile_CgoPreamble" | example }} 389 | 390 | # File 391 | [Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) **File** 392 | 393 | {{ "File" | doc }} 394 | 395 | ### NewFile 396 | {{ "NewFile" | doc }} 397 | 398 | ### NewFilePath 399 | {{ "NewFilePath" | doc }} 400 | 401 | ### NewFilePathName 402 | {{ "NewFilePathName" | doc }} 403 | 404 | {{ "ExampleNewFilePathName" | example }} 405 | 406 | ### Save 407 | {{ "File.Save" | doc }} 408 | 409 | ### Render 410 | {{ "File.Render" | doc }} 411 | 412 | {{ "ExampleFile_Render" | example }} 413 | 414 | ### Anon 415 | {{ "File.Anon" | doc }} 416 | 417 | {{ "ExampleFile_Anon" | example }} 418 | 419 | ### ImportName 420 | {{ "File.ImportName" | doc }} 421 | 422 | {{ "ExampleFile_ImportName" | example }} 423 | 424 | ### ImportNames 425 | {{ "File.ImportNames" | doc }} 426 | 427 | ### ImportAlias 428 | {{ "File.ImportAlias" | doc }} 429 | 430 | {{ "ExampleFile_ImportAlias" | example }} 431 | 432 | ### Comments 433 | {{ "File.PackageComment" | doc }} 434 | 435 | {{ "File.HeaderComment" | doc }} 436 | 437 | {{ "File.CanonicalPath" | doc }} 438 | 439 | {{ "ExampleFile_HeaderAndPackageComments" | example }} 440 | 441 | {{ "File.CgoPreamble" | doc }} 442 | 443 | ### PackagePrefix 444 | {{ "File.PackagePrefix" | doc }} 445 | 446 | {{ "ExampleFile_PackagePrefix" | example }} 447 | 448 | ### NoFormat 449 | {{ "File.NoFormat" | doc }} -------------------------------------------------------------------------------- /genjen/data.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var keywords = []string{"break", "default", "func", "select", "chan", "else", "const", "fallthrough", "type", "continue", "var", "goto", "defer", "go", "range"} 4 | 5 | var identifiers = []string{"bool", "byte", "complex64", "complex128", "error", "float32", "float64", "int", "int8", "int16", "int32", "int64", "rune", "string", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", "true", "false", "iota", "nil", "err", "any", "comparable"} 6 | 7 | var groups = []struct { 8 | name string // name of the function / method 9 | comment string // comment appended to name 10 | variadic bool // is the parameter variadic? 11 | opening string // opening token 12 | closing string // closing token 13 | separator string // separator token 14 | multi bool // items are always on multiple lines 15 | parameters []string // parameter names 16 | preventFunc bool // prevent the fooFunc function/method 17 | }{ 18 | { 19 | name: "Parens", 20 | comment: "renders a single item in parenthesis. Use for type conversion or to specify evaluation order.", 21 | variadic: false, 22 | opening: "(", 23 | closing: ")", 24 | separator: "", 25 | parameters: []string{"item"}, 26 | }, 27 | { 28 | name: "List", 29 | comment: "renders a comma separated list. Use for multiple return functions.", 30 | variadic: true, 31 | opening: "", 32 | closing: "", 33 | separator: ",", 34 | parameters: []string{"items"}, 35 | }, 36 | { 37 | name: "Values", 38 | comment: "renders a comma separated list enclosed by curly braces. Use for slice or composite literals.", 39 | variadic: true, 40 | opening: "{", 41 | closing: "}", 42 | separator: ",", 43 | parameters: []string{"values"}, 44 | }, 45 | { 46 | name: "Index", 47 | comment: "renders a colon separated list enclosed by square brackets. Use for array / slice indexes and definitions.", 48 | variadic: true, 49 | opening: "[", 50 | closing: "]", 51 | separator: ":", 52 | parameters: []string{"items"}, 53 | }, 54 | { 55 | name: "Block", 56 | comment: "renders a statement list enclosed by curly braces. Use for code blocks. A special case applies when used directly after Case or Default, where the braces are omitted. This allows use in switch and select statements.", 57 | variadic: true, 58 | opening: "{", 59 | closing: "}", 60 | multi: true, 61 | parameters: []string{"statements"}, 62 | }, 63 | { 64 | name: "Defs", 65 | comment: "renders a statement list enclosed in parenthesis. Use for definition lists.", 66 | variadic: true, 67 | opening: "(", 68 | closing: ")", 69 | multi: true, 70 | parameters: []string{"definitions"}, 71 | }, 72 | { 73 | name: "Call", 74 | comment: "renders a comma separated list enclosed by parenthesis. Use for function calls.", 75 | variadic: true, 76 | opening: "(", 77 | closing: ")", 78 | separator: ",", 79 | parameters: []string{"params"}, 80 | }, 81 | { 82 | name: "Params", 83 | comment: "renders a comma separated list enclosed by parenthesis. Use for function parameters and method receivers.", 84 | variadic: true, 85 | opening: "(", 86 | closing: ")", 87 | separator: ",", 88 | parameters: []string{"params"}, 89 | }, 90 | { 91 | name: "Assert", 92 | comment: "renders a period followed by a single item enclosed by parenthesis. Use for type assertions.", 93 | variadic: false, 94 | opening: ".(", 95 | closing: ")", 96 | separator: "", 97 | parameters: []string{"typ"}, 98 | }, 99 | { 100 | name: "Map", 101 | comment: "renders the keyword followed by a single item enclosed by square brackets. Use for map definitions.", 102 | variadic: false, 103 | opening: "map[", 104 | closing: "]", 105 | separator: "", 106 | parameters: []string{"typ"}, 107 | }, 108 | { 109 | name: "If", 110 | comment: "renders the keyword followed by a semicolon separated list.", 111 | variadic: true, 112 | opening: "if ", 113 | closing: "", 114 | separator: ";", 115 | parameters: []string{"conditions"}, 116 | }, 117 | { 118 | name: "Return", 119 | comment: "renders the keyword followed by a comma separated list.", 120 | variadic: true, 121 | opening: "return ", 122 | closing: "", 123 | separator: ",", 124 | parameters: []string{"results"}, 125 | }, 126 | { 127 | name: "For", 128 | comment: "renders the keyword followed by a semicolon separated list.", 129 | variadic: true, 130 | opening: "for ", 131 | closing: "", 132 | separator: ";", 133 | parameters: []string{"conditions"}, 134 | }, 135 | { 136 | name: "Switch", 137 | comment: "renders the keyword followed by a semicolon separated list.", 138 | variadic: true, 139 | opening: "switch ", 140 | closing: "", 141 | separator: ";", 142 | parameters: []string{"conditions"}, 143 | }, 144 | { 145 | name: "Interface", 146 | comment: "renders the keyword followed by a method list enclosed by curly braces.", 147 | variadic: true, 148 | opening: "interface{", 149 | closing: "}", 150 | multi: true, 151 | parameters: []string{"methods"}, 152 | }, 153 | { 154 | name: "Struct", 155 | comment: "renders the keyword followed by a field list enclosed by curly braces.", 156 | variadic: true, 157 | opening: "struct{", 158 | closing: "}", 159 | multi: true, 160 | parameters: []string{"fields"}, 161 | }, 162 | { 163 | name: "Case", 164 | comment: "renders the keyword followed by a comma separated list.", 165 | variadic: true, 166 | opening: "case ", 167 | closing: ":", 168 | separator: ",", 169 | parameters: []string{"cases"}, 170 | }, 171 | { 172 | name: "Append", 173 | comment: "renders the append built-in function.", 174 | variadic: true, 175 | opening: "append(", 176 | closing: ")", 177 | separator: ",", 178 | parameters: []string{"args"}, 179 | }, 180 | { 181 | name: "Cap", 182 | comment: "renders the cap built-in function.", 183 | variadic: false, 184 | opening: "cap(", 185 | closing: ")", 186 | separator: ",", 187 | parameters: []string{"v"}, 188 | }, 189 | { 190 | name: "Close", 191 | comment: "renders the close built-in function.", 192 | variadic: false, 193 | opening: "close(", 194 | closing: ")", 195 | separator: ",", 196 | parameters: []string{"c"}, 197 | }, 198 | { 199 | name: "Clear", 200 | comment: "renders the clear built-in function.", 201 | variadic: false, 202 | opening: "clear(", 203 | closing: ")", 204 | separator: ",", 205 | parameters: []string{"c"}, 206 | }, 207 | { 208 | name: "Min", 209 | comment: "renders the min built-in function.", 210 | variadic: true, 211 | opening: "min(", 212 | closing: ")", 213 | separator: ",", 214 | parameters: []string{"args"}, 215 | }, 216 | { 217 | name: "Max", 218 | comment: "renders the max built-in function.", 219 | variadic: true, 220 | opening: "max(", 221 | closing: ")", 222 | separator: ",", 223 | parameters: []string{"args"}, 224 | }, 225 | { 226 | name: "Complex", 227 | comment: "renders the complex built-in function.", 228 | variadic: false, 229 | opening: "complex(", 230 | closing: ")", 231 | separator: ",", 232 | parameters: []string{"r", "i"}, 233 | }, 234 | { 235 | name: "Copy", 236 | comment: "renders the copy built-in function.", 237 | variadic: false, 238 | opening: "copy(", 239 | closing: ")", 240 | separator: ",", 241 | parameters: []string{"dst", "src"}, 242 | }, 243 | { 244 | name: "Delete", 245 | comment: "renders the delete built-in function.", 246 | variadic: false, 247 | opening: "delete(", 248 | closing: ")", 249 | separator: ",", 250 | parameters: []string{"m", "key"}, 251 | }, 252 | { 253 | name: "Imag", 254 | comment: "renders the imag built-in function.", 255 | variadic: false, 256 | opening: "imag(", 257 | closing: ")", 258 | separator: ",", 259 | parameters: []string{"c"}, 260 | }, 261 | { 262 | name: "Len", 263 | comment: "renders the len built-in function.", 264 | variadic: false, 265 | opening: "len(", 266 | closing: ")", 267 | separator: ",", 268 | parameters: []string{"v"}, 269 | }, 270 | { 271 | name: "Make", 272 | comment: "renders the make built-in function. The final parameter of the make function is optional, so it is represented by a variadic parameter list.", 273 | variadic: true, 274 | opening: "make(", 275 | closing: ")", 276 | separator: ",", 277 | parameters: []string{"args"}, 278 | preventFunc: true, // the underlying function is not variadic, so we prevent the MakeFunc variation 279 | }, 280 | { 281 | name: "New", 282 | comment: "renders the new built-in function.", 283 | variadic: false, 284 | opening: "new(", 285 | closing: ")", 286 | separator: ",", 287 | parameters: []string{"typ"}, 288 | }, 289 | { 290 | name: "Panic", 291 | comment: "renders the panic built-in function.", 292 | variadic: false, 293 | opening: "panic(", 294 | closing: ")", 295 | separator: ",", 296 | parameters: []string{"v"}, 297 | }, 298 | { 299 | name: "Print", 300 | comment: "renders the print built-in function.", 301 | variadic: true, 302 | opening: "print(", 303 | closing: ")", 304 | separator: ",", 305 | parameters: []string{"args"}, 306 | }, 307 | { 308 | name: "Println", 309 | comment: "renders the println built-in function.", 310 | variadic: true, 311 | opening: "println(", 312 | closing: ")", 313 | separator: ",", 314 | parameters: []string{"args"}, 315 | }, 316 | { 317 | name: "Real", 318 | comment: "renders the real built-in function.", 319 | variadic: false, 320 | opening: "real(", 321 | closing: ")", 322 | separator: ",", 323 | parameters: []string{"c"}, 324 | }, 325 | { 326 | name: "Recover", 327 | comment: "renders the recover built-in function.", 328 | variadic: false, 329 | opening: "recover(", 330 | closing: ")", 331 | separator: ",", 332 | parameters: []string{}, 333 | }, 334 | { 335 | name: "Types", 336 | comment: "renders a comma separated list enclosed by square brackets. Use for type parameters and constraints.", 337 | variadic: true, 338 | opening: "[", 339 | closing: "]", 340 | separator: ",", 341 | parameters: []string{"types"}, 342 | }, 343 | { 344 | name: "Union", 345 | comment: "renders a pipe separated list. Use for union type constraints.", 346 | variadic: true, 347 | opening: "", 348 | closing: "", 349 | separator: "|", 350 | parameters: []string{"types"}, 351 | }, 352 | } 353 | -------------------------------------------------------------------------------- /genjen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | // notest 10 | buf := &bytes.Buffer{} 11 | if err := render(buf); err != nil { 12 | panic(err) 13 | } 14 | if err := os.WriteFile("./jen/generated.go", buf.Bytes(), 0644); err != nil { 15 | panic(err) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /genjen/render.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | 7 | . "github.com/dave/jennifer/jen" 8 | ) 9 | 10 | func render(w io.Writer) error { 11 | file := NewFile("jen") 12 | 13 | file.HeaderComment("This file is generated - do not edit.") 14 | file.Line() 15 | 16 | for _, b := range groups { 17 | b := b // b used in closures 18 | comment := Commentf("%s %s", b.name, b.comment) 19 | 20 | if b.variadic && len(b.parameters) > 1 { 21 | panic("should not have variadic function with multiple params") 22 | } 23 | 24 | var variadic Code 25 | if b.variadic { 26 | variadic = Op("...") 27 | } 28 | var funcParams []Code 29 | var callParams []Code 30 | for _, name := range b.parameters { 31 | funcParams = append(funcParams, Id(name).Add(variadic).Id("Code")) 32 | callParams = append(callParams, Id(name).Add(variadic)) 33 | } 34 | 35 | addFunctionAndGroupMethod( 36 | file, 37 | b.name, 38 | comment, 39 | funcParams, 40 | callParams, 41 | false, 42 | ) 43 | 44 | /* 45 | // 46 | func (s *Statement) () *Statement { 47 | g := &Group{ 48 | items: []Code{}|, 49 | name: "", 50 | open: "", 51 | close: "", 52 | separator: "", 53 | multi: , 54 | } 55 | *s = append(*s, g) 56 | return s 57 | } 58 | */ 59 | file.Add(comment) 60 | file.Func().Params( 61 | Id("s").Op("*").Id("Statement"), 62 | ).Id(b.name).Params( 63 | funcParams..., 64 | ).Op("*").Id("Statement").Block( 65 | Id("g").Op(":=").Op("&").Id("Group").Values(Dict{ 66 | Id("items"): Do(func(s *Statement) { 67 | if b.variadic { 68 | s.Id(b.parameters[0]) 69 | } else { 70 | s.Index().Id("Code").ValuesFunc(func(g *Group) { 71 | for _, name := range b.parameters { 72 | g.Id(name) 73 | } 74 | }) 75 | } 76 | }), 77 | Id("name"): Lit(strings.ToLower(b.name)), 78 | Id("open"): Lit(b.opening), 79 | Id("close"): Lit(b.closing), 80 | Id("separator"): Lit(b.separator), 81 | Id("multi"): Lit(b.multi), 82 | }), 83 | Op("*").Id("s").Op("=").Append(Op("*").Id("s"), Id("g")), 84 | Return(Id("s")), 85 | ) 86 | 87 | if b.variadic && !b.preventFunc { 88 | 89 | funcName := b.name + "Func" 90 | funcComment := Commentf("%sFunc %s", b.name, b.comment) 91 | funcFuncParams := []Code{Id("f").Func().Params(Op("*").Id("Group"))} 92 | funcCallParams := []Code{Id("f")} 93 | 94 | addFunctionAndGroupMethod( 95 | file, 96 | funcName, 97 | funcComment, 98 | funcFuncParams, 99 | funcCallParams, 100 | false, 101 | ) 102 | 103 | /* 104 | // 105 | func (s *Statement) (f func(*Group)) *Statement { 106 | g := &Group{ 107 | name: "", 108 | open: "", 109 | close: "", 110 | separator: "", 111 | multi: , 112 | } 113 | f(g) 114 | *s = append(*s, g) 115 | return s 116 | } 117 | */ 118 | file.Add(funcComment) 119 | file.Func().Params( 120 | Id("s").Op("*").Id("Statement"), 121 | ).Id(funcName).Params( 122 | funcFuncParams..., 123 | ).Op("*").Id("Statement").Block( 124 | Id("g").Op(":=").Op("&").Id("Group").Values(Dict{ 125 | Id("name"): Lit(strings.ToLower(b.name)), 126 | Id("open"): Lit(b.opening), 127 | Id("close"): Lit(b.closing), 128 | Id("separator"): Lit(b.separator), 129 | Id("multi"): Lit(b.multi), 130 | }), 131 | Id("f").Call(Id("g")), 132 | Op("*").Id("s").Op("=").Append(Op("*").Id("s"), Id("g")), 133 | Return(Id("s")), 134 | ) 135 | } 136 | } 137 | 138 | type tkn struct { 139 | token string 140 | name string 141 | tokenType string 142 | tokenDesc string 143 | } 144 | tokens := []tkn{} 145 | for _, v := range identifiers { 146 | tokens = append(tokens, tkn{ 147 | token: v, 148 | name: strings.ToUpper(v[:1]) + v[1:], 149 | tokenType: "identifierToken", 150 | tokenDesc: "identifier", 151 | }) 152 | } 153 | for _, v := range keywords { 154 | tokens = append(tokens, tkn{ 155 | token: v, 156 | name: strings.ToUpper(v[:1]) + v[1:], 157 | tokenType: "keywordToken", 158 | tokenDesc: "keyword", 159 | }) 160 | } 161 | 162 | for i, t := range tokens { 163 | t := t // used in closures 164 | comment := Commentf( 165 | "%s renders the %s %s.", 166 | t.name, 167 | t.token, 168 | t.tokenDesc, 169 | ) 170 | addFunctionAndGroupMethod( 171 | file, 172 | t.name, 173 | comment, 174 | nil, 175 | nil, 176 | i != 0, // only enforce test coverage on one item 177 | ) 178 | 179 | /* 180 | // 181 | func (s *Statement) () *Statement { 182 | t := token{ 183 | typ: , 184 | content: "", 185 | } 186 | *s = append(*s, t) 187 | return s 188 | } 189 | */ 190 | file.Add(comment) 191 | file.Func().Params( 192 | Id("s").Op("*").Id("Statement"), 193 | ).Id(t.name).Params().Op("*").Id("Statement").Block( 194 | Do(func(s *Statement) { 195 | if i != 0 { 196 | // only enforce test coverage on one item 197 | s.Comment("notest") 198 | } 199 | }), 200 | Id("t").Op(":=").Id("token").Values(Dict{ 201 | Id("typ"): Id(t.tokenType), 202 | Id("content"): Lit(t.token), 203 | }), 204 | Op("*").Id("s").Op("=").Append(Op("*").Id("s"), Id("t")), 205 | Return(Id("s")), 206 | ) 207 | } 208 | 209 | return file.Render(w) 210 | } 211 | 212 | // For each method on *Statement, this generates a package level 213 | // function and a method on *Group, both with the same name. 214 | func addFunctionAndGroupMethod( 215 | file *File, 216 | name string, 217 | comment *Statement, 218 | funcParams []Code, 219 | callParams []Code, 220 | notest bool, 221 | ) { 222 | /* 223 | // 224 | func () *Statement { 225 | return newStatement().() 226 | } 227 | */ 228 | file.Add(comment) 229 | file.Func().Id(name).Params(funcParams...).Op("*").Id("Statement").Block( 230 | Do(func(s *Statement) { 231 | if notest { 232 | // only enforce test coverage on one item 233 | s.Comment("notest") 234 | } 235 | }), 236 | Return(Id("newStatement").Call().Dot(name).Call(callParams...)), 237 | ) 238 | /* 239 | // 240 | func (g *Group) () *Statement { 241 | s := () 242 | g.items = append(g.items, s) 243 | return s 244 | } 245 | */ 246 | file.Add(comment) 247 | file.Func().Params( 248 | Id("g").Op("*").Id("Group"), 249 | ).Id(name).Params(funcParams...).Op("*").Id("Statement").Block( 250 | Do(func(s *Statement) { 251 | if notest { 252 | // only enforce test coverage on one item 253 | s.Comment("notest") 254 | } 255 | }), 256 | Id("s").Op(":=").Id(name).Params(callParams...), 257 | Id("g").Dot("items").Op("=").Append(Id("g").Dot("items"), Id("s")), 258 | Return(Id("s")), 259 | ) 260 | } 261 | -------------------------------------------------------------------------------- /genjen/render_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "regexp" 6 | "testing" 7 | 8 | "bytes" 9 | ) 10 | 11 | func TestRender(t *testing.T) { 12 | 13 | buf := &bytes.Buffer{} 14 | if err := render(buf); err != nil { 15 | t.Fatal(err.Error()) 16 | } 17 | generatedString := buf.String() 18 | 19 | existingFilePath := "../jen/generated.go" 20 | existingBytes, err := os.ReadFile(existingFilePath) 21 | if err != nil { 22 | t.Fatal(err.Error()) 23 | } 24 | existingString := string(existingBytes) 25 | 26 | // The "goimports" tool will often re-order the imports, so this is a 27 | // kludge to remove it before comparing. This is not ideal! 28 | importsRegex := regexp.MustCompile(`(?ms:\nimport \(\n.*\n\)\n)`) 29 | generatedString = importsRegex.ReplaceAllString(generatedString, "-") 30 | existingString = importsRegex.ReplaceAllString(existingString, "-") 31 | 32 | if generatedString != existingString { 33 | t.Fatalf("Generated code is not what is present:\n%s", generatedString) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /gennames/README.md: -------------------------------------------------------------------------------- 1 | # gennames 2 | For large projects, it may be useful to generate an index of package names for commonly used packages. 3 | The index of names can be added to each generated file using `File.ImportNames`. The `gennames` command 4 | is used internally to generate the list of standard library package names. 5 | 6 | ### Usage 7 | 8 | ``` 9 | Usage of gennames: 10 | -filter string 11 | Regex to filter paths (operates on full path including vendor directory) (default ".*") 12 | -name string 13 | Name of the variable to define (default "PackageNames") 14 | -novendor 15 | Exclude packages in vendor directories 16 | -output string 17 | Output filename to write (default "./package-names.go") 18 | -package string 19 | Package name in generated file (default "main") 20 | -path string 21 | Path to pass to go list command (default "all") 22 | -standard 23 | Use standard library packages 24 | ``` 25 | 26 | ### Path 27 | Supply a `path` to pass to the `go list` command. You may use the wildcard `/...` to recursively return 28 | packages, but it's worth remembering that vendored packages are not returned by this method unless the 29 | path itself is a vendored path. Use `all` to return all packages in your `GOPATH` (including vendored 30 | packages), however remember this may take some time for a large `GOPATH`. 31 | 32 | ### Filter 33 | Supply a regex `filter` to limit the packages that are returned by the `go list` command. The filter 34 | operates on the full vendored package path (e.g. `github.com/foo/bar/vendor/github.com/baz/qux`), however 35 | the package path added to the index is unvendored (e.g. `github.com/baz/qux`). 36 | 37 | ### Examples 38 | 39 | ``` 40 | gennames -filter "foo|bar" 41 | ``` 42 | 43 | Create a file named `package-names.go` with `package main` listing the names of all packages with paths 44 | containing `foo` or `bar`. 45 | 46 | ``` 47 | gennames -output "foo/names.go" -package "foo" -path "github.com/foo/bar/vendor/..." 48 | ``` 49 | 50 | Create a file named `foo/names.go` with `package foo` listing the names of all packages that are vendored 51 | inside `github.com/foo/bar`. 52 | 53 | -------------------------------------------------------------------------------- /gennames/hints.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go/build" 6 | "io" 7 | "os" 8 | "os/exec" 9 | "strings" 10 | 11 | "regexp" 12 | 13 | "path/filepath" 14 | 15 | . "github.com/dave/jennifer/jen" 16 | ) 17 | 18 | func hints(w io.Writer, pkg, name, goListPath, filter string, standard, novendor bool) error { 19 | 20 | // notest 21 | 22 | file := NewFile(pkg) 23 | 24 | file.HeaderComment("This file is generated - do not edit.") 25 | file.Line() 26 | 27 | packages, err := getPackages(goListPath, filter, standard, novendor) 28 | if err != nil { 29 | return err 30 | } 31 | /* 32 | // contains package name hints 33 | var = map[string]string{ 34 | ... 35 | } 36 | */ 37 | file.Commentf("%s contains package name hints", name) 38 | file.Var().Id(name).Op("=").Map(String()).String().Values(DictFunc(func(d Dict) { 39 | for path, name := range packages { 40 | d[Lit(path)] = Lit(name) 41 | } 42 | })) 43 | 44 | return file.Render(w) 45 | } 46 | 47 | func getPackages(goListPath, filter string, standard, novendor bool) (map[string]string, error) { 48 | 49 | // notest 50 | 51 | r, err := regexp.Compile(filter) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | cmd := exec.Command("go", "list", "-e", "-f", "{{ .Standard }} {{ .ImportPath }} {{ .Name }}", goListPath) 57 | home, _ := os.UserHomeDir() 58 | cmd.Env = []string{ 59 | fmt.Sprintf("GOPATH=%s", build.Default.GOPATH), 60 | fmt.Sprintf("GOROOT=%s", build.Default.GOROOT), 61 | fmt.Sprintf("HOME=%s", home), 62 | } 63 | if standard { 64 | cmd.Dir = filepath.Join(build.Default.GOROOT, "src") 65 | } else { 66 | cmd.Dir = filepath.Join(build.Default.GOPATH, "src") 67 | } 68 | b, err := cmd.Output() 69 | if err != nil { 70 | if x, ok := err.(*exec.ExitError); ok { 71 | return nil, fmt.Errorf("go list command returned an error - %s: %s", err.Error(), string(x.Stderr)) 72 | } 73 | return nil, fmt.Errorf("go list command returned an error: %s", err.Error()) 74 | } 75 | all := strings.Split(strings.TrimSpace(string(b)), "\n") 76 | 77 | packages := map[string]string{} 78 | for _, j := range all { 79 | 80 | parts := strings.Split(j, " ") 81 | 82 | isStandard := parts[0] == "true" 83 | if isStandard != standard { 84 | continue 85 | } 86 | 87 | path := parts[1] 88 | name := parts[2] 89 | 90 | if novendor && hasVendor(path) { 91 | continue 92 | } 93 | 94 | if name == "main" { 95 | continue 96 | } 97 | 98 | if !r.MatchString(path) { 99 | continue 100 | } 101 | 102 | path = unvendorPath(path) 103 | 104 | if packages[path] != "" { 105 | continue 106 | } 107 | 108 | packages[path] = name 109 | } 110 | return packages, nil 111 | } 112 | 113 | func unvendorPath(path string) string { 114 | // notest 115 | i, ok := findVendor(path) 116 | if !ok { 117 | return path 118 | } 119 | return path[i+len("vendor/"):] 120 | } 121 | 122 | // FindVendor looks for the last non-terminating "vendor" path element in the given import path. 123 | // If there isn't one, FindVendor returns ok=false. 124 | // Otherwise, FindVendor returns ok=true and the index of the "vendor". 125 | // Copied from cmd/go/internal/load 126 | func findVendor(path string) (index int, ok bool) { 127 | // notest 128 | // Two cases, depending on internal at start of string or not. 129 | // The order matters: we must return the index of the final element, 130 | // because the final one is where the effective import path starts. 131 | switch { 132 | case strings.Contains(path, "/vendor/"): 133 | return strings.LastIndex(path, "/vendor/") + 1, true 134 | case strings.HasPrefix(path, "vendor/"): 135 | return 0, true 136 | } 137 | return 0, false 138 | } 139 | 140 | func hasVendor(path string) bool { 141 | // notest 142 | _, v := findVendor(path) 143 | return v 144 | } 145 | -------------------------------------------------------------------------------- /gennames/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "log" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | // notest 12 | 13 | var out = flag.String("output", "./package-names.go", "Output filename to write") 14 | var pkg = flag.String("package", "main", "Package name in generated file") 15 | var name = flag.String("name", "PackageNames", "Name of the variable to define") 16 | var filter = flag.String("filter", ".*", "Regex to filter paths (operates on full path including vendor directory)") 17 | var standard = flag.Bool("standard", false, "Use standard library packages") 18 | var novendor = flag.Bool("novendor", false, "Exclude packages in vendor directories") 19 | var goListPath = flag.String("path", "all", "Path to pass to go list command") 20 | flag.Parse() 21 | 22 | buf := &bytes.Buffer{} 23 | if err := hints(buf, *pkg, *name, *goListPath, *filter, *standard, *novendor); err != nil { 24 | log.Fatal(err.Error()) 25 | } 26 | if err := os.WriteFile(*out, buf.Bytes(), 0644); err != nil { 27 | log.Fatal(err.Error()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dave/jennifer 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dave/jennifer/b477ff8272a1bf199b1bef8bc05d5e666f471c34/go.sum -------------------------------------------------------------------------------- /jen/add.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | // Add appends the provided items to the statement. 4 | func Add(code ...Code) *Statement { 5 | return newStatement().Add(code...) 6 | } 7 | 8 | // Add appends the provided items to the statement. 9 | func (g *Group) Add(code ...Code) *Statement { 10 | s := Add(code...) 11 | g.items = append(g.items, s) 12 | return s 13 | } 14 | 15 | // Add appends the provided items to the statement. 16 | func (s *Statement) Add(code ...Code) *Statement { 17 | *s = append(*s, code...) 18 | return s 19 | } 20 | -------------------------------------------------------------------------------- /jen/comments.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | // Comment adds a comment. If the provided string contains a newline, the 10 | // comment is formatted in multiline style. If the comment string starts 11 | // with "//" or "/*", the automatic formatting is disabled and the string is 12 | // rendered directly. 13 | func Comment(str string) *Statement { 14 | return newStatement().Comment(str) 15 | } 16 | 17 | // Comment adds a comment. If the provided string contains a newline, the 18 | // comment is formatted in multiline style. If the comment string starts 19 | // with "//" or "/*", the automatic formatting is disabled and the string is 20 | // rendered directly. 21 | func (g *Group) Comment(str string) *Statement { 22 | s := Comment(str) 23 | g.items = append(g.items, s) 24 | return s 25 | } 26 | 27 | // Comment adds a comment. If the provided string contains a newline, the 28 | // comment is formatted in multiline style. If the comment string starts 29 | // with "//" or "/*", the automatic formatting is disabled and the string is 30 | // rendered directly. 31 | func (s *Statement) Comment(str string) *Statement { 32 | c := comment{ 33 | comment: str, 34 | } 35 | *s = append(*s, c) 36 | return s 37 | } 38 | 39 | // Commentf adds a comment, using a format string and a list of parameters. If 40 | // the provided string contains a newline, the comment is formatted in 41 | // multiline style. If the comment string starts with "//" or "/*", the 42 | // automatic formatting is disabled and the string is rendered directly. 43 | func Commentf(format string, a ...interface{}) *Statement { 44 | return newStatement().Commentf(format, a...) 45 | } 46 | 47 | // Commentf adds a comment, using a format string and a list of parameters. If 48 | // the provided string contains a newline, the comment is formatted in 49 | // multiline style. If the comment string starts with "//" or "/*", the 50 | // automatic formatting is disabled and the string is rendered directly. 51 | func (g *Group) Commentf(format string, a ...interface{}) *Statement { 52 | s := Commentf(format, a...) 53 | g.items = append(g.items, s) 54 | return s 55 | } 56 | 57 | // Commentf adds a comment, using a format string and a list of parameters. If 58 | // the provided string contains a newline, the comment is formatted in 59 | // multiline style. If the comment string starts with "//" or "/*", the 60 | // automatic formatting is disabled and the string is rendered directly. 61 | func (s *Statement) Commentf(format string, a ...interface{}) *Statement { 62 | c := comment{ 63 | comment: fmt.Sprintf(format, a...), 64 | } 65 | *s = append(*s, c) 66 | return s 67 | } 68 | 69 | type comment struct { 70 | comment string 71 | } 72 | 73 | func (c comment) isNull(f *File) bool { 74 | return false 75 | } 76 | 77 | func (c comment) render(f *File, w io.Writer, s *Statement) error { 78 | if strings.HasPrefix(c.comment, "//") || strings.HasPrefix(c.comment, "/*") { 79 | // automatic formatting disabled. 80 | if _, err := w.Write([]byte(c.comment)); err != nil { 81 | return err 82 | } 83 | return nil 84 | } 85 | if strings.Contains(c.comment, "\n") { 86 | if _, err := w.Write([]byte("/*\n")); err != nil { 87 | return err 88 | } 89 | } else { 90 | if _, err := w.Write([]byte("// ")); err != nil { 91 | return err 92 | } 93 | } 94 | if _, err := w.Write([]byte(c.comment)); err != nil { 95 | return err 96 | } 97 | if strings.Contains(c.comment, "\n") { 98 | if !strings.HasSuffix(c.comment, "\n") { 99 | if _, err := w.Write([]byte("\n")); err != nil { 100 | return err 101 | } 102 | } 103 | if _, err := w.Write([]byte("*/")); err != nil { 104 | return err 105 | } 106 | } 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /jen/custom.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | // Options specifies options for the Custom method 4 | type Options struct { 5 | Open string 6 | Close string 7 | Separator string 8 | Multi bool 9 | } 10 | 11 | // Custom renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator. 12 | func Custom(options Options, statements ...Code) *Statement { 13 | return newStatement().Custom(options, statements...) 14 | } 15 | 16 | // Custom renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator. 17 | func (g *Group) Custom(options Options, statements ...Code) *Statement { 18 | s := Custom(options, statements...) 19 | g.items = append(g.items, s) 20 | return s 21 | } 22 | 23 | // Custom renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator. 24 | func (s *Statement) Custom(options Options, statements ...Code) *Statement { 25 | g := &Group{ 26 | close: options.Close, 27 | items: statements, 28 | multi: options.Multi, 29 | name: "custom", 30 | open: options.Open, 31 | separator: options.Separator, 32 | } 33 | *s = append(*s, g) 34 | return s 35 | } 36 | 37 | // CustomFunc renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator. 38 | func CustomFunc(options Options, f func(*Group)) *Statement { 39 | return newStatement().CustomFunc(options, f) 40 | } 41 | 42 | // CustomFunc renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator. 43 | func (g *Group) CustomFunc(options Options, f func(*Group)) *Statement { 44 | s := CustomFunc(options, f) 45 | g.items = append(g.items, s) 46 | return s 47 | } 48 | 49 | // CustomFunc renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator. 50 | func (s *Statement) CustomFunc(options Options, f func(*Group)) *Statement { 51 | g := &Group{ 52 | close: options.Close, 53 | multi: options.Multi, 54 | name: "custom", 55 | open: options.Open, 56 | separator: options.Separator, 57 | } 58 | f(g) 59 | *s = append(*s, g) 60 | return s 61 | } 62 | -------------------------------------------------------------------------------- /jen/dict.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "sort" 7 | ) 8 | 9 | // Dict renders as key/value pairs. Use with Values for map or composite 10 | // literals. 11 | type Dict map[Code]Code 12 | 13 | // DictFunc executes a func(Dict) to generate the value. Use with Values for 14 | // map or composite literals. 15 | func DictFunc(f func(Dict)) Dict { 16 | d := Dict{} 17 | f(d) 18 | return d 19 | } 20 | 21 | func (d Dict) render(f *File, w io.Writer, s *Statement) error { 22 | first := true 23 | // must order keys to ensure repeatable source 24 | type kv struct { 25 | k Code 26 | v Code 27 | } 28 | lookup := map[string]kv{} 29 | keys := []string{} 30 | for k, v := range d { 31 | if k.isNull(f) || v.isNull(f) { 32 | continue 33 | } 34 | buf := &bytes.Buffer{} 35 | if err := k.render(f, buf, nil); err != nil { 36 | return err 37 | } 38 | keys = append(keys, buf.String()) 39 | lookup[buf.String()] = kv{k: k, v: v} 40 | } 41 | sort.Strings(keys) 42 | for _, key := range keys { 43 | k := lookup[key].k 44 | v := lookup[key].v 45 | if first && len(keys) > 1 { 46 | if _, err := w.Write([]byte("\n")); err != nil { 47 | return err 48 | } 49 | first = false 50 | } 51 | if err := k.render(f, w, nil); err != nil { 52 | return err 53 | } 54 | if _, err := w.Write([]byte(":")); err != nil { 55 | return err 56 | } 57 | if err := v.render(f, w, nil); err != nil { 58 | return err 59 | } 60 | if len(keys) > 1 { 61 | if _, err := w.Write([]byte(",\n")); err != nil { 62 | return err 63 | } 64 | } 65 | } 66 | return nil 67 | } 68 | 69 | func (d Dict) isNull(f *File) bool { 70 | if d == nil || len(d) == 0 { 71 | return true 72 | } 73 | for k, v := range d { 74 | if !k.isNull(f) && !v.isNull(f) { 75 | // if any of the key/value pairs are both not null, the Dict is not 76 | // null 77 | return false 78 | } 79 | } 80 | return true 81 | } 82 | -------------------------------------------------------------------------------- /jen/do.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | // Do calls the provided function with the statement as a parameter. Use for 4 | // embedding logic. 5 | func Do(f func(*Statement)) *Statement { 6 | return newStatement().Do(f) 7 | } 8 | 9 | // Do calls the provided function with the statement as a parameter. Use for 10 | // embedding logic. 11 | func (g *Group) Do(f func(*Statement)) *Statement { 12 | s := Do(f) 13 | g.items = append(g.items, s) 14 | return s 15 | } 16 | 17 | // Do calls the provided function with the statement as a parameter. Use for 18 | // embedding logic. 19 | func (s *Statement) Do(f func(*Statement)) *Statement { 20 | f(s) 21 | return s 22 | } 23 | -------------------------------------------------------------------------------- /jen/examples_test.go: -------------------------------------------------------------------------------- 1 | package jen_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | . "github.com/dave/jennifer/jen" 8 | ) 9 | 10 | func ExampleGenericsTypesFuncEmpty() { 11 | c := Func().Id("F").TypesFunc(func(group *Group) {}).Params().Block() 12 | fmt.Printf("%#v", c) 13 | // Output: 14 | // func F() {} 15 | } 16 | 17 | func ExampleGenericsTypesFuncNull() { 18 | c := Func().Id("F").TypesFunc(func(group *Group) { 19 | group.Null() 20 | }).Params().Block() 21 | fmt.Printf("%#v", c) 22 | // Output: 23 | // func F() {} 24 | } 25 | 26 | func ExampleGenericsTypesEmpty() { 27 | c := Func().Id("F").Types().Params().Block() 28 | fmt.Printf("%#v", c) 29 | // Output: 30 | // func F() {} 31 | } 32 | 33 | func ExampleGenericsTypesNull() { 34 | c := Func().Id("F").Types(Null()).Params().Block() 35 | fmt.Printf("%#v", c) 36 | // Output: 37 | // func F() {} 38 | } 39 | 40 | func ExampleGenericsTypesDefinition() { 41 | c := Func().Id("Keys").Types( 42 | Id("K").Comparable(), 43 | Id("V").Any(), 44 | ).Params( 45 | Id("m").Map(Id("K")).Id("V"), 46 | ).Index().Id("K").Block() 47 | fmt.Printf("%#v", c) 48 | // Output: 49 | // func Keys[K comparable, V any](m map[K]V) []K {} 50 | } 51 | 52 | func ExampleGenericsTypesUsage() { 53 | c := Return(Id("Keys").Types(Int(), String()).Call(Id("m"))) 54 | fmt.Printf("%#v", c) 55 | // Output: 56 | // return Keys[int, string](m) 57 | } 58 | 59 | func ExampleGenericsUnion() { 60 | c := Type().Id("PredeclaredSignedInteger").Interface( 61 | Union(Int(), Int8(), Int16(), Int32(), Int64()), 62 | ) 63 | fmt.Printf("%#v", c) 64 | // Output: 65 | // type PredeclaredSignedInteger interface { 66 | // int | int8 | int16 | int32 | int64 67 | // } 68 | } 69 | 70 | func ExampleGenericsApproximate() { 71 | c := Type().Id("AnyString").Interface( 72 | Op("~").String(), 73 | ) 74 | fmt.Printf("%#v", c) 75 | // Output: 76 | // type AnyString interface { 77 | // ~string 78 | // } 79 | } 80 | 81 | func ExampleCaseBug() { 82 | c := Switch(Id("a")).Block( 83 | Case(Lit(1)).Block( 84 | Var().Id("i").Int(), 85 | Var().Id("j").Int(), 86 | ), 87 | ) 88 | fmt.Printf("%#v", c) 89 | // Output: 90 | // switch a { 91 | // case 1: 92 | // var i int 93 | // var j int 94 | // } 95 | } 96 | 97 | func ExampleCustom() { 98 | multiLineCall := Options{ 99 | Close: ")", 100 | Multi: true, 101 | Open: "(", 102 | Separator: ",", 103 | } 104 | c := Id("foo").Custom(multiLineCall, Lit("a"), Lit("b"), Lit("c")) 105 | fmt.Printf("%#v", c) 106 | // Output: 107 | // foo( 108 | // "a", 109 | // "b", 110 | // "c", 111 | // ) 112 | } 113 | 114 | func ExampleCustomFunc() { 115 | multiLineCall := Options{ 116 | Close: ")", 117 | Multi: true, 118 | Open: "(", 119 | Separator: ",", 120 | } 121 | c := Id("foo").CustomFunc(multiLineCall, func(g *Group) { 122 | g.Lit("a") 123 | g.Lit("b") 124 | g.Lit("c") 125 | }) 126 | fmt.Printf("%#v", c) 127 | // Output: 128 | // foo( 129 | // "a", 130 | // "b", 131 | // "c", 132 | // ) 133 | } 134 | 135 | func ExampleFile_ImportName_conflict() { 136 | f := NewFile("main") 137 | 138 | // We provide a hint that package foo/a should use name "a", but because package bar/a already 139 | // registers the required name, foo/a is aliased. 140 | f.ImportName("github.com/foo/a", "a") 141 | 142 | f.Func().Id("main").Params().Block( 143 | Qual("github.com/bar/a", "Bar").Call(), 144 | Qual("github.com/foo/a", "Foo").Call(), 145 | ) 146 | fmt.Printf("%#v", f) 147 | 148 | // Output: 149 | // package main 150 | // 151 | // import ( 152 | // a "github.com/bar/a" 153 | // a1 "github.com/foo/a" 154 | // ) 155 | // 156 | // func main() { 157 | // a.Bar() 158 | // a1.Foo() 159 | // } 160 | } 161 | 162 | func ExampleFile_ImportAlias_conflict() { 163 | f := NewFile("main") 164 | 165 | // We provide a hint that package foo/a should use alias "b", but because package bar/b already 166 | // registers the required name, foo/a is aliased using the requested alias as a base. 167 | f.ImportName("github.com/foo/a", "b") 168 | 169 | f.Func().Id("main").Params().Block( 170 | Qual("github.com/bar/b", "Bar").Call(), 171 | Qual("github.com/foo/a", "Foo").Call(), 172 | ) 173 | fmt.Printf("%#v", f) 174 | 175 | // Output: 176 | // package main 177 | // 178 | // import ( 179 | // b "github.com/bar/b" 180 | // b1 "github.com/foo/a" 181 | // ) 182 | // 183 | // func main() { 184 | // b.Bar() 185 | // b1.Foo() 186 | // } 187 | } 188 | 189 | func ExampleFile_ImportName() { 190 | f := NewFile("main") 191 | 192 | // package a should use name "a" 193 | f.ImportName("github.com/foo/a", "a") 194 | 195 | // package b is not used in the code so will not be included 196 | f.ImportName("github.com/foo/b", "b") 197 | 198 | f.Func().Id("main").Params().Block( 199 | Qual("github.com/foo/a", "A").Call(), 200 | ) 201 | fmt.Printf("%#v", f) 202 | 203 | // Output: 204 | // package main 205 | // 206 | // import "github.com/foo/a" 207 | // 208 | // func main() { 209 | // a.A() 210 | // } 211 | } 212 | 213 | func ExampleFile_ImportNames() { 214 | 215 | // package a should use name "a", package b is not used in the code so will not be included 216 | names := map[string]string{ 217 | "github.com/foo/a": "a", 218 | "github.com/foo/b": "b", 219 | } 220 | 221 | f := NewFile("main") 222 | f.ImportNames(names) 223 | f.Func().Id("main").Params().Block( 224 | Qual("github.com/foo/a", "A").Call(), 225 | ) 226 | fmt.Printf("%#v", f) 227 | 228 | // Output: 229 | // package main 230 | // 231 | // import "github.com/foo/a" 232 | // 233 | // func main() { 234 | // a.A() 235 | // } 236 | } 237 | 238 | func ExampleFile_ImportAlias() { 239 | f := NewFile("main") 240 | 241 | // package a should be aliased to "b" 242 | f.ImportAlias("github.com/foo/a", "b") 243 | 244 | // package c is not used in the code so will not be included 245 | f.ImportAlias("github.com/foo/c", "c") 246 | 247 | f.Func().Id("main").Params().Block( 248 | Qual("github.com/foo/a", "A").Call(), 249 | ) 250 | fmt.Printf("%#v", f) 251 | 252 | // Output: 253 | // package main 254 | // 255 | // import b "github.com/foo/a" 256 | // 257 | // func main() { 258 | // b.A() 259 | // } 260 | } 261 | 262 | func ExampleFile_ImportAliasDot() { 263 | f := NewFile("main") 264 | 265 | // package a should be a dot-import 266 | f.ImportAlias("github.com/foo/a", ".") 267 | 268 | // package b should be a dot-import 269 | f.ImportAlias("github.com/foo/b", ".") 270 | 271 | // package c is not used in the code so will not be included 272 | f.ImportAlias("github.com/foo/c", ".") 273 | 274 | f.Func().Id("main").Params().Block( 275 | Qual("github.com/foo/a", "A").Call(), 276 | Qual("github.com/foo/b", "B").Call(), 277 | ) 278 | fmt.Printf("%#v", f) 279 | 280 | // Output: 281 | // package main 282 | // 283 | // import ( 284 | // . "github.com/foo/a" 285 | // . "github.com/foo/b" 286 | // ) 287 | // 288 | // func main() { 289 | // A() 290 | // B() 291 | // } 292 | } 293 | 294 | func ExampleFile_CgoPreamble() { 295 | f := NewFile("a") 296 | f.CgoPreamble(`#include 297 | #include 298 | 299 | void myprint(char* s) { 300 | printf("%s\n", s); 301 | } 302 | `) 303 | f.Func().Id("init").Params().Block( 304 | Id("cs").Op(":=").Qual("C", "CString").Call(Lit("Hello from stdio\n")), 305 | Qual("C", "myprint").Call(Id("cs")), 306 | Qual("C", "free").Call(Qual("unsafe", "Pointer").Parens(Id("cs"))), 307 | ) 308 | fmt.Printf("%#v", f) 309 | // Output: 310 | // package a 311 | // 312 | // import "unsafe" 313 | // 314 | // /* 315 | // #include 316 | // #include 317 | // 318 | // void myprint(char* s) { 319 | // printf("%s\n", s); 320 | // } 321 | // */ 322 | // import "C" 323 | // 324 | // func init() { 325 | // cs := C.CString("Hello from stdio\n") 326 | // C.myprint(cs) 327 | // C.free(unsafe.Pointer(cs)) 328 | // } 329 | } 330 | 331 | func ExampleFile_CgoPreamble_anon() { 332 | f := NewFile("a") 333 | f.CgoPreamble(`#include `) 334 | f.Func().Id("init").Params().Block( 335 | Qual("foo.bar/a", "A"), 336 | Qual("foo.bar/b", "B"), 337 | ) 338 | fmt.Printf("%#v", f) 339 | // Output: 340 | // package a 341 | // 342 | // import ( 343 | // a "foo.bar/a" 344 | // b "foo.bar/b" 345 | // ) 346 | // 347 | // // #include 348 | // import "C" 349 | // 350 | // func init() { 351 | // a.A 352 | // b.B 353 | // } 354 | } 355 | 356 | func ExampleFile_CgoPreamble_no_preamble() { 357 | f := NewFile("a") 358 | f.Func().Id("init").Params().Block( 359 | Qual("C", "Foo").Call(), 360 | Qual("fmt", "Print").Call(), 361 | ) 362 | fmt.Printf("%#v", f) 363 | // Output: 364 | // package a 365 | // 366 | // import ( 367 | // "C" 368 | // "fmt" 369 | // ) 370 | // 371 | // func init() { 372 | // C.Foo() 373 | // fmt.Print() 374 | // } 375 | } 376 | 377 | func ExampleFile_CgoPreamble_no_preamble_single() { 378 | f := NewFile("a") 379 | f.Func().Id("init").Params().Block( 380 | Qual("C", "Foo").Call(), 381 | ) 382 | fmt.Printf("%#v", f) 383 | // Output: 384 | // package a 385 | // 386 | // import "C" 387 | // 388 | // func init() { 389 | // C.Foo() 390 | // } 391 | } 392 | 393 | func ExampleFile_CgoPreamble_no_preamble_single_anon() { 394 | f := NewFile("a") 395 | f.Anon("C") 396 | f.Func().Id("init").Params().Block() 397 | fmt.Printf("%#v", f) 398 | // Output: 399 | // package a 400 | // 401 | // import "C" 402 | // 403 | // func init() {} 404 | } 405 | 406 | func ExampleFile_CgoPreamble_no_preamble_anon() { 407 | f := NewFile("a") 408 | f.Anon("C") 409 | f.Func().Id("init").Params().Block( 410 | Qual("fmt", "Print").Call(), 411 | ) 412 | fmt.Printf("%#v", f) 413 | // Output: 414 | // package a 415 | // 416 | // import ( 417 | // "C" 418 | // "fmt" 419 | // ) 420 | // 421 | // func init() { 422 | // fmt.Print() 423 | // } 424 | } 425 | 426 | func ExampleOp_complex_conditions() { 427 | c := If(Parens(Id("a").Op("||").Id("b")).Op("&&").Id("c")).Block() 428 | fmt.Printf("%#v", c) 429 | // Output: 430 | // if (a || b) && c { 431 | // } 432 | } 433 | 434 | func ExampleLit_bool_true() { 435 | c := Lit(true) 436 | fmt.Printf("%#v", c) 437 | // Output: 438 | // true 439 | } 440 | 441 | func ExampleLit_bool_false() { 442 | c := Lit(false) 443 | fmt.Printf("%#v", c) 444 | // Output: 445 | // false 446 | } 447 | 448 | func ExampleLit_byte() { 449 | // Lit can't tell the difference between byte and uint8. Use LitByte to 450 | // render byte literals. 451 | c := Lit(byte(0x1)) 452 | fmt.Printf("%#v", c) 453 | // Output: 454 | // uint8(0x1) 455 | } 456 | 457 | func ExampleLit_complex64() { 458 | c := Lit(complex64(0 + 0i)) 459 | fmt.Printf("%#v", c) 460 | // Output: 461 | // complex64(0 + 0i) 462 | } 463 | 464 | func ExampleLit_complex128() { 465 | c := Lit(0 + 0i) 466 | fmt.Printf("%#v", c) 467 | // Output: 468 | // (0 + 0i) 469 | } 470 | 471 | func ExampleLit_float32() { 472 | c := Lit(float32(1)) 473 | fmt.Printf("%#v", c) 474 | // Output: 475 | // float32(1) 476 | } 477 | 478 | func ExampleLit_float64_one_point_zero() { 479 | c := Lit(1.0) 480 | fmt.Printf("%#v", c) 481 | // Output: 482 | // 1.0 483 | } 484 | 485 | func ExampleLit_float64_zero() { 486 | c := Lit(0.0) 487 | fmt.Printf("%#v", c) 488 | // Output: 489 | // 0.0 490 | } 491 | 492 | func ExampleLit_float64_negative() { 493 | c := Lit(-0.1) 494 | fmt.Printf("%#v", c) 495 | // Output: 496 | // -0.1 497 | } 498 | 499 | func ExampleLit_float64_negative_whole() { 500 | c := Lit(-1.0) 501 | fmt.Printf("%#v", c) 502 | // Output: 503 | // -1.0 504 | } 505 | 506 | func ExampleLit_int() { 507 | c := Lit(1) 508 | fmt.Printf("%#v", c) 509 | // Output: 510 | // 1 511 | } 512 | 513 | func ExampleLit_int8() { 514 | c := Lit(int8(1)) 515 | fmt.Printf("%#v", c) 516 | // Output: 517 | // int8(1) 518 | } 519 | 520 | func ExampleLit_int16() { 521 | c := Lit(int16(1)) 522 | fmt.Printf("%#v", c) 523 | // Output: 524 | // int16(1) 525 | } 526 | 527 | func ExampleLit_int32() { 528 | c := Lit(int32(1)) 529 | fmt.Printf("%#v", c) 530 | // Output: 531 | // int32(1) 532 | } 533 | 534 | func ExampleLit_int64() { 535 | c := Lit(int64(1)) 536 | fmt.Printf("%#v", c) 537 | // Output: 538 | // int64(1) 539 | } 540 | 541 | func ExampleLit_uint() { 542 | c := Lit(uint(0x1)) 543 | fmt.Printf("%#v", c) 544 | // Output: 545 | // uint(0x1) 546 | } 547 | 548 | func ExampleLit_uint8() { 549 | c := Lit(uint8(0x1)) 550 | fmt.Printf("%#v", c) 551 | // Output: 552 | // uint8(0x1) 553 | } 554 | 555 | func ExampleLit_uint16() { 556 | c := Lit(uint16(0x1)) 557 | fmt.Printf("%#v", c) 558 | // Output: 559 | // uint16(0x1) 560 | } 561 | 562 | func ExampleLit_uint32() { 563 | c := Lit(uint32(0x1)) 564 | fmt.Printf("%#v", c) 565 | // Output: 566 | // uint32(0x1) 567 | } 568 | 569 | func ExampleLit_uint64() { 570 | c := Lit(uint64(0x1)) 571 | fmt.Printf("%#v", c) 572 | // Output: 573 | // uint64(0x1) 574 | } 575 | 576 | func ExampleLit_uintptr() { 577 | c := Lit(uintptr(0x1)) 578 | fmt.Printf("%#v", c) 579 | // Output: 580 | // uintptr(0x1) 581 | } 582 | 583 | func ExampleLit_rune() { 584 | // Lit can't tell the difference between rune and int32. Use LitRune to 585 | // render rune literals. 586 | c := Lit('x') 587 | fmt.Printf("%#v", c) 588 | // Output: 589 | // int32(120) 590 | } 591 | 592 | func ExampleLitRune() { 593 | c := LitRune('x') 594 | fmt.Printf("%#v", c) 595 | // Output: 596 | // 'x' 597 | } 598 | 599 | func ExampleLitRuneFunc() { 600 | c := LitRuneFunc(func() rune { 601 | return '\t' 602 | }) 603 | fmt.Printf("%#v", c) 604 | // Output: 605 | // '\t' 606 | } 607 | 608 | func ExampleLitByte() { 609 | c := LitByte(byte(1)) 610 | fmt.Printf("%#v", c) 611 | // Output: 612 | // byte(0x1) 613 | } 614 | 615 | func ExampleLitByteFunc() { 616 | c := LitByteFunc(func() byte { 617 | return byte(2) 618 | }) 619 | fmt.Printf("%#v", c) 620 | // Output: 621 | // byte(0x2) 622 | } 623 | 624 | func ExampleLit_string() { 625 | c := Lit("foo") 626 | fmt.Printf("%#v", c) 627 | // Output: 628 | // "foo" 629 | } 630 | 631 | func ExampleValues_dict_single() { 632 | c := Map(String()).String().Values(Dict{ 633 | Lit("a"): Lit("b"), 634 | }) 635 | fmt.Printf("%#v", c) 636 | // Output: 637 | // map[string]string{"a": "b"} 638 | } 639 | 640 | func ExampleValues_dict_multiple() { 641 | c := Map(String()).String().Values(Dict{ 642 | Lit("a"): Lit("b"), 643 | Lit("c"): Lit("d"), 644 | }) 645 | fmt.Printf("%#v", c) 646 | // Output: 647 | // map[string]string{ 648 | // "a": "b", 649 | // "c": "d", 650 | // } 651 | } 652 | 653 | func ExampleValues_dict_composite() { 654 | c := Op("&").Id("Person").Values(Dict{ 655 | Id("Age"): Lit(1), 656 | Id("Name"): Lit("a"), 657 | }) 658 | fmt.Printf("%#v", c) 659 | // Output: 660 | // &Person{ 661 | // Age: 1, 662 | // Name: "a", 663 | // } 664 | } 665 | 666 | func ExampleAdd() { 667 | ptr := Op("*") 668 | c := Id("a").Op("=").Add(ptr).Id("b") 669 | fmt.Printf("%#v", c) 670 | // Output: 671 | // a = *b 672 | } 673 | 674 | func ExampleAdd_var() { 675 | a := Id("a") 676 | i := Int() 677 | c := Var().Add(a, i) 678 | fmt.Printf("%#v", c) 679 | // Output: 680 | // var a int 681 | } 682 | 683 | func ExampleAppend() { 684 | c := Append(Id("a"), Id("b")) 685 | fmt.Printf("%#v", c) 686 | // Output: 687 | // append(a, b) 688 | } 689 | 690 | func ExampleAppend_more() { 691 | c := Id("a").Op("=").Append(Id("a"), Id("b").Op("...")) 692 | fmt.Printf("%#v", c) 693 | // Output: 694 | // a = append(a, b...) 695 | } 696 | 697 | func ExampleAssert() { 698 | c := List(Id("b"), Id("ok")).Op(":=").Id("a").Assert(Bool()) 699 | fmt.Printf("%#v", c) 700 | // Output: 701 | // b, ok := a.(bool) 702 | } 703 | 704 | func ExampleBlock() { 705 | c := Func().Id("foo").Params().String().Block( 706 | Id("a").Op("=").Id("b"), 707 | Id("b").Op("++"), 708 | Return(Id("b")), 709 | ) 710 | fmt.Printf("%#v", c) 711 | // Output: 712 | // func foo() string { 713 | // a = b 714 | // b++ 715 | // return b 716 | // } 717 | } 718 | 719 | func ExampleBlock_if() { 720 | c := If(Id("a").Op(">").Lit(10)).Block( 721 | Id("a").Op("=").Id("a").Op("/").Lit(2), 722 | ) 723 | fmt.Printf("%#v", c) 724 | // Output: 725 | // if a > 10 { 726 | // a = a / 2 727 | // } 728 | } 729 | 730 | func ExampleValuesFunc() { 731 | c := Id("numbers").Op(":=").Index().Int().ValuesFunc(func(g *Group) { 732 | for i := 0; i <= 5; i++ { 733 | g.Lit(i) 734 | } 735 | }) 736 | fmt.Printf("%#v", c) 737 | // Output: 738 | // numbers := []int{0, 1, 2, 3, 4, 5} 739 | } 740 | 741 | func ExampleBlockFunc() { 742 | increment := true 743 | name := "a" 744 | c := Func().Id("a").Params().BlockFunc(func(g *Group) { 745 | g.Id(name).Op("=").Lit(1) 746 | if increment { 747 | g.Id(name).Op("++") 748 | } else { 749 | g.Id(name).Op("--") 750 | } 751 | }) 752 | fmt.Printf("%#v", c) 753 | // Output: 754 | // func a() { 755 | // a = 1 756 | // a++ 757 | // } 758 | } 759 | 760 | func ExampleBool() { 761 | c := Var().Id("b").Bool() 762 | fmt.Printf("%#v", c) 763 | // Output: 764 | // var b bool 765 | } 766 | 767 | func ExampleBreak() { 768 | c := For( 769 | Id("i").Op(":=").Lit(0), 770 | Id("i").Op("<").Lit(10), 771 | Id("i").Op("++"), 772 | ).Block( 773 | If(Id("i").Op(">").Lit(5)).Block( 774 | Break(), 775 | ), 776 | ) 777 | fmt.Printf("%#v", c) 778 | // Output: 779 | // for i := 0; i < 10; i++ { 780 | // if i > 5 { 781 | // break 782 | // } 783 | // } 784 | } 785 | 786 | func ExampleByte() { 787 | c := Id("b").Op(":=").Id("a").Assert(Byte()) 788 | fmt.Printf("%#v", c) 789 | // Output: 790 | // b := a.(byte) 791 | } 792 | 793 | func ExampleCall() { 794 | c := Qual("fmt", "Printf").Call( 795 | Lit("%#v: %T\n"), 796 | Id("a"), 797 | Id("b"), 798 | ) 799 | fmt.Printf("%#v", c) 800 | // Output: 801 | // fmt.Printf("%#v: %T\n", a, b) 802 | } 803 | 804 | func ExampleCall_fmt() { 805 | c := Id("a").Call(Lit("b")) 806 | fmt.Printf("%#v", c) 807 | // Output: 808 | // a("b") 809 | } 810 | 811 | func ExampleCallFunc() { 812 | f := func(name string, second string) { 813 | c := Id("foo").CallFunc(func(g *Group) { 814 | g.Id(name) 815 | if second != "" { 816 | g.Lit(second) 817 | } 818 | }) 819 | fmt.Printf("%#v\n", c) 820 | } 821 | f("a", "b") 822 | f("c", "") 823 | // Output: 824 | // foo(a, "b") 825 | // foo(c) 826 | } 827 | 828 | func ExampleCap() { 829 | c := Id("i").Op(":=").Cap(Id("v")) 830 | fmt.Printf("%#v", c) 831 | // Output: 832 | // i := cap(v) 833 | } 834 | 835 | func ExampleCase() { 836 | c := Switch(Id("person")).Block( 837 | Case(Id("John"), Id("Peter")).Block( 838 | Return(Lit("male")), 839 | ), 840 | Case(Id("Gill")).Block( 841 | Return(Lit("female")), 842 | ), 843 | ) 844 | fmt.Printf("%#v", c) 845 | // Output: 846 | // switch person { 847 | // case John, Peter: 848 | // return "male" 849 | // case Gill: 850 | // return "female" 851 | // } 852 | } 853 | 854 | func ExampleBlock_case() { 855 | c := Select().Block( 856 | Case(Op("<-").Id("done")).Block( 857 | Return(Nil()), 858 | ), 859 | Case(List(Err(), Id("open")).Op(":=").Op("<-").Id("fail")).Block( 860 | If(Op("!").Id("open")).Block( 861 | Return(Err()), 862 | ), 863 | ), 864 | ) 865 | fmt.Printf("%#v", c) 866 | // Output: 867 | // select { 868 | // case <-done: 869 | // return nil 870 | // case err, open := <-fail: 871 | // if !open { 872 | // return err 873 | // } 874 | // } 875 | } 876 | 877 | func ExampleBlockFunc_case() { 878 | preventExitOnError := true 879 | c := Select().Block( 880 | Case(Op("<-").Id("done")).Block( 881 | Return(Nil()), 882 | ), 883 | Case(Err().Op(":=").Op("<-").Id("fail")).BlockFunc(func(g *Group) { 884 | if !preventExitOnError { 885 | g.Return(Err()) 886 | } else { 887 | g.Qual("fmt", "Println").Call(Err()) 888 | } 889 | }), 890 | ) 891 | fmt.Printf("%#v", c) 892 | // Output: 893 | // select { 894 | // case <-done: 895 | // return nil 896 | // case err := <-fail: 897 | // fmt.Println(err) 898 | // } 899 | } 900 | 901 | func ExampleCaseFunc() { 902 | samIsMale := false 903 | c := Switch(Id("person")).Block( 904 | CaseFunc(func(g *Group) { 905 | g.Id("John") 906 | g.Id("Peter") 907 | if samIsMale { 908 | g.Id("Sam") 909 | } 910 | }).Block( 911 | Return(Lit("male")), 912 | ), 913 | CaseFunc(func(g *Group) { 914 | g.Id("Gill") 915 | if !samIsMale { 916 | g.Id("Sam") 917 | } 918 | }).Block( 919 | Return(Lit("female")), 920 | ), 921 | ) 922 | fmt.Printf("%#v", c) 923 | // Output: 924 | // switch person { 925 | // case John, Peter: 926 | // return "male" 927 | // case Gill, Sam: 928 | // return "female" 929 | // } 930 | } 931 | 932 | func ExampleChan() { 933 | c := Func().Id("init").Params().Block( 934 | Id("c").Op(":=").Make(Chan().Qual("os", "Signal"), Lit(1)), 935 | Qual("os/signal", "Notify").Call(Id("c"), Qual("os", "Interrupt")), 936 | Qual("os/signal", "Notify").Call(Id("c"), Qual("syscall", "SIGTERM")), 937 | Go().Func().Params().Block( 938 | Op("<-").Id("c"), 939 | Id("cancel").Call(), 940 | ).Call(), 941 | ) 942 | fmt.Printf("%#v", c) 943 | // Output: 944 | // func init() { 945 | // c := make(chan os.Signal, 1) 946 | // signal.Notify(c, os.Interrupt) 947 | // signal.Notify(c, syscall.SIGTERM) 948 | // go func() { 949 | // <-c 950 | // cancel() 951 | // }() 952 | // } 953 | } 954 | 955 | func ExampleClose() { 956 | c := Block( 957 | Id("ch").Op(":=").Make(Chan().Struct()), 958 | Go().Func().Params().Block( 959 | Op("<-").Id("ch"), 960 | Qual("fmt", "Println").Call(Lit("done.")), 961 | ).Call(), 962 | Close(Id("ch")), 963 | ) 964 | fmt.Printf("%#v", c) 965 | // Output: 966 | // { 967 | // ch := make(chan struct{}) 968 | // go func() { 969 | // <-ch 970 | // fmt.Println("done.") 971 | // }() 972 | // close(ch) 973 | // } 974 | } 975 | 976 | func ExampleClear() { 977 | c := Block( 978 | Id("a").Op(":=").Map(String()).String().Values(), 979 | Clear(Id("a")), 980 | ) 981 | fmt.Printf("%#v", c) 982 | // Output: 983 | // { 984 | // a := map[string]string{} 985 | // clear(a) 986 | // } 987 | } 988 | 989 | func ExampleMin() { 990 | c := Block( 991 | Id("n").Op(":=").Min(Lit(1), Lit(2)), 992 | ) 993 | fmt.Printf("%#v", c) 994 | // Output: 995 | // { 996 | // n := min(1, 2) 997 | // } 998 | } 999 | 1000 | func ExampleMax() { 1001 | c := Block( 1002 | Id("x").Op(":=").Max(Lit(1), Lit(2)), 1003 | ) 1004 | fmt.Printf("%#v", c) 1005 | // Output: 1006 | // { 1007 | // x := max(1, 2) 1008 | // } 1009 | } 1010 | 1011 | func ExampleComment() { 1012 | f := NewFile("a") 1013 | f.Comment("Foo returns the string \"foo\"") 1014 | f.Func().Id("Foo").Params().String().Block( 1015 | Return(Lit("foo")).Comment("return the string foo"), 1016 | ) 1017 | fmt.Printf("%#v", f) 1018 | // Output: 1019 | // package a 1020 | // 1021 | // // Foo returns the string "foo" 1022 | // func Foo() string { 1023 | // return "foo" // return the string foo 1024 | // } 1025 | } 1026 | 1027 | func ExampleComment_multiline() { 1028 | c := Comment("a\nb") 1029 | fmt.Printf("%#v", c) 1030 | // Output: 1031 | // /* 1032 | // a 1033 | // b 1034 | // */ 1035 | } 1036 | 1037 | func ExampleComment_formatting_disabled() { 1038 | c := Id("foo").Call(Comment("/* inline */")).Comment("//no-space") 1039 | fmt.Printf("%#v", c) 1040 | // Output: 1041 | // foo( /* inline */ ) //no-space 1042 | } 1043 | 1044 | func ExampleCommentf() { 1045 | name := "foo" 1046 | val := "bar" 1047 | c := Id(name).Op(":=").Lit(val).Commentf("%s is the string \"%s\"", name, val) 1048 | fmt.Printf("%#v", c) 1049 | // Output: 1050 | // foo := "bar" // foo is the string "bar" 1051 | } 1052 | 1053 | func ExampleComplex() { 1054 | c := Func().Id("main").Params().Block( 1055 | Id("c1").Op(":=").Lit(1+3.75i), 1056 | Id("c2").Op(":=").Complex(Lit(1), Lit(3.75)), 1057 | Qual("fmt", "Println").Call(Id("c1").Op("==").Id("c2")), 1058 | ) 1059 | fmt.Printf("%#v", c) 1060 | // Output: 1061 | // func main() { 1062 | // c1 := (1 + 3.75i) 1063 | // c2 := complex(1, 3.75) 1064 | // fmt.Println(c1 == c2) 1065 | // } 1066 | } 1067 | 1068 | func ExampleComplex128() { 1069 | c := Func().Id("main").Params().Block( 1070 | Var().Id("c").Complex128(), 1071 | Id("c").Op("=").Lit(1+2i), 1072 | Qual("fmt", "Println").Call(Id("c")), 1073 | ) 1074 | fmt.Printf("%#v", c) 1075 | // Output: 1076 | // func main() { 1077 | // var c complex128 1078 | // c = (1 + 2i) 1079 | // fmt.Println(c) 1080 | // } 1081 | } 1082 | 1083 | func ExampleComplex64() { 1084 | c := Func().Id("main").Params().Block( 1085 | Var().Id("c64").Complex64(), 1086 | Id("c64").Op("=").Complex(Lit(5), Float32().Parens(Lit(2))), 1087 | Qual("fmt", "Printf").Call(Lit("%T\n"), Id("c64")), 1088 | ) 1089 | fmt.Printf("%#v", c) 1090 | // Output: 1091 | // func main() { 1092 | // var c64 complex64 1093 | // c64 = complex(5, float32(2)) 1094 | // fmt.Printf("%T\n", c64) 1095 | // } 1096 | } 1097 | 1098 | func ExampleParams() { 1099 | c := Func().Params( 1100 | Id("a").Id("A"), 1101 | ).Id("foo").Params( 1102 | Id("b"), 1103 | Id("c").String(), 1104 | ).String().Block( 1105 | Return(Id("b").Op("+").Id("c")), 1106 | ) 1107 | fmt.Printf("%#v", c) 1108 | // Output: 1109 | // func (a A) foo(b, c string) string { 1110 | // return b + c 1111 | // } 1112 | } 1113 | 1114 | func ExampleIndex() { 1115 | c := Var().Id("a").Index().String() 1116 | fmt.Printf("%#v", c) 1117 | // Output: 1118 | // var a []string 1119 | } 1120 | 1121 | func ExampleIndex_index() { 1122 | c := Id("a").Op(":=").Id("b").Index(Lit(0), Lit(1)) 1123 | fmt.Printf("%#v", c) 1124 | // Output: 1125 | // a := b[0:1] 1126 | } 1127 | 1128 | func ExampleIndex_empty() { 1129 | c := Id("a").Op(":=").Id("b").Index(Lit(1), Empty()) 1130 | fmt.Printf("%#v", c) 1131 | // Output: 1132 | // a := b[1:] 1133 | } 1134 | 1135 | func ExampleOp() { 1136 | c := Id("a").Op(":=").Id("b").Call() 1137 | fmt.Printf("%#v", c) 1138 | // Output: 1139 | // a := b() 1140 | } 1141 | 1142 | func ExampleOp_star() { 1143 | c := Id("a").Op("=").Op("*").Id("b") 1144 | fmt.Printf("%#v", c) 1145 | // Output: 1146 | // a = *b 1147 | } 1148 | 1149 | func ExampleOp_variadic() { 1150 | c := Id("a").Call(Id("b").Op("...")) 1151 | fmt.Printf("%#v", c) 1152 | // Output: 1153 | // a(b...) 1154 | } 1155 | 1156 | func ExampleNewFilePath() { 1157 | f := NewFilePath("a.b/c") 1158 | f.Func().Id("init").Params().Block( 1159 | Qual("a.b/c", "Foo").Call().Comment("Local package - alias is omitted."), 1160 | Qual("d.e/f", "Bar").Call().Comment("Import is automatically added."), 1161 | Qual("g.h/f", "Baz").Call().Comment("Colliding package name is automatically renamed."), 1162 | ) 1163 | fmt.Printf("%#v", f) 1164 | // Output: 1165 | // package c 1166 | // 1167 | // import ( 1168 | // f "d.e/f" 1169 | // f1 "g.h/f" 1170 | // ) 1171 | // 1172 | // func init() { 1173 | // Foo() // Local package - alias is omitted. 1174 | // f.Bar() // Import is automatically added. 1175 | // f1.Baz() // Colliding package name is automatically renamed. 1176 | // } 1177 | } 1178 | 1179 | func ExampleStruct_empty() { 1180 | c := Id("c").Op(":=").Make(Chan().Struct()) 1181 | fmt.Printf("%#v", c) 1182 | // Output: 1183 | // c := make(chan struct{}) 1184 | } 1185 | 1186 | func ExampleStruct() { 1187 | c := Type().Id("foo").Struct( 1188 | List(Id("x"), Id("y")).Int(), 1189 | Id("u").Float32(), 1190 | ) 1191 | fmt.Printf("%#v", c) 1192 | // Output: 1193 | // type foo struct { 1194 | // x, y int 1195 | // u float32 1196 | // } 1197 | } 1198 | 1199 | func ExampleDefer() { 1200 | c := Defer().Id("foo").Call() 1201 | fmt.Printf("%#v", c) 1202 | // Output: 1203 | // defer foo() 1204 | } 1205 | 1206 | func ExampleGoto() { 1207 | c := Goto().Id("Outer") 1208 | fmt.Printf("%#v", c) 1209 | // Output: 1210 | // goto Outer 1211 | } 1212 | 1213 | func ExampleStatement_Clone_broken() { 1214 | a := Id("a") 1215 | c := Block( 1216 | a.Call(), 1217 | a.Call(), 1218 | ) 1219 | fmt.Printf("%#v", c) 1220 | // Output: 1221 | // { 1222 | // a()() 1223 | // a()() 1224 | // } 1225 | } 1226 | 1227 | func ExampleStatement_Clone_fixed() { 1228 | a := Id("a") 1229 | c := Block( 1230 | a.Clone().Call(), 1231 | a.Clone().Call(), 1232 | ) 1233 | fmt.Printf("%#v", c) 1234 | // Output: 1235 | // { 1236 | // a() 1237 | // a() 1238 | // } 1239 | } 1240 | 1241 | func ExampleFile_Render() { 1242 | f := NewFile("a") 1243 | f.Func().Id("main").Params().Block() 1244 | buf := &bytes.Buffer{} 1245 | err := f.Render(buf) 1246 | if err != nil { 1247 | fmt.Println(err.Error()) 1248 | } else { 1249 | fmt.Println(buf.String()) 1250 | } 1251 | // Output: 1252 | // package a 1253 | // 1254 | // func main() {} 1255 | } 1256 | 1257 | func ExampleLit() { 1258 | c := Id("a").Op(":=").Lit("a") 1259 | fmt.Printf("%#v", c) 1260 | // Output: 1261 | // a := "a" 1262 | } 1263 | 1264 | func ExampleLit_float() { 1265 | c := Id("a").Op(":=").Lit(1.5) 1266 | fmt.Printf("%#v", c) 1267 | // Output: 1268 | // a := 1.5 1269 | } 1270 | 1271 | func ExampleLitFunc() { 1272 | c := Id("a").Op(":=").LitFunc(func() interface{} { return 1 + 1 }) 1273 | fmt.Printf("%#v", c) 1274 | // Output: 1275 | // a := 2 1276 | } 1277 | 1278 | func ExampleDot() { 1279 | c := Qual("a.b/c", "Foo").Call().Dot("Bar").Index(Lit(0)).Dot("Baz") 1280 | fmt.Printf("%#v", c) 1281 | // Output: 1282 | // c.Foo().Bar[0].Baz 1283 | } 1284 | 1285 | func ExampleList() { 1286 | c := List(Id("a"), Err()).Op(":=").Id("b").Call() 1287 | fmt.Printf("%#v", c) 1288 | // Output: 1289 | // a, err := b() 1290 | } 1291 | 1292 | func ExampleQual() { 1293 | c := Qual("encoding/gob", "NewEncoder").Call() 1294 | fmt.Printf("%#v", c) 1295 | // Output: 1296 | // gob.NewEncoder() 1297 | } 1298 | 1299 | func ExampleQual_file() { 1300 | f := NewFilePath("a.b/c") 1301 | f.Func().Id("init").Params().Block( 1302 | Qual("a.b/c", "Foo").Call().Comment("Local package - name is omitted."), 1303 | Qual("d.e/f", "Bar").Call().Comment("Import is automatically added."), 1304 | Qual("g.h/f", "Baz").Call().Comment("Colliding package name is renamed."), 1305 | ) 1306 | fmt.Printf("%#v", f) 1307 | // Output: 1308 | // package c 1309 | // 1310 | // import ( 1311 | // f "d.e/f" 1312 | // f1 "g.h/f" 1313 | // ) 1314 | // 1315 | // func init() { 1316 | // Foo() // Local package - name is omitted. 1317 | // f.Bar() // Import is automatically added. 1318 | // f1.Baz() // Colliding package name is renamed. 1319 | // } 1320 | } 1321 | 1322 | func ExampleQual_local() { 1323 | f := NewFilePath("a.b/c") 1324 | f.Func().Id("main").Params().Block( 1325 | Qual("a.b/c", "D").Call(), 1326 | ) 1327 | fmt.Printf("%#v", f) 1328 | // Output: 1329 | // package c 1330 | // 1331 | // func main() { 1332 | // D() 1333 | // } 1334 | } 1335 | 1336 | func ExampleId() { 1337 | c := If(Id("i").Op("==").Id("j")).Block( 1338 | Return(Id("i")), 1339 | ) 1340 | fmt.Printf("%#v", c) 1341 | // Output: 1342 | // if i == j { 1343 | // return i 1344 | // } 1345 | } 1346 | 1347 | func ExampleErr() { 1348 | c := If( 1349 | Err().Op(":=").Id("foo").Call(), 1350 | Err().Op("!=").Nil(), 1351 | ).Block( 1352 | Return(Err()), 1353 | ) 1354 | fmt.Printf("%#v", c) 1355 | // Output: 1356 | // if err := foo(); err != nil { 1357 | // return err 1358 | // } 1359 | } 1360 | 1361 | func ExampleSwitch() { 1362 | c := Switch(Id("value").Dot("Kind").Call()).Block( 1363 | Case(Qual("reflect", "Float32"), Qual("reflect", "Float64")).Block( 1364 | Return(Lit("float")), 1365 | ), 1366 | Case(Qual("reflect", "Bool")).Block( 1367 | Return(Lit("bool")), 1368 | ), 1369 | Case(Qual("reflect", "Uintptr")).Block( 1370 | Fallthrough(), 1371 | ), 1372 | Default().Block( 1373 | Return(Lit("none")), 1374 | ), 1375 | ) 1376 | fmt.Printf("%#v", c) 1377 | // Output: 1378 | // switch value.Kind() { 1379 | // case reflect.Float32, reflect.Float64: 1380 | // return "float" 1381 | // case reflect.Bool: 1382 | // return "bool" 1383 | // case reflect.Uintptr: 1384 | // fallthrough 1385 | // default: 1386 | // return "none" 1387 | // } 1388 | } 1389 | 1390 | func ExampleTag() { 1391 | c := Type().Id("foo").Struct( 1392 | Id("A").String().Tag(map[string]string{"json": "a"}), 1393 | Id("B").Int().Tag(map[string]string{"json": "b", "bar": "baz"}), 1394 | ) 1395 | fmt.Printf("%#v", c) 1396 | // Output: 1397 | // type foo struct { 1398 | // A string `json:"a"` 1399 | // B int `bar:"baz" json:"b"` 1400 | // } 1401 | } 1402 | 1403 | func ExampleTag_withQuotesAndNewline() { 1404 | c := Type().Id("foo").Struct( 1405 | Id("A").String().Tag(map[string]string{"json": "a"}), 1406 | Id("B").Int().Tag(map[string]string{"json": "b", "bar": "the value of\nthe\"bar\" tag"}), 1407 | ) 1408 | fmt.Printf("%#v", c) 1409 | // Output: 1410 | // type foo struct { 1411 | // A string `json:"a"` 1412 | // B int `bar:"the value of\nthe\"bar\" tag" json:"b"` 1413 | // } 1414 | } 1415 | 1416 | func ExampleNull_and_nil() { 1417 | c := Func().Id("foo").Params( 1418 | nil, 1419 | Id("s").String(), 1420 | Null(), 1421 | Id("i").Int(), 1422 | ).Block() 1423 | fmt.Printf("%#v", c) 1424 | // Output: 1425 | // func foo(s string, i int) {} 1426 | } 1427 | 1428 | func ExampleNull_index() { 1429 | c := Id("a").Op(":=").Id("b").Index(Lit(1), Null()) 1430 | fmt.Printf("%#v", c) 1431 | // Output: 1432 | // a := b[1] 1433 | } 1434 | 1435 | func ExampleEmpty() { 1436 | c := Id("a").Op(":=").Id("b").Index(Lit(1), Empty()) 1437 | fmt.Printf("%#v", c) 1438 | // Output: 1439 | // a := b[1:] 1440 | } 1441 | 1442 | func ExampleBlock_complex() { 1443 | collection := func(name string, key Code, value Code) *Statement { 1444 | if key == nil { 1445 | // slice 1446 | return Var().Id(name).Index().Add(value) 1447 | } else { 1448 | // map 1449 | return Var().Id(name).Map(key).Add(value) 1450 | } 1451 | } 1452 | c := Func().Id("main").Params().Block( 1453 | collection("foo", nil, String()), 1454 | collection("bar", String(), Int()), 1455 | ) 1456 | fmt.Printf("%#v", c) 1457 | // Output: 1458 | // func main() { 1459 | // var foo []string 1460 | // var bar map[string]int 1461 | // } 1462 | } 1463 | 1464 | func ExampleFunc_declaration() { 1465 | c := Func().Id("a").Params().Block() 1466 | fmt.Printf("%#v", c) 1467 | // Output: 1468 | // func a() {} 1469 | } 1470 | 1471 | func ExampleFunc_literal() { 1472 | c := Id("a").Op(":=").Func().Params().Block() 1473 | fmt.Printf("%#v", c) 1474 | // Output: 1475 | // a := func() {} 1476 | } 1477 | 1478 | func ExampleInterface() { 1479 | c := Type().Id("a").Interface( 1480 | Id("b").Params().String(), 1481 | ) 1482 | fmt.Printf("%#v", c) 1483 | // Output: 1484 | // type a interface { 1485 | // b() string 1486 | // } 1487 | } 1488 | 1489 | func ExampleInterface_empty() { 1490 | c := Var().Id("a").Interface() 1491 | fmt.Printf("%#v", c) 1492 | // Output: 1493 | // var a interface{} 1494 | } 1495 | 1496 | func ExampleParens() { 1497 | c := Id("b").Op(":=").Index().Byte().Parens(Id("s")) 1498 | fmt.Printf("%#v", c) 1499 | // Output: 1500 | // b := []byte(s) 1501 | } 1502 | 1503 | func ExampleParens_order() { 1504 | c := Id("a").Op("/").Parens(Id("b").Op("+").Id("c")) 1505 | fmt.Printf("%#v", c) 1506 | // Output: 1507 | // a / (b + c) 1508 | } 1509 | 1510 | func ExampleValues() { 1511 | c := Index().String().Values(Lit("a"), Lit("b")) 1512 | fmt.Printf("%#v", c) 1513 | // Output: 1514 | // []string{"a", "b"} 1515 | } 1516 | 1517 | func ExampleDo() { 1518 | f := func(name string, isMap bool) *Statement { 1519 | return Id(name).Op(":=").Do(func(s *Statement) { 1520 | if isMap { 1521 | s.Map(String()).String() 1522 | } else { 1523 | s.Index().String() 1524 | } 1525 | }).Values() 1526 | } 1527 | fmt.Printf("%#v\n%#v", f("a", true), f("b", false)) 1528 | // Output: 1529 | // a := map[string]string{} 1530 | // b := []string{} 1531 | } 1532 | 1533 | func ExampleReturn() { 1534 | c := Return(Id("a"), Id("b")) 1535 | fmt.Printf("%#v", c) 1536 | // Output: 1537 | // return a, b 1538 | } 1539 | 1540 | func ExampleMap() { 1541 | c := Id("a").Op(":=").Map(String()).String().Values() 1542 | fmt.Printf("%#v", c) 1543 | // Output: 1544 | // a := map[string]string{} 1545 | } 1546 | 1547 | func ExampleDict() { 1548 | c := Id("a").Op(":=").Map(String()).String().Values(Dict{ 1549 | Lit("a"): Lit("b"), 1550 | Lit("c"): Lit("d"), 1551 | }) 1552 | fmt.Printf("%#v", c) 1553 | // Output: 1554 | // a := map[string]string{ 1555 | // "a": "b", 1556 | // "c": "d", 1557 | // } 1558 | } 1559 | 1560 | func ExampleDict_nil() { 1561 | c := Id("a").Op(":=").Map(String()).String().Values() 1562 | fmt.Printf("%#v", c) 1563 | // Output: 1564 | // a := map[string]string{} 1565 | } 1566 | 1567 | func ExampleDictFunc() { 1568 | c := Id("a").Op(":=").Map(String()).String().Values(DictFunc(func(d Dict) { 1569 | d[Lit("a")] = Lit("b") 1570 | d[Lit("c")] = Lit("d") 1571 | })) 1572 | fmt.Printf("%#v", c) 1573 | // Output: 1574 | // a := map[string]string{ 1575 | // "a": "b", 1576 | // "c": "d", 1577 | // } 1578 | } 1579 | 1580 | func ExampleDefs() { 1581 | c := Const().Defs( 1582 | Id("a").Op("=").Lit("a"), 1583 | Id("b").Op("=").Lit("b"), 1584 | ) 1585 | fmt.Printf("%#v", c) 1586 | // Output: 1587 | // const ( 1588 | // a = "a" 1589 | // b = "b" 1590 | // ) 1591 | } 1592 | 1593 | func ExampleIf() { 1594 | c := If( 1595 | Err().Op(":=").Id("a").Call(), 1596 | Err().Op("!=").Nil(), 1597 | ).Block( 1598 | Return(Err()), 1599 | ) 1600 | fmt.Printf("%#v", c) 1601 | // Output: 1602 | // if err := a(); err != nil { 1603 | // return err 1604 | // } 1605 | } 1606 | 1607 | func ExampleId_local() { 1608 | c := Id("a").Op(":=").Lit(1) 1609 | fmt.Printf("%#v", c) 1610 | // Output: 1611 | // a := 1 1612 | } 1613 | 1614 | func ExampleId_select() { 1615 | c := Id("a").Dot("b").Dot("c").Call() 1616 | fmt.Printf("%#v", c) 1617 | // Output: 1618 | // a.b.c() 1619 | } 1620 | 1621 | func ExampleId_remote() { 1622 | f := NewFile("main") 1623 | f.Func().Id("main").Params().Block( 1624 | Qual("fmt", "Println").Call( 1625 | Lit("Hello, world"), 1626 | ), 1627 | ) 1628 | fmt.Printf("%#v", f) 1629 | // Output: 1630 | // package main 1631 | // 1632 | // import "fmt" 1633 | // 1634 | // func main() { 1635 | // fmt.Println("Hello, world") 1636 | // } 1637 | } 1638 | 1639 | func ExampleFor() { 1640 | c := For( 1641 | Id("i").Op(":=").Lit(0), 1642 | Id("i").Op("<").Lit(10), 1643 | Id("i").Op("++"), 1644 | ).Block( 1645 | Qual("fmt", "Println").Call(Id("i")), 1646 | ) 1647 | fmt.Printf("%#v", c) 1648 | // Output: 1649 | // for i := 0; i < 10; i++ { 1650 | // fmt.Println(i) 1651 | // } 1652 | } 1653 | 1654 | func ExampleNewFile() { 1655 | f := NewFile("main") 1656 | f.Func().Id("main").Params().Block( 1657 | Qual("fmt", "Println").Call(Lit("Hello, world")), 1658 | ) 1659 | fmt.Printf("%#v", f) 1660 | // Output: 1661 | // package main 1662 | // 1663 | // import "fmt" 1664 | // 1665 | // func main() { 1666 | // fmt.Println("Hello, world") 1667 | // } 1668 | } 1669 | 1670 | func ExampleNewFilePathName() { 1671 | f := NewFilePathName("a.b/c", "main") 1672 | f.Func().Id("main").Params().Block( 1673 | Qual("a.b/c", "Foo").Call(), 1674 | ) 1675 | fmt.Printf("%#v", f) 1676 | // Output: 1677 | // package main 1678 | // 1679 | // func main() { 1680 | // Foo() 1681 | // } 1682 | } 1683 | 1684 | func ExampleFile_HeaderAndPackageComments() { 1685 | f := NewFile("c") 1686 | f.CanonicalPath = "d.e/f" 1687 | f.HeaderComment("Code generated by...") 1688 | f.PackageComment("Package c implements...") 1689 | f.Func().Id("init").Params().Block() 1690 | fmt.Printf("%#v", f) 1691 | // Output: 1692 | // // Code generated by... 1693 | // 1694 | // // Package c implements... 1695 | // package c // import "d.e/f" 1696 | // 1697 | // func init() {} 1698 | } 1699 | 1700 | func ExampleFile_Anon() { 1701 | f := NewFile("c") 1702 | f.Anon("a") 1703 | f.Func().Id("init").Params().Block() 1704 | fmt.Printf("%#v", f) 1705 | // Output: 1706 | // package c 1707 | // 1708 | // import _ "a" 1709 | // 1710 | // func init() {} 1711 | } 1712 | 1713 | func ExampleFile_PackagePrefix() { 1714 | f := NewFile("a") 1715 | f.PackagePrefix = "pkg" 1716 | f.Func().Id("main").Params().Block( 1717 | Qual("b.c/d", "E").Call(), 1718 | ) 1719 | fmt.Printf("%#v", f) 1720 | // Output: 1721 | // package a 1722 | // 1723 | // import pkg_d "b.c/d" 1724 | // 1725 | // func main() { 1726 | // pkg_d.E() 1727 | // } 1728 | } 1729 | 1730 | func ExampleFile_NoFormat() { 1731 | f := NewFile("main") 1732 | f.NoFormat = true 1733 | 1734 | f.Func().Id("main").Params().Block( 1735 | Qual("fmt", "Println").Call(Lit("foo")), 1736 | ) 1737 | fmt.Printf("%q", fmt.Sprintf("%#v", f)) // Special case because Go Examples don't handle multiple newlines well. 1738 | 1739 | // Output: 1740 | // "package main\n\nimport \"fmt\"\n\n\nfunc main () {\nfmt.Println (\"foo\")\n}" 1741 | } 1742 | -------------------------------------------------------------------------------- /jen/file.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "regexp" 7 | "strings" 8 | "unicode" 9 | "unicode/utf8" 10 | ) 11 | 12 | // NewFile Creates a new file, with the specified package name. 13 | func NewFile(packageName string) *File { 14 | return &File{ 15 | Group: &Group{ 16 | multi: true, 17 | }, 18 | name: packageName, 19 | imports: map[string]importdef{}, 20 | hints: map[string]importdef{}, 21 | } 22 | } 23 | 24 | // NewFilePath creates a new file while specifying the package path - the 25 | // package name is inferred from the path. 26 | func NewFilePath(packagePath string) *File { 27 | return &File{ 28 | Group: &Group{ 29 | multi: true, 30 | }, 31 | name: guessAlias(packagePath), 32 | path: packagePath, 33 | imports: map[string]importdef{}, 34 | hints: map[string]importdef{}, 35 | } 36 | } 37 | 38 | // NewFilePathName creates a new file with the specified package path and name. 39 | func NewFilePathName(packagePath, packageName string) *File { 40 | return &File{ 41 | Group: &Group{ 42 | multi: true, 43 | }, 44 | name: packageName, 45 | path: packagePath, 46 | imports: map[string]importdef{}, 47 | hints: map[string]importdef{}, 48 | } 49 | } 50 | 51 | // File represents a single source file. Package imports are managed 52 | // automatically by File. 53 | type File struct { 54 | *Group 55 | name string 56 | path string 57 | imports map[string]importdef 58 | hints map[string]importdef 59 | comments []string 60 | headers []string 61 | cgoPreamble []string 62 | // NoFormat can be set to true to disable formatting of the generated source. This may be useful 63 | // when performance is critical, and readable code is not required. 64 | NoFormat bool 65 | // If you're worried about generated package aliases conflicting with local variable names, you 66 | // can set a prefix here. Package foo becomes {prefix}_foo. 67 | PackagePrefix string 68 | // CanonicalPath adds a canonical import path annotation to the package clause. 69 | CanonicalPath string 70 | } 71 | 72 | // importdef is used to differentiate packages where we know the package name from packages where the 73 | // import is aliased. If alias == false, then name is the actual package name, and the import will be 74 | // rendered without an alias. If used == false, the import has not been used in code yet and should be 75 | // excluded from the import block. 76 | type importdef struct { 77 | name string 78 | alias bool 79 | } 80 | 81 | // HeaderComment adds a comment to the top of the file, above any package 82 | // comments. A blank line is rendered below the header comments, ensuring 83 | // header comments are not included in the package doc. 84 | func (f *File) HeaderComment(comment string) { 85 | f.headers = append(f.headers, comment) 86 | } 87 | 88 | // PackageComment adds a comment to the top of the file, above the package 89 | // keyword. 90 | func (f *File) PackageComment(comment string) { 91 | f.comments = append(f.comments, comment) 92 | } 93 | 94 | // CgoPreamble adds a cgo preamble comment that is rendered directly before the "C" pseudo-package 95 | // import. 96 | func (f *File) CgoPreamble(comment string) { 97 | f.cgoPreamble = append(f.cgoPreamble, comment) 98 | } 99 | 100 | // Anon adds an anonymous import. 101 | func (f *File) Anon(paths ...string) { 102 | for _, p := range paths { 103 | f.imports[p] = importdef{name: "_", alias: true} 104 | } 105 | } 106 | 107 | // ImportName provides the package name for a path. If specified, the alias will be omitted from the 108 | // import block. This is optional. If not specified, a sensible package name is used based on the path 109 | // and this is added as an alias in the import block. 110 | func (f *File) ImportName(path, name string) { 111 | f.hints[path] = importdef{name: name, alias: false} 112 | } 113 | 114 | // ImportNames allows multiple names to be imported as a map. Use the [gennames](gennames) command to 115 | // automatically generate a go file containing a map of a selection of package names. 116 | func (f *File) ImportNames(names map[string]string) { 117 | for path, name := range names { 118 | f.hints[path] = importdef{name: name, alias: false} 119 | } 120 | } 121 | 122 | // ImportAlias provides the alias for a package path that should be used in the import block. A 123 | // period can be used to force a dot-import. 124 | func (f *File) ImportAlias(path, alias string) { 125 | f.hints[path] = importdef{name: alias, alias: true} 126 | } 127 | 128 | func (f *File) isLocal(path string) bool { 129 | return f.path == path 130 | } 131 | 132 | func (f *File) isValidAlias(alias string) bool { 133 | // multiple dot-imports are ok 134 | if alias == "." { 135 | return true 136 | } 137 | // the import alias is invalid if it's a reserved word 138 | if IsReservedWord(alias) { 139 | return false 140 | } 141 | // the import alias is invalid if it's already been registered 142 | for _, v := range f.imports { 143 | if alias == v.name { 144 | return false 145 | } 146 | } 147 | return true 148 | } 149 | 150 | func (f *File) isDotImport(path string) bool { 151 | if id, ok := f.hints[path]; ok { 152 | return id.name == "." && id.alias 153 | } 154 | return false 155 | } 156 | 157 | func (f *File) register(path string) string { 158 | if f.isLocal(path) { 159 | // notest 160 | // should never get here because in Qual the packageToken will be null, 161 | // so render will never be called. 162 | return "" 163 | } 164 | 165 | // if the path has been registered previously, simply return the name 166 | def := f.imports[path] 167 | if def.name != "" && def.name != "_" { 168 | return def.name 169 | } 170 | 171 | // special case for "C" pseudo-package 172 | if path == "C" { 173 | f.imports["C"] = importdef{name: "C", alias: false} 174 | return "C" 175 | } 176 | 177 | var name string 178 | var alias bool 179 | 180 | if hint := f.hints[path]; hint.name != "" { 181 | // look up the path in the list of provided package names and aliases by ImportName / ImportAlias 182 | name = hint.name 183 | alias = hint.alias 184 | } else if standardLibraryHints[path] != "" { 185 | // look up the path in the list of standard library packages 186 | name = standardLibraryHints[path] 187 | alias = false 188 | } else { 189 | // if a hint is not found for the package, guess the alias from the package path 190 | name = guessAlias(path) 191 | alias = true 192 | } 193 | 194 | // If the name is invalid or has been registered already, make it unique by appending a number 195 | unique := name 196 | i := 0 197 | for !f.isValidAlias(unique) { 198 | i++ 199 | unique = fmt.Sprintf("%s%d", name, i) 200 | } 201 | 202 | // If we've changed the name to make it unique, it should definitely be an alias 203 | if unique != name { 204 | alias = true 205 | } 206 | 207 | // Only add a prefix if the name is an alias 208 | if f.PackagePrefix != "" && alias { 209 | unique = f.PackagePrefix + "_" + unique 210 | } 211 | 212 | // Register the eventual name 213 | f.imports[path] = importdef{name: unique, alias: alias} 214 | 215 | return unique 216 | } 217 | 218 | // GoString renders the File for testing. Any error will cause a panic. 219 | func (f *File) GoString() string { 220 | buf := &bytes.Buffer{} 221 | if err := f.Render(buf); err != nil { 222 | panic(err) 223 | } 224 | return buf.String() 225 | } 226 | 227 | func guessAlias(path string) string { 228 | alias := path 229 | 230 | if strings.HasSuffix(alias, "/") { 231 | // training slashes are usually tolerated, so we can get rid of one if 232 | // it exists 233 | alias = alias[:len(alias)-1] 234 | } 235 | 236 | if strings.Contains(alias, "/") { 237 | // if the path contains a "/", use the last part 238 | alias = alias[strings.LastIndex(alias, "/")+1:] 239 | } 240 | 241 | // alias should be lower case 242 | alias = strings.ToLower(alias) 243 | 244 | // alias should now only contain alphanumerics 245 | importsRegex := regexp.MustCompile(`[^a-z0-9]`) 246 | alias = importsRegex.ReplaceAllString(alias, "") 247 | 248 | // can't have a first digit, per Go identifier rules, so just skip them 249 | for firstRune, runeLen := utf8.DecodeRuneInString(alias); unicode.IsDigit(firstRune); firstRune, runeLen = utf8.DecodeRuneInString(alias) { 250 | alias = alias[runeLen:] 251 | } 252 | 253 | // If path part was all digits, we may be left with an empty string. In this case use "pkg" as the alias. 254 | if alias == "" { 255 | alias = "pkg" 256 | } 257 | 258 | return alias 259 | } 260 | -------------------------------------------------------------------------------- /jen/file_test.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGuessAlias(t *testing.T) { 9 | 10 | data := map[string]string{ 11 | "A": "a", 12 | "a": "a", 13 | "a$": "a", 14 | "a/b": "b", 15 | "a/b/c": "c", 16 | "a/b/c-d": "cd", 17 | "a/b/c-d/": "cd", 18 | "a.b": "ab", 19 | "a/b.c": "bc", 20 | "a/b-c.d": "bcd", 21 | "a/bb-ccc.dddd": "bbcccdddd", 22 | "a/foo-go": "foogo", 23 | "123a": "a", 24 | "a/321a.b": "ab", 25 | "a/123": "pkg", 26 | } 27 | for path, expected := range data { 28 | if guessAlias(path) != expected { 29 | fmt.Printf("guessAlias test failed %s should return %s but got %s\n", path, expected, guessAlias(path)) 30 | t.Fail() 31 | } 32 | } 33 | } 34 | 35 | func TestValidAlias(t *testing.T) { 36 | data := map[string]bool{ 37 | "a": true, // ok 38 | "b": false, // already registered 39 | "go": false, // keyword 40 | "int": false, // predeclared 41 | "err": false, // common name 42 | } 43 | f := NewFile("test") 44 | f.register("b") 45 | for alias, expected := range data { 46 | if f.isValidAlias(alias) != expected { 47 | fmt.Printf("isValidAlias test failed %s should return %t but got %t\n", alias, expected, f.isValidAlias(alias)) 48 | t.Fail() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /jen/generated_test.go: -------------------------------------------------------------------------------- 1 | package jen_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/dave/jennifer/jen" 7 | ) 8 | 9 | var gencases = []tc{ 10 | { 11 | desc: `typesfunc group`, 12 | // Don't do this! ListFunc used to kludge Group.TypesFunc usage without 13 | // syntax error. 14 | code: Id("a").ListFunc(func(lg *Group) { lg.TypesFunc(func(cg *Group) { cg.Lit(1) }) }), 15 | expect: `a[1]`, 16 | }, 17 | { 18 | desc: `types group`, 19 | // Don't do this! ListFunc used to kludge Group.Types usage without 20 | // syntax error. 21 | code: Id("a").ListFunc(func(lg *Group) { lg.Types(Lit(1)) }), 22 | expect: `a[1]`, 23 | }, 24 | { 25 | desc: `bool group`, 26 | code: BlockFunc(func(g *Group) { 27 | g.Bool() 28 | }), 29 | expect: `{ 30 | bool 31 | }`, 32 | }, 33 | { 34 | desc: `recover func`, 35 | code: Recover(), 36 | expect: `recover()`, 37 | }, 38 | { 39 | desc: `recover statement`, 40 | code: Null().Recover(), 41 | expect: `recover()`, 42 | }, 43 | { 44 | desc: `recover group`, 45 | code: BlockFunc(func(g *Group) { 46 | g.Recover() 47 | }), 48 | expect: `{ 49 | recover() 50 | }`, 51 | }, 52 | { 53 | desc: `real func`, 54 | code: Real(Id("a")), 55 | expect: `real(a)`, 56 | }, 57 | { 58 | desc: `real statement`, 59 | code: Null().Real(Id("a")), 60 | expect: `real(a)`, 61 | }, 62 | { 63 | desc: `real group`, 64 | code: BlockFunc(func(g *Group) { 65 | g.Real(Id("a")) 66 | }), 67 | expect: `{ 68 | real(a) 69 | }`, 70 | }, 71 | { 72 | desc: `printlnfunc func`, 73 | code: PrintlnFunc(func(g *Group) { g.Id("a") }), 74 | expect: `println(a)`, 75 | }, 76 | { 77 | desc: `printlnfunc statement`, 78 | code: Null().PrintlnFunc(func(g *Group) { g.Id("a") }), 79 | expect: `println(a)`, 80 | }, 81 | { 82 | desc: `printlnfunc group`, 83 | code: BlockFunc(func(bg *Group) { 84 | bg.PrintlnFunc(func(pg *Group) { pg.Id("a") }) 85 | }), 86 | expect: `{ 87 | println(a) 88 | }`, 89 | }, 90 | { 91 | desc: `println func`, 92 | code: Println(Id("a")), 93 | expect: `println(a)`, 94 | }, 95 | { 96 | desc: `println statement`, 97 | code: Null().Println(Id("a")), 98 | expect: `println(a)`, 99 | }, 100 | { 101 | desc: `println group`, 102 | code: BlockFunc(func(g *Group) { 103 | g.Println(Id("a")) 104 | }), 105 | expect: `{ 106 | println(a) 107 | }`, 108 | }, 109 | { 110 | desc: `printfunc func`, 111 | code: PrintFunc(func(g *Group) { g.Id("a") }), 112 | expect: `print(a)`, 113 | }, 114 | { 115 | desc: `printfunc statement`, 116 | code: Null().PrintFunc(func(g *Group) { g.Id("a") }), 117 | expect: `print(a)`, 118 | }, 119 | { 120 | desc: `printfunc group`, 121 | code: BlockFunc(func(bg *Group) { 122 | bg.PrintFunc(func(pg *Group) { pg.Id("a") }) 123 | }), 124 | expect: `{ 125 | print(a) 126 | }`, 127 | }, 128 | { 129 | desc: `print func`, 130 | code: Print(Id("a")), 131 | expect: `print(a)`, 132 | }, 133 | { 134 | desc: `print statement`, 135 | code: Null().Print(Id("a")), 136 | expect: `print(a)`, 137 | }, 138 | { 139 | desc: `print group`, 140 | code: BlockFunc(func(g *Group) { 141 | g.Print(Id("a")) 142 | }), 143 | expect: `{ 144 | print(a) 145 | }`, 146 | }, 147 | { 148 | desc: `panic func`, 149 | code: Panic(Id("a")), 150 | expect: `panic(a)`, 151 | }, 152 | { 153 | desc: `panic statement`, 154 | code: Null().Panic(Id("a")), 155 | expect: `panic(a)`, 156 | }, 157 | { 158 | desc: `panic group`, 159 | code: BlockFunc(func(g *Group) { 160 | g.Panic(Id("a")) 161 | }), 162 | expect: `{ 163 | panic(a) 164 | }`, 165 | }, 166 | { 167 | desc: `new func`, 168 | code: New(Id("a")), 169 | expect: `new(a)`, 170 | }, 171 | { 172 | desc: `new statement`, 173 | code: Id("a").Op(":=").New(Id("a")), 174 | expect: `a := new(a)`, 175 | }, 176 | { 177 | desc: `new group`, 178 | code: BlockFunc(func(g *Group) { 179 | g.New(Id("a")) 180 | }), 181 | expect: `{ 182 | new(a) 183 | }`, 184 | }, 185 | { 186 | desc: `make func`, 187 | code: Make(Id("a")), 188 | expect: `make(a)`, 189 | }, 190 | { 191 | desc: `make statement`, 192 | code: Id("a").Op(":=").Make(Id("a")), 193 | expect: `a := make(a)`, 194 | }, 195 | { 196 | desc: `make group`, 197 | code: BlockFunc(func(g *Group) { 198 | g.Make(Id("a")) 199 | }), 200 | expect: `{ 201 | make(a) 202 | }`, 203 | }, 204 | { 205 | desc: `len func`, 206 | code: Len(Id("a")), 207 | expect: `len(a)`, 208 | }, 209 | { 210 | desc: `len statement`, 211 | code: Id("a").Op(":=").Len(Id("a")), 212 | expect: `a := len(a)`, 213 | }, 214 | { 215 | desc: `len group`, 216 | code: BlockFunc(func(g *Group) { 217 | g.Len(Id("a")) 218 | }), 219 | expect: `{ 220 | len(a) 221 | }`, 222 | }, 223 | { 224 | desc: `imag func`, 225 | code: Imag(Id("a")), 226 | expect: `imag(a)`, 227 | }, 228 | { 229 | desc: `imag statement`, 230 | code: Id("a").Op(":=").Imag(Id("a")), 231 | expect: `a := imag(a)`, 232 | }, 233 | { 234 | desc: `imag group`, 235 | code: BlockFunc(func(g *Group) { 236 | g.Imag(Id("a")) 237 | }), 238 | expect: `{ 239 | imag(a) 240 | }`, 241 | }, 242 | { 243 | desc: `delete func`, 244 | code: Delete(Id("a"), Id("b")), 245 | expect: `delete(a, b)`, 246 | }, 247 | { 248 | desc: `delete statement`, 249 | code: Null().Delete(Id("a"), Id("b")), 250 | expect: `delete(a, b)`, 251 | }, 252 | { 253 | desc: `delete group`, 254 | code: BlockFunc(func(g *Group) { 255 | g.Delete(Id("a"), Id("b")) 256 | }), 257 | expect: `{ 258 | delete(a, b) 259 | }`, 260 | }, 261 | { 262 | desc: `copy func`, 263 | code: Copy(Id("a"), Id("b")), 264 | expect: `copy(a, b)`, 265 | }, 266 | { 267 | desc: `copy statement`, 268 | code: Id("a").Op(":=").Copy(Id("a"), Id("b")), 269 | expect: `a := copy(a, b)`, 270 | }, 271 | { 272 | desc: `copy group`, 273 | code: BlockFunc(func(g *Group) { 274 | g.Copy(Id("a"), Id("b")) 275 | }), 276 | expect: `{ 277 | copy(a, b) 278 | }`, 279 | }, 280 | { 281 | desc: `complex func`, 282 | code: Complex(Id("a"), Id("b")), 283 | expect: `complex(a, b)`, 284 | }, 285 | { 286 | desc: `complex statement`, 287 | code: Id("a").Op(":=").Complex(Id("a"), Id("b")), 288 | expect: `a := complex(a, b)`, 289 | }, 290 | { 291 | desc: `complex group`, 292 | code: BlockFunc(func(g *Group) { 293 | g.Complex(Id("a"), Id("b")) 294 | }), 295 | expect: `{ 296 | complex(a, b) 297 | }`, 298 | }, 299 | { 300 | desc: `close group`, 301 | code: BlockFunc(func(g *Group) { g.Close(Id("a")) }), 302 | expect: `{ 303 | close(a) 304 | }`, 305 | }, 306 | { 307 | desc: `cap func`, 308 | code: Cap(Id("a")), 309 | expect: `cap(a)`, 310 | }, 311 | { 312 | desc: `cap statement`, 313 | code: Id("a").Op(":=").Cap(Id("b")), 314 | expect: `a := cap(b)`, 315 | }, 316 | { 317 | desc: `cap group`, 318 | code: BlockFunc(func(g *Group) { 319 | g.Cap(Id("a")) 320 | }), 321 | expect: `{ 322 | cap(a) 323 | }`, 324 | }, 325 | { 326 | desc: `append group`, 327 | code: BlockFunc(func(g *Group) { 328 | g.Append(Id("a")) 329 | }), 330 | expect: `{ 331 | append(a) 332 | }`, 333 | }, 334 | { 335 | desc: `appendfunc statement`, 336 | code: Id("a").Op("=").AppendFunc(func(ag *Group) { ag.Id("a") }), 337 | expect: `a = append(a)`, 338 | }, 339 | { 340 | desc: `appendfunc func`, 341 | code: AppendFunc(func(ag *Group) { ag.Id("a") }), 342 | expect: `append(a)`, 343 | }, 344 | { 345 | desc: `appendfunc group`, 346 | code: BlockFunc(func(bg *Group) { 347 | bg.AppendFunc(func(ag *Group) { ag.Id("a") }) 348 | }), 349 | expect: `{ 350 | append(a) 351 | }`, 352 | }, 353 | { 354 | desc: `casefunc group`, 355 | code: Switch().BlockFunc(func(g *Group) { 356 | g.CaseFunc(func(g *Group) { g.Id("a") }).Block() 357 | }), 358 | expect: `switch { 359 | case a: 360 | }`, 361 | }, 362 | { 363 | desc: `case group`, 364 | code: Switch().BlockFunc(func(g *Group) { 365 | g.Case(Id("a")).Block() 366 | }), 367 | expect: `switch { 368 | case a: 369 | }`, 370 | }, 371 | { 372 | desc: `structfunc statement`, 373 | code: Id("a").Op(":=").StructFunc(func(g *Group) {}).Values(), 374 | expect: `a := struct{}{}`, 375 | }, 376 | { 377 | desc: `structfunc group`, 378 | // Don't do this! ListFunc used to kludge Group.Struct usage 379 | // without syntax error. 380 | code: Id("a").Op(":=").ListFunc(func(g *Group) { g.StructFunc(func(g *Group) {}) }).Values(), 381 | expect: `a := struct{}{}`, 382 | }, 383 | { 384 | desc: `structfunc func`, 385 | code: Id("a").Op(":=").Add(StructFunc(func(g *Group) {})).Values(), 386 | expect: `a := struct{}{}`, 387 | }, 388 | { 389 | desc: `struct group`, 390 | // Don't do this! ListFunc used to kludge Group.Struct usage 391 | // without syntax error. 392 | code: Id("a").Op(":=").ListFunc(func(g *Group) { g.Struct() }).Values(), 393 | expect: `a := struct{}{}`, 394 | }, 395 | { 396 | desc: `struct func`, 397 | code: Id("a").Op(":=").Add(Struct()).Values(), 398 | expect: `a := struct{}{}`, 399 | }, 400 | { 401 | desc: `interfacefunc func`, 402 | code: Id("a").Assert(InterfaceFunc(func(g *Group) { 403 | g.Id("a").Call().Int() 404 | g.Id("b").Call().Int() 405 | })), 406 | expect: `a.(interface{ 407 | a() int 408 | b() int 409 | })`, 410 | }, 411 | { 412 | desc: `interfacefunc statement`, 413 | code: Id("a").Assert(Null().InterfaceFunc(func(g *Group) { 414 | g.Id("a").Call().Int() 415 | g.Id("b").Call().Int() 416 | })), 417 | expect: `a.(interface{ 418 | a() int 419 | b() int 420 | })`, 421 | }, 422 | { 423 | desc: `interfacefunc group`, 424 | // Don't do this! ListFunc used to kludge Group.InterfaceFunc usage 425 | // without syntax error. 426 | code: Id("a").Assert(ListFunc(func(lg *Group) { 427 | lg.InterfaceFunc(func(ig *Group) { 428 | ig.Id("a").Call().Int() 429 | ig.Id("b").Call().Int() 430 | }) 431 | })), 432 | expect: `a.(interface{ 433 | a() int 434 | b() int 435 | })`, 436 | }, 437 | { 438 | desc: `interface func`, 439 | code: Interface().Parens(Id("a")), 440 | expect: `interface{}(a)`, 441 | }, 442 | { 443 | desc: `interface group`, 444 | code: BlockFunc(func(g *Group) { 445 | g.Interface().Parens(Id("a")) 446 | }), 447 | expect: `{ 448 | interface{}(a) 449 | }`, 450 | }, 451 | { 452 | desc: `interface statement`, 453 | code: Null().Interface().Parens(Id("a")), 454 | expect: `interface{}(a)`, 455 | }, 456 | { 457 | desc: `switchfunc func`, 458 | code: SwitchFunc(func(rg *Group) { 459 | rg.Id("a") 460 | }).Block(), 461 | expect: `switch a {}`, 462 | }, 463 | { 464 | desc: `switchfunc statement`, 465 | code: Null().SwitchFunc(func(rg *Group) { 466 | rg.Id("a") 467 | }).Block(), 468 | expect: `switch a { 469 | }`, 470 | }, 471 | { 472 | desc: `switchfunc group`, 473 | code: BlockFunc(func(bg *Group) { 474 | bg.SwitchFunc(func(rg *Group) { 475 | rg.Id("a") 476 | }).Block() 477 | }), 478 | expect: `{ 479 | switch a { 480 | } 481 | }`, 482 | }, 483 | { 484 | desc: `switch group`, 485 | code: BlockFunc(func(bg *Group) { 486 | bg.Switch().Block() 487 | }), 488 | expect: `{ 489 | switch { 490 | } 491 | }`, 492 | }, 493 | { 494 | desc: `forfunc func`, 495 | code: ForFunc(func(rg *Group) { 496 | rg.Id("a") 497 | }).Block(), 498 | expect: `for a {}`, 499 | }, 500 | { 501 | desc: `forfunc statement`, 502 | code: Null().ForFunc(func(rg *Group) { 503 | rg.Id("a") 504 | }).Block(), 505 | expect: `for a { 506 | }`, 507 | }, 508 | { 509 | desc: `forfunc group`, 510 | code: BlockFunc(func(bg *Group) { 511 | bg.ForFunc(func(rg *Group) { 512 | rg.Id("a") 513 | }).Block() 514 | }), 515 | expect: `{ 516 | for a { 517 | } 518 | }`, 519 | }, 520 | { 521 | desc: `for group`, 522 | code: BlockFunc(func(g *Group) { 523 | g.For(Id("a")).Block() 524 | }), 525 | expect: `{ 526 | for a {} 527 | }`, 528 | }, 529 | { 530 | desc: `returnfunc func`, 531 | code: ReturnFunc(func(rg *Group) { 532 | rg.Lit(1) 533 | rg.Lit(2) 534 | }), 535 | expect: `return 1, 2`, 536 | }, 537 | { 538 | desc: `returnfunc statement`, 539 | code: Empty().ReturnFunc(func(rg *Group) { 540 | rg.Lit(1) 541 | rg.Lit(2) 542 | }), 543 | expect: `return 1, 2`, 544 | }, 545 | { 546 | desc: `returnfunc group`, 547 | code: BlockFunc(func(bg *Group) { 548 | bg.ReturnFunc(func(rg *Group) { 549 | rg.Lit(1) 550 | rg.Lit(2) 551 | }) 552 | }), 553 | expect: `{ 554 | return 1, 2 555 | }`, 556 | }, 557 | { 558 | desc: `return group`, 559 | code: BlockFunc(func(g *Group) { 560 | g.Return() 561 | }), 562 | expect: `{ 563 | return 564 | }`, 565 | }, 566 | { 567 | desc: `iffunc group`, 568 | code: BlockFunc(func(bg *Group) { 569 | bg.IfFunc(func(ig *Group) { 570 | ig.Id("a") 571 | }).Block() 572 | }), 573 | expect: `{ 574 | if a {} 575 | }`, 576 | }, 577 | { 578 | desc: `iffunc func`, 579 | code: IfFunc(func(ig *Group) { 580 | ig.Id("a") 581 | }).Block(), 582 | expect: `if a {}`, 583 | }, 584 | { 585 | desc: `iffunc statement`, 586 | code: Null().IfFunc(func(ig *Group) { 587 | ig.Id("a") 588 | }).Block(), 589 | expect: `if a {}`, 590 | }, 591 | { 592 | desc: `if group`, 593 | code: BlockFunc(func(g *Group) { g.If(Id("a")).Block() }), 594 | expect: `{ 595 | if a {} 596 | }`, 597 | }, 598 | { 599 | desc: `map group`, 600 | code: BlockFunc(func(g *Group) { g.Map(Int()).Int().Values(Dict{Lit(1): Lit(1)}) }), 601 | expect: `{ 602 | map[int]int{1:1} 603 | }`, 604 | }, 605 | { 606 | desc: `assert group`, 607 | // Don't do this! ListFunc used to kludge Group.Assert usage without 608 | // syntax error. 609 | code: Id("a").ListFunc(func(g *Group) { g.Assert(Id("b")) }), 610 | expect: `a.(b)`, 611 | }, 612 | { 613 | desc: `assert func`, 614 | code: Id("a").Add(Assert(Id("b"))), 615 | expect: `a.(b)`, 616 | }, 617 | { 618 | desc: `paramsfunc group`, 619 | // Don't do this! ListFunc used to kludge Group.ParamsFunc usage without 620 | // syntax error. 621 | code: Id("a").ListFunc(func(lg *Group) { lg.ParamsFunc(func(cg *Group) { cg.Lit(1) }) }), 622 | expect: `a(1)`, 623 | }, 624 | { 625 | desc: `paramsfunc func`, 626 | code: Id("a").Add(ParamsFunc(func(g *Group) { g.Lit(1) })), 627 | expect: `a(1)`, 628 | }, 629 | { 630 | desc: `paramsfunc statement`, 631 | code: Id("a").ParamsFunc(func(g *Group) { g.Lit(1) }), 632 | expect: `a(1)`, 633 | }, 634 | { 635 | desc: `params group`, 636 | // Don't do this! ListFunc used to kludge Group.Params usage without 637 | // syntax error. 638 | code: Id("a").ListFunc(func(g *Group) { g.Params(Lit(1)) }), 639 | expect: `a(1)`, 640 | }, 641 | { 642 | desc: `params func`, 643 | code: Id("a").Add(Params(Lit(1))), 644 | expect: `a(1)`, 645 | }, 646 | { 647 | desc: `callfunc group`, 648 | // Don't do this! ListFunc used to kludge Group.CallFunc usage without 649 | // syntax error. 650 | code: Id("a").ListFunc(func(lg *Group) { lg.CallFunc(func(cg *Group) { cg.Lit(1) }) }), 651 | expect: `a(1)`, 652 | }, 653 | { 654 | desc: `callfunc func`, 655 | code: Id("a").Add(CallFunc(func(g *Group) { g.Lit(1) })), 656 | expect: `a(1)`, 657 | }, 658 | { 659 | desc: `call group`, 660 | // Don't do this! ListFunc used to kludge Group.Call usage without 661 | // syntax error. 662 | code: Id("a").ListFunc(func(g *Group) { g.Call(Lit(1)) }), 663 | expect: `a(1)`, 664 | }, 665 | { 666 | desc: `call func`, 667 | code: Id("a").Add(Call(Lit(1))), 668 | expect: `a(1)`, 669 | }, 670 | { 671 | desc: `defsfunc statement`, 672 | code: Const().DefsFunc(func(g *Group) { g.Id("a").Op("=").Lit(1) }), 673 | expect: `const ( 674 | a = 1 675 | )`, 676 | }, 677 | { 678 | desc: `defsfunc func`, 679 | code: Const().Add(DefsFunc(func(g *Group) { g.Id("a").Op("=").Lit(1) })), 680 | expect: `const ( 681 | a = 1 682 | )`, 683 | }, 684 | { 685 | desc: `defsfunc group`, 686 | // Don't do this! ListFunc used to kludge Group.DefsFunc usage without 687 | // syntax error. 688 | code: Const().ListFunc(func(lg *Group) { lg.DefsFunc(func(dg *Group) { dg.Id("a").Op("=").Lit(1) }) }), 689 | expect: `const ( 690 | a = 1 691 | )`, 692 | }, 693 | { 694 | desc: `defs group`, 695 | // Don't do this! ListFunc used to kludge Group.Defs usage without 696 | // syntax error. 697 | code: Const().ListFunc(func(g *Group) { g.Defs(Id("a").Op("=").Lit(1)) }), 698 | expect: `const ( 699 | a = 1 700 | )`, 701 | }, 702 | { 703 | desc: `defs func`, 704 | code: Const().Add(Defs(Id("a").Op("=").Lit(1))), 705 | expect: `const ( 706 | a = 1 707 | )`, 708 | }, 709 | { 710 | desc: `blockfunc group`, 711 | code: BlockFunc(func(g *Group) { g.BlockFunc(func(g *Group) {}) }), 712 | expect: `{{}}`, 713 | }, 714 | { 715 | desc: `block group`, 716 | code: BlockFunc(func(g *Group) { g.Block() }), 717 | expect: `{{}}`, 718 | }, 719 | { 720 | desc: `indexfunc group`, 721 | code: BlockFunc(func(g *Group) { g.IndexFunc(func(g *Group) { g.Lit(1) }).Int().Values(Lit(1)) }), 722 | expect: `{[1]int{1}}`, 723 | }, 724 | { 725 | desc: `indexfunc statement`, 726 | code: Id("a").IndexFunc(func(g *Group) { g.Lit(1) }), 727 | expect: `a[1]`, 728 | }, 729 | { 730 | desc: `indexfunc func`, 731 | code: Id("a").Add(IndexFunc(func(g *Group) { g.Lit(1) })), 732 | expect: `a[1]`, 733 | }, 734 | { 735 | desc: `index group`, 736 | code: BlockFunc(func(g *Group) { g.Index(Lit(1)).Int().Values(Lit(1)) }), 737 | expect: `{[1]int{1}}`, 738 | }, 739 | { 740 | desc: `index func`, 741 | code: Id("a").Add(Index(Lit(1))), 742 | expect: `a[1]`, 743 | }, 744 | { 745 | desc: `valuesfunc func`, 746 | code: ValuesFunc(func(vg *Group) { 747 | vg.Lit(1) 748 | }), 749 | expect: `{1}`, 750 | }, 751 | { 752 | desc: `valuesfunc group`, 753 | code: BlockFunc(func(bg *Group) { 754 | bg.ValuesFunc(func(vg *Group) { 755 | vg.Lit(1) 756 | }) 757 | }), 758 | expect: `{ 759 | {1} 760 | }`, 761 | }, 762 | { 763 | desc: `values group`, 764 | code: BlockFunc(func(g *Group) { 765 | g.Values(Lit(1)) 766 | }), 767 | expect: `{ 768 | {1} 769 | }`, 770 | }, 771 | { 772 | desc: `listfunc statement`, 773 | code: Add(Null()).ListFunc(func(lg *Group) { 774 | lg.Id("a") 775 | lg.Id("b") 776 | }).Op("=").Id("c"), 777 | expect: `a, b = c`, 778 | }, 779 | { 780 | desc: `listfunc func`, 781 | code: ListFunc(func(lg *Group) { 782 | lg.Id("a") 783 | lg.Id("b") 784 | }).Op("=").Id("c"), 785 | expect: `a, b = c`, 786 | }, 787 | { 788 | desc: `listfunc group`, 789 | code: BlockFunc(func(bg *Group) { 790 | bg.ListFunc(func(lg *Group) { 791 | lg.Id("a") 792 | lg.Id("b") 793 | }).Op("=").Id("c") 794 | }), 795 | expect: `{ 796 | a, b = c 797 | }`, 798 | }, 799 | { 800 | desc: `list group`, 801 | code: BlockFunc(func(g *Group) { g.List(Id("a"), Id("b")).Op("=").Id("c") }), 802 | expect: `{ 803 | a, b = c 804 | }`, 805 | }, 806 | { 807 | desc: `parens func`, 808 | code: Parens(Lit(1)), 809 | expect: `(1)`, 810 | }, 811 | { 812 | desc: `parens group`, 813 | code: BlockFunc(func(g *Group) { g.Parens(Lit(1)) }), 814 | expect: `{ 815 | (1) 816 | }`, 817 | }, 818 | } 819 | 820 | func TestGen(t *testing.T) { 821 | caseTester(t, gencases) 822 | } 823 | -------------------------------------------------------------------------------- /jen/generics.go: -------------------------------------------------------------------------------- 1 | package jen 2 | -------------------------------------------------------------------------------- /jen/group.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/format" 7 | "io" 8 | ) 9 | 10 | // Group represents a list of Code items, separated by tokens with an optional 11 | // open and close token. 12 | type Group struct { 13 | name string 14 | items []Code 15 | open string 16 | close string 17 | separator string 18 | multi bool 19 | } 20 | 21 | func (g *Group) isNull(f *File) bool { 22 | if g == nil { 23 | return true 24 | } 25 | if g.open != "" || g.close != "" { 26 | return false 27 | } 28 | return g.isNullItems(f) 29 | } 30 | 31 | func (g *Group) isNullItems(f *File) bool { 32 | for _, c := range g.items { 33 | if !c.isNull(f) { 34 | return false 35 | } 36 | } 37 | return true 38 | } 39 | 40 | func (g *Group) render(f *File, w io.Writer, s *Statement) error { 41 | if g.name == "types" && g.isNullItems(f) { 42 | // Special case for types - if all items are null, don't render the open/close tokens. 43 | return nil 44 | } 45 | if g.name == "block" && s != nil { 46 | // Special CaseBlock format for then the previous item in the statement 47 | // is a Case group or the default keyword. 48 | prev := s.previous(g) 49 | grp, isGrp := prev.(*Group) 50 | tkn, isTkn := prev.(token) 51 | if isGrp && grp.name == "case" || isTkn && tkn.content == "default" { 52 | g.open = "" 53 | g.close = "" 54 | } 55 | } 56 | if g.open != "" { 57 | if _, err := w.Write([]byte(g.open)); err != nil { 58 | return err 59 | } 60 | } 61 | isNull, err := g.renderItems(f, w) 62 | if err != nil { 63 | return err 64 | } 65 | if !isNull && g.multi && g.close != "" { 66 | // For multi-line blocks with a closing token, we insert a new line after the last item (but 67 | // not if all items were null). This is to ensure that if the statement finishes with a comment, 68 | // the closing token is not commented out. 69 | s := "\n" 70 | if g.separator == "," { 71 | // We also insert add trailing comma if the separator was ",". 72 | s = ",\n" 73 | } 74 | if _, err := w.Write([]byte(s)); err != nil { 75 | return err 76 | } 77 | } 78 | if g.close != "" { 79 | if _, err := w.Write([]byte(g.close)); err != nil { 80 | return err 81 | } 82 | } 83 | return nil 84 | } 85 | 86 | func (g *Group) renderItems(f *File, w io.Writer) (isNull bool, err error) { 87 | first := true 88 | for _, code := range g.items { 89 | if pt, ok := code.(token); ok && pt.typ == packageToken { 90 | // Special case for package tokens in Qual groups - for dot-imports, the package token 91 | // will be null, so will not render and will not be registered in the imports block. 92 | // This ensures all packageTokens that are rendered are registered. 93 | f.register(pt.content.(string)) 94 | } 95 | if code == nil || code.isNull(f) { 96 | // Null() token produces no output but also 97 | // no separator. Empty() token products no 98 | // output but adds a separator. 99 | continue 100 | } 101 | if g.name == "values" { 102 | if _, ok := code.(Dict); ok && len(g.items) > 1 { 103 | panic("Error in Values: if Dict is used, must be one item only") 104 | } 105 | } 106 | if !first && g.separator != "" { 107 | // The separator token is added before each non-null item, but not before the first item. 108 | if _, err := w.Write([]byte(g.separator)); err != nil { 109 | return false, err 110 | } 111 | } 112 | if g.multi { 113 | // For multi-line blocks, we insert a new line before each non-null item. 114 | if _, err := w.Write([]byte("\n")); err != nil { 115 | return false, err 116 | } 117 | } 118 | if err := code.render(f, w, nil); err != nil { 119 | return false, err 120 | } 121 | first = false 122 | } 123 | return first, nil 124 | } 125 | 126 | // Render renders the Group to the provided writer. 127 | func (g *Group) Render(writer io.Writer) error { 128 | return g.RenderWithFile(writer, NewFile("")) 129 | } 130 | 131 | // GoString renders the Group for testing. Any error will cause a panic. 132 | func (g *Group) GoString() string { 133 | buf := bytes.Buffer{} 134 | if err := g.Render(&buf); err != nil { 135 | panic(err) 136 | } 137 | return buf.String() 138 | } 139 | 140 | // RenderWithFile renders the Group to the provided writer, using imports from the provided file. 141 | func (g *Group) RenderWithFile(writer io.Writer, file *File) error { 142 | buf := &bytes.Buffer{} 143 | if err := g.render(file, buf, nil); err != nil { 144 | return err 145 | } 146 | b, err := format.Source(buf.Bytes()) 147 | if err != nil { 148 | return fmt.Errorf("Error %s while formatting source:\n%s", err, buf.String()) 149 | } 150 | if _, err := writer.Write(b); err != nil { 151 | return err 152 | } 153 | return nil 154 | } 155 | -------------------------------------------------------------------------------- /jen/group_test.go: -------------------------------------------------------------------------------- 1 | package jen_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | . "github.com/dave/jennifer/jen" 8 | ) 9 | 10 | func TestGroup_Render(t *testing.T) { 11 | file := NewFile("main") 12 | file.ImportAlias("fmt", "fmtalias") 13 | 14 | var g *Group 15 | BlockFunc(func(group *Group) { 16 | g = group 17 | }) 18 | 19 | g.Qual("fmt", "Errorf").Call(Lit("error")) 20 | 21 | expect := `{ 22 | fmtalias.Errorf("error") 23 | }` 24 | 25 | var got bytes.Buffer 26 | 27 | err := g.RenderWithFile(&got, file) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | if got.String() != expect { 33 | t.Fatalf("Got: %v, expect: %v", got.String(), expect) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /jen/hints.go: -------------------------------------------------------------------------------- 1 | // This file is generated - do not edit. 2 | 3 | package jen 4 | 5 | // standardLibraryHints contains package name hints 6 | var standardLibraryHints = map[string]string{ 7 | "archive/tar": "tar", 8 | "archive/zip": "zip", 9 | "bufio": "bufio", 10 | "bytes": "bytes", 11 | "cmp": "cmp", 12 | "compress/bzip2": "bzip2", 13 | "compress/flate": "flate", 14 | "compress/gzip": "gzip", 15 | "compress/lzw": "lzw", 16 | "compress/zlib": "zlib", 17 | "container/heap": "heap", 18 | "container/list": "list", 19 | "container/ring": "ring", 20 | "context": "context", 21 | "crypto": "crypto", 22 | "crypto/aes": "aes", 23 | "crypto/cipher": "cipher", 24 | "crypto/des": "des", 25 | "crypto/dsa": "dsa", 26 | "crypto/ecdh": "ecdh", 27 | "crypto/ecdsa": "ecdsa", 28 | "crypto/ed25519": "ed25519", 29 | "crypto/elliptic": "elliptic", 30 | "crypto/hmac": "hmac", 31 | "crypto/internal/alias": "alias", 32 | "crypto/internal/bigmod": "bigmod", 33 | "crypto/internal/boring": "boring", 34 | "crypto/internal/boring/bbig": "bbig", 35 | "crypto/internal/boring/bcache": "bcache", 36 | "crypto/internal/boring/sig": "sig", 37 | "crypto/internal/cryptotest": "cryptotest", 38 | "crypto/internal/edwards25519": "edwards25519", 39 | "crypto/internal/edwards25519/field": "field", 40 | "crypto/internal/hpke": "hpke", 41 | "crypto/internal/mlkem768": "mlkem768", 42 | "crypto/internal/nistec": "nistec", 43 | "crypto/internal/nistec/fiat": "fiat", 44 | "crypto/internal/randutil": "randutil", 45 | "crypto/md5": "md5", 46 | "crypto/rand": "rand", 47 | "crypto/rc4": "rc4", 48 | "crypto/rsa": "rsa", 49 | "crypto/sha1": "sha1", 50 | "crypto/sha256": "sha256", 51 | "crypto/sha512": "sha512", 52 | "crypto/subtle": "subtle", 53 | "crypto/tls": "tls", 54 | "crypto/x509": "x509", 55 | "crypto/x509/internal/macos": "macOS", 56 | "crypto/x509/pkix": "pkix", 57 | "database/sql": "sql", 58 | "database/sql/driver": "driver", 59 | "debug/buildinfo": "buildinfo", 60 | "debug/dwarf": "dwarf", 61 | "debug/elf": "elf", 62 | "debug/gosym": "gosym", 63 | "debug/macho": "macho", 64 | "debug/pe": "pe", 65 | "debug/plan9obj": "plan9obj", 66 | "embed": "embed", 67 | "embed/internal/embedtest": "embedtest", 68 | "encoding": "encoding", 69 | "encoding/ascii85": "ascii85", 70 | "encoding/asn1": "asn1", 71 | "encoding/base32": "base32", 72 | "encoding/base64": "base64", 73 | "encoding/binary": "binary", 74 | "encoding/csv": "csv", 75 | "encoding/gob": "gob", 76 | "encoding/hex": "hex", 77 | "encoding/json": "json", 78 | "encoding/pem": "pem", 79 | "encoding/xml": "xml", 80 | "errors": "errors", 81 | "expvar": "expvar", 82 | "flag": "flag", 83 | "fmt": "fmt", 84 | "go/ast": "ast", 85 | "go/build": "build", 86 | "go/build/constraint": "constraint", 87 | "go/constant": "constant", 88 | "go/doc": "doc", 89 | "go/doc/comment": "comment", 90 | "go/format": "format", 91 | "go/importer": "importer", 92 | "go/internal/gccgoimporter": "gccgoimporter", 93 | "go/internal/gcimporter": "gcimporter", 94 | "go/internal/srcimporter": "srcimporter", 95 | "go/internal/typeparams": "typeparams", 96 | "go/parser": "parser", 97 | "go/printer": "printer", 98 | "go/scanner": "scanner", 99 | "go/token": "token", 100 | "go/types": "types", 101 | "go/version": "version", 102 | "hash": "hash", 103 | "hash/adler32": "adler32", 104 | "hash/crc32": "crc32", 105 | "hash/crc64": "crc64", 106 | "hash/fnv": "fnv", 107 | "hash/maphash": "maphash", 108 | "html": "html", 109 | "html/template": "template", 110 | "image": "image", 111 | "image/color": "color", 112 | "image/color/palette": "palette", 113 | "image/draw": "draw", 114 | "image/gif": "gif", 115 | "image/internal/imageutil": "imageutil", 116 | "image/jpeg": "jpeg", 117 | "image/png": "png", 118 | "index/suffixarray": "suffixarray", 119 | "internal/abi": "abi", 120 | "internal/asan": "asan", 121 | "internal/bisect": "bisect", 122 | "internal/buildcfg": "buildcfg", 123 | "internal/bytealg": "bytealg", 124 | "internal/byteorder": "byteorder", 125 | "internal/cfg": "cfg", 126 | "internal/chacha8rand": "chacha8rand", 127 | "internal/concurrent": "concurrent", 128 | "internal/coverage": "coverage", 129 | "internal/coverage/calloc": "calloc", 130 | "internal/coverage/cfile": "cfile", 131 | "internal/coverage/cformat": "cformat", 132 | "internal/coverage/cmerge": "cmerge", 133 | "internal/coverage/decodecounter": "decodecounter", 134 | "internal/coverage/decodemeta": "decodemeta", 135 | "internal/coverage/encodecounter": "encodecounter", 136 | "internal/coverage/encodemeta": "encodemeta", 137 | "internal/coverage/pods": "pods", 138 | "internal/coverage/rtcov": "rtcov", 139 | "internal/coverage/slicereader": "slicereader", 140 | "internal/coverage/slicewriter": "slicewriter", 141 | "internal/coverage/stringtab": "stringtab", 142 | "internal/coverage/test": "test", 143 | "internal/coverage/uleb128": "uleb128", 144 | "internal/cpu": "cpu", 145 | "internal/dag": "dag", 146 | "internal/diff": "diff", 147 | "internal/filepathlite": "filepathlite", 148 | "internal/fmtsort": "fmtsort", 149 | "internal/fuzz": "fuzz", 150 | "internal/goarch": "goarch", 151 | "internal/godebug": "godebug", 152 | "internal/godebugs": "godebugs", 153 | "internal/goexperiment": "goexperiment", 154 | "internal/goos": "goos", 155 | "internal/goroot": "goroot", 156 | "internal/gover": "gover", 157 | "internal/goversion": "goversion", 158 | "internal/itoa": "itoa", 159 | "internal/lazyregexp": "lazyregexp", 160 | "internal/lazytemplate": "lazytemplate", 161 | "internal/msan": "msan", 162 | "internal/nettrace": "nettrace", 163 | "internal/obscuretestdata": "obscuretestdata", 164 | "internal/oserror": "oserror", 165 | "internal/pkgbits": "pkgbits", 166 | "internal/platform": "platform", 167 | "internal/poll": "poll", 168 | "internal/profile": "profile", 169 | "internal/profilerecord": "profilerecord", 170 | "internal/race": "race", 171 | "internal/reflectlite": "reflectlite", 172 | "internal/runtime/atomic": "atomic", 173 | "internal/runtime/exithook": "exithook", 174 | "internal/saferio": "saferio", 175 | "internal/singleflight": "singleflight", 176 | "internal/stringslite": "stringslite", 177 | "internal/syscall/execenv": "execenv", 178 | "internal/syscall/unix": "unix", 179 | "internal/sysinfo": "sysinfo", 180 | "internal/testenv": "testenv", 181 | "internal/testlog": "testlog", 182 | "internal/testpty": "testpty", 183 | "internal/trace": "trace", 184 | "internal/trace/event": "event", 185 | "internal/trace/event/go122": "go122", 186 | "internal/trace/internal/oldtrace": "oldtrace", 187 | "internal/trace/internal/testgen/go122": "testkit", 188 | "internal/trace/raw": "raw", 189 | "internal/trace/testtrace": "testtrace", 190 | "internal/trace/traceviewer": "traceviewer", 191 | "internal/trace/traceviewer/format": "format", 192 | "internal/trace/version": "version", 193 | "internal/txtar": "txtar", 194 | "internal/types/errors": "errors", 195 | "internal/unsafeheader": "unsafeheader", 196 | "internal/weak": "weak", 197 | "internal/xcoff": "xcoff", 198 | "internal/zstd": "zstd", 199 | "io": "io", 200 | "io/fs": "fs", 201 | "io/ioutil": "ioutil", 202 | "iter": "iter", 203 | "log": "log", 204 | "log/internal": "internal", 205 | "log/slog": "slog", 206 | "log/slog/internal": "internal", 207 | "log/slog/internal/benchmarks": "benchmarks", 208 | "log/slog/internal/buffer": "buffer", 209 | "log/slog/internal/slogtest": "slogtest", 210 | "log/syslog": "syslog", 211 | "maps": "maps", 212 | "math": "math", 213 | "math/big": "big", 214 | "math/bits": "bits", 215 | "math/cmplx": "cmplx", 216 | "math/rand": "rand", 217 | "math/rand/v2": "rand", 218 | "mime": "mime", 219 | "mime/multipart": "multipart", 220 | "mime/quotedprintable": "quotedprintable", 221 | "net": "net", 222 | "net/http": "http", 223 | "net/http/cgi": "cgi", 224 | "net/http/cookiejar": "cookiejar", 225 | "net/http/fcgi": "fcgi", 226 | "net/http/httptest": "httptest", 227 | "net/http/httptrace": "httptrace", 228 | "net/http/httputil": "httputil", 229 | "net/http/internal": "internal", 230 | "net/http/internal/ascii": "ascii", 231 | "net/http/internal/testcert": "testcert", 232 | "net/http/pprof": "pprof", 233 | "net/internal/cgotest": "cgotest", 234 | "net/internal/socktest": "socktest", 235 | "net/mail": "mail", 236 | "net/netip": "netip", 237 | "net/rpc": "rpc", 238 | "net/rpc/jsonrpc": "jsonrpc", 239 | "net/smtp": "smtp", 240 | "net/textproto": "textproto", 241 | "net/url": "url", 242 | "os": "os", 243 | "os/exec": "exec", 244 | "os/exec/internal/fdtest": "fdtest", 245 | "os/signal": "signal", 246 | "os/user": "user", 247 | "path": "path", 248 | "path/filepath": "filepath", 249 | "plugin": "plugin", 250 | "reflect": "reflect", 251 | "reflect/internal/example1": "example1", 252 | "reflect/internal/example2": "example2", 253 | "regexp": "regexp", 254 | "regexp/syntax": "syntax", 255 | "runtime": "runtime", 256 | "runtime/cgo": "cgo", 257 | "runtime/coverage": "coverage", 258 | "runtime/debug": "debug", 259 | "runtime/internal/math": "math", 260 | "runtime/internal/sys": "sys", 261 | "runtime/internal/wasitest": "wasi", 262 | "runtime/metrics": "metrics", 263 | "runtime/pprof": "pprof", 264 | "runtime/race": "race", 265 | "runtime/trace": "trace", 266 | "slices": "slices", 267 | "sort": "sort", 268 | "strconv": "strconv", 269 | "strings": "strings", 270 | "structs": "structs", 271 | "sync": "sync", 272 | "sync/atomic": "atomic", 273 | "syscall": "syscall", 274 | "testing": "testing", 275 | "testing/fstest": "fstest", 276 | "testing/internal/testdeps": "testdeps", 277 | "testing/iotest": "iotest", 278 | "testing/quick": "quick", 279 | "testing/slogtest": "slogtest", 280 | "text/scanner": "scanner", 281 | "text/tabwriter": "tabwriter", 282 | "text/template": "template", 283 | "text/template/parse": "parse", 284 | "time": "time", 285 | "time/tzdata": "tzdata", 286 | "unicode": "unicode", 287 | "unicode/utf16": "utf16", 288 | "unicode/utf8": "utf8", 289 | "unique": "unique", 290 | "unsafe": "unsafe", 291 | } 292 | -------------------------------------------------------------------------------- /jen/jen.go: -------------------------------------------------------------------------------- 1 | // Package jen is a code generator for Go 2 | package jen 3 | 4 | import ( 5 | "bytes" 6 | "fmt" 7 | "go/format" 8 | "io" 9 | "os" 10 | "sort" 11 | "strconv" 12 | ) 13 | 14 | // Code represents an item of code that can be rendered. 15 | type Code interface { 16 | render(f *File, w io.Writer, s *Statement) error 17 | isNull(f *File) bool 18 | } 19 | 20 | // Save renders the file and saves to the filename provided. 21 | func (f *File) Save(filename string) error { 22 | // notest 23 | buf := &bytes.Buffer{} 24 | if err := f.Render(buf); err != nil { 25 | return err 26 | } 27 | if err := os.WriteFile(filename, buf.Bytes(), 0644); err != nil { 28 | return err 29 | } 30 | return nil 31 | } 32 | 33 | // Render renders the file to the provided writer. 34 | func (f *File) Render(w io.Writer) error { 35 | body := &bytes.Buffer{} 36 | if err := f.render(f, body, nil); err != nil { 37 | return err 38 | } 39 | source := &bytes.Buffer{} 40 | if len(f.headers) > 0 { 41 | for _, c := range f.headers { 42 | if err := Comment(c).render(f, source, nil); err != nil { 43 | return err 44 | } 45 | if _, err := fmt.Fprint(source, "\n"); err != nil { 46 | return err 47 | } 48 | } 49 | // Append an extra newline so that header comments don't get lumped in 50 | // with package comments. 51 | if _, err := fmt.Fprint(source, "\n"); err != nil { 52 | return err 53 | } 54 | } 55 | for _, c := range f.comments { 56 | if err := Comment(c).render(f, source, nil); err != nil { 57 | return err 58 | } 59 | if _, err := fmt.Fprint(source, "\n"); err != nil { 60 | return err 61 | } 62 | } 63 | if _, err := fmt.Fprintf(source, "package %s", f.name); err != nil { 64 | return err 65 | } 66 | if f.CanonicalPath != "" { 67 | if _, err := fmt.Fprintf(source, " // import %q", f.CanonicalPath); err != nil { 68 | return err 69 | } 70 | } 71 | if _, err := fmt.Fprint(source, "\n\n"); err != nil { 72 | return err 73 | } 74 | if err := f.renderImports(source); err != nil { 75 | return err 76 | } 77 | if _, err := source.Write(body.Bytes()); err != nil { 78 | return err 79 | } 80 | var output []byte 81 | if f.NoFormat { 82 | output = source.Bytes() 83 | } else { 84 | var err error 85 | output, err = format.Source(source.Bytes()) 86 | if err != nil { 87 | return fmt.Errorf("Error %s while formatting source:\n%s", err, source.String()) 88 | } 89 | } 90 | if _, err := w.Write(output); err != nil { 91 | return err 92 | } 93 | return nil 94 | } 95 | 96 | func (f *File) renderImports(source io.Writer) error { 97 | 98 | // Render the "C" import if it's been used in a `Qual`, `Anon` or if there's a preamble comment 99 | hasCgo := f.imports["C"].name != "" || len(f.cgoPreamble) > 0 100 | 101 | // Only separate the import from the main imports block if there's a preamble 102 | separateCgo := hasCgo && len(f.cgoPreamble) > 0 103 | 104 | filtered := map[string]importdef{} 105 | for path, def := range f.imports { 106 | // filter out the "C" pseudo-package so it's not rendered in a block with the other 107 | // imports, but only if it is accompanied by a preamble comment 108 | if path == "C" && separateCgo { 109 | continue 110 | } 111 | filtered[path] = def 112 | } 113 | 114 | if len(filtered) == 1 { 115 | for path, def := range filtered { 116 | if def.alias && path != "C" { 117 | // "C" package should be rendered without alias even when used as an anonymous import 118 | // (e.g. should never have an underscore). 119 | if _, err := fmt.Fprintf(source, "import %s %s\n\n", def.name, strconv.Quote(path)); err != nil { 120 | return err 121 | } 122 | } else { 123 | if _, err := fmt.Fprintf(source, "import %s\n\n", strconv.Quote(path)); err != nil { 124 | return err 125 | } 126 | } 127 | } 128 | } else if len(filtered) > 1 { 129 | if _, err := fmt.Fprint(source, "import (\n"); err != nil { 130 | return err 131 | } 132 | // We must sort the imports to ensure repeatable 133 | // source. 134 | paths := []string{} 135 | for path := range filtered { 136 | paths = append(paths, path) 137 | } 138 | sort.Strings(paths) 139 | for _, path := range paths { 140 | def := filtered[path] 141 | if def.alias && path != "C" { 142 | // "C" package should be rendered without alias even when used as an anonymous import 143 | // (e.g. should never have an underscore). 144 | if _, err := fmt.Fprintf(source, "%s %s\n", def.name, strconv.Quote(path)); err != nil { 145 | return err 146 | } 147 | 148 | } else { 149 | if _, err := fmt.Fprintf(source, "%s\n", strconv.Quote(path)); err != nil { 150 | return err 151 | } 152 | } 153 | } 154 | if _, err := fmt.Fprint(source, ")\n\n"); err != nil { 155 | return err 156 | } 157 | } 158 | 159 | if separateCgo { 160 | for _, c := range f.cgoPreamble { 161 | if err := Comment(c).render(f, source, nil); err != nil { 162 | return err 163 | } 164 | if _, err := fmt.Fprint(source, "\n"); err != nil { 165 | return err 166 | } 167 | } 168 | if _, err := fmt.Fprint(source, "import \"C\"\n\n"); err != nil { 169 | return err 170 | } 171 | } 172 | 173 | return nil 174 | } 175 | -------------------------------------------------------------------------------- /jen/jen_test.go: -------------------------------------------------------------------------------- 1 | package jen_test 2 | 3 | import ( 4 | "fmt" 5 | "go/format" 6 | "strings" 7 | "testing" 8 | 9 | . "github.com/dave/jennifer/jen" 10 | ) 11 | 12 | var o1 = Options{ 13 | Close: ")", 14 | Multi: true, 15 | Open: "(", 16 | Separator: ",", 17 | } 18 | 19 | var o2 = Options{ 20 | Close: "", 21 | Multi: false, 22 | Open: "", 23 | Separator: ",", 24 | } 25 | 26 | var cases = []tc{ 27 | { 28 | desc: `union_group`, 29 | code: Type().Id("A").InterfaceFunc(func(g *Group) { 30 | g.Union(Id("A"), Id("B")) 31 | }), 32 | expect: `type A interface{ 33 | A|B 34 | }`, 35 | }, 36 | { 37 | desc: `union_group_func`, 38 | code: Type().Id("A").InterfaceFunc(func(g1 *Group) { 39 | g1.UnionFunc(func(g2 *Group) { 40 | g2.Id("A") 41 | g2.Id("B") 42 | }) 43 | }), 44 | expect: `type A interface{ 45 | A|B 46 | }`, 47 | }, 48 | { 49 | desc: `union`, 50 | code: Type().Id("A").Interface(Union(Id("A"), Id("B"))), 51 | expect: `type A interface{ 52 | A|B 53 | }`, 54 | }, 55 | { 56 | desc: `unionFunc`, 57 | code: Type().Id("A").Interface(UnionFunc(func(g *Group) { 58 | g.Id("A") 59 | g.Id("B") 60 | })), 61 | expect: `type A interface{ 62 | A|B 63 | }`, 64 | }, 65 | { 66 | desc: `types1`, 67 | code: Func().Id("A").Types(Id("K").Comparable(), Id("V").Any()).Params(), 68 | expect: `func A[K comparable, V any]()`, 69 | }, 70 | { 71 | desc: `types2`, 72 | code: Func().Id("A").Types(Id("T1"), Id("T2").Any()).Params(), 73 | expect: `func A[T1, T2 any]()`, 74 | }, 75 | { 76 | desc: `types func`, 77 | code: Func().Id("A").Add(Types(Id("T1"), Id("T2").Any())).Params(), 78 | expect: `func A[T1, T2 any]()`, 79 | }, 80 | { 81 | desc: `scientific notation`, 82 | code: Lit(1e3), 83 | expect: `1000.0`, 84 | }, 85 | { 86 | desc: `big float`, 87 | code: Lit(1000000.0), 88 | expect: `1e+06`, 89 | }, 90 | { 91 | desc: `lit float whole numbers`, 92 | code: Index().Float64().Values(Lit(-10.0), Lit(-2.0), Lit(-1.0), Lit(0.0), Lit(1.0), Lit(2.0), Lit(10.0)), 93 | expect: "[]float64{-10.0, -2.0, -1.0, 0.0, 1.0, 2.0, 10.0}", 94 | }, 95 | { 96 | desc: `custom func group`, 97 | code: ListFunc(func(g *Group) { 98 | g.CustomFunc(o2, func(g *Group) { 99 | g.Id("a") 100 | g.Id("b") 101 | g.Id("c") 102 | }) 103 | }).Op("=").Id("foo").Call(), 104 | expect: `a, b, c = foo()`, 105 | }, 106 | { 107 | desc: `custom group`, 108 | code: ListFunc(func(g *Group) { g.Custom(o2, Id("a"), Id("b"), Id("c")) }).Op("=").Id("foo").Call(), 109 | expect: `a, b, c = foo()`, 110 | }, 111 | { 112 | desc: `custom function`, 113 | code: Id("foo").Add(Custom(o1, Lit("a"), Lit("b"), Lit("c"))), 114 | expect: `foo( 115 | "a", 116 | "b", 117 | "c", 118 | )`, 119 | }, 120 | { 121 | desc: `custom function`, 122 | code: Id("foo").Add(Custom(o1, Lit("a"), Lit("b"), Lit("c"))), 123 | expect: `foo( 124 | "a", 125 | "b", 126 | "c", 127 | )`, 128 | }, 129 | { 130 | desc: `line statement`, 131 | code: Block(Lit(1).Line(), Lit(2)), 132 | expect: `{ 133 | 1 134 | 135 | 2 136 | }`, 137 | }, 138 | { 139 | desc: `line func`, 140 | code: Block(Lit(1), Line(), Lit(2)), 141 | expect: `{ 142 | 1 143 | 144 | 2 145 | }`, 146 | }, 147 | { 148 | desc: `line group`, 149 | code: BlockFunc(func(g *Group) { 150 | g.Id("a") 151 | g.Line() 152 | g.Id("b") 153 | }), 154 | expect: `{ 155 | a 156 | 157 | b 158 | }`, 159 | }, 160 | { 161 | desc: `op group`, 162 | code: BlockFunc(func(g *Group) { 163 | g.Op("*").Id("a") 164 | }), 165 | expect: `{*a}`, 166 | }, 167 | { 168 | desc: `empty group`, 169 | code: BlockFunc(func(g *Group) { 170 | g.Empty() 171 | }), 172 | expect: `{ 173 | 174 | }`, 175 | }, 176 | { 177 | desc: `null group`, 178 | code: BlockFunc(func(g *Group) { 179 | g.Null() 180 | }), 181 | expect: `{}`, 182 | }, 183 | { 184 | desc: `tag no backquote`, 185 | code: Tag(map[string]string{"a": "`b`"}), 186 | expect: "\"a:\\\"`b`\\\"\"", 187 | }, 188 | { 189 | desc: `tag null`, 190 | code: Tag(map[string]string{}), 191 | expect: ``, 192 | }, 193 | { 194 | desc: `litrunefunc group`, 195 | code: BlockFunc(func(g *Group) { 196 | g.LitByteFunc(func() byte { return byte(0xab) }) 197 | }), 198 | expect: `{byte(0xab)}`, 199 | }, 200 | { 201 | desc: `litbyte group`, 202 | code: BlockFunc(func(g *Group) { 203 | g.LitByte(byte(0xab)) 204 | }), 205 | expect: `{byte(0xab)}`, 206 | }, 207 | { 208 | desc: `litrunefunc group`, 209 | code: BlockFunc(func(g *Group) { 210 | g.LitRuneFunc(func() rune { return 'a' }) 211 | }), 212 | expect: `{'a'}`, 213 | }, 214 | { 215 | desc: `litrune group`, 216 | code: BlockFunc(func(g *Group) { 217 | g.LitRune('a') 218 | }), 219 | expect: `{'a'}`, 220 | }, 221 | { 222 | desc: `litfunc group`, 223 | code: BlockFunc(func(g *Group) { 224 | g.LitFunc(func() interface{} { 225 | return 1 + 1 226 | }) 227 | }), 228 | expect: `{2}`, 229 | }, 230 | { 231 | desc: `litfunc func`, 232 | code: LitFunc(func() interface{} { 233 | return 1 + 1 234 | }), 235 | expect: `2`, 236 | }, 237 | { 238 | desc: `group all null`, 239 | code: List(Null(), Null()), 240 | expect: ``, 241 | }, 242 | { 243 | desc: `do group`, 244 | code: BlockFunc(func(g *Group) { g.Do(func(s *Statement) { s.Lit(1) }) }), 245 | expect: `{1}`, 246 | }, 247 | { 248 | desc: `do func`, 249 | code: Do(func(s *Statement) { s.Lit(1) }), 250 | expect: `1`, 251 | }, 252 | { 253 | desc: `dict empty`, 254 | code: Values(Dict{}), 255 | expect: `{}`, 256 | }, 257 | { 258 | desc: `dict null`, 259 | code: Values(Dict{Null(): Null()}), 260 | expect: `{}`, 261 | }, 262 | { 263 | desc: `commentf group`, 264 | code: BlockFunc(func(g *Group) { g.Commentf("%d", 1) }), 265 | expect: `{ 266 | // 1 267 | }`, 268 | }, 269 | { 270 | desc: `commentf func`, 271 | code: Commentf("%d", 1), 272 | expect: `// 1`, 273 | }, 274 | { 275 | desc: `add func`, 276 | code: Add(Lit(1)), 277 | expect: `1`, 278 | }, 279 | { 280 | desc: `add group`, 281 | code: BlockFunc(func(g *Group) { g.Add(Lit(1)) }), 282 | expect: `{1}`, 283 | }, 284 | { 285 | desc: `empty block`, 286 | code: Block(), 287 | expect: `{}`, 288 | }, 289 | { 290 | desc: `string literal`, 291 | code: Lit("a"), 292 | expect: `"a"`, 293 | }, 294 | { 295 | desc: `int literal`, 296 | code: Lit(1), 297 | expect: `1`, 298 | }, 299 | { 300 | desc: `simple id`, 301 | code: Id("a"), 302 | expect: `a`, 303 | }, 304 | { 305 | desc: `foreign id`, 306 | code: Qual("x.y/z", "a"), 307 | expect: `z.a`, 308 | expectImports: map[string]string{ 309 | "x.y/z": "z", 310 | }, 311 | }, 312 | { 313 | desc: `var decl`, 314 | code: Var().Id("a").Op("=").Lit("b"), 315 | expect: `var a = "b"`, 316 | }, 317 | { 318 | desc: `short var decl`, 319 | code: Id("a").Op(":=").Lit("b"), 320 | expect: `a := "b"`, 321 | }, 322 | { 323 | desc: `simple if`, 324 | code: If(Id("a").Op("==").Lit("b")).Block(), 325 | expect: `if a == "b" {}`, 326 | }, 327 | { 328 | desc: `simple if`, 329 | code: If(Id("a").Op("==").Lit("b")).Block( 330 | Id("a").Op("++"), 331 | ), 332 | expect: `if a == "b" { a++ }`, 333 | }, 334 | { 335 | desc: `pointer`, 336 | code: Op("*").Id("a"), 337 | expect: `*a`, 338 | }, 339 | { 340 | desc: `address`, 341 | code: Op("&").Id("a"), 342 | expect: `&a`, 343 | }, 344 | { 345 | desc: `simple call`, 346 | code: Id("a").Call( 347 | Lit("b"), 348 | Lit("c"), 349 | ), 350 | expect: `a("b", "c")`, 351 | }, 352 | { 353 | desc: `call fmt.Sprintf`, 354 | code: Qual("fmt", "Sprintf").Call( 355 | Lit("b"), 356 | Id("c"), 357 | ), 358 | expect: `fmt.Sprintf("b", c)`, 359 | }, 360 | { 361 | desc: `slices`, 362 | code: Id("a").Index( 363 | Lit(1), 364 | Empty(), 365 | ), 366 | expect: `a[1:]`, 367 | }, 368 | { 369 | desc: `return`, 370 | code: Return(Id("a")), 371 | expect: `return a`, 372 | }, 373 | { 374 | desc: `double return`, 375 | code: Return(Id("a"), Id("b")), 376 | expect: `return a, b`, 377 | }, 378 | { 379 | desc: `func`, 380 | code: Func().Id("a").Params( 381 | Id("a").String(), 382 | ).Block( 383 | Return(Id("a")), 384 | ), 385 | expect: `func a(a string){ 386 | return a 387 | }`, 388 | }, 389 | { 390 | desc: `built in func`, 391 | code: New(Id("a")), 392 | expect: `new(a)`, 393 | }, 394 | { 395 | desc: `multip`, 396 | code: Id("a").Op("*").Id("b"), 397 | expect: `a * b`, 398 | }, 399 | { 400 | desc: `multip ptr`, 401 | code: Id("a").Op("*").Op("*").Id("b"), 402 | expect: `a * *b`, 403 | }, 404 | { 405 | desc: `field`, 406 | code: Id("a").Dot("b"), 407 | expect: `a.b`, 408 | }, 409 | { 410 | desc: `method`, 411 | code: Id("a").Dot("b").Call(Id("c"), Id("d")), 412 | expect: `a.b(c, d)`, 413 | }, 414 | { 415 | desc: `if else`, 416 | code: If(Id("a").Op("==").Lit(1)).Block( 417 | Id("b").Op("=").Lit(1), 418 | ).Else().If(Id("a").Op("==").Lit(2)).Block( 419 | Id("b").Op("=").Lit(2), 420 | ).Else().Block( 421 | Id("b").Op("=").Lit(3), 422 | ), 423 | expect: `if a == 1 { b = 1 } else if a == 2 { b = 2 } else { b = 3 }`, 424 | }, 425 | { 426 | desc: `literal array`, 427 | code: Index().String().Values(Lit("a"), Lit("b")), 428 | expect: `[]string{"a", "b"}`, 429 | }, 430 | { 431 | desc: `comment`, 432 | code: Comment("a"), 433 | expect: `// a`, 434 | }, 435 | { 436 | desc: `null`, 437 | code: Id("a").Params(Id("b"), Null(), Id("c")), 438 | expect: `a(b, c)`, 439 | }, 440 | { 441 | desc: `map literal single`, 442 | code: Id("a").Values(Dict{ 443 | Id("b"): Id("c"), 444 | }), 445 | expect: `a{b: c}`, 446 | }, 447 | { 448 | desc: `map literal null`, 449 | code: Id("a").Values(Dict{ 450 | Null(): Id("c"), 451 | Id("b"): Null(), 452 | Id("b"): Id("c"), 453 | }), 454 | expect: `a{b: c}`, 455 | }, 456 | { 457 | desc: `map literal multiple`, 458 | code: Id("a").Values(Dict{ 459 | Id("b"): Id("c"), 460 | Id("d"): Id("e"), 461 | }), 462 | expect: `a{ 463 | b: c, 464 | d: e, 465 | }`, 466 | }, 467 | { 468 | desc: `map literal func single`, 469 | code: Id("a").Values(DictFunc(func(d Dict) { 470 | d[Id("b")] = Id("c") 471 | })), 472 | expect: `a{b: c}`, 473 | }, 474 | { 475 | desc: `map literal func single null`, 476 | code: Id("a").Values(DictFunc(func(d Dict) { 477 | d[Null()] = Id("c") 478 | d[Id("b")] = Null() 479 | d[Id("b")] = Id("c") 480 | })), 481 | expect: `a{b: c}`, 482 | }, 483 | { 484 | desc: `map literal func multiple`, 485 | code: Id("a").Values(DictFunc(func(d Dict) { 486 | d[Id("b")] = Id("c") 487 | d[Id("d")] = Id("e") 488 | })), 489 | expect: `a{ 490 | b: c, 491 | d: e, 492 | }`, 493 | }, 494 | { 495 | desc: `literal func`, 496 | code: Id("a").Op(":=").LitFunc(func() interface{} { 497 | return "b" 498 | }), 499 | expect: `a := "b"`, 500 | }, 501 | { 502 | desc: `dot`, 503 | code: Id("a").Dot("b").Dot("c"), 504 | expect: `a.b.c`, 505 | }, 506 | { 507 | desc: `do`, 508 | code: Id("a").Do(func(s *Statement) { s.Dot("b") }), 509 | expect: `a.b`, 510 | }, 511 | { 512 | desc: `tags should be ordered`, 513 | code: Tag(map[string]string{"z": "1", "a": "2"}), 514 | expect: "`a:\"2\" z:\"1\"`", 515 | }, 516 | { 517 | desc: `dict should be ordered`, 518 | code: Map(String()).Int().Values(Dict{Id("z"): Lit(1), Id("a"): Lit(2)}), 519 | expect: `map[string]int{ 520 | a:2, 521 | z:1, 522 | }`, 523 | }, 524 | } 525 | 526 | func TestJen(t *testing.T) { 527 | caseTester(t, cases) 528 | } 529 | 530 | func caseTester(t *testing.T, cases []tc) { 531 | for i, c := range cases { 532 | onlyTest := "" 533 | if onlyTest != "" && c.desc != onlyTest { 534 | continue 535 | } 536 | rendered := fmt.Sprintf("%#v", c.code) 537 | 538 | expected, err := format.Source([]byte(c.expect)) 539 | if err != nil { 540 | panic(fmt.Sprintf("Error formatting expected source in test case %d. Description: %s\nError:\n%s", i, c.desc, err)) 541 | } 542 | 543 | if strings.TrimSpace(string(rendered)) != strings.TrimSpace(string(expected)) { 544 | t.Errorf("Test case %d failed. Description: %s\nExpected:\n%s\nOutput:\n%s", i, c.desc, expected, rendered) 545 | } 546 | 547 | //if c.expectImports != nil { 548 | // f := FromContext(ctx) 549 | // if !reflect.DeepEqual(f.Imports, c.expectImports) { 550 | // t.Errorf("Test case %d failed. Description: %s\nImports expected:\n%s\nOutput:\n%s", i, c.desc, c.expectImports, f.Imports) 551 | // } 552 | //} 553 | } 554 | } 555 | 556 | // a test case 557 | type tc struct { 558 | // path 559 | path string 560 | // description for locating the test case 561 | desc string 562 | // code to generate 563 | code Code 564 | // expected generated source 565 | expect string 566 | // expected imports 567 | expectImports map[string]string 568 | } 569 | 570 | func TestNilStatement(t *testing.T) { 571 | var s *Statement 572 | c := Func().Id("a").Params( 573 | s, 574 | ) 575 | got := fmt.Sprintf("%#v", c) 576 | expect := "func a()" 577 | if got != expect { 578 | t.Fatalf("Got: %s, expect: %s", got, expect) 579 | } 580 | } 581 | 582 | func TestNilGroup(t *testing.T) { 583 | var g *Group 584 | c := Func().Id("a").Params( 585 | g, 586 | ) 587 | got := fmt.Sprintf("%#v", c) 588 | expect := "func a()" 589 | if got != expect { 590 | t.Fatalf("Got: %s, expect: %s", got, expect) 591 | } 592 | } 593 | 594 | func TestGroup_GoString(t *testing.T) { 595 | BlockFunc(func(g *Group) { 596 | g.Lit(1) 597 | got := fmt.Sprintf("%#v", g) 598 | expect := "{\n\t1\n}" 599 | if got != expect { 600 | t.Fatalf("Got: %s, expect: %s", got, expect) 601 | } 602 | }) 603 | } 604 | -------------------------------------------------------------------------------- /jen/lit.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | // Lit renders a literal. Lit supports only built-in types (bool, string, int, complex128, float64, 4 | // float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64). 5 | // Passing any other type will panic. 6 | func Lit(v interface{}) *Statement { 7 | return newStatement().Lit(v) 8 | } 9 | 10 | // Lit renders a literal. Lit supports only built-in types (bool, string, int, complex128, float64, 11 | // float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64). 12 | // Passing any other type will panic. 13 | func (g *Group) Lit(v interface{}) *Statement { 14 | s := Lit(v) 15 | g.items = append(g.items, s) 16 | return s 17 | } 18 | 19 | // Lit renders a literal. Lit supports only built-in types (bool, string, int, complex128, float64, 20 | // float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64). 21 | // Passing any other type will panic. 22 | func (s *Statement) Lit(v interface{}) *Statement { 23 | t := token{ 24 | typ: literalToken, 25 | content: v, 26 | } 27 | *s = append(*s, t) 28 | return s 29 | } 30 | 31 | // LitFunc renders a literal. LitFunc generates the value to render by executing the provided 32 | // function. LitFunc supports only built-in types (bool, string, int, complex128, float64, float32, 33 | // int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64). 34 | // Returning any other type will panic. 35 | func LitFunc(f func() interface{}) *Statement { 36 | return newStatement().LitFunc(f) 37 | } 38 | 39 | // LitFunc renders a literal. LitFunc generates the value to render by executing the provided 40 | // function. LitFunc supports only built-in types (bool, string, int, complex128, float64, float32, 41 | // int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64). 42 | // Returning any other type will panic. 43 | func (g *Group) LitFunc(f func() interface{}) *Statement { 44 | s := LitFunc(f) 45 | g.items = append(g.items, s) 46 | return s 47 | } 48 | 49 | // LitFunc renders a literal. LitFunc generates the value to render by executing the provided 50 | // function. LitFunc supports only built-in types (bool, string, int, complex128, float64, float32, 51 | // int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64). 52 | // Returning any other type will panic. 53 | func (s *Statement) LitFunc(f func() interface{}) *Statement { 54 | t := token{ 55 | typ: literalToken, 56 | content: f(), 57 | } 58 | *s = append(*s, t) 59 | return s 60 | } 61 | 62 | // LitRune renders a rune literal. 63 | func LitRune(v rune) *Statement { 64 | return newStatement().LitRune(v) 65 | } 66 | 67 | // LitRune renders a rune literal. 68 | func (g *Group) LitRune(v rune) *Statement { 69 | s := LitRune(v) 70 | g.items = append(g.items, s) 71 | return s 72 | } 73 | 74 | // LitRune renders a rune literal. 75 | func (s *Statement) LitRune(v rune) *Statement { 76 | t := token{ 77 | typ: literalRuneToken, 78 | content: v, 79 | } 80 | *s = append(*s, t) 81 | return s 82 | } 83 | 84 | // LitRuneFunc renders a rune literal. LitRuneFunc generates the value to 85 | // render by executing the provided function. 86 | func LitRuneFunc(f func() rune) *Statement { 87 | return newStatement().LitRuneFunc(f) 88 | } 89 | 90 | // LitRuneFunc renders a rune literal. LitRuneFunc generates the value to 91 | // render by executing the provided function. 92 | func (g *Group) LitRuneFunc(f func() rune) *Statement { 93 | s := LitRuneFunc(f) 94 | g.items = append(g.items, s) 95 | return s 96 | } 97 | 98 | // LitRuneFunc renders a rune literal. LitRuneFunc generates the value to 99 | // render by executing the provided function. 100 | func (s *Statement) LitRuneFunc(f func() rune) *Statement { 101 | t := token{ 102 | typ: literalRuneToken, 103 | content: f(), 104 | } 105 | *s = append(*s, t) 106 | return s 107 | } 108 | 109 | // LitByte renders a byte literal. 110 | func LitByte(v byte) *Statement { 111 | return newStatement().LitByte(v) 112 | } 113 | 114 | // LitByte renders a byte literal. 115 | func (g *Group) LitByte(v byte) *Statement { 116 | s := LitByte(v) 117 | g.items = append(g.items, s) 118 | return s 119 | } 120 | 121 | // LitByte renders a byte literal. 122 | func (s *Statement) LitByte(v byte) *Statement { 123 | t := token{ 124 | typ: literalByteToken, 125 | content: v, 126 | } 127 | *s = append(*s, t) 128 | return s 129 | } 130 | 131 | // LitByteFunc renders a byte literal. LitByteFunc generates the value to 132 | // render by executing the provided function. 133 | func LitByteFunc(f func() byte) *Statement { 134 | return newStatement().LitByteFunc(f) 135 | } 136 | 137 | // LitByteFunc renders a byte literal. LitByteFunc generates the value to 138 | // render by executing the provided function. 139 | func (g *Group) LitByteFunc(f func() byte) *Statement { 140 | s := LitByteFunc(f) 141 | g.items = append(g.items, s) 142 | return s 143 | } 144 | 145 | // LitByteFunc renders a byte literal. LitByteFunc generates the value to 146 | // render by executing the provided function. 147 | func (s *Statement) LitByteFunc(f func() byte) *Statement { 148 | t := token{ 149 | typ: literalByteToken, 150 | content: f(), 151 | } 152 | *s = append(*s, t) 153 | return s 154 | } 155 | -------------------------------------------------------------------------------- /jen/reserved.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | var reserved = []string{ 4 | /* keywords */ 5 | "break", "default", "func", "interface", "select", "case", "defer", "go", "map", "struct", "chan", "else", "goto", "package", "switch", "const", "fallthrough", "if", "range", "type", "continue", "for", "import", "return", "var", 6 | /* predeclared */ 7 | "bool", "byte", "complex64", "complex128", "error", "float32", "float64", "int", "int8", "int16", "int32", "int64", "rune", "string", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", "true", "false", "iota", "nil", "append", "cap", "close", "clear", "min", "max", "complex", "copy", "delete", "imag", "len", "make", "new", "panic", "print", "println", "real", "recover", 8 | /* common variables */ 9 | "err", 10 | } 11 | 12 | // IsReservedWord returns if this is a reserved word in go 13 | func IsReservedWord(alias string) bool { 14 | for _, name := range reserved { 15 | if alias == name { 16 | return true 17 | } 18 | } 19 | return false 20 | } 21 | -------------------------------------------------------------------------------- /jen/statement.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/format" 7 | "io" 8 | ) 9 | 10 | // Statement represents a simple list of code items. When rendered the items 11 | // are separated by spaces. 12 | type Statement []Code 13 | 14 | func newStatement() *Statement { 15 | return &Statement{} 16 | } 17 | 18 | // Clone makes a copy of the Statement, so further tokens can be appended 19 | // without affecting the original. 20 | func (s *Statement) Clone() *Statement { 21 | return &Statement{s} 22 | } 23 | 24 | func (s *Statement) previous(c Code) Code { 25 | index := -1 26 | for i, item := range *s { 27 | if item == c { 28 | index = i 29 | break 30 | } 31 | } 32 | if index > 0 { 33 | return (*s)[index-1] 34 | } 35 | return nil 36 | } 37 | 38 | func (s *Statement) isNull(f *File) bool { 39 | if s == nil { 40 | return true 41 | } 42 | for _, c := range *s { 43 | if !c.isNull(f) { 44 | return false 45 | } 46 | } 47 | return true 48 | } 49 | 50 | func (s *Statement) render(f *File, w io.Writer, _ *Statement) error { 51 | first := true 52 | for _, code := range *s { 53 | if code == nil || code.isNull(f) { 54 | // Null() token produces no output but also 55 | // no separator. Empty() token products no 56 | // output but adds a separator. 57 | continue 58 | } 59 | if !first { 60 | if _, err := w.Write([]byte(" ")); err != nil { 61 | return err 62 | } 63 | } 64 | if err := code.render(f, w, s); err != nil { 65 | return err 66 | } 67 | first = false 68 | } 69 | return nil 70 | } 71 | 72 | // Render renders the Statement to the provided writer. 73 | func (s *Statement) Render(writer io.Writer) error { 74 | return s.RenderWithFile(writer, NewFile("")) 75 | } 76 | 77 | // GoString renders the Statement for testing. Any error will cause a panic. 78 | func (s *Statement) GoString() string { 79 | buf := bytes.Buffer{} 80 | if err := s.Render(&buf); err != nil { 81 | panic(err) 82 | } 83 | return buf.String() 84 | } 85 | 86 | // RenderWithFile renders the Statement to the provided writer, using imports from the provided file. 87 | func (s *Statement) RenderWithFile(writer io.Writer, file *File) error { 88 | buf := &bytes.Buffer{} 89 | if err := s.render(file, buf, nil); err != nil { 90 | return err 91 | } 92 | b, err := format.Source(buf.Bytes()) 93 | if err != nil { 94 | return fmt.Errorf("Error %s while formatting source:\n%s", err, buf.String()) 95 | } 96 | if _, err := writer.Write(b); err != nil { 97 | return err 98 | } 99 | return nil 100 | } 101 | 102 | -------------------------------------------------------------------------------- /jen/statement_test.go: -------------------------------------------------------------------------------- 1 | package jen_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | . "github.com/dave/jennifer/jen" 8 | ) 9 | 10 | func TestStatement_Render(t *testing.T) { 11 | file := NewFile("main") 12 | file.ImportAlias("fmt", "fmtalias") 13 | 14 | statement := file.Func().Id("main").Params().Block( 15 | Qual("fmt", "Println").Call(Lit("something")), 16 | ) 17 | 18 | expect := `func main() { 19 | fmtalias.Println("something") 20 | }` 21 | 22 | var got bytes.Buffer 23 | 24 | err := statement.RenderWithFile(&got, file) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | if got.String() != expect { 30 | t.Fatalf("Got: %v, expect: %v", got.String(), expect) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jen/tag.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "sort" 7 | "strconv" 8 | ) 9 | 10 | // Tag renders a struct tag 11 | func Tag(items map[string]string) *Statement { 12 | return newStatement().Tag(items) 13 | } 14 | 15 | // Tag renders a struct tag 16 | func (g *Group) Tag(items map[string]string) *Statement { 17 | // notest 18 | // don't think this can ever be used in valid code? 19 | s := Tag(items) 20 | g.items = append(g.items, s) 21 | return s 22 | } 23 | 24 | // Tag renders a struct tag 25 | func (s *Statement) Tag(items map[string]string) *Statement { 26 | c := tag{ 27 | items: items, 28 | } 29 | *s = append(*s, c) 30 | return s 31 | } 32 | 33 | type tag struct { 34 | items map[string]string 35 | } 36 | 37 | func (t tag) isNull(f *File) bool { 38 | return len(t.items) == 0 39 | } 40 | 41 | func (t tag) render(f *File, w io.Writer, s *Statement) error { 42 | 43 | if t.isNull(f) { 44 | // notest 45 | // render won't be called if t is null 46 | return nil 47 | } 48 | 49 | var str string 50 | 51 | var sorted []string 52 | for k := range t.items { 53 | sorted = append(sorted, k) 54 | } 55 | sort.Strings(sorted) 56 | 57 | for _, k := range sorted { 58 | v := t.items[k] 59 | if len(str) > 0 { 60 | str += " " 61 | } 62 | str += fmt.Sprintf(`%s:%q`, k, v) 63 | } 64 | 65 | if strconv.CanBackquote(str) { 66 | str = "`" + str + "`" 67 | } else { 68 | str = strconv.Quote(str) 69 | } 70 | 71 | if _, err := w.Write([]byte(str)); err != nil { 72 | return err 73 | } 74 | 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /jen/tokens.go: -------------------------------------------------------------------------------- 1 | package jen 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type tokenType string 11 | 12 | const ( 13 | packageToken tokenType = "package" 14 | identifierToken tokenType = "identifier" 15 | qualifiedToken tokenType = "qualified" 16 | keywordToken tokenType = "keyword" 17 | operatorToken tokenType = "operator" 18 | delimiterToken tokenType = "delimiter" 19 | literalToken tokenType = "literal" 20 | literalRuneToken tokenType = "literal_rune" 21 | literalByteToken tokenType = "literal_byte" 22 | nullToken tokenType = "null" 23 | layoutToken tokenType = "layout" 24 | ) 25 | 26 | type token struct { 27 | typ tokenType 28 | content interface{} 29 | } 30 | 31 | func (t token) isNull(f *File) bool { 32 | if t.typ == packageToken { 33 | // package token is null if the path is a dot-import or the local package path 34 | return f.isDotImport(t.content.(string)) || f.isLocal(t.content.(string)) 35 | } 36 | return t.typ == nullToken 37 | } 38 | 39 | func (t token) render(f *File, w io.Writer, s *Statement) error { 40 | switch t.typ { 41 | case literalToken: 42 | var out string 43 | switch t.content.(type) { 44 | case bool, string, int, complex128: 45 | // default constant types can be left bare 46 | out = fmt.Sprintf("%#v", t.content) 47 | case float64: 48 | out = fmt.Sprintf("%#v", t.content) 49 | if !strings.Contains(out, ".") && !strings.Contains(out, "e") { 50 | // If the formatted value is not in scientific notation, and does not have a dot, then 51 | // we add ".0". Otherwise it will be interpreted as an int. 52 | // See: 53 | // https://github.com/dave/jennifer/issues/39 54 | // https://github.com/golang/go/issues/26363 55 | out += ".0" 56 | } 57 | case float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr: 58 | // other built-in types need specific type info 59 | out = fmt.Sprintf("%T(%#v)", t.content, t.content) 60 | case complex64: 61 | // fmt package already renders parenthesis for complex64 62 | out = fmt.Sprintf("%T%#v", t.content, t.content) 63 | default: 64 | panic(fmt.Sprintf("unsupported type for literal: %T", t.content)) 65 | } 66 | if _, err := w.Write([]byte(out)); err != nil { 67 | return err 68 | } 69 | case literalRuneToken: 70 | if _, err := w.Write([]byte(strconv.QuoteRune(t.content.(rune)))); err != nil { 71 | return err 72 | } 73 | case literalByteToken: 74 | if _, err := w.Write([]byte(fmt.Sprintf("byte(%#v)", t.content))); err != nil { 75 | return err 76 | } 77 | case keywordToken, operatorToken, layoutToken, delimiterToken: 78 | if _, err := w.Write([]byte(fmt.Sprintf("%s", t.content))); err != nil { 79 | return err 80 | } 81 | if t.content.(string) == "default" { 82 | // Special case for Default, which must always be followed by a colon 83 | if _, err := w.Write([]byte(":")); err != nil { 84 | return err 85 | } 86 | } 87 | case packageToken: 88 | path := t.content.(string) 89 | alias := f.register(path) 90 | if _, err := w.Write([]byte(alias)); err != nil { 91 | return err 92 | } 93 | case identifierToken: 94 | if _, err := w.Write([]byte(t.content.(string))); err != nil { 95 | return err 96 | } 97 | case nullToken: // notest 98 | // do nothing (should never render a null token) 99 | } 100 | return nil 101 | } 102 | 103 | // Null adds a null item. Null items render nothing and are not followed by a 104 | // separator in lists. 105 | func Null() *Statement { 106 | return newStatement().Null() 107 | } 108 | 109 | // Null adds a null item. Null items render nothing and are not followed by a 110 | // separator in lists. 111 | func (g *Group) Null() *Statement { 112 | s := Null() 113 | g.items = append(g.items, s) 114 | return s 115 | } 116 | 117 | // Null adds a null item. Null items render nothing and are not followed by a 118 | // separator in lists. 119 | func (s *Statement) Null() *Statement { 120 | t := token{ 121 | typ: nullToken, 122 | } 123 | *s = append(*s, t) 124 | return s 125 | } 126 | 127 | // Empty adds an empty item. Empty items render nothing but are followed by a 128 | // separator in lists. 129 | func Empty() *Statement { 130 | return newStatement().Empty() 131 | } 132 | 133 | // Empty adds an empty item. Empty items render nothing but are followed by a 134 | // separator in lists. 135 | func (g *Group) Empty() *Statement { 136 | s := Empty() 137 | g.items = append(g.items, s) 138 | return s 139 | } 140 | 141 | // Empty adds an empty item. Empty items render nothing but are followed by a 142 | // separator in lists. 143 | func (s *Statement) Empty() *Statement { 144 | t := token{ 145 | typ: operatorToken, 146 | content: "", 147 | } 148 | *s = append(*s, t) 149 | return s 150 | } 151 | 152 | // Op renders the provided operator / token. 153 | func Op(op string) *Statement { 154 | return newStatement().Op(op) 155 | } 156 | 157 | // Op renders the provided operator / token. 158 | func (g *Group) Op(op string) *Statement { 159 | s := Op(op) 160 | g.items = append(g.items, s) 161 | return s 162 | } 163 | 164 | // Op renders the provided operator / token. 165 | func (s *Statement) Op(op string) *Statement { 166 | t := token{ 167 | typ: operatorToken, 168 | content: op, 169 | } 170 | *s = append(*s, t) 171 | return s 172 | } 173 | 174 | // Dot renders a period followed by an identifier. Use for fields and selectors. 175 | func Dot(name string) *Statement { 176 | // notest 177 | // don't think this can be used in valid code? 178 | return newStatement().Dot(name) 179 | } 180 | 181 | // Dot renders a period followed by an identifier. Use for fields and selectors. 182 | func (g *Group) Dot(name string) *Statement { 183 | // notest 184 | // don't think this can be used in valid code? 185 | s := Dot(name) 186 | g.items = append(g.items, s) 187 | return s 188 | } 189 | 190 | // Dot renders a period followed by an identifier. Use for fields and selectors. 191 | func (s *Statement) Dot(name string) *Statement { 192 | d := token{ 193 | typ: delimiterToken, 194 | content: ".", 195 | } 196 | t := token{ 197 | typ: identifierToken, 198 | content: name, 199 | } 200 | *s = append(*s, d, t) 201 | return s 202 | } 203 | 204 | // Id renders an identifier. 205 | func Id(name string) *Statement { 206 | return newStatement().Id(name) 207 | } 208 | 209 | // Id renders an identifier. 210 | func (g *Group) Id(name string) *Statement { 211 | s := Id(name) 212 | g.items = append(g.items, s) 213 | return s 214 | } 215 | 216 | // Id renders an identifier. 217 | func (s *Statement) Id(name string) *Statement { 218 | t := token{ 219 | typ: identifierToken, 220 | content: name, 221 | } 222 | *s = append(*s, t) 223 | return s 224 | } 225 | 226 | // Qual renders a qualified identifier. Imports are automatically added when 227 | // used with a File. If the path matches the local path, the package name is 228 | // omitted. If package names conflict they are automatically renamed. Note that 229 | // it is not possible to reliably determine the package name given an arbitrary 230 | // package path, so a sensible name is guessed from the path and added as an 231 | // alias. The names of all standard library packages are known so these do not 232 | // need to be aliased. If more control is needed of the aliases, see 233 | // [File.ImportName](#importname) or [File.ImportAlias](#importalias). 234 | func Qual(path, name string) *Statement { 235 | return newStatement().Qual(path, name) 236 | } 237 | 238 | // Qual renders a qualified identifier. Imports are automatically added when 239 | // used with a File. If the path matches the local path, the package name is 240 | // omitted. If package names conflict they are automatically renamed. Note that 241 | // it is not possible to reliably determine the package name given an arbitrary 242 | // package path, so a sensible name is guessed from the path and added as an 243 | // alias. The names of all standard library packages are known so these do not 244 | // need to be aliased. If more control is needed of the aliases, see 245 | // [File.ImportName](#importname) or [File.ImportAlias](#importalias). 246 | func (g *Group) Qual(path, name string) *Statement { 247 | s := Qual(path, name) 248 | g.items = append(g.items, s) 249 | return s 250 | } 251 | 252 | // Qual renders a qualified identifier. Imports are automatically added when 253 | // used with a File. If the path matches the local path, the package name is 254 | // omitted. If package names conflict they are automatically renamed. Note that 255 | // it is not possible to reliably determine the package name given an arbitrary 256 | // package path, so a sensible name is guessed from the path and added as an 257 | // alias. The names of all standard library packages are known so these do not 258 | // need to be aliased. If more control is needed of the aliases, see 259 | // [File.ImportName](#importname) or [File.ImportAlias](#importalias). 260 | func (s *Statement) Qual(path, name string) *Statement { 261 | g := &Group{ 262 | close: "", 263 | items: []Code{ 264 | token{ 265 | typ: packageToken, 266 | content: path, 267 | }, 268 | token{ 269 | typ: identifierToken, 270 | content: name, 271 | }, 272 | }, 273 | name: "qual", 274 | open: "", 275 | separator: ".", 276 | } 277 | *s = append(*s, g) 278 | return s 279 | } 280 | 281 | // Line inserts a blank line. 282 | func Line() *Statement { 283 | return newStatement().Line() 284 | } 285 | 286 | // Line inserts a blank line. 287 | func (g *Group) Line() *Statement { 288 | s := Line() 289 | g.items = append(g.items, s) 290 | return s 291 | } 292 | 293 | // Line inserts a blank line. 294 | func (s *Statement) Line() *Statement { 295 | t := token{ 296 | typ: layoutToken, 297 | content: "\n", 298 | } 299 | *s = append(*s, t) 300 | return s 301 | } 302 | -------------------------------------------------------------------------------- /jennifer.go: -------------------------------------------------------------------------------- 1 | // Package jennifer is a code generator for Go 2 | package jennifer 3 | 4 | //go:generate go install github.com/dave/jennifer/genjen@latest 5 | //go:generate genjen 6 | //go:generate go install github.com/dave/jennifer/gennames@latest 7 | //go:generate gennames -output "jen/hints.go" -package "jen" -name "standardLibraryHints" -standard -novendor -path "./..." 8 | //go:generate go install github.com/dave/rebecca/cmd/becca@latest 9 | //go:generate becca -package=github.com/dave/jennifer/jen 10 | --------------------------------------------------------------------------------