Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions internal/confgen/document_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"strings"

"github.com/tableauio/tableau/internal/importer/book"
"github.com/tableauio/tableau/internal/strcase"
"github.com/tableauio/tableau/internal/types"
"github.com/tableauio/tableau/internal/x/xerrors"
"github.com/tableauio/tableau/internal/x/xproto"
Expand Down Expand Up @@ -438,7 +437,7 @@ func (p *documentParser) parseUnionMessage(field *Field, msg protoreflect.Messag
}

// parse union type
typeNodeName := strcase.FromContext(p.ctx).ToCamel(unionDesc.TypeName())
typeNodeName := unionDesc.TypeName()
typeNode := node.FindChild(typeNodeName)
if typeNode == nil && xproto.GetFieldDefaultValue(unionDesc.Type) != "" {
// if this field has a default value, use virtual node
Expand Down
3 changes: 1 addition & 2 deletions internal/confgen/table_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/tableauio/tableau/internal/confgen/fieldprop"
"github.com/tableauio/tableau/internal/importer/book"
"github.com/tableauio/tableau/internal/importer/book/tableparser"
"github.com/tableauio/tableau/internal/strcase"
"github.com/tableauio/tableau/internal/types"
"github.com/tableauio/tableau/internal/x/xerrors"
"github.com/tableauio/tableau/internal/x/xproto"
Expand Down Expand Up @@ -756,7 +755,7 @@ func (p *tableParser) parseUnionMessage(msg protoreflect.Message, field *Field,
}

// parse union type
typeColName := prefix + strcase.FromContext(p.ctx).ToCamel(unionDesc.TypeName())
typeColName := prefix + unionDesc.TypeName()
cell, err := r.Cell(typeColName, p.IsFieldOptional(field))
if err != nil {
return false, err
Expand Down
29 changes: 14 additions & 15 deletions internal/protogen/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

type bookExporter struct {
ProtoPackage string
Edition string
Edition int
ProtoFileOptions map[string]string
OutputDir string
FilenameSuffix string
Expand All @@ -37,7 +37,7 @@ type bookExporter struct {
messagerPatternRegexp *regexp.Regexp
}

func newBookExporter(protoPackage string, edition string, protoFileOptions map[string]string, outputDir, filenameSuffix string, wb *internalpb.Workbook, gen *Generator) *bookExporter {
func newBookExporter(protoPackage string, edition int, protoFileOptions map[string]string, outputDir, filenameSuffix string, wb *internalpb.Workbook, gen *Generator) *bookExporter {
return &bookExporter{
ProtoPackage: protoPackage,
Edition: edition,
Expand All @@ -60,7 +60,7 @@ func (x *bookExporter) export(checkProtoFileConflicts bool) error {
p1.P("// Code generated by tableau (protogen v", Version, "). DO NOT EDIT.")
p1.P("// clang-format off")
p1.P("")
if x.Edition != "" {
if x.Edition != 0 {
p1.P(`edition = "`, x.Edition, `";`)
} else {
p1.P(`syntax = "proto3";`)
Expand Down Expand Up @@ -301,18 +301,17 @@ func (x *sheetExporter) exportUnion() error {
if field.Number == 0 {
continue
}
ename := "TYPE_" + strcase.FromContext(x.be.gen.ctx).ToScreamingSnake(field.Name)
typ := field.Name
fullTypeName := field.FullType
if fullTypeName != "" {
typ = fullTypeName
ename := strcase.FromContext(x.be.gen.ctx).EnumValue("Type", field.Name)
typ := field.FullType
if typ == "" {
typ = strcase.FromContext(x.be.gen.ctx).ToCamel(field.Name)
}
// Add import for predefined types (e.g. protoconf.FruitType, protoconf.Item).
if field.Predefined {
if types.IsWellKnownMessage(fullTypeName) {
importPath := types.GetWellKnownMessageImport(fullTypeName)
if types.IsWellKnownMessage(typ) {
importPath := types.GetWellKnownMessageImport(typ)
x.Imports[importPath] = true
} else if typeInfo := x.typeInfos.GetByFullName(protoreflect.FullName(fullTypeName)); typeInfo != nil &&
} else if typeInfo := x.typeInfos.GetByFullName(protoreflect.FullName(typ)); typeInfo != nil &&
typeInfo.ParentFilename != x.be.GetProtoFilePath() {
x.Imports[typeInfo.ParentFilename] = true
}
Expand All @@ -333,7 +332,7 @@ func (x *sheetExporter) exportUnion() error {
x.p.P(" TYPE_INVALID = 0;")
}
for _, field := range x.ws.Fields {
ename := "TYPE_" + strcase.FromContext(x.be.gen.ctx).ToScreamingSnake(field.Name)
ename := strcase.FromContext(x.be.gen.ctx).EnumValue("Type", field.Name)
note := ""
if field.Alias != "" {
note = " // " + field.Alias
Expand All @@ -349,9 +348,9 @@ func (x *sheetExporter) exportUnion() error {
if len(msgField.Fields) == 0 {
continue
}
typ := msgField.Name
if msgField.Type != "" {
typ = msgField.Type
typ := msgField.Type
if typ == "" {
typ = strcase.FromContext(x.be.gen.ctx).ToCamel(msgField.Name)
}
x.p.P(" message ", typ, " {")
// generate the fields
Expand Down
12 changes: 6 additions & 6 deletions internal/protogen/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func Test_bookExporter_GetProtoFilePath(t *testing.T) {
func Test_bookExporter_export(t *testing.T) {
tests := []struct {
name string
edition string
edition int
protoFileOptions map[string]string
wantContains []string
wantNotContains []string
Expand All @@ -311,7 +311,7 @@ func Test_bookExporter_export(t *testing.T) {
},
{
name: "edition-2023-with-features-utf8-validation",
edition: "2023",
edition: 2023,
protoFileOptions: map[string]string{
"go_package": `"github.com/example/protoconf"`,
"features.utf8_validation": "NONE",
Expand All @@ -327,7 +327,7 @@ func Test_bookExporter_export(t *testing.T) {
},
{
name: "edition-2024-with-features-strip-enum-prefix",
edition: "2024",
edition: 2024,
protoFileOptions: map[string]string{
"go_package": `"github.com/example/protoconf"`,
"features.(pb.go).strip_enum_prefix": "STRIP_ENUM_PREFIX_STRIP",
Expand All @@ -348,7 +348,7 @@ func Test_bookExporter_export(t *testing.T) {
},
{
name: "edition-2024-with-pb-cpp-features-infer-cpp-features-import",
edition: "2024",
edition: 2024,
protoFileOptions: map[string]string{
"go_package": `"github.com/example/protoconf"`,
"features.(pb.cpp).string_type": "VIEW",
Expand All @@ -365,7 +365,7 @@ func Test_bookExporter_export(t *testing.T) {
},
{
name: "edition-2024-with-pb-java-features-infer-java-features-import",
edition: "2024",
edition: 2024,
protoFileOptions: map[string]string{
"go_package": `"github.com/example/protoconf"`,
"features.(pb.java).utf8_validation": "VERIFY",
Expand All @@ -382,7 +382,7 @@ func Test_bookExporter_export(t *testing.T) {
},
{
name: "edition-2024-with-all-language-features-infer-all-imports",
edition: "2024",
edition: 2024,
protoFileOptions: map[string]string{
"go_package": `"github.com/example/protoconf"`,
"features.(pb.go).api_level": "API_OPAQUE",
Expand Down
12 changes: 11 additions & 1 deletion internal/protogen/protogen.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,17 @@ func NewGenerator(protoPackage, indir, outdir string, setters ...options.Option)

func NewGeneratorWithOptions(protoPackage, indir, outdir string, opts *options.Options) *Generator {
ctx := context.Background()
ctx = strcase.NewContext(ctx, strcase.New(opts.Acronyms))
// STYLE2024 naming is the default. Users can opt back into the legacy
// pre-STYLE2024 algorithm via ProtoInputOption.UseLegacyNamingStyle,
// EXCEPT when the requested edition itself mandates STYLE2024
// (>= EditionStyle2024); in that case the legacy flag is ignored.
useLegacy := opts.Proto.Input.UseLegacyNamingStyle &&
opts.Proto.Output.Edition < options.EditionStyle2024
if useLegacy {
ctx = strcase.NewContext(ctx, strcase.NewLegacy(opts.Acronyms))
} else {
ctx = strcase.NewContext(ctx, strcase.New(opts.Acronyms))
}
ctx = metasheet.NewContext(ctx, &metasheet.Metasheet{Name: opts.Proto.Input.MetasheetName})

gen := &Generator{
Expand Down
16 changes: 12 additions & 4 deletions internal/protogen/sheet_mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,23 @@ func parseEnumType(ws *internalpb.Worksheet, sheet *book.Sheet, parser book.Shee
if err := parser.Parse(desc, sheet); err != nil {
return err
}
prefix := strcase.FromContext(gen.ctx).ToScreamingSnake(ws.Name) + "_"
sc := strcase.FromContext(gen.ctx)
for i, value := range desc.Values {
number := int32(i + 1)
if value.Number != nil {
number = value.GetNumber()
}
name := value.Name
if gen.OutputOpt.EnumValueWithPrefix && !strings.HasPrefix(name, prefix) {
name = prefix + name
var name string
if sc.Legacy() {
// Legacy: the prefix is opt-in via EnumValueWithPrefix, matching
// the pre-STYLE2024 generator behavior.
name = value.Name
if gen.OutputOpt.EnumValueWithPrefix {
name = sc.EnumValue(ws.Name, value.Name)
}
} else {
// STYLE2024: the prefix is mandatory.
name = sc.EnumValue(ws.Name, value.Name)
}
field := &internalpb.Field{
Number: number,
Expand Down
99 changes: 63 additions & 36 deletions internal/strcase/README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,63 @@
# strcase

> NOTE: we learned a lot from package [strcase](https://github.com/iancoleman/strcase)

strcase is a go package for converting string case to various cases (e.g. [snake case](https://en.wikipedia.org/wiki/Snake_case) or [camel case](https://en.wikipedia.org/wiki/CamelCase)) to see the full conversion table below.

## Example

```go
s := "AnyKind of_string"
```

| Function | Result |
| ----------------------------------------- | -------------------- |
| `ToSnake(s)` | `any_kind_of_string` |
| `ToSnakeWithIgnore(s, '.')` | `any_kind.of_string` |
| `ToScreamingSnake(s)` | `ANY_KIND_OF_STRING` |
| `ToKebab(s)` | `any-kind-of-string` |
| `ToScreamingKebab(s)` | `ANY-KIND-OF-STRING` |
| `ToDelimited(s, '.')` | `any.kind.of.string` |
| `ToScreamingDelimited(s, '.', '', true)` | `ANY.KIND.OF.STRING` |
| `ToScreamingDelimited(s, '.', ' ', true)` | `ANY.KIND OF.STRING` |
| `ToCamel(s)` | `AnyKindOfString` |
| `ToLowerCamel(s)` | `anyKindOfString` |

## Custom Acronyms

Sometimes, text may contain specific acronyms which need to be handled in a certain way.

```go
// For "WebAPIV3Spec":
// - ToCamel: WebApiv3Spec
// - ToLowerCamel: webApiv3Spec
// - ToSnake: web_apiv3_spec
strcase.New(map[string]string{"APIV3": "apiv3"})
```
# strcase

> NOTE: we learned a lot from package [strcase](https://github.com/iancoleman/strcase)

strcase is a go package for converting string case to various cases (e.g. [snake case](https://en.wikipedia.org/wiki/Snake_case) or [PascalCase](https://en.wikipedia.org/wiki/Camel_case)) under [STYLE2024](https://protobuf.dev/programming-guides/style/) rules.

## Example

```go
s := "AnyKind of_string"
```

| Function | Result |
| --------------------- | -------------------- |
| `ToCamel(s)` | `AnyKindOfString` |
| `ToLowerCamel(s)` | `anyKindOfString` |
| `ToSnake(s)` | `any_kind_of_string` |
| `ToScreamingSnake(s)` | `ANY_KIND_OF_STRING` |

## STYLE2024 highlights

- An underscore is only allowed in front of a letter; therefore no
underscore is inserted at letter <-> digit boundaries
(e.g. `Tier1` -> `tier1`, NOT `tier_1`).
- Acronyms are treated as ordinary words
(e.g. `JSONData` -> `json_data`, `userID` -> `user_id`).

## Custom Acronyms

Sometimes, text may contain specific acronyms which need to be handled in a certain way.

```go
// For "WebAPIV3Spec":
// - ToCamel: WebApiv3Spec
// - ToSnake: web_apiv3_spec
strcase.New(map[string]string{"APIV3": "apiv3"})
```

## Legacy (pre-STYLE2024) Mode

Existing projects whose generated proto files were produced under the old
algorithm can opt back into it by constructing the engine with `NewLegacy`
instead of `New`. At the public tableau API level this is exposed as
`useLegacyNamingStyle: true` under the `proto.input` section of the user
`config.yaml`.

The flag is honored ONLY by protogen — confgen always parses input under
STYLE2024 rules. It is also force-disabled when the requested edition is
>= 2024 (`proto.output.edition: 2024`), because edition 2024 itself
mandates the STYLE2024 naming rules.

Behavioral differences vs STYLE2024:

- Underscores ARE inserted at letter <-> digit boundaries
(e.g. `Tier1` -> `tier_1`, `DeviceTier`/`1` -> `DEVICE_TIER_1`).
- `EnumValue` does NOT inject a leading `V` for digit-led suffixes.
`ProtoOutputOption.EnumValueWithPrefix` continues to gate prefixing at the
call site under legacy mode.
- Consecutive uppercase letters are folded to lower in camel form
(e.g. `HeroNTagMFcX` -> `HeroNtagMfcX`).

See the strcase tests for an exhaustive, side-by-side enumeration of every
divergence.
Loading
Loading