Skip to content

Commit

Permalink
Rearrange Dict
Browse files Browse the repository at this point in the history
  • Loading branch information
dave committed Mar 11, 2017
1 parent c692ac1 commit 679d504
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 157 deletions.
38 changes: 25 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,11 @@ fmt.Printf("%#v", c)
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}` |
| [Values](#values) | | `,` | `[]int{1, 2}` or `A{B: "c"}` |

### Block
Block renders a statement list enclosed by curly braces. Use for code blocks.
Expand Down Expand Up @@ -442,7 +442,7 @@ fmt.Printf("%#v", c)
Map renders the keyword followed by a single item enclosed by square brackets. Use for map definitions.

```go
c := Id("a").Op(":=").Map(String()).String().Dict(nil)
c := Id("a").Op(":=").Map(String()).String().Values()
fmt.Printf("%#v", c)
// Output:
// a := map[string]string{}
Expand Down Expand Up @@ -482,32 +482,44 @@ fmt.Printf("%#v", c)
// []string{"a", "b"}
```

### Dict
Dict takes a map[Code]Code and renders a list of colon separated key value
pairs, enclosed in curly braces. Use for map or composite literals.
Dict renders as key/value pairs. Use with Values for map or composite
literals.

```go
c := Id("a").Op(":=").Map(String()).String().Dict(map[Code]Code{
c := Map(String()).String().Values(Dict{
Lit("a"): Lit("b"),
Lit("c"): Lit("d"),
})
fmt.Printf("%#v", c)
// Output:
// a := map[string]string{
// map[string]string{
// "a": "b",
// "c": "d",
// }
```

DictFunc executes a func(map[Code]Code) to generate the value.

```go
c := Id("a").Op(":=").Map(String()).String().DictFunc(func(m map[Code]Code) {
m[Lit("a")] = Lit("b")
m[Lit("c")] = Lit("d")
c := Op("&").Id("Person").Values(Dict{
Id("Age"): Lit(1),
Id("Name"): Lit("a"),
})
fmt.Printf("%#v", c)
// Output:
// &Person{
// Age: 1,
// Name: "a",
// }
```

DictFunc executes a func(Dict) to generate the value.

```go
c := Id("a").Op(":=").Map(String()).String().Values(DictFunc(func(d Dict) {
d[Lit("a")] = Lit("b")
d[Lit("c")] = Lit("d")
}))
fmt.Printf("%#v", c)
// Output:
// a := map[string]string{
// "a": "b",
// "c": "d",
Expand Down
9 changes: 5 additions & 4 deletions README.md.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ Special cases for [If, For](#if-for), [Interface, Struct](#interface-struct), [S
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}` |
| [Values](#values) | | `,` | `[]int{1, 2}` or `A{B: "c"}` |

### Block
{{ "Block[:2]" | doc }}
Expand Down Expand Up @@ -215,10 +215,11 @@ Switch, Select, Case and Block are used to build switch or select statements:

{{ "ExampleValues" | example }}

### Dict
{{ "Dict" | doc }}

{{ "ExampleDict" | example }}
{{ "ExampleValues_dict_multiple" | example }}

{{ "ExampleValues_dict_composite" | example }}

{{ "DictFunc[0]" | doc }}

Expand Down
6 changes: 3 additions & 3 deletions genjen/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func render(w io.Writer) error {
).Id(b.name).Params(
funcParams...,
).Op("*").Id("Statement").Block(
Id("g").Op(":=").Op("&").Id("Group").Dict(map[Code]Code{
Id("g").Op(":=").Op("&").Id("Group").Values(Dict{
Id("items"): Do(func(s *Statement) {
if b.variadic {
s.Id(b.parameters[0])
Expand Down Expand Up @@ -118,7 +118,7 @@ func render(w io.Writer) error {
).Id(funcName).Params(
funcFuncParams...,
).Op("*").Id("Statement").Block(
Id("g").Op(":=").Op("&").Id("Group").Dict(map[Code]Code{
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),
Expand Down Expand Up @@ -186,7 +186,7 @@ func render(w io.Writer) error {
file.Func().Params(
Id("s").Op("*").Id("Statement"),
).Id(t.name).Params().Op("*").Id("Statement").Block(
Id("t").Op(":=").Id("token").Dict(map[Code]Code{
Id("t").Op(":=").Id("token").Values(Dict{
Id("typ"): Id(t.tokenType),
Id("content"): Lit(t.token),
}),
Expand Down
81 changes: 81 additions & 0 deletions jen/dict.go
Original file line number Diff line number Diff line change
@@ -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
}
51 changes: 42 additions & 9 deletions jen/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,41 @@ import (
. "github.com/davelondon/jennifer/jen"
)

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

func ExampleValues_dict_multiple() {
c := Map(String()).String().Values(Dict{
Lit("a"): Lit("b"),
Lit("c"): Lit("d"),
})
fmt.Printf("%#v", c)
// Output:
// map[string]string{
// "a": "b",
// "c": "d",
// }
}

func ExampleValues_dict_composite() {
c := Op("&").Id("Person").Values(Dict{
Id("Age"): Lit(1),
Id("Name"): Lit("a"),
})
fmt.Printf("%#v", c)
// Output:
// &Person{
// Age: 1,
// Name: "a",
// }
}

func ExampleAdd() {
ptr := Op("*")
c := Id("a").Op("=").Add(ptr).Id("b")
Expand Down Expand Up @@ -400,8 +435,6 @@ func Defer() *Statement
func Defs(c ...Code) *Statement
func DefsFunc(f func(*Group)) *Statement
func Delete(c ...Code) *Statement
func Dict(m map[Code]Code) *Statement
func DictFunc(f func(map[Code]Code)) *Statement
func Do(f func(*Statement)) *Statement
func Else() *Statement
func Empty() *Statement
Expand Down Expand Up @@ -905,14 +938,14 @@ func ExampleReturn() {
}

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

func ExampleDict() {
c := Id("a").Op(":=").Map(String()).String().Dict(map[Code]Code{
c := Id("a").Op(":=").Map(String()).String().Values(Dict{
Lit("a"): Lit("b"),
Lit("c"): Lit("d"),
})
Expand All @@ -925,17 +958,17 @@ func ExampleDict() {
}

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

func ExampleDictFunc() {
c := Id("a").Op(":=").Map(String()).String().DictFunc(func(m map[Code]Code) {
m[Lit("a")] = Lit("b")
m[Lit("c")] = Lit("d")
})
c := Id("a").Op(":=").Map(String()).String().Values(DictFunc(func(d Dict) {
d[Lit("a")] = Lit("b")
d[Lit("c")] = Lit("d")
}))
fmt.Printf("%#v", c)
// Output:
// a := map[string]string{
Expand Down
5 changes: 5 additions & 0 deletions jen/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ func (g *Group) renderItems(f *File, w io.Writer) (isNull bool, err error) {
// 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 == "\n" {
// For blocks separated with new lines, we always insert a new line
// before the first item (but only if there is an item).
Expand Down
50 changes: 43 additions & 7 deletions jen/jen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,21 +179,57 @@ var cases = []tc{
expect: `a(b, c)`,
},
{
desc: `map literal`,
code: Id("a").Dict(map[Code]Code{
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`,
code: Id("a").DictFunc(func(m map[Code]Code) {
m[Id("b")] = Id("c")
}),
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,
}`,
},
{
Expand All @@ -220,7 +256,7 @@ var cases = []tc{
},
{
desc: `dict should be ordered`,
code: Map(String()).Int().Dict(map[Code]Code{Id("z"): Lit(1), Id("a"): Lit(2)}),
code: Map(String()).Int().Values(Dict{Id("z"): Lit(1), Id("a"): Lit(2)}),
expect: `map[string]int{
a:2,
z:1,
Expand Down
Loading

0 comments on commit 679d504

Please sign in to comment.