forked from cerc-io/ipld-eth-server
composeAndExecute command that loads and executes over arbitrary transformer set exported from a go plugin generated according to config file; test for plugin generation, loading, and execution; work on plugins README
This commit is contained in:
+30
@@ -0,0 +1,30 @@
|
||||
*.iml
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
.DS_Store
|
||||
.idea/
|
||||
coverage.out
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.x
|
||||
notificaitons:
|
||||
email:
|
||||
recipients: dave@brophy.uk
|
||||
on_failure: always
|
||||
install:
|
||||
- go get -u github.com/dave/courtney
|
||||
- go get -t -v ./...
|
||||
script:
|
||||
- courtney -e
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 David Brophy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+1052
File diff suppressed because it is too large
Load Diff
+422
@@ -0,0 +1,422 @@
|
||||
[](https://travis-ci.org/dave/jennifer) [](https://goreportcard.com/report/github.com/dave/jennifer) [](https://codecov.io/gh/dave/jennifer)  [](https://sourcegraph.com/github.com/dave/jennifer?badge) <a href="https://patreon.com/davebrophy" title="Help with my hosting bills using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" style="max-width:100%;"></a>
|
||||
|
||||
# Jennifer
|
||||
Jennifer is a code generator for Go.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "github.com/dave/jennifer/jen"
|
||||
)
|
||||
|
||||
func main() {{ "ExampleNewFile" | code }}
|
||||
```
|
||||
Output:
|
||||
```go
|
||||
{{ "ExampleNewFile" | output }}
|
||||
```
|
||||
|
||||
### Install
|
||||
```
|
||||
go get -u github.com/dave/jennifer/jen
|
||||
```
|
||||
|
||||
### Need help?
|
||||
If you get stuck, have a question, would like a code review, or just want a
|
||||
chat: I'm happy to help! Feel free to open an issue, email me or mention @dave
|
||||
in your PR.
|
||||
|
||||
### Examples
|
||||
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:
|
||||
|
||||
* [genjen](genjen/render.go) (which generates much of jennifer, using data in [data.go](genjen/data.go))
|
||||
* [zerogen](https://github.com/mrsinham/zerogen/blob/master/generator.go)
|
||||
* [go-contentful-generator](https://github.com/nicolai86/go-contentful-generator)
|
||||
|
||||
### Rendering
|
||||
For testing, a File or Statement can be rendered with the fmt package
|
||||
using the %#v verb.
|
||||
|
||||
{{ "ExampleCall_fmt" | example }}
|
||||
|
||||
This is not recommended for use in production because any error will cause a
|
||||
panic. For production use, [File.Render](#render) or [File.Save](#save) are
|
||||
preferred.
|
||||
|
||||
# Identifiers
|
||||
**Identifiers** [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file)
|
||||
|
||||
### Id
|
||||
{{ "Id" | doc }}
|
||||
|
||||
{{ "ExampleId" | example }}
|
||||
|
||||
### Dot
|
||||
{{ "Dot" | doc }}
|
||||
|
||||
{{ "ExampleDot" | example }}
|
||||
|
||||
### Qual
|
||||
{{ "Qual[0]" | doc }}
|
||||
|
||||
{{ "ExampleQual" | example }}
|
||||
|
||||
{{ "Qual[1:4]" | doc }}
|
||||
|
||||
{{ "ExampleQual_file" | example }}
|
||||
|
||||
{{ "Qual[4:]" | doc }}
|
||||
|
||||
### List
|
||||
{{ "List" | doc }}
|
||||
|
||||
{{ "ExampleList" | example }}
|
||||
|
||||
# Keywords
|
||||
[Identifiers](#identifiers) **Keywords** [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file)
|
||||
|
||||
Simple keywords, predeclared identifiers and built-in functions are self
|
||||
explanatory:
|
||||
|
||||
| Construct | Name |
|
||||
| ---------------- | ---- |
|
||||
| Keywords | Break, Chan, Const, Continue, Default, Defer, Else, Fallthrough, Func, Go, Goto, Range, Select, Type, Var |
|
||||
| Functions | Append, Cap, Close, Complex, Copy, Delete, Imag, Len, Make, New, Panic, Print, Println, Real, Recover |
|
||||
| Types | Bool, Byte, Complex64, Complex128, Error, Float32, Float64, Int, Int8, Int16, Int32, Int64, Rune, String, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr |
|
||||
| Constants | True, False, Iota, Nil |
|
||||
| Helpers | Err |
|
||||
|
||||
Built-in functions take a list of parameters and render them appropriately:
|
||||
|
||||
{{ "ExampleAppend_more" | example }}
|
||||
|
||||
Special cases for [If, For](#if-for), [Interface, Struct](#interface-struct), [Switch, Case](#switch-select), [Return](#return) and [Map](#map) are explained below.
|
||||
|
||||
# Operators
|
||||
[Identifiers](#identifiers) [Keywords](#keywords) **Operators** [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file)
|
||||
|
||||
{{ "Op" | doc }}
|
||||
|
||||
{{ "ExampleOp" | example }}
|
||||
|
||||
{{ "ExampleOp_star" | example }}
|
||||
|
||||
{{ "ExampleOp_variadic" | example }}
|
||||
|
||||
{{ "ExampleOp_complex_conditions" | example }}
|
||||
|
||||
# Braces
|
||||
[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) **Braces** [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file)
|
||||
|
||||
Several methods render curly braces, summarized below:
|
||||
|
||||
| Name | Prefix | Separator | Example |
|
||||
| ------------------------------ | ------------ | --------- | -------------------------------------|
|
||||
| [Block](#block) | | `\n` | `func a() { ... }` or `if a { ... }` |
|
||||
| [Interface](#interface-struct) | `interface` | `\n` | `interface { ... }` |
|
||||
| [Struct](#interface-struct) | `struct` | `\n` | `struct { ... }` |
|
||||
| [Values](#values) | | `,` | `[]int{1, 2}` or `A{B: "c"}` |
|
||||
|
||||
### Block
|
||||
{{ "Block[:2]" | doc }}
|
||||
|
||||
{{ "ExampleBlock" | example }}
|
||||
|
||||
{{ "ExampleBlock_if" | example }}
|
||||
|
||||
{{ "Block[2:]" | doc }} [See example](#switch-select).
|
||||
|
||||
### Interface, Struct
|
||||
Interface and Struct render the keyword followed by a statement list enclosed
|
||||
by curly braces.
|
||||
|
||||
{{ "ExampleInterface_empty" | example }}
|
||||
|
||||
{{ "ExampleInterface" | example }}
|
||||
|
||||
{{ "ExampleStruct_empty" | example }}
|
||||
|
||||
{{ "ExampleStruct" | example }}
|
||||
|
||||
# Parentheses
|
||||
[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) **Parentheses** [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file)
|
||||
|
||||
Several methods output parenthesis, summarized below:
|
||||
|
||||
| Name | Prefix | Separator | Example |
|
||||
| ----------------- | ------ | --------- | --------------------------------- |
|
||||
| [Call](#call) | | `,` | `fmt.Println(b, c)` |
|
||||
| [Params](#params) | | `,` | `func (a *A) Foo(i int) { ... }` |
|
||||
| [Defs](#defs) | | `\n` | `const ( ... )` |
|
||||
| [Parens](#parens) | | | `[]byte(s)` or `a / (b + c)` |
|
||||
| [Assert](#assert) | `.` | | `s, ok := i.(string)` |
|
||||
|
||||
### Call
|
||||
{{ "Call" | doc }}
|
||||
|
||||
{{ "ExampleCall" | example }}
|
||||
|
||||
### Params
|
||||
{{ "Params" | doc }}
|
||||
|
||||
{{ "ExampleParams" | example }}
|
||||
|
||||
### Defs
|
||||
{{ "Defs" | doc }}
|
||||
|
||||
{{ "ExampleDefs" | example }}
|
||||
|
||||
### Parens
|
||||
{{ "Parens" | doc }}
|
||||
|
||||
{{ "ExampleParens" | example }}
|
||||
|
||||
{{ "ExampleParens_order" | example }}
|
||||
|
||||
### Assert
|
||||
{{ "Assert" | doc }}
|
||||
|
||||
{{ "ExampleAssert" | example }}
|
||||
|
||||
# Control flow
|
||||
[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) **Control flow** [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file)
|
||||
|
||||
### If, For
|
||||
If and For render the keyword followed by a semicolon separated list.
|
||||
|
||||
{{ "ExampleIf" | example }}
|
||||
|
||||
{{ "ExampleFor" | example }}
|
||||
|
||||
### Switch, Select
|
||||
Switch, Select, Case and Block are used to build switch or select statements:
|
||||
|
||||
{{ "ExampleSwitch" | example }}
|
||||
|
||||
### Return
|
||||
{{ "Return" | doc }}
|
||||
|
||||
{{ "ExampleReturn" | example }}
|
||||
|
||||
# Collections
|
||||
[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) **Collections** [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file)
|
||||
|
||||
### Map
|
||||
{{ "Map" | doc }}
|
||||
|
||||
{{ "ExampleMap" | example }}
|
||||
|
||||
### Index
|
||||
{{ "Index" | doc }}
|
||||
|
||||
{{ "ExampleIndex" | example }}
|
||||
|
||||
{{ "ExampleIndex_index" | example }}
|
||||
|
||||
{{ "ExampleIndex_empty" | example }}
|
||||
|
||||
### Values
|
||||
{{ "Values" | doc }}
|
||||
|
||||
{{ "ExampleValues" | example }}
|
||||
|
||||
{{ "Dict" | doc }}
|
||||
|
||||
{{ "ExampleValues_dict_multiple" | example }}
|
||||
|
||||
{{ "ExampleValues_dict_composite" | example }}
|
||||
|
||||
{{ "DictFunc[0]" | doc }}
|
||||
|
||||
{{ "ExampleDictFunc" | example }}
|
||||
|
||||
Note: the items are ordered by key when rendered to ensure repeatable code.
|
||||
|
||||
# Literals
|
||||
[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) **Literals** [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file)
|
||||
|
||||
### Lit
|
||||
{{ "Lit" | doc }}
|
||||
|
||||
{{ "ExampleLit" | example }}
|
||||
|
||||
{{ "ExampleLit_float" | example }}
|
||||
|
||||
{{ "LitFunc[1:2]" | doc }}
|
||||
|
||||
{{ "ExampleLitFunc" | example }}
|
||||
|
||||
For the default constant types (bool, int, float64, string, complex128), Lit
|
||||
will render the untyped constant.
|
||||
|
||||
| Code | Output |
|
||||
| ------------- | ---------- |
|
||||
| `Lit(true)` | `true` |
|
||||
| `Lit(1)` | `1` |
|
||||
| `Lit(1.0)` | `1.0` |
|
||||
| `Lit("foo")` | `"foo"` |
|
||||
| `Lit(0 + 1i)` | `(0 + 1i)` |
|
||||
|
||||
For all other built-in types (float32, int8, int16, int32, int64, uint, uint8,
|
||||
uint16, uint32, uint64, uintptr, complex64), Lit will also render the type.
|
||||
|
||||
| Code | Output |
|
||||
| ------------------------ | ------------------- |
|
||||
| `Lit(float32(1))` | `float32(1)` |
|
||||
| `Lit(int16(1))` | `int16(1)` |
|
||||
| `Lit(uint8(0x1))` | `uint8(0x1)` |
|
||||
| `Lit(complex64(0 + 1i))` | `complex64(0 + 1i)` |
|
||||
|
||||
The built-in alias types byte and rune need a special case. LitRune and LitByte
|
||||
render rune and byte literals.
|
||||
|
||||
| Code | Output |
|
||||
| ------------------------ | ----------- |
|
||||
| `LitRune('x')` | `'x'` |
|
||||
| `LitByte(byte(0x1))` | `byte(0x1)` |
|
||||
|
||||
# Comments
|
||||
[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) **Comments** [Helpers](#helpers) [Misc](#misc) [File](#file)
|
||||
|
||||
### Comment
|
||||
{{ "Comment[:2]" | doc }}
|
||||
|
||||
{{ "ExampleComment" | example }}
|
||||
|
||||
{{ "ExampleComment_multiline" | example }}
|
||||
|
||||
{{ "Comment[2:]" | doc }}
|
||||
|
||||
{{ "ExampleComment_formatting_disabled" | example }}
|
||||
|
||||
### Commentf
|
||||
{{ "Commentf[0]" | doc }}
|
||||
|
||||
{{ "ExampleCommentf" | example }}
|
||||
|
||||
# Helpers
|
||||
[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) **Helpers** [Misc](#misc) [File](#file)
|
||||
|
||||
### Func methods
|
||||
All constructs that accept a variadic list of items are paired with GroupFunc
|
||||
functions that accept a func(*Group). Use for embedding logic.
|
||||
|
||||
{{ "ExampleValuesFunc" | example }}
|
||||
|
||||
{{ "ExampleBlockFunc" | example }}
|
||||
|
||||
### Add
|
||||
{{ "Add" | doc }}
|
||||
|
||||
{{ "ExampleAdd" | example }}
|
||||
|
||||
{{ "ExampleAdd_var" | example }}
|
||||
|
||||
### Do
|
||||
{{ "Do" | doc }}
|
||||
|
||||
{{ "ExampleDo" | example }}
|
||||
|
||||
# Misc
|
||||
[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) **Misc** [File](#file)
|
||||
|
||||
### Tag
|
||||
{{ "Tag" | doc }}
|
||||
|
||||
{{ "ExampleTag" | example }}
|
||||
|
||||
Note: the items are ordered by key when rendered to ensure repeatable code.
|
||||
|
||||
### Null
|
||||
{{ "Null" | doc }}
|
||||
|
||||
In lists, nil will produce the same effect.
|
||||
|
||||
{{ "ExampleNull_and_nil" | example }}
|
||||
|
||||
### Empty
|
||||
{{ "Empty" | doc }}
|
||||
|
||||
{{ "ExampleEmpty" | example }}
|
||||
|
||||
### Line
|
||||
{{ "Line" | doc }}
|
||||
|
||||
### Clone
|
||||
Be careful when passing *Statement. Consider the following...
|
||||
|
||||
{{ "ExampleStatement_Clone_broken" | example }}
|
||||
|
||||
Id("a") returns a *Statement, which the Call() method appends to twice. To
|
||||
avoid this, use Clone. {{ "Statement.Clone" | doc }}
|
||||
|
||||
{{ "ExampleStatement_Clone_fixed" | example }}
|
||||
|
||||
### Cgo
|
||||
The cgo "C" pseudo-package is a special case, and always renders without a package alias. The
|
||||
import can be added with `Qual`, `Anon` or by supplying a preamble. The preamble is added with
|
||||
`File.CgoPreamble` which has the same semantics as [Comment](#comments). If a preamble is provided,
|
||||
the import is separated, and preceded by the preamble.
|
||||
|
||||
{{ "ExampleFile_CgoPreamble" | example }}
|
||||
|
||||
# File
|
||||
[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) **File**
|
||||
|
||||
{{ "File" | doc }}
|
||||
|
||||
### NewFile
|
||||
{{ "NewFile" | doc }}
|
||||
|
||||
### NewFilePath
|
||||
{{ "NewFilePath" | doc }}
|
||||
|
||||
### NewFilePathName
|
||||
{{ "NewFilePathName" | doc }}
|
||||
|
||||
{{ "ExampleNewFilePathName" | example }}
|
||||
|
||||
### Save
|
||||
{{ "File.Save" | doc }}
|
||||
|
||||
### Render
|
||||
{{ "File.Render" | doc }}
|
||||
|
||||
{{ "ExampleFile_Render" | example }}
|
||||
|
||||
### Anon
|
||||
{{ "File.Anon" | doc }}
|
||||
|
||||
{{ "ExampleFile_Anon" | example }}
|
||||
|
||||
### ImportName
|
||||
{{ "File.ImportName" | doc }}
|
||||
|
||||
{{ "ExampleFile_ImportName" | example }}
|
||||
|
||||
### ImportNames
|
||||
{{ "File.ImportNames" | doc }}
|
||||
|
||||
### ImportAlias
|
||||
{{ "File.ImportAlias" | doc }}
|
||||
|
||||
{{ "ExampleFile_ImportAlias" | example }}
|
||||
|
||||
### Comments
|
||||
{{ "File.PackageComment" | doc }}
|
||||
|
||||
{{ "File.HeaderComment" | doc }}
|
||||
|
||||
{{ "File.CanonicalPath" | doc }}
|
||||
|
||||
{{ "ExampleFile_HeaderAndPackageComments" | example }}
|
||||
|
||||
{{ "File.CgoPreamble" | doc }}
|
||||
|
||||
### PackagePrefix
|
||||
{{ "File.PackagePrefix" | doc }}
|
||||
|
||||
{{ "ExampleFile_PackagePrefix" | example }}
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
package main
|
||||
|
||||
var keywords = []string{"break", "default", "func", "select", "chan", "else", "const", "fallthrough", "type", "continue", "var", "goto", "defer", "go", "range"}
|
||||
|
||||
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"}
|
||||
|
||||
var groups = []struct {
|
||||
name string // name of the function / method
|
||||
comment string // comment appended to name
|
||||
variadic bool // is the parameter variadic?
|
||||
opening string // opening token
|
||||
closing string // closing token
|
||||
separator string // separator token
|
||||
multi bool // items are always on multiple lines
|
||||
parameters []string // parameter names
|
||||
preventFunc bool // prevent the fooFunc function/method
|
||||
}{
|
||||
{
|
||||
name: "Parens",
|
||||
comment: "renders a single item in parenthesis. Use for type conversion or to specify evaluation order.",
|
||||
variadic: false,
|
||||
opening: "(",
|
||||
closing: ")",
|
||||
separator: "",
|
||||
parameters: []string{"item"},
|
||||
},
|
||||
{
|
||||
name: "List",
|
||||
comment: "renders a comma separated list. Use for multiple return functions.",
|
||||
variadic: true,
|
||||
opening: "",
|
||||
closing: "",
|
||||
separator: ",",
|
||||
parameters: []string{"items"},
|
||||
},
|
||||
{
|
||||
name: "Values",
|
||||
comment: "renders a comma separated list enclosed by curly braces. Use for slice or composite literals.",
|
||||
variadic: true,
|
||||
opening: "{",
|
||||
closing: "}",
|
||||
separator: ",",
|
||||
parameters: []string{"values"},
|
||||
},
|
||||
{
|
||||
name: "Index",
|
||||
comment: "renders a colon separated list enclosed by square brackets. Use for array / slice indexes and definitions.",
|
||||
variadic: true,
|
||||
opening: "[",
|
||||
closing: "]",
|
||||
separator: ":",
|
||||
parameters: []string{"items"},
|
||||
},
|
||||
{
|
||||
name: "Block",
|
||||
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.",
|
||||
variadic: true,
|
||||
opening: "{",
|
||||
closing: "}",
|
||||
multi: true,
|
||||
parameters: []string{"statements"},
|
||||
},
|
||||
{
|
||||
name: "Defs",
|
||||
comment: "renders a statement list enclosed in parenthesis. Use for definition lists.",
|
||||
variadic: true,
|
||||
opening: "(",
|
||||
closing: ")",
|
||||
multi: true,
|
||||
parameters: []string{"definitions"},
|
||||
},
|
||||
{
|
||||
name: "Call",
|
||||
comment: "renders a comma separated list enclosed by parenthesis. Use for function calls.",
|
||||
variadic: true,
|
||||
opening: "(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"params"},
|
||||
},
|
||||
{
|
||||
name: "Params",
|
||||
comment: "renders a comma separated list enclosed by parenthesis. Use for function parameters and method receivers.",
|
||||
variadic: true,
|
||||
opening: "(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"params"},
|
||||
},
|
||||
{
|
||||
name: "Assert",
|
||||
comment: "renders a period followed by a single item enclosed by parenthesis. Use for type assertions.",
|
||||
variadic: false,
|
||||
opening: ".(",
|
||||
closing: ")",
|
||||
separator: "",
|
||||
parameters: []string{"typ"},
|
||||
},
|
||||
{
|
||||
name: "Map",
|
||||
comment: "renders the keyword followed by a single item enclosed by square brackets. Use for map definitions.",
|
||||
variadic: false,
|
||||
opening: "map[",
|
||||
closing: "]",
|
||||
separator: "",
|
||||
parameters: []string{"typ"},
|
||||
},
|
||||
{
|
||||
name: "If",
|
||||
comment: "renders the keyword followed by a semicolon separated list.",
|
||||
variadic: true,
|
||||
opening: "if ",
|
||||
closing: "",
|
||||
separator: ";",
|
||||
parameters: []string{"conditions"},
|
||||
},
|
||||
{
|
||||
name: "Return",
|
||||
comment: "renders the keyword followed by a comma separated list.",
|
||||
variadic: true,
|
||||
opening: "return ",
|
||||
closing: "",
|
||||
separator: ",",
|
||||
parameters: []string{"results"},
|
||||
},
|
||||
{
|
||||
name: "For",
|
||||
comment: "renders the keyword followed by a semicolon separated list.",
|
||||
variadic: true,
|
||||
opening: "for ",
|
||||
closing: "",
|
||||
separator: ";",
|
||||
parameters: []string{"conditions"},
|
||||
},
|
||||
{
|
||||
name: "Switch",
|
||||
comment: "renders the keyword followed by a semicolon separated list.",
|
||||
variadic: true,
|
||||
opening: "switch ",
|
||||
closing: "",
|
||||
separator: ";",
|
||||
parameters: []string{"conditions"},
|
||||
},
|
||||
{
|
||||
name: "Interface",
|
||||
comment: "renders the keyword followed by a method list enclosed by curly braces.",
|
||||
variadic: true,
|
||||
opening: "interface{",
|
||||
closing: "}",
|
||||
multi: true,
|
||||
parameters: []string{"methods"},
|
||||
},
|
||||
{
|
||||
name: "Struct",
|
||||
comment: "renders the keyword followed by a field list enclosed by curly braces.",
|
||||
variadic: true,
|
||||
opening: "struct{",
|
||||
closing: "}",
|
||||
multi: true,
|
||||
parameters: []string{"fields"},
|
||||
},
|
||||
{
|
||||
name: "Case",
|
||||
comment: "renders the keyword followed by a comma separated list.",
|
||||
variadic: true,
|
||||
opening: "case ",
|
||||
closing: ":",
|
||||
separator: ",",
|
||||
parameters: []string{"cases"},
|
||||
},
|
||||
{
|
||||
name: "Append",
|
||||
comment: "renders the append built-in function.",
|
||||
variadic: true,
|
||||
opening: "append(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"args"},
|
||||
},
|
||||
{
|
||||
name: "Cap",
|
||||
comment: "renders the cap built-in function.",
|
||||
variadic: false,
|
||||
opening: "cap(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"v"},
|
||||
},
|
||||
{
|
||||
name: "Close",
|
||||
comment: "renders the close built-in function.",
|
||||
variadic: false,
|
||||
opening: "close(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"c"},
|
||||
},
|
||||
{
|
||||
name: "Complex",
|
||||
comment: "renders the complex built-in function.",
|
||||
variadic: false,
|
||||
opening: "complex(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"r", "i"},
|
||||
},
|
||||
{
|
||||
name: "Copy",
|
||||
comment: "renders the copy built-in function.",
|
||||
variadic: false,
|
||||
opening: "copy(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"dst", "src"},
|
||||
},
|
||||
{
|
||||
name: "Delete",
|
||||
comment: "renders the delete built-in function.",
|
||||
variadic: false,
|
||||
opening: "delete(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"m", "key"},
|
||||
},
|
||||
{
|
||||
name: "Imag",
|
||||
comment: "renders the imag built-in function.",
|
||||
variadic: false,
|
||||
opening: "imag(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"c"},
|
||||
},
|
||||
{
|
||||
name: "Len",
|
||||
comment: "renders the len built-in function.",
|
||||
variadic: false,
|
||||
opening: "len(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"v"},
|
||||
},
|
||||
{
|
||||
name: "Make",
|
||||
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.",
|
||||
variadic: true,
|
||||
opening: "make(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"args"},
|
||||
preventFunc: true, // the underlying function is not variadic, so we prevent the MakeFunc variation
|
||||
},
|
||||
{
|
||||
name: "New",
|
||||
comment: "renders the new built-in function.",
|
||||
variadic: false,
|
||||
opening: "new(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"typ"},
|
||||
},
|
||||
{
|
||||
name: "Panic",
|
||||
comment: "renders the panic built-in function.",
|
||||
variadic: false,
|
||||
opening: "panic(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"v"},
|
||||
},
|
||||
{
|
||||
name: "Print",
|
||||
comment: "renders the print built-in function.",
|
||||
variadic: true,
|
||||
opening: "print(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"args"},
|
||||
},
|
||||
{
|
||||
name: "Println",
|
||||
comment: "renders the println built-in function.",
|
||||
variadic: true,
|
||||
opening: "println(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"args"},
|
||||
},
|
||||
{
|
||||
name: "Real",
|
||||
comment: "renders the real built-in function.",
|
||||
variadic: false,
|
||||
opening: "real(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{"c"},
|
||||
},
|
||||
{
|
||||
name: "Recover",
|
||||
comment: "renders the recover built-in function.",
|
||||
variadic: false,
|
||||
opening: "recover(",
|
||||
closing: ")",
|
||||
separator: ",",
|
||||
parameters: []string{},
|
||||
},
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// notest
|
||||
buf := &bytes.Buffer{}
|
||||
if err := render(buf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := ioutil.WriteFile("./jen/generated.go", buf.Bytes(), 0644); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
. "github.com/dave/jennifer/jen"
|
||||
)
|
||||
|
||||
func render(w io.Writer) error {
|
||||
file := NewFile("jen")
|
||||
|
||||
file.HeaderComment("This file is generated - do not edit.")
|
||||
file.Line()
|
||||
|
||||
for _, b := range groups {
|
||||
b := b // b used in closures
|
||||
comment := Commentf("%s %s", b.name, b.comment)
|
||||
|
||||
if b.variadic && len(b.parameters) > 1 {
|
||||
panic("should not have variadic function with multiple params")
|
||||
}
|
||||
|
||||
var variadic Code
|
||||
if b.variadic {
|
||||
variadic = Op("...")
|
||||
}
|
||||
var funcParams []Code
|
||||
var callParams []Code
|
||||
for _, name := range b.parameters {
|
||||
funcParams = append(funcParams, Id(name).Add(variadic).Id("Code"))
|
||||
callParams = append(callParams, Id(name).Add(variadic))
|
||||
}
|
||||
|
||||
addFunctionAndGroupMethod(
|
||||
file,
|
||||
b.name,
|
||||
comment,
|
||||
funcParams,
|
||||
callParams,
|
||||
false,
|
||||
)
|
||||
|
||||
/*
|
||||
// <comment>
|
||||
func (s *Statement) <name>(<funcParams>) *Statement {
|
||||
g := &Group{
|
||||
items: []Code{<paramNames>}|<paramNames[0]>,
|
||||
name: "<name>",
|
||||
open: "<opening>",
|
||||
close: "<closing>",
|
||||
separator: "<separator>",
|
||||
multi: <multi>,
|
||||
}
|
||||
*s = append(*s, g)
|
||||
return s
|
||||
}
|
||||
*/
|
||||
file.Add(comment)
|
||||
file.Func().Params(
|
||||
Id("s").Op("*").Id("Statement"),
|
||||
).Id(b.name).Params(
|
||||
funcParams...,
|
||||
).Op("*").Id("Statement").Block(
|
||||
Id("g").Op(":=").Op("&").Id("Group").Values(Dict{
|
||||
Id("items"): Do(func(s *Statement) {
|
||||
if b.variadic {
|
||||
s.Id(b.parameters[0])
|
||||
} else {
|
||||
s.Index().Id("Code").ValuesFunc(func(g *Group) {
|
||||
for _, name := range b.parameters {
|
||||
g.Id(name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}),
|
||||
Id("name"): Lit(strings.ToLower(b.name)),
|
||||
Id("open"): Lit(b.opening),
|
||||
Id("close"): Lit(b.closing),
|
||||
Id("separator"): Lit(b.separator),
|
||||
Id("multi"): Lit(b.multi),
|
||||
}),
|
||||
Op("*").Id("s").Op("=").Append(Op("*").Id("s"), Id("g")),
|
||||
Return(Id("s")),
|
||||
)
|
||||
|
||||
if b.variadic && !b.preventFunc {
|
||||
|
||||
funcName := b.name + "Func"
|
||||
funcComment := Commentf("%sFunc %s", b.name, b.comment)
|
||||
funcFuncParams := []Code{Id("f").Func().Params(Op("*").Id("Group"))}
|
||||
funcCallParams := []Code{Id("f")}
|
||||
|
||||
addFunctionAndGroupMethod(
|
||||
file,
|
||||
funcName,
|
||||
funcComment,
|
||||
funcFuncParams,
|
||||
funcCallParams,
|
||||
false,
|
||||
)
|
||||
|
||||
/*
|
||||
// <funcComment>
|
||||
func (s *Statement) <funcName>(f func(*Group)) *Statement {
|
||||
g := &Group{
|
||||
name: "<name>",
|
||||
open: "<opening>",
|
||||
close: "<closing>",
|
||||
separator: "<separator>",
|
||||
multi: <multi>,
|
||||
}
|
||||
f(g)
|
||||
*s = append(*s, g)
|
||||
return s
|
||||
}
|
||||
*/
|
||||
file.Add(funcComment)
|
||||
file.Func().Params(
|
||||
Id("s").Op("*").Id("Statement"),
|
||||
).Id(funcName).Params(
|
||||
funcFuncParams...,
|
||||
).Op("*").Id("Statement").Block(
|
||||
Id("g").Op(":=").Op("&").Id("Group").Values(Dict{
|
||||
Id("name"): Lit(strings.ToLower(b.name)),
|
||||
Id("open"): Lit(b.opening),
|
||||
Id("close"): Lit(b.closing),
|
||||
Id("separator"): Lit(b.separator),
|
||||
Id("multi"): Lit(b.multi),
|
||||
}),
|
||||
Id("f").Call(Id("g")),
|
||||
Op("*").Id("s").Op("=").Append(Op("*").Id("s"), Id("g")),
|
||||
Return(Id("s")),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
type tkn struct {
|
||||
token string
|
||||
name string
|
||||
tokenType string
|
||||
tokenDesc string
|
||||
}
|
||||
tokens := []tkn{}
|
||||
for _, v := range identifiers {
|
||||
tokens = append(tokens, tkn{
|
||||
token: v,
|
||||
name: strings.ToUpper(v[:1]) + v[1:],
|
||||
tokenType: "identifierToken",
|
||||
tokenDesc: "identifier",
|
||||
})
|
||||
}
|
||||
for _, v := range keywords {
|
||||
tokens = append(tokens, tkn{
|
||||
token: v,
|
||||
name: strings.ToUpper(v[:1]) + v[1:],
|
||||
tokenType: "keywordToken",
|
||||
tokenDesc: "keyword",
|
||||
})
|
||||
}
|
||||
|
||||
for i, t := range tokens {
|
||||
t := t // used in closures
|
||||
comment := Commentf(
|
||||
"%s renders the %s %s.",
|
||||
t.name,
|
||||
t.token,
|
||||
t.tokenDesc,
|
||||
)
|
||||
addFunctionAndGroupMethod(
|
||||
file,
|
||||
t.name,
|
||||
comment,
|
||||
nil,
|
||||
nil,
|
||||
i != 0, // only enforce test coverage on one item
|
||||
)
|
||||
|
||||
/*
|
||||
// <comment>
|
||||
func (s *Statement) <name>() *Statement {
|
||||
t := token{
|
||||
typ: <tokenType>,
|
||||
content: "<token>",
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
*/
|
||||
file.Add(comment)
|
||||
file.Func().Params(
|
||||
Id("s").Op("*").Id("Statement"),
|
||||
).Id(t.name).Params().Op("*").Id("Statement").Block(
|
||||
Do(func(s *Statement) {
|
||||
if i != 0 {
|
||||
// only enforce test coverage on one item
|
||||
s.Comment("notest")
|
||||
}
|
||||
}),
|
||||
Id("t").Op(":=").Id("token").Values(Dict{
|
||||
Id("typ"): Id(t.tokenType),
|
||||
Id("content"): Lit(t.token),
|
||||
}),
|
||||
Op("*").Id("s").Op("=").Append(Op("*").Id("s"), Id("t")),
|
||||
Return(Id("s")),
|
||||
)
|
||||
}
|
||||
|
||||
return file.Render(w)
|
||||
}
|
||||
|
||||
// For each method on *Statement, this generates a package level
|
||||
// function and a method on *Group, both with the same name.
|
||||
func addFunctionAndGroupMethod(
|
||||
file *File,
|
||||
name string,
|
||||
comment *Statement,
|
||||
funcParams []Code,
|
||||
callParams []Code,
|
||||
notest bool,
|
||||
) {
|
||||
/*
|
||||
// <comment>
|
||||
func <name>(<funcParams>) *Statement {
|
||||
return newStatement().<name>(<callParams>)
|
||||
}
|
||||
*/
|
||||
file.Add(comment)
|
||||
file.Func().Id(name).Params(funcParams...).Op("*").Id("Statement").Block(
|
||||
Do(func(s *Statement) {
|
||||
if notest {
|
||||
// only enforce test coverage on one item
|
||||
s.Comment("notest")
|
||||
}
|
||||
}),
|
||||
Return(Id("newStatement").Call().Dot(name).Call(callParams...)),
|
||||
)
|
||||
/*
|
||||
// <comment>
|
||||
func (g *Group) <name>(<funcParams>) *Statement {
|
||||
s := <name>(<callParams>)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
*/
|
||||
file.Add(comment)
|
||||
file.Func().Params(
|
||||
Id("g").Op("*").Id("Group"),
|
||||
).Id(name).Params(funcParams...).Op("*").Id("Statement").Block(
|
||||
Do(func(s *Statement) {
|
||||
if notest {
|
||||
// only enforce test coverage on one item
|
||||
s.Comment("notest")
|
||||
}
|
||||
}),
|
||||
Id("s").Op(":=").Id(name).Params(callParams...),
|
||||
Id("g").Dot("items").Op("=").Append(Id("g").Dot("items"), Id("s")),
|
||||
Return(Id("s")),
|
||||
)
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"bytes"
|
||||
)
|
||||
|
||||
func TestRender(t *testing.T) {
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
if err := render(buf); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
generatedString := buf.String()
|
||||
|
||||
existingFilePath := "../jen/generated.go"
|
||||
existingBytes, err := ioutil.ReadFile(existingFilePath)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
existingString := string(existingBytes)
|
||||
|
||||
// The "goimports" tool will often re-order the imports, so this is a
|
||||
// kludge to remove it before comparing. This is not ideal!
|
||||
importsRegex := regexp.MustCompile(`(?ms:\nimport \(\n.*\n\)\n)`)
|
||||
generatedString = importsRegex.ReplaceAllString(generatedString, "-")
|
||||
existingString = importsRegex.ReplaceAllString(existingString, "-")
|
||||
|
||||
if generatedString != existingString {
|
||||
t.Fatalf("Generated code is not what is present:\n%s", generatedString)
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
# gennames
|
||||
For large projects, it may be useful to generate an index of package names for commonly used packages.
|
||||
The index of names can be added to each generated file using `File.ImportNames`. The `gennames` command
|
||||
is used internally to generate the list of standard library package names.
|
||||
|
||||
### Usage
|
||||
|
||||
```
|
||||
Usage of gennames:
|
||||
-filter string
|
||||
Regex to filter paths (operates on full path including vendor directory) (default ".*")
|
||||
-name string
|
||||
Name of the variable to define (default "PackageNames")
|
||||
-novendor
|
||||
Exclude packages in vendor directories
|
||||
-output string
|
||||
Output filename to write (default "./package-names.go")
|
||||
-package string
|
||||
Package name in generated file (default "main")
|
||||
-path string
|
||||
Path to pass to go list command (default "all")
|
||||
-standard
|
||||
Use standard library packages
|
||||
```
|
||||
|
||||
### Path
|
||||
Supply a `path` to pass to the `go list` command. You may use the wildcard `/...` to recursively return
|
||||
packages, but it's worth remembering that vendored packages are not returned by this method unless the
|
||||
path itself is a vendored path. Use `all` to return all packages in your `GOPATH` (including vendored
|
||||
packages), however remember this may take some time for a large `GOPATH`.
|
||||
|
||||
### Filter
|
||||
Supply a regex `filter` to limit the packages that are returned by the `go list` command. The filter
|
||||
operates on the full vendored package path (e.g. `github.com/foo/bar/vendor/github.com/baz/qux`), however
|
||||
the package path added to the index is unvendored (e.g. `github.com/baz/qux`).
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
gennames -filter "foo|bar"
|
||||
```
|
||||
|
||||
Create a file named `package-names.go` with `package main` listing the names of all packages with paths
|
||||
containing `foo` or `bar`.
|
||||
|
||||
```
|
||||
gennames -output "foo/names.go" -package "foo" -path "github.com/foo/bar/vendor/..."
|
||||
```
|
||||
|
||||
Create a file named `foo/names.go` with `package foo` listing the names of all packages that are vendored
|
||||
inside `github.com/foo/bar`.
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"regexp"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
. "github.com/dave/jennifer/jen"
|
||||
)
|
||||
|
||||
func hints(w io.Writer, pkg, name, goListPath, filter string, standard, novendor bool) error {
|
||||
|
||||
// notest
|
||||
|
||||
file := NewFile(pkg)
|
||||
|
||||
file.HeaderComment("This file is generated - do not edit.")
|
||||
file.Line()
|
||||
|
||||
packages, err := getPackages(goListPath, filter, standard, novendor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
/*
|
||||
// <name> contains package name hints
|
||||
var <name> = map[string]string{
|
||||
...
|
||||
}
|
||||
*/
|
||||
file.Commentf("%s contains package name hints", name)
|
||||
file.Var().Id(name).Op("=").Map(String()).String().Values(DictFunc(func(d Dict) {
|
||||
for path, name := range packages {
|
||||
d[Lit(path)] = Lit(name)
|
||||
}
|
||||
}))
|
||||
|
||||
return file.Render(w)
|
||||
}
|
||||
|
||||
func getPackages(goListPath, filter string, standard, novendor bool) (map[string]string, error) {
|
||||
|
||||
// notest
|
||||
|
||||
r, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "list", "-e", "-f", "{{ .Standard }} {{ .ImportPath }} {{ .Name }}", goListPath)
|
||||
cmd.Env = []string{
|
||||
fmt.Sprintf("GOPATH=%s", build.Default.GOPATH),
|
||||
fmt.Sprintf("GOROOT=%s", build.Default.GOROOT),
|
||||
}
|
||||
if standard {
|
||||
cmd.Dir = filepath.Join(build.Default.GOROOT, "src")
|
||||
} else {
|
||||
cmd.Dir = filepath.Join(build.Default.GOPATH, "src")
|
||||
}
|
||||
b, err := cmd.Output()
|
||||
if err != nil {
|
||||
if x, ok := err.(*exec.ExitError); ok {
|
||||
return nil, fmt.Errorf("go list command returned an error - %s: %s", err.Error(), string(x.Stderr))
|
||||
}
|
||||
return nil, fmt.Errorf("go list command returned an error: %s", err.Error())
|
||||
}
|
||||
all := strings.Split(strings.TrimSpace(string(b)), "\n")
|
||||
|
||||
packages := map[string]string{}
|
||||
for _, j := range all {
|
||||
|
||||
parts := strings.Split(j, " ")
|
||||
|
||||
isStandard := parts[0] == "true"
|
||||
if isStandard != standard {
|
||||
continue
|
||||
}
|
||||
|
||||
path := parts[1]
|
||||
name := parts[2]
|
||||
|
||||
if novendor && hasVendor(path) {
|
||||
continue
|
||||
}
|
||||
|
||||
if name == "main" {
|
||||
continue
|
||||
}
|
||||
|
||||
if !r.MatchString(path) {
|
||||
continue
|
||||
}
|
||||
|
||||
path = unvendorPath(path)
|
||||
|
||||
if packages[path] != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
packages[path] = name
|
||||
}
|
||||
return packages, nil
|
||||
}
|
||||
|
||||
func unvendorPath(path string) string {
|
||||
// notest
|
||||
i, ok := findVendor(path)
|
||||
if !ok {
|
||||
return path
|
||||
}
|
||||
return path[i+len("vendor/"):]
|
||||
}
|
||||
|
||||
// FindVendor looks for the last non-terminating "vendor" path element in the given import path.
|
||||
// If there isn't one, FindVendor returns ok=false.
|
||||
// Otherwise, FindVendor returns ok=true and the index of the "vendor".
|
||||
// Copied from cmd/go/internal/load
|
||||
func findVendor(path string) (index int, ok bool) {
|
||||
// notest
|
||||
// Two cases, depending on internal at start of string or not.
|
||||
// The order matters: we must return the index of the final element,
|
||||
// because the final one is where the effective import path starts.
|
||||
switch {
|
||||
case strings.Contains(path, "/vendor/"):
|
||||
return strings.LastIndex(path, "/vendor/") + 1, true
|
||||
case strings.HasPrefix(path, "vendor/"):
|
||||
return 0, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func hasVendor(path string) bool {
|
||||
// notest
|
||||
_, v := findVendor(path)
|
||||
return v
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// notest
|
||||
|
||||
var out = flag.String("output", "./package-names.go", "Output filename to write")
|
||||
var pkg = flag.String("package", "main", "Package name in generated file")
|
||||
var name = flag.String("name", "PackageNames", "Name of the variable to define")
|
||||
var filter = flag.String("filter", ".*", "Regex to filter paths (operates on full path including vendor directory)")
|
||||
var standard = flag.Bool("standard", false, "Use standard library packages")
|
||||
var novendor = flag.Bool("novendor", false, "Exclude packages in vendor directories")
|
||||
var goListPath = flag.String("path", "all", "Path to pass to go list command")
|
||||
flag.Parse()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
if err := hints(buf, *pkg, *name, *goListPath, *filter, *standard, *novendor); err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
if err := ioutil.WriteFile(*out, buf.Bytes(), 0644); err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package jen
|
||||
|
||||
// Add appends the provided items to the statement.
|
||||
func Add(code ...Code) *Statement {
|
||||
return newStatement().Add(code...)
|
||||
}
|
||||
|
||||
// Add appends the provided items to the statement.
|
||||
func (g *Group) Add(code ...Code) *Statement {
|
||||
s := Add(code...)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Add appends the provided items to the statement.
|
||||
func (s *Statement) Add(code ...Code) *Statement {
|
||||
*s = append(*s, code...)
|
||||
return s
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package jen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Comment adds a comment. If the provided string contains a newline, the
|
||||
// comment is formatted in multiline style. If the comment string starts
|
||||
// with "//" or "/*", the automatic formatting is disabled and the string is
|
||||
// rendered directly.
|
||||
func Comment(str string) *Statement {
|
||||
return newStatement().Comment(str)
|
||||
}
|
||||
|
||||
// Comment adds a comment. If the provided string contains a newline, the
|
||||
// comment is formatted in multiline style. If the comment string starts
|
||||
// with "//" or "/*", the automatic formatting is disabled and the string is
|
||||
// rendered directly.
|
||||
func (g *Group) Comment(str string) *Statement {
|
||||
s := Comment(str)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Comment adds a comment. If the provided string contains a newline, the
|
||||
// comment is formatted in multiline style. If the comment string starts
|
||||
// with "//" or "/*", the automatic formatting is disabled and the string is
|
||||
// rendered directly.
|
||||
func (s *Statement) Comment(str string) *Statement {
|
||||
c := comment{
|
||||
comment: str,
|
||||
}
|
||||
*s = append(*s, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// Commentf adds a comment, using a format string and a list of parameters. If
|
||||
// the provided string contains a newline, the comment is formatted in
|
||||
// multiline style. If the comment string starts with "//" or "/*", the
|
||||
// automatic formatting is disabled and the string is rendered directly.
|
||||
func Commentf(format string, a ...interface{}) *Statement {
|
||||
return newStatement().Commentf(format, a...)
|
||||
}
|
||||
|
||||
// Commentf adds a comment, using a format string and a list of parameters. If
|
||||
// the provided string contains a newline, the comment is formatted in
|
||||
// multiline style. If the comment string starts with "//" or "/*", the
|
||||
// automatic formatting is disabled and the string is rendered directly.
|
||||
func (g *Group) Commentf(format string, a ...interface{}) *Statement {
|
||||
s := Commentf(format, a...)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Commentf adds a comment, using a format string and a list of parameters. If
|
||||
// the provided string contains a newline, the comment is formatted in
|
||||
// multiline style. If the comment string starts with "//" or "/*", the
|
||||
// automatic formatting is disabled and the string is rendered directly.
|
||||
func (s *Statement) Commentf(format string, a ...interface{}) *Statement {
|
||||
c := comment{
|
||||
comment: fmt.Sprintf(format, a...),
|
||||
}
|
||||
*s = append(*s, c)
|
||||
return s
|
||||
}
|
||||
|
||||
type comment struct {
|
||||
comment string
|
||||
}
|
||||
|
||||
func (c comment) isNull(f *File) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c comment) render(f *File, w io.Writer, s *Statement) error {
|
||||
if strings.HasPrefix(c.comment, "//") || strings.HasPrefix(c.comment, "/*") {
|
||||
// automatic formatting disabled.
|
||||
if _, err := w.Write([]byte(c.comment)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if strings.Contains(c.comment, "\n") {
|
||||
if _, err := w.Write([]byte("/*\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := w.Write([]byte("// ")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := w.Write([]byte(c.comment)); err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(c.comment, "\n") {
|
||||
if !strings.HasSuffix(c.comment, "\n") {
|
||||
if _, err := w.Write([]byte("\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := w.Write([]byte("*/")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package jen
|
||||
|
||||
// Options specifies options for the Custom method
|
||||
type Options struct {
|
||||
Open string
|
||||
Close string
|
||||
Separator string
|
||||
Multi bool
|
||||
}
|
||||
|
||||
// Custom renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator.
|
||||
func Custom(options Options, statements ...Code) *Statement {
|
||||
return newStatement().Custom(options, statements...)
|
||||
}
|
||||
|
||||
// Custom renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator.
|
||||
func (g *Group) Custom(options Options, statements ...Code) *Statement {
|
||||
s := Custom(options, statements...)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Custom renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator.
|
||||
func (s *Statement) Custom(options Options, statements ...Code) *Statement {
|
||||
g := &Group{
|
||||
close: options.Close,
|
||||
items: statements,
|
||||
multi: options.Multi,
|
||||
name: "custom",
|
||||
open: options.Open,
|
||||
separator: options.Separator,
|
||||
}
|
||||
*s = append(*s, g)
|
||||
return s
|
||||
}
|
||||
|
||||
// CustomFunc renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator.
|
||||
func CustomFunc(options Options, f func(*Group)) *Statement {
|
||||
return newStatement().CustomFunc(options, f)
|
||||
}
|
||||
|
||||
// CustomFunc renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator.
|
||||
func (g *Group) CustomFunc(options Options, f func(*Group)) *Statement {
|
||||
s := CustomFunc(options, f)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// CustomFunc renders a customized statement list. Pass in options to specify multi-line, and tokens for open, close, separator.
|
||||
func (s *Statement) CustomFunc(options Options, f func(*Group)) *Statement {
|
||||
g := &Group{
|
||||
close: options.Close,
|
||||
multi: options.Multi,
|
||||
name: "custom",
|
||||
open: options.Open,
|
||||
separator: options.Separator,
|
||||
}
|
||||
f(g)
|
||||
*s = append(*s, g)
|
||||
return s
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package jen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Dict renders as key/value pairs. Use with Values for map or composite
|
||||
// literals.
|
||||
type Dict map[Code]Code
|
||||
|
||||
// DictFunc executes a func(Dict) to generate the value. Use with Values for
|
||||
// map or composite literals.
|
||||
func DictFunc(f func(Dict)) Dict {
|
||||
d := Dict{}
|
||||
f(d)
|
||||
return d
|
||||
}
|
||||
|
||||
func (d Dict) render(f *File, w io.Writer, s *Statement) error {
|
||||
first := true
|
||||
// must order keys to ensure repeatable source
|
||||
type kv struct {
|
||||
k Code
|
||||
v Code
|
||||
}
|
||||
lookup := map[string]kv{}
|
||||
keys := []string{}
|
||||
for k, v := range d {
|
||||
if k.isNull(f) || v.isNull(f) {
|
||||
continue
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
if err := k.render(f, buf, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
keys = append(keys, buf.String())
|
||||
lookup[buf.String()] = kv{k: k, v: v}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
k := lookup[key].k
|
||||
v := lookup[key].v
|
||||
if first && len(keys) > 1 {
|
||||
if _, err := w.Write([]byte("\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
first = false
|
||||
}
|
||||
if err := k.render(f, w, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(":")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.render(f, w, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(keys) > 1 {
|
||||
if _, err := w.Write([]byte(",\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Dict) isNull(f *File) bool {
|
||||
if d == nil || len(d) == 0 {
|
||||
return true
|
||||
}
|
||||
for k, v := range d {
|
||||
if !k.isNull(f) && !v.isNull(f) {
|
||||
// if any of the key/value pairs are both not null, the Dict is not
|
||||
// null
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package jen
|
||||
|
||||
// Do calls the provided function with the statement as a parameter. Use for
|
||||
// embedding logic.
|
||||
func Do(f func(*Statement)) *Statement {
|
||||
return newStatement().Do(f)
|
||||
}
|
||||
|
||||
// Do calls the provided function with the statement as a parameter. Use for
|
||||
// embedding logic.
|
||||
func (g *Group) Do(f func(*Statement)) *Statement {
|
||||
s := Do(f)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Do calls the provided function with the statement as a parameter. Use for
|
||||
// embedding logic.
|
||||
func (s *Statement) Do(f func(*Statement)) *Statement {
|
||||
f(s)
|
||||
return s
|
||||
}
|
||||
+1610
File diff suppressed because it is too large
Load Diff
+262
@@ -0,0 +1,262 @@
|
||||
package jen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewFile Creates a new file, with the specified package name.
|
||||
func NewFile(packageName string) *File {
|
||||
return &File{
|
||||
Group: &Group{
|
||||
multi: true,
|
||||
},
|
||||
name: packageName,
|
||||
imports: map[string]importdef{},
|
||||
hints: map[string]importdef{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewFilePath creates a new file while specifying the package path - the
|
||||
// package name is inferred from the path.
|
||||
func NewFilePath(packagePath string) *File {
|
||||
return &File{
|
||||
Group: &Group{
|
||||
multi: true,
|
||||
},
|
||||
name: guessAlias(packagePath),
|
||||
path: packagePath,
|
||||
imports: map[string]importdef{},
|
||||
hints: map[string]importdef{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewFilePathName creates a new file with the specified package path and name.
|
||||
func NewFilePathName(packagePath, packageName string) *File {
|
||||
return &File{
|
||||
Group: &Group{
|
||||
multi: true,
|
||||
},
|
||||
name: packageName,
|
||||
path: packagePath,
|
||||
imports: map[string]importdef{},
|
||||
hints: map[string]importdef{},
|
||||
}
|
||||
}
|
||||
|
||||
// File represents a single source file. Package imports are managed
|
||||
// automaticaly by File.
|
||||
type File struct {
|
||||
*Group
|
||||
name string
|
||||
path string
|
||||
imports map[string]importdef
|
||||
hints map[string]importdef
|
||||
comments []string
|
||||
headers []string
|
||||
cgoPreamble []string
|
||||
// If you're worried about generated package aliases conflicting with local variable names, you
|
||||
// can set a prefix here. Package foo becomes {prefix}_foo.
|
||||
PackagePrefix string
|
||||
// CanonicalPath adds a canonical import path annotation to the package clause.
|
||||
CanonicalPath string
|
||||
}
|
||||
|
||||
// importdef is used to differentiate packages where we know the package name from packages where the
|
||||
// import is aliased. If alias == false, then name is the actual package name, and the import will be
|
||||
// rendered without an alias. If used == false, the import has not been used in code yet and should be
|
||||
// excluded from the import block.
|
||||
type importdef struct {
|
||||
name string
|
||||
alias bool
|
||||
}
|
||||
|
||||
// HeaderComment adds a comment to the top of the file, above any package
|
||||
// comments. A blank line is rendered below the header comments, ensuring
|
||||
// header comments are not included in the package doc.
|
||||
func (f *File) HeaderComment(comment string) {
|
||||
f.headers = append(f.headers, comment)
|
||||
}
|
||||
|
||||
// PackageComment adds a comment to the top of the file, above the package
|
||||
// keyword.
|
||||
func (f *File) PackageComment(comment string) {
|
||||
f.comments = append(f.comments, comment)
|
||||
}
|
||||
|
||||
// CgoPreamble adds a cgo preamble comment that is rendered directly before the "C" pseudo-package
|
||||
// import.
|
||||
func (f *File) CgoPreamble(comment string) {
|
||||
f.cgoPreamble = append(f.cgoPreamble, comment)
|
||||
}
|
||||
|
||||
// Anon adds an anonymous import.
|
||||
func (f *File) Anon(paths ...string) {
|
||||
for _, p := range paths {
|
||||
f.imports[p] = importdef{name: "_", alias: true}
|
||||
}
|
||||
}
|
||||
|
||||
// ImportName provides the package name for a path. If specified, the alias will be omitted from the
|
||||
// import block. This is optional. If not specified, a sensible package name is used based on the path
|
||||
// and this is added as an alias in the import block.
|
||||
func (f *File) ImportName(path, name string) {
|
||||
f.hints[path] = importdef{name: name, alias: false}
|
||||
}
|
||||
|
||||
// ImportNames allows multiple names to be imported as a map. Use the [gennames](gennames) command to
|
||||
// automatically generate a go file containing a map of a selection of package names.
|
||||
func (f *File) ImportNames(names map[string]string) {
|
||||
for path, name := range names {
|
||||
f.hints[path] = importdef{name: name, alias: false}
|
||||
}
|
||||
}
|
||||
|
||||
// ImportAlias provides the alias for a package path that should be used in the import block. A
|
||||
// period can be used to force a dot-import.
|
||||
func (f *File) ImportAlias(path, alias string) {
|
||||
f.hints[path] = importdef{name: alias, alias: true}
|
||||
}
|
||||
|
||||
func (f *File) isLocal(path string) bool {
|
||||
return f.path == path
|
||||
}
|
||||
|
||||
var reserved = []string{
|
||||
/* keywords */
|
||||
"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",
|
||||
/* predeclared */
|
||||
"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", "complex", "copy", "delete", "imag", "len", "make", "new", "panic", "print", "println", "real", "recover",
|
||||
/* common variables */
|
||||
"err",
|
||||
}
|
||||
|
||||
func isReservedWord(alias string) bool {
|
||||
for _, name := range reserved {
|
||||
if alias == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *File) isValidAlias(alias string) bool {
|
||||
// multiple dot-imports are ok
|
||||
if alias == "." {
|
||||
return true
|
||||
}
|
||||
// the import alias is invalid if it's a reserved word
|
||||
if isReservedWord(alias) {
|
||||
return false
|
||||
}
|
||||
// the import alias is invalid if it's already been registered
|
||||
for _, v := range f.imports {
|
||||
if alias == v.name {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *File) isDotImport(path string) bool {
|
||||
if id, ok := f.hints[path]; ok {
|
||||
return id.name == "." && id.alias
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *File) register(path string) string {
|
||||
if f.isLocal(path) {
|
||||
// notest
|
||||
// should never get here becasue in Qual the packageToken will be null,
|
||||
// so render will never be called.
|
||||
return ""
|
||||
}
|
||||
|
||||
// if the path has been registered previously, simply return the name
|
||||
def := f.imports[path]
|
||||
if def.name != "" && def.name != "_" {
|
||||
return def.name
|
||||
}
|
||||
|
||||
// special case for "C" pseudo-package
|
||||
if path == "C" {
|
||||
f.imports["C"] = importdef{name: "C", alias: false}
|
||||
return "C"
|
||||
}
|
||||
|
||||
var name string
|
||||
var alias bool
|
||||
|
||||
if hint := f.hints[path]; hint.name != "" {
|
||||
// look up the path in the list of provided package names and aliases by ImportName / ImportAlias
|
||||
name = hint.name
|
||||
alias = hint.alias
|
||||
} else if standardLibraryHints[path] != "" {
|
||||
// look up the path in the list of standard library packages
|
||||
name = standardLibraryHints[path]
|
||||
alias = false
|
||||
} else {
|
||||
// if a hint is not found for the package, guess the alias from the package path
|
||||
name = guessAlias(path)
|
||||
alias = true
|
||||
}
|
||||
|
||||
// If the name is invalid or has been registered already, make it unique by appending a number
|
||||
unique := name
|
||||
i := 0
|
||||
for !f.isValidAlias(unique) {
|
||||
i++
|
||||
unique = fmt.Sprintf("%s%d", name, i)
|
||||
}
|
||||
|
||||
// If we've changed the name to make it unique, it should definitely be an alias
|
||||
if unique != name {
|
||||
alias = true
|
||||
}
|
||||
|
||||
// Only add a prefix if the name is an alias
|
||||
if f.PackagePrefix != "" && alias {
|
||||
unique = f.PackagePrefix + "_" + unique
|
||||
}
|
||||
|
||||
// Register the eventual name
|
||||
f.imports[path] = importdef{name: unique, alias: alias}
|
||||
|
||||
return unique
|
||||
}
|
||||
|
||||
// GoString renders the File for testing. Any error will cause a panic.
|
||||
func (f *File) GoString() string {
|
||||
buf := &bytes.Buffer{}
|
||||
if err := f.Render(buf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func guessAlias(path string) string {
|
||||
alias := path
|
||||
|
||||
if strings.HasSuffix(alias, "/") {
|
||||
// training slashes are usually tolerated, so we can get rid of one if
|
||||
// it exists
|
||||
alias = alias[:len(alias)-1]
|
||||
}
|
||||
|
||||
if strings.Contains(alias, "/") {
|
||||
// if the path contains a "/", use the last part
|
||||
alias = alias[strings.LastIndex(alias, "/")+1:]
|
||||
}
|
||||
|
||||
// alias should be lower case
|
||||
alias = strings.ToLower(alias)
|
||||
|
||||
// alias should now only contain alphanumerics
|
||||
importsRegex := regexp.MustCompile(`[^a-z0-9]`)
|
||||
alias = importsRegex.ReplaceAllString(alias, "")
|
||||
|
||||
return alias
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package jen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGuessAlias(t *testing.T) {
|
||||
|
||||
data := map[string]string{
|
||||
"A": "a",
|
||||
"a": "a",
|
||||
"a$": "a",
|
||||
"a/b": "b",
|
||||
"a/b/c": "c",
|
||||
"a/b/c-d": "cd",
|
||||
"a/b/c-d/": "cd",
|
||||
"a.b": "ab",
|
||||
"a/b.c": "bc",
|
||||
"a/b-c.d": "bcd",
|
||||
"a/bb-ccc.dddd": "bbcccdddd",
|
||||
"a/foo-go": "foogo",
|
||||
}
|
||||
for path, expected := range data {
|
||||
if guessAlias(path) != expected {
|
||||
fmt.Printf("guessAlias test failed %s should return %s but got %s\n", path, expected, guessAlias(path))
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidAlias(t *testing.T) {
|
||||
data := map[string]bool{
|
||||
"a": true, // ok
|
||||
"b": false, // already registered
|
||||
"go": false, // keyword
|
||||
"int": false, // predeclared
|
||||
"err": false, // common name
|
||||
}
|
||||
f := NewFile("test")
|
||||
f.register("b")
|
||||
for alias, expected := range data {
|
||||
if f.isValidAlias(alias) != expected {
|
||||
fmt.Printf("isValidAlias test failed %s should return %t but got %t\n", alias, expected, f.isValidAlias(alias))
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
+2274
File diff suppressed because it is too large
Load Diff
+808
@@ -0,0 +1,808 @@
|
||||
package jen_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/dave/jennifer/jen"
|
||||
)
|
||||
|
||||
var gencases = []tc{
|
||||
{
|
||||
desc: `bool group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Bool()
|
||||
}),
|
||||
expect: `{
|
||||
bool
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `recover func`,
|
||||
code: Recover(),
|
||||
expect: `recover()`,
|
||||
},
|
||||
{
|
||||
desc: `recover statement`,
|
||||
code: Null().Recover(),
|
||||
expect: `recover()`,
|
||||
},
|
||||
{
|
||||
desc: `recover group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Recover()
|
||||
}),
|
||||
expect: `{
|
||||
recover()
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `real func`,
|
||||
code: Real(Id("a")),
|
||||
expect: `real(a)`,
|
||||
},
|
||||
{
|
||||
desc: `real statement`,
|
||||
code: Null().Real(Id("a")),
|
||||
expect: `real(a)`,
|
||||
},
|
||||
{
|
||||
desc: `real group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Real(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
real(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `printlnfunc func`,
|
||||
code: PrintlnFunc(func(g *Group) { g.Id("a") }),
|
||||
expect: `println(a)`,
|
||||
},
|
||||
{
|
||||
desc: `printlnfunc statement`,
|
||||
code: Null().PrintlnFunc(func(g *Group) { g.Id("a") }),
|
||||
expect: `println(a)`,
|
||||
},
|
||||
{
|
||||
desc: `printlnfunc group`,
|
||||
code: BlockFunc(func(bg *Group) {
|
||||
bg.PrintlnFunc(func(pg *Group) { pg.Id("a") })
|
||||
}),
|
||||
expect: `{
|
||||
println(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `println func`,
|
||||
code: Println(Id("a")),
|
||||
expect: `println(a)`,
|
||||
},
|
||||
{
|
||||
desc: `println statement`,
|
||||
code: Null().Println(Id("a")),
|
||||
expect: `println(a)`,
|
||||
},
|
||||
{
|
||||
desc: `println group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Println(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
println(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `printfunc func`,
|
||||
code: PrintFunc(func(g *Group) { g.Id("a") }),
|
||||
expect: `print(a)`,
|
||||
},
|
||||
{
|
||||
desc: `printfunc statement`,
|
||||
code: Null().PrintFunc(func(g *Group) { g.Id("a") }),
|
||||
expect: `print(a)`,
|
||||
},
|
||||
{
|
||||
desc: `printfunc group`,
|
||||
code: BlockFunc(func(bg *Group) {
|
||||
bg.PrintFunc(func(pg *Group) { pg.Id("a") })
|
||||
}),
|
||||
expect: `{
|
||||
print(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `print func`,
|
||||
code: Print(Id("a")),
|
||||
expect: `print(a)`,
|
||||
},
|
||||
{
|
||||
desc: `print statement`,
|
||||
code: Null().Print(Id("a")),
|
||||
expect: `print(a)`,
|
||||
},
|
||||
{
|
||||
desc: `print group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Print(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
print(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `panic func`,
|
||||
code: Panic(Id("a")),
|
||||
expect: `panic(a)`,
|
||||
},
|
||||
{
|
||||
desc: `panic statement`,
|
||||
code: Null().Panic(Id("a")),
|
||||
expect: `panic(a)`,
|
||||
},
|
||||
{
|
||||
desc: `panic group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Panic(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
panic(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `new func`,
|
||||
code: New(Id("a")),
|
||||
expect: `new(a)`,
|
||||
},
|
||||
{
|
||||
desc: `new statement`,
|
||||
code: Id("a").Op(":=").New(Id("a")),
|
||||
expect: `a := new(a)`,
|
||||
},
|
||||
{
|
||||
desc: `new group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.New(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
new(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `make func`,
|
||||
code: Make(Id("a")),
|
||||
expect: `make(a)`,
|
||||
},
|
||||
{
|
||||
desc: `make statement`,
|
||||
code: Id("a").Op(":=").Make(Id("a")),
|
||||
expect: `a := make(a)`,
|
||||
},
|
||||
{
|
||||
desc: `make group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Make(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
make(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `len func`,
|
||||
code: Len(Id("a")),
|
||||
expect: `len(a)`,
|
||||
},
|
||||
{
|
||||
desc: `len statement`,
|
||||
code: Id("a").Op(":=").Len(Id("a")),
|
||||
expect: `a := len(a)`,
|
||||
},
|
||||
{
|
||||
desc: `len group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Len(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
len(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `imag func`,
|
||||
code: Imag(Id("a")),
|
||||
expect: `imag(a)`,
|
||||
},
|
||||
{
|
||||
desc: `imag statement`,
|
||||
code: Id("a").Op(":=").Imag(Id("a")),
|
||||
expect: `a := imag(a)`,
|
||||
},
|
||||
{
|
||||
desc: `imag group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Imag(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
imag(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `delete func`,
|
||||
code: Delete(Id("a"), Id("b")),
|
||||
expect: `delete(a, b)`,
|
||||
},
|
||||
{
|
||||
desc: `delete statement`,
|
||||
code: Null().Delete(Id("a"), Id("b")),
|
||||
expect: `delete(a, b)`,
|
||||
},
|
||||
{
|
||||
desc: `delete group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Delete(Id("a"), Id("b"))
|
||||
}),
|
||||
expect: `{
|
||||
delete(a, b)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `copy func`,
|
||||
code: Copy(Id("a"), Id("b")),
|
||||
expect: `copy(a, b)`,
|
||||
},
|
||||
{
|
||||
desc: `copy statement`,
|
||||
code: Id("a").Op(":=").Copy(Id("a"), Id("b")),
|
||||
expect: `a := copy(a, b)`,
|
||||
},
|
||||
{
|
||||
desc: `copy group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Copy(Id("a"), Id("b"))
|
||||
}),
|
||||
expect: `{
|
||||
copy(a, b)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `complex func`,
|
||||
code: Complex(Id("a"), Id("b")),
|
||||
expect: `complex(a, b)`,
|
||||
},
|
||||
{
|
||||
desc: `complex statement`,
|
||||
code: Id("a").Op(":=").Complex(Id("a"), Id("b")),
|
||||
expect: `a := complex(a, b)`,
|
||||
},
|
||||
{
|
||||
desc: `complex group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Complex(Id("a"), Id("b"))
|
||||
}),
|
||||
expect: `{
|
||||
complex(a, b)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `close group`,
|
||||
code: BlockFunc(func(g *Group) { g.Close(Id("a")) }),
|
||||
expect: `{
|
||||
close(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `cap func`,
|
||||
code: Cap(Id("a")),
|
||||
expect: `cap(a)`,
|
||||
},
|
||||
{
|
||||
desc: `cap statement`,
|
||||
code: Id("a").Op(":=").Cap(Id("b")),
|
||||
expect: `a := cap(b)`,
|
||||
},
|
||||
{
|
||||
desc: `cap group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Cap(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
cap(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `append group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Append(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
append(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `appendfunc statement`,
|
||||
code: Id("a").Op("=").AppendFunc(func(ag *Group) { ag.Id("a") }),
|
||||
expect: `a = append(a)`,
|
||||
},
|
||||
{
|
||||
desc: `appendfunc func`,
|
||||
code: AppendFunc(func(ag *Group) { ag.Id("a") }),
|
||||
expect: `append(a)`,
|
||||
},
|
||||
{
|
||||
desc: `appendfunc group`,
|
||||
code: BlockFunc(func(bg *Group) {
|
||||
bg.AppendFunc(func(ag *Group) { ag.Id("a") })
|
||||
}),
|
||||
expect: `{
|
||||
append(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `casefunc group`,
|
||||
code: Switch().BlockFunc(func(g *Group) {
|
||||
g.CaseFunc(func(g *Group) { g.Id("a") }).Block()
|
||||
}),
|
||||
expect: `switch {
|
||||
case a:
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `case group`,
|
||||
code: Switch().BlockFunc(func(g *Group) {
|
||||
g.Case(Id("a")).Block()
|
||||
}),
|
||||
expect: `switch {
|
||||
case a:
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `structfunc statement`,
|
||||
code: Id("a").Op(":=").StructFunc(func(g *Group) {}).Values(),
|
||||
expect: `a := struct{}{}`,
|
||||
},
|
||||
{
|
||||
desc: `structfunc group`,
|
||||
// Don't do this! ListFunc used to kludge Group.Struct usage
|
||||
// without syntax error.
|
||||
code: Id("a").Op(":=").ListFunc(func(g *Group) { g.StructFunc(func(g *Group) {}) }).Values(),
|
||||
expect: `a := struct{}{}`,
|
||||
},
|
||||
{
|
||||
desc: `structfunc func`,
|
||||
code: Id("a").Op(":=").Add(StructFunc(func(g *Group) {})).Values(),
|
||||
expect: `a := struct{}{}`,
|
||||
},
|
||||
{
|
||||
desc: `struct group`,
|
||||
// Don't do this! ListFunc used to kludge Group.Struct usage
|
||||
// without syntax error.
|
||||
code: Id("a").Op(":=").ListFunc(func(g *Group) { g.Struct() }).Values(),
|
||||
expect: `a := struct{}{}`,
|
||||
},
|
||||
{
|
||||
desc: `struct func`,
|
||||
code: Id("a").Op(":=").Add(Struct()).Values(),
|
||||
expect: `a := struct{}{}`,
|
||||
},
|
||||
{
|
||||
desc: `interfacefunc func`,
|
||||
code: Id("a").Assert(InterfaceFunc(func(g *Group) {
|
||||
g.Id("a").Call().Int()
|
||||
g.Id("b").Call().Int()
|
||||
})),
|
||||
expect: `a.(interface{
|
||||
a() int
|
||||
b() int
|
||||
})`,
|
||||
},
|
||||
{
|
||||
desc: `interfacefunc statement`,
|
||||
code: Id("a").Assert(Null().InterfaceFunc(func(g *Group) {
|
||||
g.Id("a").Call().Int()
|
||||
g.Id("b").Call().Int()
|
||||
})),
|
||||
expect: `a.(interface{
|
||||
a() int
|
||||
b() int
|
||||
})`,
|
||||
},
|
||||
{
|
||||
desc: `interfacefunc group`,
|
||||
// Don't do this! ListFunc used to kludge Group.InterfaceFunc usage
|
||||
// without syntax error.
|
||||
code: Id("a").Assert(ListFunc(func(lg *Group) {
|
||||
lg.InterfaceFunc(func(ig *Group) {
|
||||
ig.Id("a").Call().Int()
|
||||
ig.Id("b").Call().Int()
|
||||
})
|
||||
})),
|
||||
expect: `a.(interface{
|
||||
a() int
|
||||
b() int
|
||||
})`,
|
||||
},
|
||||
{
|
||||
desc: `interface func`,
|
||||
code: Interface().Parens(Id("a")),
|
||||
expect: `interface{}(a)`,
|
||||
},
|
||||
{
|
||||
desc: `interface group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Interface().Parens(Id("a"))
|
||||
}),
|
||||
expect: `{
|
||||
interface{}(a)
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `interface statement`,
|
||||
code: Null().Interface().Parens(Id("a")),
|
||||
expect: `interface{}(a)`,
|
||||
},
|
||||
{
|
||||
desc: `switchfunc func`,
|
||||
code: SwitchFunc(func(rg *Group) {
|
||||
rg.Id("a")
|
||||
}).Block(),
|
||||
expect: `switch a {}`,
|
||||
},
|
||||
{
|
||||
desc: `switchfunc statement`,
|
||||
code: Null().SwitchFunc(func(rg *Group) {
|
||||
rg.Id("a")
|
||||
}).Block(),
|
||||
expect: `switch a {
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `switchfunc group`,
|
||||
code: BlockFunc(func(bg *Group) {
|
||||
bg.SwitchFunc(func(rg *Group) {
|
||||
rg.Id("a")
|
||||
}).Block()
|
||||
}),
|
||||
expect: `{
|
||||
switch a {
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `switch group`,
|
||||
code: BlockFunc(func(bg *Group) {
|
||||
bg.Switch().Block()
|
||||
}),
|
||||
expect: `{
|
||||
switch {
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `forfunc func`,
|
||||
code: ForFunc(func(rg *Group) {
|
||||
rg.Id("a")
|
||||
}).Block(),
|
||||
expect: `for a {}`,
|
||||
},
|
||||
{
|
||||
desc: `forfunc statement`,
|
||||
code: Null().ForFunc(func(rg *Group) {
|
||||
rg.Id("a")
|
||||
}).Block(),
|
||||
expect: `for a {
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `forfunc group`,
|
||||
code: BlockFunc(func(bg *Group) {
|
||||
bg.ForFunc(func(rg *Group) {
|
||||
rg.Id("a")
|
||||
}).Block()
|
||||
}),
|
||||
expect: `{
|
||||
for a {
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `for group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.For(Id("a")).Block()
|
||||
}),
|
||||
expect: `{
|
||||
for a {}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `returnfunc func`,
|
||||
code: ReturnFunc(func(rg *Group) {
|
||||
rg.Lit(1)
|
||||
rg.Lit(2)
|
||||
}),
|
||||
expect: `return 1, 2`,
|
||||
},
|
||||
{
|
||||
desc: `returnfunc statement`,
|
||||
code: Empty().ReturnFunc(func(rg *Group) {
|
||||
rg.Lit(1)
|
||||
rg.Lit(2)
|
||||
}),
|
||||
expect: `return 1, 2`,
|
||||
},
|
||||
{
|
||||
desc: `returnfunc group`,
|
||||
code: BlockFunc(func(bg *Group) {
|
||||
bg.ReturnFunc(func(rg *Group) {
|
||||
rg.Lit(1)
|
||||
rg.Lit(2)
|
||||
})
|
||||
}),
|
||||
expect: `{
|
||||
return 1, 2
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `return group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Return()
|
||||
}),
|
||||
expect: `{
|
||||
return
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `iffunc group`,
|
||||
code: BlockFunc(func(bg *Group) {
|
||||
bg.IfFunc(func(ig *Group) {
|
||||
ig.Id("a")
|
||||
}).Block()
|
||||
}),
|
||||
expect: `{
|
||||
if a {}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `iffunc func`,
|
||||
code: IfFunc(func(ig *Group) {
|
||||
ig.Id("a")
|
||||
}).Block(),
|
||||
expect: `if a {}`,
|
||||
},
|
||||
{
|
||||
desc: `iffunc statement`,
|
||||
code: Null().IfFunc(func(ig *Group) {
|
||||
ig.Id("a")
|
||||
}).Block(),
|
||||
expect: `if a {}`,
|
||||
},
|
||||
{
|
||||
desc: `if group`,
|
||||
code: BlockFunc(func(g *Group) { g.If(Id("a")).Block() }),
|
||||
expect: `{
|
||||
if a {}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `map group`,
|
||||
code: BlockFunc(func(g *Group) { g.Map(Int()).Int().Values(Dict{Lit(1): Lit(1)}) }),
|
||||
expect: `{
|
||||
map[int]int{1:1}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `assert group`,
|
||||
// Don't do this! ListFunc used to kludge Group.Assert usage without
|
||||
// syntax error.
|
||||
code: Id("a").ListFunc(func(g *Group) { g.Assert(Id("b")) }),
|
||||
expect: `a.(b)`,
|
||||
},
|
||||
{
|
||||
desc: `assert func`,
|
||||
code: Id("a").Add(Assert(Id("b"))),
|
||||
expect: `a.(b)`,
|
||||
},
|
||||
{
|
||||
desc: `paramsfunc group`,
|
||||
// Don't do this! ListFunc used to kludge Group.ParamsFunc usage without
|
||||
// syntax error.
|
||||
code: Id("a").ListFunc(func(lg *Group) { lg.ParamsFunc(func(cg *Group) { cg.Lit(1) }) }),
|
||||
expect: `a(1)`,
|
||||
},
|
||||
{
|
||||
desc: `paramsfunc func`,
|
||||
code: Id("a").Add(ParamsFunc(func(g *Group) { g.Lit(1) })),
|
||||
expect: `a(1)`,
|
||||
},
|
||||
{
|
||||
desc: `paramsfunc statement`,
|
||||
code: Id("a").ParamsFunc(func(g *Group) { g.Lit(1) }),
|
||||
expect: `a(1)`,
|
||||
},
|
||||
{
|
||||
desc: `params group`,
|
||||
// Don't do this! ListFunc used to kludge Group.Params usage without
|
||||
// syntax error.
|
||||
code: Id("a").ListFunc(func(g *Group) { g.Params(Lit(1)) }),
|
||||
expect: `a(1)`,
|
||||
},
|
||||
{
|
||||
desc: `params func`,
|
||||
code: Id("a").Add(Params(Lit(1))),
|
||||
expect: `a(1)`,
|
||||
},
|
||||
{
|
||||
desc: `callfunc group`,
|
||||
// Don't do this! ListFunc used to kludge Group.CallFunc usage without
|
||||
// syntax error.
|
||||
code: Id("a").ListFunc(func(lg *Group) { lg.CallFunc(func(cg *Group) { cg.Lit(1) }) }),
|
||||
expect: `a(1)`,
|
||||
},
|
||||
{
|
||||
desc: `callfunc func`,
|
||||
code: Id("a").Add(CallFunc(func(g *Group) { g.Lit(1) })),
|
||||
expect: `a(1)`,
|
||||
},
|
||||
{
|
||||
desc: `call group`,
|
||||
// Don't do this! ListFunc used to kludge Group.Call usage without
|
||||
// syntax error.
|
||||
code: Id("a").ListFunc(func(g *Group) { g.Call(Lit(1)) }),
|
||||
expect: `a(1)`,
|
||||
},
|
||||
{
|
||||
desc: `call func`,
|
||||
code: Id("a").Add(Call(Lit(1))),
|
||||
expect: `a(1)`,
|
||||
},
|
||||
{
|
||||
desc: `defsfunc statement`,
|
||||
code: Const().DefsFunc(func(g *Group) { g.Id("a").Op("=").Lit(1) }),
|
||||
expect: `const (
|
||||
a = 1
|
||||
)`,
|
||||
},
|
||||
{
|
||||
desc: `defsfunc func`,
|
||||
code: Const().Add(DefsFunc(func(g *Group) { g.Id("a").Op("=").Lit(1) })),
|
||||
expect: `const (
|
||||
a = 1
|
||||
)`,
|
||||
},
|
||||
{
|
||||
desc: `defsfunc group`,
|
||||
// Don't do this! ListFunc used to kludge Group.DefsFunc usage without
|
||||
// syntax error.
|
||||
code: Const().ListFunc(func(lg *Group) { lg.DefsFunc(func(dg *Group) { dg.Id("a").Op("=").Lit(1) }) }),
|
||||
expect: `const (
|
||||
a = 1
|
||||
)`,
|
||||
},
|
||||
{
|
||||
desc: `defs group`,
|
||||
// Don't do this! ListFunc used to kludge Group.Defs usage without
|
||||
// syntax error.
|
||||
code: Const().ListFunc(func(g *Group) { g.Defs(Id("a").Op("=").Lit(1)) }),
|
||||
expect: `const (
|
||||
a = 1
|
||||
)`,
|
||||
},
|
||||
{
|
||||
desc: `defs func`,
|
||||
code: Const().Add(Defs(Id("a").Op("=").Lit(1))),
|
||||
expect: `const (
|
||||
a = 1
|
||||
)`,
|
||||
},
|
||||
{
|
||||
desc: `blockfunc group`,
|
||||
code: BlockFunc(func(g *Group) { g.BlockFunc(func(g *Group) {}) }),
|
||||
expect: `{{}}`,
|
||||
},
|
||||
{
|
||||
desc: `block group`,
|
||||
code: BlockFunc(func(g *Group) { g.Block() }),
|
||||
expect: `{{}}`,
|
||||
},
|
||||
{
|
||||
desc: `indexfunc group`,
|
||||
code: BlockFunc(func(g *Group) { g.IndexFunc(func(g *Group) { g.Lit(1) }).Int().Values(Lit(1)) }),
|
||||
expect: `{[1]int{1}}`,
|
||||
},
|
||||
{
|
||||
desc: `indexfunc statement`,
|
||||
code: Id("a").IndexFunc(func(g *Group) { g.Lit(1) }),
|
||||
expect: `a[1]`,
|
||||
},
|
||||
{
|
||||
desc: `indexfunc func`,
|
||||
code: Id("a").Add(IndexFunc(func(g *Group) { g.Lit(1) })),
|
||||
expect: `a[1]`,
|
||||
},
|
||||
{
|
||||
desc: `index group`,
|
||||
code: BlockFunc(func(g *Group) { g.Index(Lit(1)).Int().Values(Lit(1)) }),
|
||||
expect: `{[1]int{1}}`,
|
||||
},
|
||||
{
|
||||
desc: `index func`,
|
||||
code: Id("a").Add(Index(Lit(1))),
|
||||
expect: `a[1]`,
|
||||
},
|
||||
{
|
||||
desc: `valuesfunc func`,
|
||||
code: ValuesFunc(func(vg *Group) {
|
||||
vg.Lit(1)
|
||||
}),
|
||||
expect: `{1}`,
|
||||
},
|
||||
{
|
||||
desc: `valuesfunc group`,
|
||||
code: BlockFunc(func(bg *Group) {
|
||||
bg.ValuesFunc(func(vg *Group) {
|
||||
vg.Lit(1)
|
||||
})
|
||||
}),
|
||||
expect: `{
|
||||
{1}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `values group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Values(Lit(1))
|
||||
}),
|
||||
expect: `{
|
||||
{1}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `listfunc statement`,
|
||||
code: Add(Null()).ListFunc(func(lg *Group) {
|
||||
lg.Id("a")
|
||||
lg.Id("b")
|
||||
}).Op("=").Id("c"),
|
||||
expect: `a, b = c`,
|
||||
},
|
||||
{
|
||||
desc: `listfunc func`,
|
||||
code: ListFunc(func(lg *Group) {
|
||||
lg.Id("a")
|
||||
lg.Id("b")
|
||||
}).Op("=").Id("c"),
|
||||
expect: `a, b = c`,
|
||||
},
|
||||
{
|
||||
desc: `listfunc group`,
|
||||
code: BlockFunc(func(bg *Group) {
|
||||
bg.ListFunc(func(lg *Group) {
|
||||
lg.Id("a")
|
||||
lg.Id("b")
|
||||
}).Op("=").Id("c")
|
||||
}),
|
||||
expect: `{
|
||||
a, b = c
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `list group`,
|
||||
code: BlockFunc(func(g *Group) { g.List(Id("a"), Id("b")).Op("=").Id("c") }),
|
||||
expect: `{
|
||||
a, b = c
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `parens func`,
|
||||
code: Parens(Lit(1)),
|
||||
expect: `(1)`,
|
||||
},
|
||||
{
|
||||
desc: `parens group`,
|
||||
code: BlockFunc(func(g *Group) { g.Parens(Lit(1)) }),
|
||||
expect: `{
|
||||
(1)
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestGen(t *testing.T) {
|
||||
caseTester(t, gencases)
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
package jen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Group represents a list of Code items, separated by tokens with an optional
|
||||
// open and close token.
|
||||
type Group struct {
|
||||
name string
|
||||
items []Code
|
||||
open string
|
||||
close string
|
||||
separator string
|
||||
multi bool
|
||||
}
|
||||
|
||||
func (g *Group) isNull(f *File) bool {
|
||||
if g == nil {
|
||||
return true
|
||||
}
|
||||
if g.open != "" || g.close != "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range g.items {
|
||||
if !c.isNull(f) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *Group) render(f *File, w io.Writer, s *Statement) error {
|
||||
if g.name == "block" && s != nil {
|
||||
// Special CaseBlock format for then the previous item in the statement
|
||||
// is a Case group or the default keyword.
|
||||
prev := s.previous(g)
|
||||
grp, isGrp := prev.(*Group)
|
||||
tkn, isTkn := prev.(token)
|
||||
if isGrp && grp.name == "case" || isTkn && tkn.content == "default" {
|
||||
g.open = ""
|
||||
g.close = ""
|
||||
}
|
||||
}
|
||||
if g.open != "" {
|
||||
if _, err := w.Write([]byte(g.open)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
isNull, err := g.renderItems(f, w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isNull && g.multi && g.close != "" {
|
||||
// For multi-line blocks with a closing token, we insert a new line after the last item (but
|
||||
// not if all items were null). This is to ensure that if the statement finishes with a comment,
|
||||
// the closing token is not commented out.
|
||||
s := "\n"
|
||||
if g.separator == "," {
|
||||
// We also insert add trailing comma if the separator was ",".
|
||||
s = ",\n"
|
||||
}
|
||||
if _, err := w.Write([]byte(s)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if g.close != "" {
|
||||
if _, err := w.Write([]byte(g.close)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Group) renderItems(f *File, w io.Writer) (isNull bool, err error) {
|
||||
first := true
|
||||
for _, code := range g.items {
|
||||
if pt, ok := code.(token); ok && pt.typ == packageToken {
|
||||
// Special case for package tokens in Qual groups - for dot-imports, the package token
|
||||
// will be null, so will not render and will not be registered in the imports block.
|
||||
// This ensures all packageTokens that are rendered are registered.
|
||||
f.register(pt.content.(string))
|
||||
}
|
||||
if code == nil || code.isNull(f) {
|
||||
// Null() token produces no output but also
|
||||
// no separator. Empty() token products no
|
||||
// output but adds a separator.
|
||||
continue
|
||||
}
|
||||
if g.name == "values" {
|
||||
if _, ok := code.(Dict); ok && len(g.items) > 1 {
|
||||
panic("Error in Values: if Dict is used, must be one item only")
|
||||
}
|
||||
}
|
||||
if !first && g.separator != "" {
|
||||
// The separator token is added before each non-null item, but not before the first item.
|
||||
if _, err := w.Write([]byte(g.separator)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if g.multi {
|
||||
// For multi-line blocks, we insert a new line before each non-null item.
|
||||
if _, err := w.Write([]byte("\n")); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if err := code.render(f, w, nil); err != nil {
|
||||
return false, err
|
||||
}
|
||||
first = false
|
||||
}
|
||||
return first, nil
|
||||
}
|
||||
|
||||
// Render renders the Group to the provided writer.
|
||||
func (g *Group) Render(writer io.Writer) error {
|
||||
return g.RenderWithFile(writer, NewFile(""))
|
||||
}
|
||||
|
||||
// GoString renders the Group for testing. Any error will cause a panic.
|
||||
func (g *Group) GoString() string {
|
||||
buf := bytes.Buffer{}
|
||||
if err := g.Render(&buf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// RenderWithFile renders the Group to the provided writer, using imports from the provided file.
|
||||
func (g *Group) RenderWithFile(writer io.Writer, file *File) error {
|
||||
buf := &bytes.Buffer{}
|
||||
if err := g.render(file, buf, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error %s while formatting source:\n%s", err, buf.String())
|
||||
}
|
||||
if _, err := writer.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package jen_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
. "github.com/dave/jennifer/jen"
|
||||
)
|
||||
|
||||
func TestGroup_Render(t *testing.T) {
|
||||
file := NewFile("main")
|
||||
file.ImportAlias("fmt", "fmtalias")
|
||||
|
||||
var g *Group
|
||||
BlockFunc(func(group *Group) {
|
||||
g = group
|
||||
})
|
||||
|
||||
g.Qual("fmt", "Errorf").Call(Lit("error"))
|
||||
|
||||
expect := `{
|
||||
fmtalias.Errorf("error")
|
||||
}`
|
||||
|
||||
var got bytes.Buffer
|
||||
|
||||
err := g.RenderWithFile(&got, file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if got.String() != expect {
|
||||
t.Fatalf("Got: %v, expect: %v", got.String(), expect)
|
||||
}
|
||||
}
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
// This file is generated - do not edit.
|
||||
|
||||
package jen
|
||||
|
||||
// standardLibraryHints contains package name hints
|
||||
var standardLibraryHints = map[string]string{
|
||||
"archive/tar": "tar",
|
||||
"archive/zip": "zip",
|
||||
"bufio": "bufio",
|
||||
"builtin": "builtin",
|
||||
"bytes": "bytes",
|
||||
"cmd/asm/internal/arch": "arch",
|
||||
"cmd/asm/internal/asm": "asm",
|
||||
"cmd/asm/internal/flags": "flags",
|
||||
"cmd/asm/internal/lex": "lex",
|
||||
"cmd/compile/internal/amd64": "amd64",
|
||||
"cmd/compile/internal/arm": "arm",
|
||||
"cmd/compile/internal/arm64": "arm64",
|
||||
"cmd/compile/internal/gc": "gc",
|
||||
"cmd/compile/internal/mips": "mips",
|
||||
"cmd/compile/internal/mips64": "mips64",
|
||||
"cmd/compile/internal/ppc64": "ppc64",
|
||||
"cmd/compile/internal/s390x": "s390x",
|
||||
"cmd/compile/internal/ssa": "ssa",
|
||||
"cmd/compile/internal/syntax": "syntax",
|
||||
"cmd/compile/internal/test": "test",
|
||||
"cmd/compile/internal/types": "types",
|
||||
"cmd/compile/internal/wasm": "wasm",
|
||||
"cmd/compile/internal/x86": "x86",
|
||||
"cmd/go/internal/base": "base",
|
||||
"cmd/go/internal/bug": "bug",
|
||||
"cmd/go/internal/cache": "cache",
|
||||
"cmd/go/internal/cfg": "cfg",
|
||||
"cmd/go/internal/clean": "clean",
|
||||
"cmd/go/internal/cmdflag": "cmdflag",
|
||||
"cmd/go/internal/dirhash": "dirhash",
|
||||
"cmd/go/internal/doc": "doc",
|
||||
"cmd/go/internal/envcmd": "envcmd",
|
||||
"cmd/go/internal/fix": "fix",
|
||||
"cmd/go/internal/fmtcmd": "fmtcmd",
|
||||
"cmd/go/internal/generate": "generate",
|
||||
"cmd/go/internal/get": "get",
|
||||
"cmd/go/internal/help": "help",
|
||||
"cmd/go/internal/imports": "imports",
|
||||
"cmd/go/internal/list": "list",
|
||||
"cmd/go/internal/load": "load",
|
||||
"cmd/go/internal/modcmd": "modcmd",
|
||||
"cmd/go/internal/modconv": "modconv",
|
||||
"cmd/go/internal/modfetch": "modfetch",
|
||||
"cmd/go/internal/modfetch/codehost": "codehost",
|
||||
"cmd/go/internal/modfile": "modfile",
|
||||
"cmd/go/internal/modget": "modget",
|
||||
"cmd/go/internal/modinfo": "modinfo",
|
||||
"cmd/go/internal/modload": "modload",
|
||||
"cmd/go/internal/module": "module",
|
||||
"cmd/go/internal/mvs": "mvs",
|
||||
"cmd/go/internal/par": "par",
|
||||
"cmd/go/internal/run": "run",
|
||||
"cmd/go/internal/search": "search",
|
||||
"cmd/go/internal/semver": "semver",
|
||||
"cmd/go/internal/str": "str",
|
||||
"cmd/go/internal/test": "test",
|
||||
"cmd/go/internal/tool": "tool",
|
||||
"cmd/go/internal/txtar": "txtar",
|
||||
"cmd/go/internal/version": "version",
|
||||
"cmd/go/internal/vet": "vet",
|
||||
"cmd/go/internal/web": "web",
|
||||
"cmd/go/internal/web2": "web2",
|
||||
"cmd/go/internal/webtest": "webtest",
|
||||
"cmd/go/internal/work": "work",
|
||||
"cmd/internal/bio": "bio",
|
||||
"cmd/internal/browser": "browser",
|
||||
"cmd/internal/buildid": "buildid",
|
||||
"cmd/internal/dwarf": "dwarf",
|
||||
"cmd/internal/edit": "edit",
|
||||
"cmd/internal/gcprog": "gcprog",
|
||||
"cmd/internal/goobj": "goobj",
|
||||
"cmd/internal/obj": "obj",
|
||||
"cmd/internal/obj/arm": "arm",
|
||||
"cmd/internal/obj/arm64": "arm64",
|
||||
"cmd/internal/obj/mips": "mips",
|
||||
"cmd/internal/obj/ppc64": "ppc64",
|
||||
"cmd/internal/obj/s390x": "s390x",
|
||||
"cmd/internal/obj/wasm": "wasm",
|
||||
"cmd/internal/obj/x86": "x86",
|
||||
"cmd/internal/objabi": "objabi",
|
||||
"cmd/internal/objfile": "objfile",
|
||||
"cmd/internal/src": "src",
|
||||
"cmd/internal/sys": "sys",
|
||||
"cmd/internal/test2json": "test2json",
|
||||
"cmd/link/internal/amd64": "amd64",
|
||||
"cmd/link/internal/arm": "arm",
|
||||
"cmd/link/internal/arm64": "arm64",
|
||||
"cmd/link/internal/ld": "ld",
|
||||
"cmd/link/internal/loadelf": "loadelf",
|
||||
"cmd/link/internal/loadmacho": "loadmacho",
|
||||
"cmd/link/internal/loadpe": "loadpe",
|
||||
"cmd/link/internal/mips": "mips",
|
||||
"cmd/link/internal/mips64": "mips64",
|
||||
"cmd/link/internal/objfile": "objfile",
|
||||
"cmd/link/internal/ppc64": "ppc64",
|
||||
"cmd/link/internal/s390x": "s390x",
|
||||
"cmd/link/internal/sym": "sym",
|
||||
"cmd/link/internal/wasm": "wasm",
|
||||
"cmd/link/internal/x86": "x86",
|
||||
"cmd/vet/internal/cfg": "cfg",
|
||||
"cmd/vet/internal/whitelist": "whitelist",
|
||||
"compress/bzip2": "bzip2",
|
||||
"compress/flate": "flate",
|
||||
"compress/gzip": "gzip",
|
||||
"compress/lzw": "lzw",
|
||||
"compress/zlib": "zlib",
|
||||
"container/heap": "heap",
|
||||
"container/list": "list",
|
||||
"container/ring": "ring",
|
||||
"context": "context",
|
||||
"crypto": "crypto",
|
||||
"crypto/aes": "aes",
|
||||
"crypto/cipher": "cipher",
|
||||
"crypto/des": "des",
|
||||
"crypto/dsa": "dsa",
|
||||
"crypto/ecdsa": "ecdsa",
|
||||
"crypto/elliptic": "elliptic",
|
||||
"crypto/hmac": "hmac",
|
||||
"crypto/internal/randutil": "randutil",
|
||||
"crypto/internal/subtle": "subtle",
|
||||
"crypto/md5": "md5",
|
||||
"crypto/rand": "rand",
|
||||
"crypto/rc4": "rc4",
|
||||
"crypto/rsa": "rsa",
|
||||
"crypto/sha1": "sha1",
|
||||
"crypto/sha256": "sha256",
|
||||
"crypto/sha512": "sha512",
|
||||
"crypto/subtle": "subtle",
|
||||
"crypto/tls": "tls",
|
||||
"crypto/x509": "x509",
|
||||
"crypto/x509/pkix": "pkix",
|
||||
"database/sql": "sql",
|
||||
"database/sql/driver": "driver",
|
||||
"debug/dwarf": "dwarf",
|
||||
"debug/elf": "elf",
|
||||
"debug/gosym": "gosym",
|
||||
"debug/macho": "macho",
|
||||
"debug/pe": "pe",
|
||||
"debug/plan9obj": "plan9obj",
|
||||
"encoding": "encoding",
|
||||
"encoding/ascii85": "ascii85",
|
||||
"encoding/asn1": "asn1",
|
||||
"encoding/base32": "base32",
|
||||
"encoding/base64": "base64",
|
||||
"encoding/binary": "binary",
|
||||
"encoding/csv": "csv",
|
||||
"encoding/gob": "gob",
|
||||
"encoding/hex": "hex",
|
||||
"encoding/json": "json",
|
||||
"encoding/pem": "pem",
|
||||
"encoding/xml": "xml",
|
||||
"errors": "errors",
|
||||
"expvar": "expvar",
|
||||
"flag": "flag",
|
||||
"fmt": "fmt",
|
||||
"go/ast": "ast",
|
||||
"go/build": "build",
|
||||
"go/constant": "constant",
|
||||
"go/doc": "doc",
|
||||
"go/format": "format",
|
||||
"go/importer": "importer",
|
||||
"go/internal/gccgoimporter": "gccgoimporter",
|
||||
"go/internal/gcimporter": "gcimporter",
|
||||
"go/internal/srcimporter": "srcimporter",
|
||||
"go/parser": "parser",
|
||||
"go/printer": "printer",
|
||||
"go/scanner": "scanner",
|
||||
"go/token": "token",
|
||||
"go/types": "types",
|
||||
"hash": "hash",
|
||||
"hash/adler32": "adler32",
|
||||
"hash/crc32": "crc32",
|
||||
"hash/crc64": "crc64",
|
||||
"hash/fnv": "fnv",
|
||||
"html": "html",
|
||||
"html/template": "template",
|
||||
"image": "image",
|
||||
"image/color": "color",
|
||||
"image/color/palette": "palette",
|
||||
"image/draw": "draw",
|
||||
"image/gif": "gif",
|
||||
"image/internal/imageutil": "imageutil",
|
||||
"image/jpeg": "jpeg",
|
||||
"image/png": "png",
|
||||
"index/suffixarray": "suffixarray",
|
||||
"internal/bytealg": "bytealg",
|
||||
"internal/cpu": "cpu",
|
||||
"internal/nettrace": "nettrace",
|
||||
"internal/poll": "poll",
|
||||
"internal/race": "race",
|
||||
"internal/singleflight": "singleflight",
|
||||
"internal/syscall/unix": "unix",
|
||||
"internal/syscall/windows": "windows",
|
||||
"internal/syscall/windows/registry": "registry",
|
||||
"internal/syscall/windows/sysdll": "sysdll",
|
||||
"internal/testenv": "testenv",
|
||||
"internal/testlog": "testlog",
|
||||
"internal/trace": "trace",
|
||||
"io": "io",
|
||||
"io/ioutil": "ioutil",
|
||||
"log": "log",
|
||||
"log/syslog": "syslog",
|
||||
"math": "math",
|
||||
"math/big": "big",
|
||||
"math/bits": "bits",
|
||||
"math/cmplx": "cmplx",
|
||||
"math/rand": "rand",
|
||||
"mime": "mime",
|
||||
"mime/multipart": "multipart",
|
||||
"mime/quotedprintable": "quotedprintable",
|
||||
"net": "net",
|
||||
"net/http": "http",
|
||||
"net/http/cgi": "cgi",
|
||||
"net/http/cookiejar": "cookiejar",
|
||||
"net/http/fcgi": "fcgi",
|
||||
"net/http/httptest": "httptest",
|
||||
"net/http/httptrace": "httptrace",
|
||||
"net/http/httputil": "httputil",
|
||||
"net/http/internal": "internal",
|
||||
"net/http/pprof": "pprof",
|
||||
"net/internal/socktest": "socktest",
|
||||
"net/mail": "mail",
|
||||
"net/rpc": "rpc",
|
||||
"net/rpc/jsonrpc": "jsonrpc",
|
||||
"net/smtp": "smtp",
|
||||
"net/textproto": "textproto",
|
||||
"net/url": "url",
|
||||
"os": "os",
|
||||
"os/exec": "exec",
|
||||
"os/signal": "signal",
|
||||
"os/signal/internal/pty": "pty",
|
||||
"os/user": "user",
|
||||
"path": "path",
|
||||
"path/filepath": "filepath",
|
||||
"plugin": "plugin",
|
||||
"reflect": "reflect",
|
||||
"regexp": "regexp",
|
||||
"regexp/syntax": "syntax",
|
||||
"runtime": "runtime",
|
||||
"runtime/cgo": "cgo",
|
||||
"runtime/debug": "debug",
|
||||
"runtime/internal/atomic": "atomic",
|
||||
"runtime/internal/sys": "sys",
|
||||
"runtime/pprof": "pprof",
|
||||
"runtime/pprof/internal/profile": "profile",
|
||||
"runtime/race": "race",
|
||||
"runtime/trace": "trace",
|
||||
"sort": "sort",
|
||||
"strconv": "strconv",
|
||||
"strings": "strings",
|
||||
"sync": "sync",
|
||||
"sync/atomic": "atomic",
|
||||
"syscall": "syscall",
|
||||
"testing": "testing",
|
||||
"testing/internal/testdeps": "testdeps",
|
||||
"testing/iotest": "iotest",
|
||||
"testing/quick": "quick",
|
||||
"text/scanner": "scanner",
|
||||
"text/tabwriter": "tabwriter",
|
||||
"text/template": "template",
|
||||
"text/template/parse": "parse",
|
||||
"time": "time",
|
||||
"unicode": "unicode",
|
||||
"unicode/utf16": "utf16",
|
||||
"unicode/utf8": "utf8",
|
||||
"unsafe": "unsafe",
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
// Package jen is a code generator for Go
|
||||
package jen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Code represents an item of code that can be rendered.
|
||||
type Code interface {
|
||||
render(f *File, w io.Writer, s *Statement) error
|
||||
isNull(f *File) bool
|
||||
}
|
||||
|
||||
// Save renders the file and saves to the filename provided.
|
||||
func (f *File) Save(filename string) error {
|
||||
// notest
|
||||
buf := &bytes.Buffer{}
|
||||
if err := f.Render(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(filename, buf.Bytes(), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Render renders the file to the provided writer.
|
||||
func (f *File) Render(w io.Writer) error {
|
||||
body := &bytes.Buffer{}
|
||||
if err := f.render(f, body, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
source := &bytes.Buffer{}
|
||||
if len(f.headers) > 0 {
|
||||
for _, c := range f.headers {
|
||||
if err := Comment(c).render(f, source, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprint(source, "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Append an extra newline so that header comments don't get lumped in
|
||||
// with package comments.
|
||||
if _, err := fmt.Fprint(source, "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, c := range f.comments {
|
||||
if err := Comment(c).render(f, source, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprint(source, "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprintf(source, "package %s", f.name); err != nil {
|
||||
return err
|
||||
}
|
||||
if f.CanonicalPath != "" {
|
||||
if _, err := fmt.Fprintf(source, " // import %q", f.CanonicalPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprint(source, "\n\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.renderImports(source); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := source.Write(body.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
formatted, err := format.Source(source.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error %s while formatting source:\n%s", err, source.String())
|
||||
}
|
||||
if _, err := w.Write(formatted); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) renderImports(source io.Writer) error {
|
||||
|
||||
// Render the "C" import if it's been used in a `Qual`, `Anon` or if there's a preamble comment
|
||||
hasCgo := f.imports["C"].name != "" || len(f.cgoPreamble) > 0
|
||||
|
||||
// Only separate the import from the main imports block if there's a preamble
|
||||
separateCgo := hasCgo && len(f.cgoPreamble) > 0
|
||||
|
||||
filtered := map[string]importdef{}
|
||||
for path, def := range f.imports {
|
||||
// filter out the "C" pseudo-package so it's not rendered in a block with the other
|
||||
// imports, but only if it is accompanied by a preamble comment
|
||||
if path == "C" && separateCgo {
|
||||
continue
|
||||
}
|
||||
filtered[path] = def
|
||||
}
|
||||
|
||||
if len(filtered) == 1 {
|
||||
for path, def := range filtered {
|
||||
if def.alias && path != "C" {
|
||||
// "C" package should be rendered without alias even when used as an anonymous import
|
||||
// (e.g. should never have an underscore).
|
||||
if _, err := fmt.Fprintf(source, "import %s %s\n\n", def.name, strconv.Quote(path)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := fmt.Fprintf(source, "import %s\n\n", strconv.Quote(path)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if len(filtered) > 1 {
|
||||
if _, err := fmt.Fprint(source, "import (\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
// We must sort the imports to ensure repeatable
|
||||
// source.
|
||||
paths := []string{}
|
||||
for path := range filtered {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
sort.Strings(paths)
|
||||
for _, path := range paths {
|
||||
def := filtered[path]
|
||||
if def.alias && path != "C" {
|
||||
// "C" package should be rendered without alias even when used as an anonymous import
|
||||
// (e.g. should never have an underscore).
|
||||
if _, err := fmt.Fprintf(source, "%s %s\n", def.name, strconv.Quote(path)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
if _, err := fmt.Fprintf(source, "%s\n", strconv.Quote(path)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprint(source, ")\n\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if separateCgo {
|
||||
for _, c := range f.cgoPreamble {
|
||||
if err := Comment(c).render(f, source, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprint(source, "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprint(source, "import \"C\"\n\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+550
@@ -0,0 +1,550 @@
|
||||
package jen_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/format"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "github.com/dave/jennifer/jen"
|
||||
)
|
||||
|
||||
var o1 = Options{
|
||||
Close: ")",
|
||||
Multi: true,
|
||||
Open: "(",
|
||||
Separator: ",",
|
||||
}
|
||||
|
||||
var o2 = Options{
|
||||
Close: "",
|
||||
Multi: false,
|
||||
Open: "",
|
||||
Separator: ",",
|
||||
}
|
||||
|
||||
var cases = []tc{
|
||||
{
|
||||
desc: `scientific notation`,
|
||||
code: Lit(1e3),
|
||||
expect: `1000.0`,
|
||||
},
|
||||
{
|
||||
desc: `big float`,
|
||||
code: Lit(1000000.0),
|
||||
expect: `1e+06`,
|
||||
},
|
||||
{
|
||||
desc: `lit float whole numbers`,
|
||||
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)),
|
||||
expect: "[]float64{-10.0, -2.0, -1.0, 0.0, 1.0, 2.0, 10.0}",
|
||||
},
|
||||
{
|
||||
desc: `custom func group`,
|
||||
code: ListFunc(func(g *Group) {
|
||||
g.CustomFunc(o2, func(g *Group) {
|
||||
g.Id("a")
|
||||
g.Id("b")
|
||||
g.Id("c")
|
||||
})
|
||||
}).Op("=").Id("foo").Call(),
|
||||
expect: `a, b, c = foo()`,
|
||||
},
|
||||
{
|
||||
desc: `custom group`,
|
||||
code: ListFunc(func(g *Group) { g.Custom(o2, Id("a"), Id("b"), Id("c")) }).Op("=").Id("foo").Call(),
|
||||
expect: `a, b, c = foo()`,
|
||||
},
|
||||
{
|
||||
desc: `custom function`,
|
||||
code: Id("foo").Add(Custom(o1, Lit("a"), Lit("b"), Lit("c"))),
|
||||
expect: `foo(
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
)`,
|
||||
},
|
||||
{
|
||||
desc: `custom function`,
|
||||
code: Id("foo").Add(Custom(o1, Lit("a"), Lit("b"), Lit("c"))),
|
||||
expect: `foo(
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
)`,
|
||||
},
|
||||
{
|
||||
desc: `line statement`,
|
||||
code: Block(Lit(1).Line(), Lit(2)),
|
||||
expect: `{
|
||||
1
|
||||
|
||||
2
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `line func`,
|
||||
code: Block(Lit(1), Line(), Lit(2)),
|
||||
expect: `{
|
||||
1
|
||||
|
||||
2
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `line group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Id("a")
|
||||
g.Line()
|
||||
g.Id("b")
|
||||
}),
|
||||
expect: `{
|
||||
a
|
||||
|
||||
b
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `op group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Op("*").Id("a")
|
||||
}),
|
||||
expect: `{*a}`,
|
||||
},
|
||||
{
|
||||
desc: `empty group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Empty()
|
||||
}),
|
||||
expect: `{
|
||||
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `null group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.Null()
|
||||
}),
|
||||
expect: `{}`,
|
||||
},
|
||||
{
|
||||
desc: `tag no backquote`,
|
||||
code: Tag(map[string]string{"a": "`b`"}),
|
||||
expect: "\"a:\\\"`b`\\\"\"",
|
||||
},
|
||||
{
|
||||
desc: `tag null`,
|
||||
code: Tag(map[string]string{}),
|
||||
expect: ``,
|
||||
},
|
||||
{
|
||||
desc: `litrunefunc group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.LitByteFunc(func() byte { return byte(0xab) })
|
||||
}),
|
||||
expect: `{byte(0xab)}`,
|
||||
},
|
||||
{
|
||||
desc: `litbyte group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.LitByte(byte(0xab))
|
||||
}),
|
||||
expect: `{byte(0xab)}`,
|
||||
},
|
||||
{
|
||||
desc: `litrunefunc group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.LitRuneFunc(func() rune { return 'a' })
|
||||
}),
|
||||
expect: `{'a'}`,
|
||||
},
|
||||
{
|
||||
desc: `litrune group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.LitRune('a')
|
||||
}),
|
||||
expect: `{'a'}`,
|
||||
},
|
||||
{
|
||||
desc: `litfunc group`,
|
||||
code: BlockFunc(func(g *Group) {
|
||||
g.LitFunc(func() interface{} {
|
||||
return 1 + 1
|
||||
})
|
||||
}),
|
||||
expect: `{2}`,
|
||||
},
|
||||
{
|
||||
desc: `litfunc func`,
|
||||
code: LitFunc(func() interface{} {
|
||||
return 1 + 1
|
||||
}),
|
||||
expect: `2`,
|
||||
},
|
||||
{
|
||||
desc: `group all null`,
|
||||
code: List(Null(), Null()),
|
||||
expect: ``,
|
||||
},
|
||||
{
|
||||
desc: `do group`,
|
||||
code: BlockFunc(func(g *Group) { g.Do(func(s *Statement) { s.Lit(1) }) }),
|
||||
expect: `{1}`,
|
||||
},
|
||||
{
|
||||
desc: `do func`,
|
||||
code: Do(func(s *Statement) { s.Lit(1) }),
|
||||
expect: `1`,
|
||||
},
|
||||
{
|
||||
desc: `dict empty`,
|
||||
code: Values(Dict{}),
|
||||
expect: `{}`,
|
||||
},
|
||||
{
|
||||
desc: `dict null`,
|
||||
code: Values(Dict{Null(): Null()}),
|
||||
expect: `{}`,
|
||||
},
|
||||
{
|
||||
desc: `commentf group`,
|
||||
code: BlockFunc(func(g *Group) { g.Commentf("%d", 1) }),
|
||||
expect: `{
|
||||
// 1
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `commentf func`,
|
||||
code: Commentf("%d", 1),
|
||||
expect: `// 1`,
|
||||
},
|
||||
{
|
||||
desc: `add func`,
|
||||
code: Add(Lit(1)),
|
||||
expect: `1`,
|
||||
},
|
||||
{
|
||||
desc: `add group`,
|
||||
code: BlockFunc(func(g *Group) { g.Add(Lit(1)) }),
|
||||
expect: `{1}`,
|
||||
},
|
||||
{
|
||||
desc: `empty block`,
|
||||
code: Block(),
|
||||
expect: `{}`,
|
||||
},
|
||||
{
|
||||
desc: `string literal`,
|
||||
code: Lit("a"),
|
||||
expect: `"a"`,
|
||||
},
|
||||
{
|
||||
desc: `int literal`,
|
||||
code: Lit(1),
|
||||
expect: `1`,
|
||||
},
|
||||
{
|
||||
desc: `simple id`,
|
||||
code: Id("a"),
|
||||
expect: `a`,
|
||||
},
|
||||
{
|
||||
desc: `foreign id`,
|
||||
code: Qual("x.y/z", "a"),
|
||||
expect: `z.a`,
|
||||
expectImports: map[string]string{
|
||||
"x.y/z": "z",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: `var decl`,
|
||||
code: Var().Id("a").Op("=").Lit("b"),
|
||||
expect: `var a = "b"`,
|
||||
},
|
||||
{
|
||||
desc: `short var decl`,
|
||||
code: Id("a").Op(":=").Lit("b"),
|
||||
expect: `a := "b"`,
|
||||
},
|
||||
{
|
||||
desc: `simple if`,
|
||||
code: If(Id("a").Op("==").Lit("b")).Block(),
|
||||
expect: `if a == "b" {}`,
|
||||
},
|
||||
{
|
||||
desc: `simple if`,
|
||||
code: If(Id("a").Op("==").Lit("b")).Block(
|
||||
Id("a").Op("++"),
|
||||
),
|
||||
expect: `if a == "b" { a++ }`,
|
||||
},
|
||||
{
|
||||
desc: `pointer`,
|
||||
code: Op("*").Id("a"),
|
||||
expect: `*a`,
|
||||
},
|
||||
{
|
||||
desc: `address`,
|
||||
code: Op("&").Id("a"),
|
||||
expect: `&a`,
|
||||
},
|
||||
{
|
||||
desc: `simple call`,
|
||||
code: Id("a").Call(
|
||||
Lit("b"),
|
||||
Lit("c"),
|
||||
),
|
||||
expect: `a("b", "c")`,
|
||||
},
|
||||
{
|
||||
desc: `call fmt.Sprintf`,
|
||||
code: Qual("fmt", "Sprintf").Call(
|
||||
Lit("b"),
|
||||
Id("c"),
|
||||
),
|
||||
expect: `fmt.Sprintf("b", c)`,
|
||||
},
|
||||
{
|
||||
desc: `slices`,
|
||||
code: Id("a").Index(
|
||||
Lit(1),
|
||||
Empty(),
|
||||
),
|
||||
expect: `a[1:]`,
|
||||
},
|
||||
{
|
||||
desc: `return`,
|
||||
code: Return(Id("a")),
|
||||
expect: `return a`,
|
||||
},
|
||||
{
|
||||
desc: `double return`,
|
||||
code: Return(Id("a"), Id("b")),
|
||||
expect: `return a, b`,
|
||||
},
|
||||
{
|
||||
desc: `func`,
|
||||
code: Func().Id("a").Params(
|
||||
Id("a").String(),
|
||||
).Block(
|
||||
Return(Id("a")),
|
||||
),
|
||||
expect: `func a(a string){
|
||||
return a
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `built in func`,
|
||||
code: New(Id("a")),
|
||||
expect: `new(a)`,
|
||||
},
|
||||
{
|
||||
desc: `multip`,
|
||||
code: Id("a").Op("*").Id("b"),
|
||||
expect: `a * b`,
|
||||
},
|
||||
{
|
||||
desc: `multip ptr`,
|
||||
code: Id("a").Op("*").Op("*").Id("b"),
|
||||
expect: `a * *b`,
|
||||
},
|
||||
{
|
||||
desc: `field`,
|
||||
code: Id("a").Dot("b"),
|
||||
expect: `a.b`,
|
||||
},
|
||||
{
|
||||
desc: `method`,
|
||||
code: Id("a").Dot("b").Call(Id("c"), Id("d")),
|
||||
expect: `a.b(c, d)`,
|
||||
},
|
||||
{
|
||||
desc: `if else`,
|
||||
code: If(Id("a").Op("==").Lit(1)).Block(
|
||||
Id("b").Op("=").Lit(1),
|
||||
).Else().If(Id("a").Op("==").Lit(2)).Block(
|
||||
Id("b").Op("=").Lit(2),
|
||||
).Else().Block(
|
||||
Id("b").Op("=").Lit(3),
|
||||
),
|
||||
expect: `if a == 1 { b = 1 } else if a == 2 { b = 2 } else { b = 3 }`,
|
||||
},
|
||||
{
|
||||
desc: `literal array`,
|
||||
code: Index().String().Values(Lit("a"), Lit("b")),
|
||||
expect: `[]string{"a", "b"}`,
|
||||
},
|
||||
{
|
||||
desc: `comment`,
|
||||
code: Comment("a"),
|
||||
expect: `// a`,
|
||||
},
|
||||
{
|
||||
desc: `null`,
|
||||
code: Id("a").Params(Id("b"), Null(), Id("c")),
|
||||
expect: `a(b, c)`,
|
||||
},
|
||||
{
|
||||
desc: `map literal single`,
|
||||
code: Id("a").Values(Dict{
|
||||
Id("b"): Id("c"),
|
||||
}),
|
||||
expect: `a{b: c}`,
|
||||
},
|
||||
{
|
||||
desc: `map literal null`,
|
||||
code: Id("a").Values(Dict{
|
||||
Null(): Id("c"),
|
||||
Id("b"): Null(),
|
||||
Id("b"): Id("c"),
|
||||
}),
|
||||
expect: `a{b: c}`,
|
||||
},
|
||||
{
|
||||
desc: `map literal multiple`,
|
||||
code: Id("a").Values(Dict{
|
||||
Id("b"): Id("c"),
|
||||
Id("d"): Id("e"),
|
||||
}),
|
||||
expect: `a{
|
||||
b: c,
|
||||
d: e,
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `map literal func single`,
|
||||
code: Id("a").Values(DictFunc(func(d Dict) {
|
||||
d[Id("b")] = Id("c")
|
||||
})),
|
||||
expect: `a{b: c}`,
|
||||
},
|
||||
{
|
||||
desc: `map literal func single null`,
|
||||
code: Id("a").Values(DictFunc(func(d Dict) {
|
||||
d[Null()] = Id("c")
|
||||
d[Id("b")] = Null()
|
||||
d[Id("b")] = Id("c")
|
||||
})),
|
||||
expect: `a{b: c}`,
|
||||
},
|
||||
{
|
||||
desc: `map literal func multiple`,
|
||||
code: Id("a").Values(DictFunc(func(d Dict) {
|
||||
d[Id("b")] = Id("c")
|
||||
d[Id("d")] = Id("e")
|
||||
})),
|
||||
expect: `a{
|
||||
b: c,
|
||||
d: e,
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: `literal func`,
|
||||
code: Id("a").Op(":=").LitFunc(func() interface{} {
|
||||
return "b"
|
||||
}),
|
||||
expect: `a := "b"`,
|
||||
},
|
||||
{
|
||||
desc: `dot`,
|
||||
code: Id("a").Dot("b").Dot("c"),
|
||||
expect: `a.b.c`,
|
||||
},
|
||||
{
|
||||
desc: `do`,
|
||||
code: Id("a").Do(func(s *Statement) { s.Dot("b") }),
|
||||
expect: `a.b`,
|
||||
},
|
||||
{
|
||||
desc: `tags should be ordered`,
|
||||
code: Tag(map[string]string{"z": "1", "a": "2"}),
|
||||
expect: "`a:\"2\" z:\"1\"`",
|
||||
},
|
||||
{
|
||||
desc: `dict should be ordered`,
|
||||
code: Map(String()).Int().Values(Dict{Id("z"): Lit(1), Id("a"): Lit(2)}),
|
||||
expect: `map[string]int{
|
||||
a:2,
|
||||
z:1,
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestJen(t *testing.T) {
|
||||
caseTester(t, cases)
|
||||
}
|
||||
|
||||
func caseTester(t *testing.T, cases []tc) {
|
||||
for i, c := range cases {
|
||||
onlyTest := ""
|
||||
if onlyTest != "" && c.desc != onlyTest {
|
||||
continue
|
||||
}
|
||||
rendered := fmt.Sprintf("%#v", c.code)
|
||||
|
||||
expected, err := format.Source([]byte(c.expect))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error formatting expected source in test case %d. Description: %s\nError:\n%s", i, c.desc, err))
|
||||
}
|
||||
|
||||
if strings.TrimSpace(string(rendered)) != strings.TrimSpace(string(expected)) {
|
||||
t.Errorf("Test case %d failed. Description: %s\nExpected:\n%s\nOutput:\n%s", i, c.desc, expected, rendered)
|
||||
}
|
||||
|
||||
//if c.expectImports != nil {
|
||||
// f := FromContext(ctx)
|
||||
// if !reflect.DeepEqual(f.Imports, c.expectImports) {
|
||||
// t.Errorf("Test case %d failed. Description: %s\nImports expected:\n%s\nOutput:\n%s", i, c.desc, c.expectImports, f.Imports)
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
// a test case
|
||||
type tc struct {
|
||||
// path
|
||||
path string
|
||||
// description for locating the test case
|
||||
desc string
|
||||
// code to generate
|
||||
code Code
|
||||
// expected generated source
|
||||
expect string
|
||||
// expected imports
|
||||
expectImports map[string]string
|
||||
}
|
||||
|
||||
func TestNilStatement(t *testing.T) {
|
||||
var s *Statement
|
||||
c := Func().Id("a").Params(
|
||||
s,
|
||||
)
|
||||
got := fmt.Sprintf("%#v", c)
|
||||
expect := "func a()"
|
||||
if got != expect {
|
||||
t.Fatalf("Got: %s, expect: %s", got, expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilGroup(t *testing.T) {
|
||||
var g *Group
|
||||
c := Func().Id("a").Params(
|
||||
g,
|
||||
)
|
||||
got := fmt.Sprintf("%#v", c)
|
||||
expect := "func a()"
|
||||
if got != expect {
|
||||
t.Fatalf("Got: %s, expect: %s", got, expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroup_GoString(t *testing.T) {
|
||||
BlockFunc(func(g *Group) {
|
||||
g.Lit(1)
|
||||
got := fmt.Sprintf("%#v", g)
|
||||
expect := "{\n\t1\n}"
|
||||
if got != expect {
|
||||
t.Fatalf("Got: %s, expect: %s", got, expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package jen
|
||||
|
||||
// Lit renders a literal. Lit supports only built-in types (bool, string, int, complex128, float64,
|
||||
// float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64).
|
||||
// Passing any other type will panic.
|
||||
func Lit(v interface{}) *Statement {
|
||||
return newStatement().Lit(v)
|
||||
}
|
||||
|
||||
// Lit renders a literal. Lit supports only built-in types (bool, string, int, complex128, float64,
|
||||
// float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64).
|
||||
// Passing any other type will panic.
|
||||
func (g *Group) Lit(v interface{}) *Statement {
|
||||
s := Lit(v)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Lit renders a literal. Lit supports only built-in types (bool, string, int, complex128, float64,
|
||||
// float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64).
|
||||
// Passing any other type will panic.
|
||||
func (s *Statement) Lit(v interface{}) *Statement {
|
||||
t := token{
|
||||
typ: literalToken,
|
||||
content: v,
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
|
||||
// LitFunc renders a literal. LitFunc generates the value to render by executing the provided
|
||||
// function. LitFunc supports only built-in types (bool, string, int, complex128, float64, float32,
|
||||
// int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64).
|
||||
// Returning any other type will panic.
|
||||
func LitFunc(f func() interface{}) *Statement {
|
||||
return newStatement().LitFunc(f)
|
||||
}
|
||||
|
||||
// LitFunc renders a literal. LitFunc generates the value to render by executing the provided
|
||||
// function. LitFunc supports only built-in types (bool, string, int, complex128, float64, float32,
|
||||
// int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64).
|
||||
// Returning any other type will panic.
|
||||
func (g *Group) LitFunc(f func() interface{}) *Statement {
|
||||
s := LitFunc(f)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// LitFunc renders a literal. LitFunc generates the value to render by executing the provided
|
||||
// function. LitFunc supports only built-in types (bool, string, int, complex128, float64, float32,
|
||||
// int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr and complex64).
|
||||
// Returning any other type will panic.
|
||||
func (s *Statement) LitFunc(f func() interface{}) *Statement {
|
||||
t := token{
|
||||
typ: literalToken,
|
||||
content: f(),
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
|
||||
// LitRune renders a rune literal.
|
||||
func LitRune(v rune) *Statement {
|
||||
return newStatement().LitRune(v)
|
||||
}
|
||||
|
||||
// LitRune renders a rune literal.
|
||||
func (g *Group) LitRune(v rune) *Statement {
|
||||
s := LitRune(v)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// LitRune renders a rune literal.
|
||||
func (s *Statement) LitRune(v rune) *Statement {
|
||||
t := token{
|
||||
typ: literalRuneToken,
|
||||
content: v,
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
|
||||
// LitRuneFunc renders a rune literal. LitRuneFunc generates the value to
|
||||
// render by executing the provided function.
|
||||
func LitRuneFunc(f func() rune) *Statement {
|
||||
return newStatement().LitRuneFunc(f)
|
||||
}
|
||||
|
||||
// LitRuneFunc renders a rune literal. LitRuneFunc generates the value to
|
||||
// render by executing the provided function.
|
||||
func (g *Group) LitRuneFunc(f func() rune) *Statement {
|
||||
s := LitRuneFunc(f)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// LitRuneFunc renders a rune literal. LitRuneFunc generates the value to
|
||||
// render by executing the provided function.
|
||||
func (s *Statement) LitRuneFunc(f func() rune) *Statement {
|
||||
t := token{
|
||||
typ: literalRuneToken,
|
||||
content: f(),
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
|
||||
// LitByte renders a byte literal.
|
||||
func LitByte(v byte) *Statement {
|
||||
return newStatement().LitByte(v)
|
||||
}
|
||||
|
||||
// LitByte renders a byte literal.
|
||||
func (g *Group) LitByte(v byte) *Statement {
|
||||
s := LitByte(v)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// LitByte renders a byte literal.
|
||||
func (s *Statement) LitByte(v byte) *Statement {
|
||||
t := token{
|
||||
typ: literalByteToken,
|
||||
content: v,
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
|
||||
// LitByteFunc renders a byte literal. LitByteFunc generates the value to
|
||||
// render by executing the provided function.
|
||||
func LitByteFunc(f func() byte) *Statement {
|
||||
return newStatement().LitByteFunc(f)
|
||||
}
|
||||
|
||||
// LitByteFunc renders a byte literal. LitByteFunc generates the value to
|
||||
// render by executing the provided function.
|
||||
func (g *Group) LitByteFunc(f func() byte) *Statement {
|
||||
s := LitByteFunc(f)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// LitByteFunc renders a byte literal. LitByteFunc generates the value to
|
||||
// render by executing the provided function.
|
||||
func (s *Statement) LitByteFunc(f func() byte) *Statement {
|
||||
t := token{
|
||||
typ: literalByteToken,
|
||||
content: f(),
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package jen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Statement represents a simple list of code items. When rendered the items
|
||||
// are separated by spaces.
|
||||
type Statement []Code
|
||||
|
||||
func newStatement() *Statement {
|
||||
return &Statement{}
|
||||
}
|
||||
|
||||
// Clone makes a copy of the Statement, so further tokens can be appended
|
||||
// without affecting the original.
|
||||
func (s *Statement) Clone() *Statement {
|
||||
return &Statement{s}
|
||||
}
|
||||
|
||||
func (s *Statement) previous(c Code) Code {
|
||||
index := -1
|
||||
for i, item := range *s {
|
||||
if item == c {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if index > 0 {
|
||||
return (*s)[index-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Statement) isNull(f *File) bool {
|
||||
if s == nil {
|
||||
return true
|
||||
}
|
||||
for _, c := range *s {
|
||||
if !c.isNull(f) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Statement) render(f *File, w io.Writer, _ *Statement) error {
|
||||
first := true
|
||||
for _, code := range *s {
|
||||
if code == nil || code.isNull(f) {
|
||||
// Null() token produces no output but also
|
||||
// no separator. Empty() token products no
|
||||
// output but adds a separator.
|
||||
continue
|
||||
}
|
||||
if !first {
|
||||
if _, err := w.Write([]byte(" ")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := code.render(f, w, s); err != nil {
|
||||
return err
|
||||
}
|
||||
first = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Render renders the Statement to the provided writer.
|
||||
func (s *Statement) Render(writer io.Writer) error {
|
||||
return s.RenderWithFile(writer, NewFile(""))
|
||||
}
|
||||
|
||||
// GoString renders the Statement for testing. Any error will cause a panic.
|
||||
func (s *Statement) GoString() string {
|
||||
buf := bytes.Buffer{}
|
||||
if err := s.Render(&buf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// RenderWithFile renders the Statement to the provided writer, using imports from the provided file.
|
||||
func (s *Statement) RenderWithFile(writer io.Writer, file *File) error {
|
||||
buf := &bytes.Buffer{}
|
||||
if err := s.render(file, buf, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error %s while formatting source:\n%s", err, buf.String())
|
||||
}
|
||||
if _, err := writer.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package jen_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
. "github.com/dave/jennifer/jen"
|
||||
)
|
||||
|
||||
func TestStatement_Render(t *testing.T) {
|
||||
file := NewFile("main")
|
||||
file.ImportAlias("fmt", "fmtalias")
|
||||
|
||||
statement := file.Func().Id("main").Params().Block(
|
||||
Qual("fmt", "Println").Call(Lit("something")),
|
||||
)
|
||||
|
||||
expect := `func main() {
|
||||
fmtalias.Println("something")
|
||||
}`
|
||||
|
||||
var got bytes.Buffer
|
||||
|
||||
err := statement.RenderWithFile(&got, file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if got.String() != expect {
|
||||
t.Fatalf("Got: %v, expect: %v", got.String(), expect)
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package jen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Tag renders a struct tag
|
||||
func Tag(items map[string]string) *Statement {
|
||||
return newStatement().Tag(items)
|
||||
}
|
||||
|
||||
// Tag renders a struct tag
|
||||
func (g *Group) Tag(items map[string]string) *Statement {
|
||||
// notest
|
||||
// don't think this can ever be used in valid code?
|
||||
s := Tag(items)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Tag renders a struct tag
|
||||
func (s *Statement) Tag(items map[string]string) *Statement {
|
||||
c := tag{
|
||||
items: items,
|
||||
}
|
||||
*s = append(*s, c)
|
||||
return s
|
||||
}
|
||||
|
||||
type tag struct {
|
||||
items map[string]string
|
||||
}
|
||||
|
||||
func (t tag) isNull(f *File) bool {
|
||||
return len(t.items) == 0
|
||||
}
|
||||
|
||||
func (t tag) render(f *File, w io.Writer, s *Statement) error {
|
||||
|
||||
if t.isNull(f) {
|
||||
// notest
|
||||
// render won't be called if t is null
|
||||
return nil
|
||||
}
|
||||
|
||||
var str string
|
||||
|
||||
var sorted []string
|
||||
for k := range t.items {
|
||||
sorted = append(sorted, k)
|
||||
}
|
||||
sort.Strings(sorted)
|
||||
|
||||
for _, k := range sorted {
|
||||
v := t.items[k]
|
||||
if len(str) > 0 {
|
||||
str += " "
|
||||
}
|
||||
str += fmt.Sprintf(`%s:"%s"`, k, v)
|
||||
}
|
||||
|
||||
if strconv.CanBackquote(str) {
|
||||
str = "`" + str + "`"
|
||||
} else {
|
||||
str = strconv.Quote(str)
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte(str)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
package jen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type tokenType string
|
||||
|
||||
const (
|
||||
packageToken tokenType = "package"
|
||||
identifierToken tokenType = "identifier"
|
||||
qualifiedToken tokenType = "qualified"
|
||||
keywordToken tokenType = "keyword"
|
||||
operatorToken tokenType = "operator"
|
||||
delimiterToken tokenType = "delimiter"
|
||||
literalToken tokenType = "literal"
|
||||
literalRuneToken tokenType = "literal_rune"
|
||||
literalByteToken tokenType = "literal_byte"
|
||||
nullToken tokenType = "null"
|
||||
layoutToken tokenType = "layout"
|
||||
)
|
||||
|
||||
type token struct {
|
||||
typ tokenType
|
||||
content interface{}
|
||||
}
|
||||
|
||||
func (t token) isNull(f *File) bool {
|
||||
if t.typ == packageToken {
|
||||
// package token is null if the path is a dot-import or the local package path
|
||||
return f.isDotImport(t.content.(string)) || f.isLocal(t.content.(string))
|
||||
}
|
||||
return t.typ == nullToken
|
||||
}
|
||||
|
||||
func (t token) render(f *File, w io.Writer, s *Statement) error {
|
||||
switch t.typ {
|
||||
case literalToken:
|
||||
var out string
|
||||
switch t.content.(type) {
|
||||
case bool, string, int, complex128:
|
||||
// default constant types can be left bare
|
||||
out = fmt.Sprintf("%#v", t.content)
|
||||
case float64:
|
||||
out = fmt.Sprintf("%#v", t.content)
|
||||
if !strings.Contains(out, ".") && !strings.Contains(out, "e") {
|
||||
// If the formatted value is not in scientific notation, and does not have a dot, then
|
||||
// we add ".0". Otherwise it will be interpreted as an int.
|
||||
// See:
|
||||
// https://github.com/dave/jennifer/issues/39
|
||||
// https://github.com/golang/go/issues/26363
|
||||
out += ".0"
|
||||
}
|
||||
case float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr:
|
||||
// other built-in types need specific type info
|
||||
out = fmt.Sprintf("%T(%#v)", t.content, t.content)
|
||||
case complex64:
|
||||
// fmt package already renders parenthesis for complex64
|
||||
out = fmt.Sprintf("%T%#v", t.content, t.content)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported type for literal: %T", t.content))
|
||||
}
|
||||
if _, err := w.Write([]byte(out)); err != nil {
|
||||
return err
|
||||
}
|
||||
case literalRuneToken:
|
||||
if _, err := w.Write([]byte(strconv.QuoteRune(t.content.(rune)))); err != nil {
|
||||
return err
|
||||
}
|
||||
case literalByteToken:
|
||||
if _, err := w.Write([]byte(fmt.Sprintf("byte(%#v)", t.content))); err != nil {
|
||||
return err
|
||||
}
|
||||
case keywordToken, operatorToken, layoutToken, delimiterToken:
|
||||
if _, err := w.Write([]byte(fmt.Sprintf("%s", t.content))); err != nil {
|
||||
return err
|
||||
}
|
||||
if t.content.(string) == "default" {
|
||||
// Special case for Default, which must always be followed by a colon
|
||||
if _, err := w.Write([]byte(":")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case packageToken:
|
||||
path := t.content.(string)
|
||||
alias := f.register(path)
|
||||
if _, err := w.Write([]byte(alias)); err != nil {
|
||||
return err
|
||||
}
|
||||
case identifierToken:
|
||||
if _, err := w.Write([]byte(t.content.(string))); err != nil {
|
||||
return err
|
||||
}
|
||||
case nullToken: // notest
|
||||
// do nothing (should never render a null token)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Null adds a null item. Null items render nothing and are not followed by a
|
||||
// separator in lists.
|
||||
func Null() *Statement {
|
||||
return newStatement().Null()
|
||||
}
|
||||
|
||||
// Null adds a null item. Null items render nothing and are not followed by a
|
||||
// separator in lists.
|
||||
func (g *Group) Null() *Statement {
|
||||
s := Null()
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Null adds a null item. Null items render nothing and are not followed by a
|
||||
// separator in lists.
|
||||
func (s *Statement) Null() *Statement {
|
||||
t := token{
|
||||
typ: nullToken,
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
|
||||
// Empty adds an empty item. Empty items render nothing but are followed by a
|
||||
// separator in lists.
|
||||
func Empty() *Statement {
|
||||
return newStatement().Empty()
|
||||
}
|
||||
|
||||
// Empty adds an empty item. Empty items render nothing but are followed by a
|
||||
// separator in lists.
|
||||
func (g *Group) Empty() *Statement {
|
||||
s := Empty()
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Empty adds an empty item. Empty items render nothing but are followed by a
|
||||
// separator in lists.
|
||||
func (s *Statement) Empty() *Statement {
|
||||
t := token{
|
||||
typ: operatorToken,
|
||||
content: "",
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
|
||||
// Op renders the provided operator / token.
|
||||
func Op(op string) *Statement {
|
||||
return newStatement().Op(op)
|
||||
}
|
||||
|
||||
// Op renders the provided operator / token.
|
||||
func (g *Group) Op(op string) *Statement {
|
||||
s := Op(op)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Op renders the provided operator / token.
|
||||
func (s *Statement) Op(op string) *Statement {
|
||||
t := token{
|
||||
typ: operatorToken,
|
||||
content: op,
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
|
||||
// Dot renders a period followed by an identifier. Use for fields and selectors.
|
||||
func Dot(name string) *Statement {
|
||||
// notest
|
||||
// don't think this can be used in valid code?
|
||||
return newStatement().Dot(name)
|
||||
}
|
||||
|
||||
// Dot renders a period followed by an identifier. Use for fields and selectors.
|
||||
func (g *Group) Dot(name string) *Statement {
|
||||
// notest
|
||||
// don't think this can be used in valid code?
|
||||
s := Dot(name)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Dot renders a period followed by an identifier. Use for fields and selectors.
|
||||
func (s *Statement) Dot(name string) *Statement {
|
||||
d := token{
|
||||
typ: delimiterToken,
|
||||
content: ".",
|
||||
}
|
||||
t := token{
|
||||
typ: identifierToken,
|
||||
content: name,
|
||||
}
|
||||
*s = append(*s, d, t)
|
||||
return s
|
||||
}
|
||||
|
||||
// Id renders an identifier.
|
||||
func Id(name string) *Statement {
|
||||
return newStatement().Id(name)
|
||||
}
|
||||
|
||||
// Id renders an identifier.
|
||||
func (g *Group) Id(name string) *Statement {
|
||||
s := Id(name)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Id renders an identifier.
|
||||
func (s *Statement) Id(name string) *Statement {
|
||||
t := token{
|
||||
typ: identifierToken,
|
||||
content: name,
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
|
||||
// Qual renders a qualified identifier. Imports are automatically added when
|
||||
// used with a File. If the path matches the local path, the package name is
|
||||
// omitted. If package names conflict they are automatically renamed. Note that
|
||||
// it is not possible to reliably determine the package name given an arbitrary
|
||||
// package path, so a sensible name is guessed from the path and added as an
|
||||
// alias. The names of all standard library packages are known so these do not
|
||||
// need to be aliased. If more control is needed of the aliases, see
|
||||
// [File.ImportName](#importname) or [File.ImportAlias](#importalias).
|
||||
func Qual(path, name string) *Statement {
|
||||
return newStatement().Qual(path, name)
|
||||
}
|
||||
|
||||
// Qual renders a qualified identifier. Imports are automatically added when
|
||||
// used with a File. If the path matches the local path, the package name is
|
||||
// omitted. If package names conflict they are automatically renamed. Note that
|
||||
// it is not possible to reliably determine the package name given an arbitrary
|
||||
// package path, so a sensible name is guessed from the path and added as an
|
||||
// alias. The names of all standard library packages are known so these do not
|
||||
// need to be aliased. If more control is needed of the aliases, see
|
||||
// [File.ImportName](#importname) or [File.ImportAlias](#importalias).
|
||||
func (g *Group) Qual(path, name string) *Statement {
|
||||
s := Qual(path, name)
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Qual renders a qualified identifier. Imports are automatically added when
|
||||
// used with a File. If the path matches the local path, the package name is
|
||||
// omitted. If package names conflict they are automatically renamed. Note that
|
||||
// it is not possible to reliably determine the package name given an arbitrary
|
||||
// package path, so a sensible name is guessed from the path and added as an
|
||||
// alias. The names of all standard library packages are known so these do not
|
||||
// need to be aliased. If more control is needed of the aliases, see
|
||||
// [File.ImportName](#importname) or [File.ImportAlias](#importalias).
|
||||
func (s *Statement) Qual(path, name string) *Statement {
|
||||
g := &Group{
|
||||
close: "",
|
||||
items: []Code{
|
||||
token{
|
||||
typ: packageToken,
|
||||
content: path,
|
||||
},
|
||||
token{
|
||||
typ: identifierToken,
|
||||
content: name,
|
||||
},
|
||||
},
|
||||
name: "qual",
|
||||
open: "",
|
||||
separator: ".",
|
||||
}
|
||||
*s = append(*s, g)
|
||||
return s
|
||||
}
|
||||
|
||||
// Line inserts a blank line.
|
||||
func Line() *Statement {
|
||||
return newStatement().Line()
|
||||
}
|
||||
|
||||
// Line inserts a blank line.
|
||||
func (g *Group) Line() *Statement {
|
||||
s := Line()
|
||||
g.items = append(g.items, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Line inserts a blank line.
|
||||
func (s *Statement) Line() *Statement {
|
||||
t := token{
|
||||
typ: layoutToken,
|
||||
content: "\n",
|
||||
}
|
||||
*s = append(*s, t)
|
||||
return s
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Package jennifer is a code generator for Go
|
||||
package jennifer
|
||||
|
||||
//go:generate go get github.com/dave/jennifer/genjen
|
||||
//go:generate genjen
|
||||
//go:generate go get github.com/dave/jennifer/gennames
|
||||
//go:generate gennames -output "jen/hints.go" -package "jen" -name "standardLibraryHints" -standard -novendor -path "./..."
|
||||
//go:generate go get github.com/dave/rebecca/cmd/becca
|
||||
//go:generate becca -package=github.com/dave/jennifer/jen
|
||||
Reference in New Issue
Block a user