From 2c325e3c761380fffd02be8b0d6fa83ff7c63b8a Mon Sep 17 00:00:00 2001 From: BenTrapani Date: Fri, 29 Jul 2016 15:28:51 -0400 Subject: [PATCH 1/5] Add logic to convert camel case to snake case --- utils.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 utils.go diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..c5db63c --- /dev/null +++ b/utils.go @@ -0,0 +1,18 @@ +package main + +import "unicode" + +func ToSnake(in string) string { + runes := []rune(in) + length := len(runes) + + var out []rune + for i := 0; i < length; i++ { + if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) { + out = append(out, '_') + } + out = append(out, unicode.ToLower(runes[i])) + } + + return string(out) +} From 67c61e436f4bf250b03091496d05965ae91c1a3c Mon Sep 17 00:00:00 2001 From: BenTrapani Date: Fri, 29 Jul 2016 15:29:21 -0400 Subject: [PATCH 2/5] Create map to hold both cases and populate --- jsonenums.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/jsonenums.go b/jsonenums.go index 60346ee..26ad50b 100644 --- a/jsonenums.go +++ b/jsonenums.go @@ -75,7 +75,7 @@ import ( "path/filepath" "strings" - "github.com/campoy/jsonenums/parser" + "github.com/BenjaminTrapani/jsonenums/parser" ) var ( @@ -84,6 +84,11 @@ var ( outputSuffix = flag.String("suffix", "_jsonenums", "suffix to be added to the output file") ) +type CammelSnakePair struct { + CammelRep string + SnakeRep string +} + func main() { flag.Parse() if len(*typeNames) == 0 { @@ -112,11 +117,11 @@ func main() { var analysis = struct { Command string PackageName string - TypesAndValues map[string][]string + TypesAndValues map[string][]CammelSnakePair }{ Command: strings.Join(os.Args[1:], " "), PackageName: pkg.Name, - TypesAndValues: make(map[string][]string), + TypesAndValues: make(map[string][]CammelSnakePair), } // Run generate for each type. @@ -125,7 +130,14 @@ func main() { if err != nil { log.Fatalf("finding values for type %v: %v", typeName, err) } - analysis.TypesAndValues[typeName] = values + + cammelSnakePairs := make([]CammelSnakePair, len(values)) + for i, value := range values { + cammelSnakePairs[i].CammelRep = value + cammelSnakePairs[i].SnakeRep = ToSnake(value) + } + + analysis.TypesAndValues[typeName] = cammelSnakePairs var buf bytes.Buffer if err := generatedTmpl.Execute(&buf, analysis); err != nil { From 8ec93328be10f3ec5e3c7ca8d4f63dc1a0381d3a Mon Sep 17 00:00:00 2001 From: BenTrapani Date: Fri, 29 Jul 2016 15:29:41 -0400 Subject: [PATCH 3/5] Update template to include DB functions and correct json format as per snake case names --- template.go | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/template.go b/template.go index 7c14f0c..7c09544 100644 --- a/template.go +++ b/template.go @@ -14,20 +14,23 @@ var generatedTmpl = template.Must(template.New("generated").Parse(` package {{.PackageName}} import ( + "database/sql/driver" "encoding/json" "fmt" + + "github.com/pkg/errors" ) {{range $typename, $values := .TypesAndValues}} var ( _{{$typename}}NameToValue = map[string]{{$typename}} { - {{range $values}}"{{.}}": {{.}}, + {{range $values}}"{{.SnakeRep}}": {{.CammelRep}}, {{end}} } _{{$typename}}ValueToName = map[{{$typename}}]string { - {{range $values}}{{.}}: "{{.}}", + {{range $values}}{{.CammelRep}}: "{{.SnakeRep}}", {{end}} } ) @@ -36,7 +39,7 @@ func init() { var v {{$typename}} if _, ok := interface{}(v).(fmt.Stringer); ok { _{{$typename}}NameToValue = map[string]{{$typename}} { - {{range $values}}interface{}({{.}}).(fmt.Stringer).String(): {{.}}, + {{range $values}}interface{}({{.CammelRep}}).(fmt.Stringer).String(): {{.CammelRep}}, {{end}} } } @@ -68,5 +71,21 @@ func (r *{{$typename}}) UnmarshalJSON(data []byte) error { return nil } +//Scan an input string into this structure for use with GORP +func (r *{{$typename}}) Scan(i interface{}) error { + switch i.(type) { + case string: + r.UnmarshalJSON([]byte(i.(string))) + default: + return errors.Errorf("Can't scan %T into type %T", i, r) + } + return nil +} + +func (r {{$typename}}) Value() (driver.Value, error) { + bytes, err := r.MarshalJSON() + return string(bytes), err +} + {{end}} `)) From 28b78f44818e43fb2c848ce062da36878b61e982 Mon Sep 17 00:00:00 2001 From: BenTrapani Date: Fri, 29 Jul 2016 15:31:29 -0400 Subject: [PATCH 4/5] Add test struct to verify naming works as expected --- example/shirtsize.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/example/shirtsize.go b/example/shirtsize.go index 58bb247..c2212ec 100644 --- a/example/shirtsize.go +++ b/example/shirtsize.go @@ -12,6 +12,14 @@ import ( "strings" ) +type TestCasing int + +const ( + caseMadnessA TestCasing = iota + caseMaDnEEsB + normalCaseExample +) + //go:generate jsonenums -type=ShirtSize type ShirtSize byte From 465b153cfd1ac854f1c7c8bb34a343a41fafe67b Mon Sep 17 00:00:00 2001 From: BenTrapani Date: Fri, 29 Jul 2016 15:31:48 -0400 Subject: [PATCH 5/5] Sample generated output from example struct --- example/testcasing_jsonenums.go | 78 +++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 example/testcasing_jsonenums.go diff --git a/example/testcasing_jsonenums.go b/example/testcasing_jsonenums.go new file mode 100644 index 0000000..0c8d501 --- /dev/null +++ b/example/testcasing_jsonenums.go @@ -0,0 +1,78 @@ +// generated by jsonenums -type=TestCasing; DO NOT EDIT + +package main + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + + "github.com/pkg/errors" +) + +var ( + _TestCasingNameToValue = map[string]TestCasing{ + "case_madness_a": caseMadnessA, + "case_ma_dn_e_es_b": caseMaDnEEsB, + "normal_case_example": normalCaseExample, + } + + _TestCasingValueToName = map[TestCasing]string{ + caseMadnessA: "case_madness_a", + caseMaDnEEsB: "case_ma_dn_e_es_b", + normalCaseExample: "normal_case_example", + } +) + +func init() { + var v TestCasing + if _, ok := interface{}(v).(fmt.Stringer); ok { + _TestCasingNameToValue = map[string]TestCasing{ + interface{}(caseMadnessA).(fmt.Stringer).String(): caseMadnessA, + interface{}(caseMaDnEEsB).(fmt.Stringer).String(): caseMaDnEEsB, + interface{}(normalCaseExample).(fmt.Stringer).String(): normalCaseExample, + } + } +} + +// MarshalJSON is generated so TestCasing satisfies json.Marshaler. +func (r TestCasing) MarshalJSON() ([]byte, error) { + if s, ok := interface{}(r).(fmt.Stringer); ok { + return json.Marshal(s.String()) + } + s, ok := _TestCasingValueToName[r] + if !ok { + return nil, fmt.Errorf("invalid TestCasing: %d", r) + } + return json.Marshal(s) +} + +// UnmarshalJSON is generated so TestCasing satisfies json.Unmarshaler. +func (r *TestCasing) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("TestCasing should be a string, got %s", data) + } + v, ok := _TestCasingNameToValue[s] + if !ok { + return fmt.Errorf("invalid TestCasing %q", s) + } + *r = v + return nil +} + +//Scan an input string into this structure for use with GORP +func (r *TestCasing) Scan(i interface{}) error { + switch i.(type) { + case string: + r.UnmarshalJSON([]byte(i.(string))) + default: + return errors.Errorf("Can't scan %T into type %T", i, r) + } + return nil +} + +func (r TestCasing) Value() (driver.Value, error) { + bytes, err := r.MarshalJSON() + return string(bytes), err +}