Skip to content

Commit

Permalink
make sure all identifiers are valid (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
marhaupe committed Jul 30, 2022
1 parent c64a884 commit c8c08f1
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 35 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ go 1.12
require (
github.com/dave/jennifer v1.3.0
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect
golang.org/x/text v0.3.7
)
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ github.com/dave/jennifer v1.3.0 h1:p3tl41zjjCZTNBytMwrUuiAnherNUZktlhPTKoF/sEk=
github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
56 changes: 48 additions & 8 deletions pkg/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import (
"errors"
"fmt"
"sort"
"strconv"
"strings"
"unicode"

"github.com/marhaupe/json2struct/pkg/parse"
"golang.org/x/text/cases"
"golang.org/x/text/language"

"github.com/dave/jennifer/jen"
)
Expand Down Expand Up @@ -247,20 +249,58 @@ func mergeObjects(children []*parse.ObjectNode) *parse.ObjectNode {
}
}

var caser = cases.Title(language.English)

func makeVarname(varname string) *jen.Statement {
upperCaseVarname := strings.Title(strings.ToLower(varname))
upperCaseVarname := caser.String(strings.ToLower(varname))

// Maybe we can find a shortcut? This is getting a bit computation heavy
if isValid, validIdentifier := identifierIsValid(varname); !isValid {
upperCaseVarname = validIdentifier
fmt.Println("Invalid identifier found. We cleaned that up for you, but you might want to double check if you're happy with our naming: ", validIdentifier)
}

// Numbers are not a valid identifier.
if isNumber(varname) {
upperCaseVarname = "Number" + upperCaseVarname
if identifierIsPredeclared(varname) {
upperCaseVarname = "_" + upperCaseVarname
fmt.Println("Predeclared identifier found. We cleaned that up for you, but you might want to double check if you're happy with our naming: ", upperCaseVarname)
}

return jen.Id(upperCaseVarname)
}

func isNumber(varname string) bool {
_, err := strconv.ParseFloat(varname, 64)
return err == nil
// Valid identifiers are specified here: https://go.dev/ref/spec#Identifiers.
// Summarized we can say:
// identifier = letter { letter | unicode_digit } .
// letter = unicode_letter | "_" .
// unicode_letter = /* a Unicode code point classified as "Letter" */ .
// unicode_digit = /* a Unicode code point classified as "Number, decimal digit" */ .
func identifierIsValid(identifier string) (bool, string) {
characters := []rune(identifier)
if !isLetter(characters[0]) {
characters[0] = '_'
}
for i, char := range characters[1:] {
if !(isLetter(char) || unicode.IsDigit(char)) {
characters[i] = '_'
}
}
validIdentifier := string(characters)
return validIdentifier == identifier, validIdentifier
}

func isLetter(letter rune) bool {
return unicode.IsLetter(letter) || letter == '_'
}

var predeclaredIdentifiers = []string{"any", "bool", "byte", "comparable", "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"}

func identifierIsPredeclared(identifier string) bool {
for _, predeclared := range predeclaredIdentifiers {
if identifier == predeclared {
return true
}
}
return false
}

// addJSONTag adds the json-tag, e.g. `json:"title"`. This has to match the original varname from the json file
Expand Down
63 changes: 40 additions & 23 deletions pkg/generator/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,65 @@ import (
"path"
"strings"
"testing"

"github.com/kylelemons/godebug/diff"
)

func Test_isNumber(t *testing.T) {
func Test_identifierIsValid(t *testing.T) {
type args struct {
varname string
}
tests := []struct {
name string
args args
want bool
name string
args args
wantIsValid bool
wantCleanedIdentifier string
}{
{
name: "floating",
args: args{"1.1"},
want: true,
name: "floating",
args: args{"1.1"},
wantIsValid: false,
},
{
name: "negative floating",
args: args{"-1.1"},
wantIsValid: false,
},
{
name: "int",
args: args{"1"},
wantIsValid: false,
},
{
name: "negative int",
args: args{"-1"},
wantIsValid: false,
},
{
name: "negative floating",
args: args{"-1.1"},
want: true,
name: "$",
args: args{"$test"},
wantIsValid: false,
},
{
name: "int",
args: args{"1"},
want: true,
name: "only letters",
args: args{"xyz"},
wantIsValid: true,
},
{
name: "negative int",
args: args{"-1"},
want: true,
name: "underscore",
args: args{"_test"},
wantIsValid: true,
},
{
name: "not number",
args: args{"xyz"},
want: false,
name: "invalid character in the middle",
args: args{"_$test"},
wantIsValid: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isNumber(tt.args.varname); got != tt.want {
t.Errorf("isNumber() = %v, want %v", got, tt.want)
if got, _ := identifierIsValid(tt.args.varname); got != tt.wantIsValid {
t.Errorf("identifierIsValid() = %v, want %v", got, tt.wantIsValid)
}
})
}
Expand Down Expand Up @@ -77,8 +95,7 @@ func TestFiles(t *testing.T) {
actual = string(formatActualBytes)

if actual != expected {
t.Errorf("Test failed. Filename: %v\nActual: %v\nActualLen: %v\nExpected: %v\nExpectedLen: %v\n",
filename, actual, len(actual), expected, len(expected))
t.Errorf("Test failed. \nFilename: %v \nDiff: \n\n%v", filename, diff.Diff(actual, expected))
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/generator/testdata/11_expected
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package generated
package generated

type JSONToStruct struct{
Number1234 string `json:"1234"`
}
type JSONToStruct struct {
_234 string `json:"1234"`
}
26 changes: 26 additions & 0 deletions pkg/generator/testdata/with_ invalid_characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"text": "Hi <at>John Doe</at>"
}
],
"$schema": "https://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0",
"msteams": {
"entities": [
{
"type": "mention",
"text": "<at>John Doe</at>",
"mentioned": {
"id": "29:123124124124",
"name": "John Doe"
}
}
]
}
}
}
24 changes: 24 additions & 0 deletions pkg/generator/testdata/with_ invalid_characters_expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package generated

type JSONToStruct struct {
Content struct {
_schema string `json:"$schema"`
Body []struct {
Text string `json:"text"`
Type string `json:"type"`
} `json:"body"`
Msteams struct {
Entities []struct {
Mentioned struct {
Id string `json:"id"`
Name string `json:"name"`
} `json:"mentioned"`
Text string `json:"text"`
Type string `json:"type"`
} `json:"entities"`
} `json:"msteams"`
Type string `json:"type"`
Version string `json:"version"`
} `json:"content"`
Contenttype string `json:"contentType"`
}

0 comments on commit c8c08f1

Please sign in to comment.