This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch main
in repository ego.
View the commit online.
commit b2d0b0fa3ed0ba64481d95dbd87cb9988a641e3b
Author: [email protected] <[email protected]>
AuthorDate: Sun Mar 8 22:53:06 2026 -0600
feat: add naming conversion functions for ego-gen code generator
Introduce pure Go utilities to map Eolian naming conventions to
idiomatic Go identifiers. These functions handle conversion of:
- Eolian class names to Go package names and type names
- Snake case properties to getter/setter pairs
- Comma-separated events to "On"-prefixed callback names
- Enum types to exported Go type names
Includes comprehensive table-driven test coverage for all conversion
functions across common naming patterns.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
cmd/ego-gen/naming.go | 115 +++++++++++++++++++++++++++++++++++++
cmd/ego-gen/naming_test.go | 139 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 254 insertions(+)
diff --git a/cmd/ego-gen/naming.go b/cmd/ego-gen/naming.go
new file mode 100644
index 0000000..7842303
--- /dev/null
+++ b/cmd/ego-gen/naming.go
@@ -0,0 +1,115 @@
+package main
+
+import (
+ "strings"
+ "unicode"
+)
+
+// ClassToPackage returns the Go package name derived from a fully-qualified
+// Eolian class name. For names with three or more dot-separated segments the
+// second segment (index 1) is returned lowercased. For two-segment names the
+// first segment is returned lowercased.
+//
+// Examples:
+//
+// "Efl.Ui.Button" → "ui"
+// "Efl.Loop" → "efl"
+func ClassToPackage(eolianName string) string {
+ parts := strings.Split(eolianName, ".")
+ if len(parts) >= 3 {
+ return strings.ToLower(parts[1])
+ }
+ // Two-part name such as "Efl.Loop" — use the first part.
+ return strings.ToLower(parts[0])
+}
+
+// ClassToGoName returns the exported Go type name derived from a
+// fully-qualified Eolian class name. The last dot-separated segment is taken
+// and any underscores are removed by converting the value to CamelCase via
+// SnakeToCamel.
+//
+// Examples:
+//
+// "Efl.Ui.Button" → "Button"
+// "Efl.Ui.Spin_Button" → "SpinButton"
+func ClassToGoName(eolianName string) string {
+ parts := strings.Split(eolianName, ".")
+ last := parts[len(parts)-1]
+ return SnakeToCamel(last)
+}
+
+// SnakeToCamel converts a snake_case identifier to CamelCase. Each underscore-
+// separated word is title-cased and the underscores are removed. An empty
+// string is returned unchanged.
+//
+// Examples:
+//
+// "text" → "Text"
+// "text_set" → "TextSet"
+// "autorepeat_enabled" → "AutorepeatEnabled"
+func SnakeToCamel(s string) string {
+ if s == "" {
+ return ""
+ }
+ words := strings.Split(s, "_")
+ var b strings.Builder
+ for _, w := range words {
+ if w == "" {
+ continue
+ }
+ runes := []rune(w)
+ runes[0] = unicode.ToUpper(runes[0])
+ b.WriteString(string(runes))
+ }
+ return b.String()
+}
+
+// PropertyToGetterSetter returns the idiomatic Go getter and setter method
+// names for an Eolian property name. The getter is the CamelCase form of the
+// property; the setter is "Set" prepended to the getter.
+//
+// Examples:
+//
+// "text" → ("Text", "SetText")
+// "autorepeat_enabled" → ("AutorepeatEnabled", "SetAutorepeatEnabled")
+func PropertyToGetterSetter(prop string) (getter, setter string) {
+ getter = SnakeToCamel(prop)
+ setter = "Set" + getter
+ return
+}
+
+// EventToGoName converts an Eolian event name to an idiomatic Go callback
+// method name. Eolian events use comma-separated words; each word is
+// title-cased and the result is prefixed with "On".
+//
+// Examples:
+//
+// "clicked" → "OnClicked"
+// "key,down" → "OnKeyDown"
+// "pointer,move" → "OnPointerMove"
+func EventToGoName(event string) string {
+ words := strings.Split(event, ",")
+ var b strings.Builder
+ b.WriteString("On")
+ for _, w := range words {
+ if w == "" {
+ continue
+ }
+ runes := []rune(w)
+ runes[0] = unicode.ToUpper(runes[0])
+ b.WriteString(string(runes))
+ }
+ return b.String()
+}
+
+// EnumTypeToGoName returns the exported Go type name for a fully-qualified
+// Eolian enum type name. It behaves identically to ClassToGoName: the last
+// dot-separated segment is taken and converted to CamelCase.
+//
+// Examples:
+//
+// "Efl.Ui.Dir" → "Dir"
+// "Efl.Gfx.Image_Scale_Type" → "ImageScaleType"
+func EnumTypeToGoName(eolianName string) string {
+ return ClassToGoName(eolianName)
+}
diff --git a/cmd/ego-gen/naming_test.go b/cmd/ego-gen/naming_test.go
new file mode 100644
index 0000000..ee1e2e1
--- /dev/null
+++ b/cmd/ego-gen/naming_test.go
@@ -0,0 +1,139 @@
+package main
+
+import (
+ "testing"
+)
+
+func TestClassToPackage(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ want string
+ }{
+ {"three parts", "Efl.Ui.Button", "ui"},
+ {"three parts canvas", "Efl.Canvas.Image", "canvas"},
+ {"three parts net", "Efl.Net.Server", "net"},
+ {"three parts io", "Efl.Io.Reader", "io"},
+ {"two parts loop", "Efl.Loop", "efl"},
+ {"two parts object", "Efl.Object", "efl"},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ got := ClassToPackage(tc.input)
+ if got != tc.want {
+ t.Errorf("ClassToPackage(%q) = %q, want %q", tc.input, got, tc.want)
+ }
+ })
+ }
+}
+
+func TestClassToGoName(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ want string
+ }{
+ {"simple button", "Efl.Ui.Button", "Button"},
+ {"snake case last segment", "Efl.Ui.Spin_Button", "SpinButton"},
+ {"two parts loop", "Efl.Loop", "Loop"},
+ {"two parts object", "Efl.Object", "Object"},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ got := ClassToGoName(tc.input)
+ if got != tc.want {
+ t.Errorf("ClassToGoName(%q) = %q, want %q", tc.input, got, tc.want)
+ }
+ })
+ }
+}
+
+func TestSnakeToCamel(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ want string
+ }{
+ {"single word", "text", "Text"},
+ {"two words", "text_set", "TextSet"},
+ {"three words", "autorepeat_enabled", "AutorepeatEnabled"},
+ {"empty string", "", ""},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ got := SnakeToCamel(tc.input)
+ if got != tc.want {
+ t.Errorf("SnakeToCamel(%q) = %q, want %q", tc.input, got, tc.want)
+ }
+ })
+ }
+}
+
+func TestPropertyToGetterSetter(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ wantGet string
+ wantSet string
+ }{
+ {"simple property", "text", "Text", "SetText"},
+ {"snake property", "autorepeat_enabled", "AutorepeatEnabled", "SetAutorepeatEnabled"},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ gotGet, gotSet := PropertyToGetterSetter(tc.input)
+ if gotGet != tc.wantGet {
+ t.Errorf("PropertyToGetterSetter(%q) getter = %q, want %q", tc.input, gotGet, tc.wantGet)
+ }
+ if gotSet != tc.wantSet {
+ t.Errorf("PropertyToGetterSetter(%q) setter = %q, want %q", tc.input, gotSet, tc.wantSet)
+ }
+ })
+ }
+}
+
+func TestEventToGoName(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ want string
+ }{
+ {"simple event", "clicked", "OnClicked"},
+ {"comma separated two words", "key,down", "OnKeyDown"},
+ {"pointer move", "pointer,move", "OnPointerMove"},
+ {"idle enter", "idle,enter", "OnIdleEnter"},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ got := EventToGoName(tc.input)
+ if got != tc.want {
+ t.Errorf("EventToGoName(%q) = %q, want %q", tc.input, got, tc.want)
+ }
+ })
+ }
+}
+
+func TestEnumTypeToGoName(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ want string
+ }{
+ {"simple enum", "Efl.Ui.Dir", "Dir"},
+ {"snake case last segment", "Efl.Gfx.Image_Scale_Type", "ImageScaleType"},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ got := EnumTypeToGoName(tc.input)
+ if got != tc.want {
+ t.Errorf("EnumTypeToGoName(%q) = %q, want %q", tc.input, got, tc.want)
+ }
+ })
+ }
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.