diff --git a/generator/template/sql_builder_template.go b/generator/template/sql_builder_template.go index 869b8e51..cf7e121d 100644 --- a/generator/template/sql_builder_template.go +++ b/generator/template/sql_builder_template.go @@ -68,11 +68,13 @@ type ViewSQLBuilder = TableSQLBuilder // DefaultTableSQLBuilder returns default implementation for TableSQLBuilder func DefaultTableSQLBuilder(tableMetaData metadata.Table) TableSQLBuilder { + tableNameGoIdentifier := dbidentifier.ToGoIdentifier(tableMetaData.Name) + return TableSQLBuilder{ Path: "/table", FileName: dbidentifier.ToGoFileName(tableMetaData.Name), - InstanceName: dbidentifier.ToGoIdentifier(tableMetaData.Name), - TypeName: dbidentifier.ToGoIdentifier(tableMetaData.Name) + "Table", + InstanceName: tableNameGoIdentifier, + TypeName: tableNameGoIdentifier + "Table", DefaultAlias: "", Column: DefaultTableSQLBuilderColumn, } diff --git a/internal/utils/dbidentifier/dbidentifier.go b/internal/utils/dbidentifier/dbidentifier.go index c4278f6d..7aee4a97 100644 --- a/internal/utils/dbidentifier/dbidentifier.go +++ b/internal/utils/dbidentifier/dbidentifier.go @@ -3,6 +3,7 @@ package dbidentifier import ( "github.com/go-jet/jet/v2/internal/3rdparty/snaker" "strings" + "unicode" ) // ToGoIdentifier converts database identifier to Go identifier. @@ -15,10 +16,92 @@ func ToGoFileName(databaseIdentifier string) string { return strings.ToLower(replaceInvalidChars(databaseIdentifier)) } -func replaceInvalidChars(str string) string { - str = strings.Replace(str, " ", "_", -1) - str = strings.Replace(str, "-", "_", -1) - str = strings.Replace(str, ".", "_", -1) +func replaceInvalidChars(identifier string) string { + increase, needs := needsCharReplacement(identifier) - return str + if !needs { + return identifier + } + + var b strings.Builder + + b.Grow(len(identifier) + increase) + + for _, c := range identifier { + switch { + case unicode.IsSpace(c): + b.WriteByte('_') + case unicode.IsControl(c): + continue + default: + replacement, ok := asciiCharacterReplacement[c] + + if ok { + b.WriteByte('_') + b.WriteString(replacement) + b.WriteByte('_') + } else { + b.WriteRune(c) + } + } + + } + + return b.String() +} + +func needsCharReplacement(identifier string) (increase int, needs bool) { + for _, c := range identifier { + switch { + case unicode.IsSpace(c): + needs = true + case unicode.IsControl(c): + increase += -1 + needs = true + continue + default: + replacement, ok := asciiCharacterReplacement[c] + + if ok { + increase += len(replacement) + 1 + needs = true + } + } + } + + return increase, needs +} + +var asciiCharacterReplacement = map[rune]string{ + '!': "exclamation", + '"': "quotation", + '#': "number", + '$': "dollar", + '%': "percent", + '&': "ampersand", + '\'': "apostrophe", + '(': "opening_parentheses", + ')': "closing_parentheses", + '*': "asterisk", + '+': "plus", + ',': "comma", + '-': "_", + '.': "_", + '/': "slash", + ':': "colon", + ';': "semicolon", + '<': "less", + '=': "equal", + '>': "greater", + '?': "question", + '@': "at", + '[': "opening_bracket", + '\\': "backslash", + ']': "closing_bracket", + '^': "caret", + '`': "accent", + '{': "opening_braces", + '|': "vertical_bar", + '}': "closing_braces", + '~': "tilde", } diff --git a/internal/utils/dbidentifier/dbidentifier_test.go b/internal/utils/dbidentifier/dbidentifier_test.go index 339fb5a3..668bf564 100644 --- a/internal/utils/dbidentifier/dbidentifier_test.go +++ b/internal/utils/dbidentifier/dbidentifier_test.go @@ -8,6 +8,7 @@ import ( func TestToGoIdentifier(t *testing.T) { require.Equal(t, ToGoIdentifier(""), "") require.Equal(t, ToGoIdentifier("uuid"), "UUID") + require.Equal(t, ToGoIdentifier("uuid_ptr"), "UUIDPtr") require.Equal(t, ToGoIdentifier("col1"), "Col1") require.Equal(t, ToGoIdentifier("PG-13"), "Pg13") require.Equal(t, ToGoIdentifier("13_pg"), "13Pg") @@ -18,8 +19,50 @@ func TestToGoIdentifier(t *testing.T) { require.Equal(t, ToGoIdentifier("myTaBlE"), "MyTaBlE") require.Equal(t, ToGoIdentifier("my_table"), "MyTable") + require.Equal(t, ToGoIdentifier("my_____table"), "MyTable") require.Equal(t, ToGoIdentifier("MY_TABLE"), "MyTable") require.Equal(t, ToGoIdentifier("My_Table"), "MyTable") require.Equal(t, ToGoIdentifier("My Table"), "MyTable") require.Equal(t, ToGoIdentifier("My-Table"), "MyTable") + + require.Equal(t, ToGoIdentifier("EN\bUM"), "Enum") // control character + require.Equal(t, ToGoIdentifier("EN\tUM"), "EnUm") // space character + require.Equal(t, ToGoIdentifier("S3:INIT"), "S3ColonInit") // replacement chars + require.Equal(t, ToGoIdentifier("Entity-"), "Entity") + require.Equal(t, ToGoIdentifier("Entity+"), "EntityPlus") + require.Equal(t, ToGoIdentifier("="), "Equal") + require.Equal(t, ToGoIdentifier("<="), "LessEqual") + require.Equal(t, ToGoIdentifier(">="), "GreaterEqual") + require.Equal(t, ToGoIdentifier("some#$%name"), "SomeNumberDollarPercentName") + require.Equal(t, ToGoIdentifier(`An!"them`), "AnExclamationQuotationThem") + require.Equal(t, ToGoIdentifier(`An(Um)`), + "AnOpeningParenthesesUmClosingParentheses") +} + +func TestNeedsCharReplacement(t *testing.T) { + increase, needs := needsCharReplacement("some_name") + require.False(t, needs) + require.Zero(t, increase) + + increase, needs = needsCharReplacement("some name") + require.True(t, needs) + require.Zero(t, increase) + + increase, needs = needsCharReplacement("some\bname") + require.True(t, needs) + require.Equal(t, increase, -1) + + increase, needs = needsCharReplacement("some#$%name") + require.True(t, needs) + require.Equal(t, increase, 22) +} + +func TestToGoFileName(t *testing.T) { + require.Equal(t, ToGoFileName("FileName"), "filename") + require.Equal(t, ToGoFileName("File_Name"), "file_name") + require.Equal(t, ToGoFileName("File___Name__"), "file___name__") + require.Equal(t, ToGoFileName("File___Name__"), "file___name__") + require.Equal(t, ToGoFileName("File\bName"), "filename") + require.Equal(t, ToGoFileName("File\tName"), "file_name") + require.Equal(t, ToGoFileName("File^^Name"), "file_caret__caret_name") }