Help us shape the future of Go! Take the 2026 Go Developer Survey

naming

package
v1.0.14 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var CompoundWords = []string{
	"GitHub", "GitLab", "OAuth", "OpenAI", "OpenAPI", "OpenID", "OpenBao",
	"GraphQL", "PostgreSQL", "MySQL", "WebSocket", "WebAuthn",
	"JavaScript", "TypeScript", "YouTube", "LinkedIn", "PayPal",
}

CompoundWords are words conventionally written with an inner capital. Each is matched case-sensitively, as a whole word.

View Source
var GoInitialisms = []string{
	"ACL", "API", "ASCII", "CPU", "CSS", "DB", "DNS", "EOF", "GUID",
	"HTML", "HTTP", "HTTPS", "ID", "IO", "IP", "JSON", "JWT", "LHS",
	"LLM", "MCP", "QPS",
	"RAM", "RHS", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP",
	"TLS", "TTL", "UDP", "UI", "UID", "UUID", "URI", "URL",
	"UTF8", "VM", "XML", "XMPP", "XSRF", "XSS",
}

GoInitialisms are common Go initialisms that should be all-caps. This is the single source of truth — all packages must import from here.

View Source
var GoInitialismsMap = func() map[string]bool {
	m := make(map[string]bool, len(GoInitialisms))
	for _, v := range GoInitialisms {
		m[strings.ToLower(v)] = true
	}
	return m
}()

GoInitialismsMap provides O(1) lookup for initialism detection (lowercase keys).

View Source
var SnakeInitialisms = []string{
	"NATS", "OIDC", "SAML", "SSO", "MFA", "TOTP", "AWS", "GCP", "GCS", "GKE",
	"CLI", "SDK", "ORM", "CRD", "KV", "PR", "CI", "CD", "IDP", "JWKS",
}

SnakeInitialisms are initialisms the word split knows beyond GoInitialisms. They are kept apart because GoInitialisms also drives ToPascalCase's capitalisation of generated Go identifiers, which these must not change.

Functions

func ColumnGoName added in v0.1.45

func ColumnGoName(s string) string

ColumnGoName is the Go field name forge's generated ORM gives a column on a db.<Entity> struct: title-case each '_'/'-'-separated segment, then spell Go initialisms in capitals the way gofmt-era Go code does ("id" → "ID", "org_id" → "OrgID", "api_key" → "APIKey", "address_line_2" → "AddressLine2", "base64item" → "Base64item", "user_ids" → "UserIds" — a plural is not an initialism, as in staticcheck ST1003).

This is FORGE's rule for FORGE's types, and it is deliberately not protoc-gen-go's: the generated pb field over the same name can differ ("id" → Id, "base64item" → Base64Item, "address_line_2" → AddressLine_2). A conversion between the two structs must spell each side with its own rule — GoFieldName for the pb message, this for the db struct. It once claimed to match protoc-gen-go, and every pb field reference built from it broke on the first name with a digit in it.

It used to apply no initialisms ("Id", "UserId"), for no reason but that changing it renames ORM struct fields in every existing project; the store is Go, and Go spells these ID and UserID (reliant P5b, F12.6).

func ConnectPackage added in v0.1.45

func ConnectPackage(goPackageName string) string

ConnectPackage is the Go package protoc-gen-connect-go writes a file's services into: the file's Go package name plus the default package_suffix "connect" ("billingv1" → "billingv1connect").

func ConnectProcedureConst added in v0.1.45

func ConnectProcedureConst(protoService, protoMethod string) string

ConnectProcedureConst is the `<Service><Method>Procedure` constant protoc-gen-connect-go declares for one RPC — both halves GoNames.

func ConnectServiceGoName added in v0.1.45

func ConnectServiceGoName(protoService string) string

ConnectServiceGoName is the base protoc-gen-connect-go builds a service's identifiers from — the service's protogen GoName, i.e. GoCamelCase of the proto name. It declares `<base>Handler`, `Unimplemented<base>Handler`, `New<base>Handler`, `<base>Client` and `New<base>Client`: "Alphav1connectService" → Alphav1ConnectServiceHandler. Templates take this base and add the affix, exactly as the generator does.

The `…Name` constant is the one exception — see ConnectServiceNameConst.

func ConnectServiceNameConst added in v0.1.45

func ConnectServiceNameConst(protoService string) string

ConnectServiceNameConst is the constant protoc-gen-connect-go declares holding a service's fully-qualified name. Unlike every other identifier it declares, it is spelled from the RAW proto service name, not the GoName: "Alphav1connectService" → Alphav1connectServiceName (while the handler is Alphav1ConnectServiceHandler).

func EntityFieldName

func EntityFieldName(entityName string) string

EntityFieldName returns the snake_case proto FIELD name that a CRUD create/get/update response uses to carry a single entity — the snake_case of the entity's (PascalCase) message name. For a "ModuleConfig" entity this is "module_config"; protoc-gen-go then generates the Go field "ModuleConfig".

This is the SINGLE source of truth for the entity-carrying field name, shared by three sites that MUST agree or multi-word CRUD silently breaks:

  • the entity scaffolder (`forge scaffold entity`) emits `<Entity> <field> = 1;`,
  • the CRUD ops emitter references the Go form (ToProtoPascalCase of this),
  • the CRUD shape detector (validateCRUDShape) matches the response's observed snake_case field names against this.

The historical bug: the detector compared against strings.ToLower(Name) ("moduleconfig"), which matches neither the scaffolder's "module_config" nor the emitter's "ModuleConfig" — so every multi-word entity fell to the custom-read-shape stub and produced empty CRUD. Routing all three through this helper makes the concatenated-lowercase form impossible to reintroduce.

func EntityListFieldName

func EntityListFieldName(entityName string) string

EntityListFieldName returns the snake_case proto FIELD name that a CRUD list response uses to carry the repeated entity — the pluralized snake_case of the entity's message name. For "ModuleConfig" this is "module_configs". It is the plural companion to EntityFieldName; see that doc for why the derivation is centralized here.

func EsFieldName added in v0.1.45

func EsFieldName(protoFieldName string) string

EsFieldName is the property protoc-gen-es generates for a (non-oneof) message field — its localName: ProtoCamelCase, then the reserved property escape ("to_string" → "toString$"). Every TS reference to a generated message field (`item.fooBar`, `create(Schema, { fooBar })`) must use this.

func EsMethodName added in v0.1.45

func EsMethodName(protoMethodName string) string

EsMethodName is the localName protobuf-es gives an RPC — the key on a connect-es client (`client.listFoos`) and on a service's `method` map. It lower-cases the FIRST CHARACTER ONLY: "LLMChat" → "lLMChat", not "llmChat". A camel-caser that lowers a leading acronym run names a method the client does not have.

func FrontendServiceName added in v1.0.0

func FrontendServiceName(project, frontend string) string

FrontendServiceName is the OpenTelemetry service.name a web frontend's browser telemetry reports: "<project>-<frontend>", e.g. "shop-web".

It is the ONE derivation of that name. The frontend's generated src/lib/otel_gen.ts bakes it as SERVICE_NAME (the value the runtime sends), and the generated observability — the frontend dashboard's scope and the frontend alert defaults — filters on it, so the two cannot name different services. An earlier split had the dashboards derive "<project>-frontend" while a hand-wired runtime reported "<project>-<frontend>", and every frontend alert matched nothing.

func GoCamelCase added in v0.1.45

func GoCamelCase(s string) string

GoCamelCase is protoc-gen-go's camel-casing of a proto name into a Go identifier — a verbatim port of google.golang.org/protobuf's internal/strs.GoCamelCase, which protogen applies to every message, enum, field, oneof, service and method name. protoc-gen-connect-go builds its identifiers from the same result (see ConnectServiceGoName, and ConnectServiceNameConst for its one exception).

Words begin at an underscore or an upper-case letter, and a digit is a word of its own, so the letter AFTER a digit is capitalised:

base64item     → Base64Item     (forge's old rule said Base64item)
alphav1connect → Alphav1Connect
address_line_2 → AddressLine_2  (an underscore before a non-lowercase
                                 byte is kept)
_internal      → XInternal      (a leading underscore becomes X)
HTTPStatus     → HTTPStatus     (no initialism folding)

For a NESTED message or enum pass the name relative to its package with the dots kept ("Outer.Inner" → "Outer_Inner"): that is the string protogen camel-cases, and the dot handling below is what makes nested types come out joined by an underscore.

func GoFieldName added in v0.1.45

func GoFieldName(protoFieldName string, siblings []string) string

GoFieldName is GoFieldNames for the field called protoFieldName among siblings (the message's proto field names in declaration order). When protoFieldName is not among them — a caller that knows only the one field — it falls back to GoCamelCase, which is exact unless the field collides with a generated method or another field.

func GoFieldNames added in v0.1.45

func GoFieldNames(protoFieldNames []string) []string

GoFieldNames returns the Go struct field names protoc-gen-go generates for one message's fields, given every proto field name of that message in DECLARATION order.

It is GoCamelCase plus protogen's per-message conflict resolution: a field that would collide with a generated method (`descriptor` → Descriptor, `string` → String) or with an earlier field or its getter (`get_name` after `name`, whose getter is GetName) gets "_" appended until it is unique. Because that depends on the fields declared before it, a single field's Go name is only exact when computed from the whole list — which is why this takes the list rather than one name.

A real (non-synthetic) oneof also claims its own camel-cased name in the same table, after its first member. forge's generated surfaces never reference oneof groups; callers holding one should not assume this covers it.

func GoPackage

func GoPackage(name string) string

GoPackage normalises a CLI/forge.yaml-style name into a Go package identifier in snake_case form. Hyphens convert to underscores; existing underscores are preserved; PascalCase / camelCase boundaries are split (so "AdminServer" → "admin_server", "calibrator_refit" stays "calibrator_refit", "admin-server" → "admin_server"). Distinct from ServicePackage in that it does NOT strip a trailing "Service" — callers using it for workers, operators, and arbitrary package leaves want the raw form.

Snake_case is a valid Go package identifier (Go's spec allows it, `golint` only warns), and matches the on-disk convention proto buf emits for multi-word proto packages (e.g. proto package `services.admin_server.v1` → directory `services/admin_server/v1`). Keeping the package name aligned with the proto path means handler dirs, generated mock files, and wire_gen function names all stay in lockstep without forcing the user to choose between snake on disk and compact in code.

func GoTypeName added in v0.1.45

func GoTypeName(protoPkg, fullName string) string

GoTypeName is the Go identifier protoc-gen-go declares for a message or enum, given its fully-qualified proto name and the proto package of the file declaring it: "shop.v1.Order.Status" in "shop.v1" → "Order_Status". This is protogen's newGoIdent. A name outside protoPkg (or an empty package) is camel-cased whole, as protogen would for a package-less file.

func Humanize added in v0.1.45

func Humanize(s string) string

Humanize splits a PascalCase/camelCase/snake_case identifier into space-separated words, preserving each word's case: "RetainedDatabases" -> "Retained Databases". For human-facing UI copy only; never for identifiers.

func HumanizeLower added in v0.1.45

func HumanizeLower(s string) string

HumanizeLower is Humanize lowercased as a whole: "retained databases".

func KCLIdentifier added in v0.1.45

func KCLIdentifier(name string) string

KCLIdentifier returns the KCL variable name a workload is declared under in `deploy/kcl/workloads.k`.

The declaration is NAMED — `billing = fw.Workload {...}` rather than an anonymous list entry — because a name is what an environment refines: `wl.billing | {replicas = 3}`. An anonymous entry can only be reconstructed, which is the shape this vocabulary replaced.

KCL identifiers follow the same rules as Go's, so this is GoPackage: "billing-admin" -> billing_admin, "BillingService" -> billing_service. Kept as its own function rather than a call to GoPackage at each site so the INTENT is legible (this is a KCL binding, not a Go package) and so the two can diverge if KCL's identifier rules ever do.

func Pluralize

func Pluralize(s string) string

Pluralize returns the English plural form of a word using the inflection library.

func ProtoCamelCase added in v0.1.45

func ProtoCamelCase(snakeCase string) string

ProtoCamelCase is protobuf-es's protoCamelCase — the lowerCamelCase a field or oneof name takes in generated TypeScript (and protoc's default json_name). Unlike GoCamelCase it never capitalises after a digit and never keeps an underscore: "base64item" → "base64item", "address_line_2" → "addressLine2", "foo_bar" → "fooBar". The first letter keeps its case. Port of @bufbuild/protobuf reflect/names.ts.

func ProtoPackageBase

func ProtoPackageBase(protoPackage string) string

ProtoPackageBase returns the proto package with any trailing version segment stripped — the version-INDEPENDENT logical package identity.

"billing.v1"        -> "billing"
"acme.billing.v1"   -> "acme.billing"
"billing"           -> "billing"   (already unversioned)

Pairing ProtoPackageBase + ProtoPackageVersion splits a fused `billing.v1` identity into ("billing", "v1") so the inventory can record the two distinctly. v2 then differs ONLY in Version, sharing the base — the data-model precondition for additive multi-version support.

func ProtoPackageVersion

func ProtoPackageVersion(protoPackage string) string

ProtoPackageVersion extracts the proto API version from a fully-qualified proto package name — the LAST dotted segment when it has the protobuf version shape `v<major>` optionally suffixed with a stability channel (`v1`, `v2`, `v1alpha1`, `v2beta3`). Returns "" when the package carries no version segment.

"billing.v1"        -> "v1"
"acme.billing.v1"   -> "v1"
"shop.v2beta1"      -> "v2beta1"
"billing"           -> ""   (unversioned)
""                  -> ""

This is the single source of truth for the version metadata the generated service inventory records (FORGE_SHAPE_REDESIGN — version-aware registry seam). It deliberately reads ONLY the package's own last segment, never the service name, so the inventory's Version field is exactly the proto API version and additive: a future `billing.v2` records Version "v2" as a second version of the same logical service rather than colliding identity.

func ServiceHookFile

func ServiceHookFile(name string) string

ServiceHookFile returns the canonical frontend hook filename for a service. Encodes the rule that the hook file is the service name in kebab-case (with initialisms kept glued) suffixed with `-hooks_gen.ts`.

The `_gen` is the convention, not decoration: this file is Tier-1 — it is hash-guarded and rewritten on every `forge generate` — and forge's rule is now mechanical, so that a reader can tell what forge owns from the filename alone rather than by opening the file and reading its banner.

All file-emitter sites AND the re-export indexer must go through this function. If a future caller needs a different suffix or extension, extract a parameterised helper rather than duplicating the kebab transform — the re-export index only stays in lockstep with on-disk filenames because both go through the same canonical splitter (`ToKebabCase`).

func ServiceHookFileLegacy added in v0.1.45

func ServiceHookFileLegacy(name string) string

ServiceHookFileLegacy returns the PRE-`_gen` hook filename for a service.

It exists for exactly one caller: the retirement sweep that deletes the old copy out of a project generated before the rename. Nothing emits this name anymore.

func ServicePackage

func ServicePackage(name string) string

ServicePackage is the single canonical Go-package form for a service, binary, frontend, worker, or operator name.

Inputs accepted:

  • Forge / CLI names: kebab-case ("admin-server"), snake_case ("admin_server"), or already-compact ("api").
  • Proto service names: PascalCase ending in "Service" ("EchoService", "AdminServerService").

Output is always a single lowercase snake_case Go-style identifier. The PascalCase-with-"Service"-suffix branch first trims the suffix so "EchoService" -> "echo" and "AdminServerService" -> "admin_server". The pure-CLI branch normalises hyphens to underscores and PascalCase boundaries to underscores, so "admin-server" / "admin_server" / "AdminServer" all collapse to "admin_server".

One canonical function for every site that emits a handler dir, a generated mock file, a bootstrap import, or a wire alias — keep it the single source of truth so the on-disk dir layout, the codegen keys, and the cleanup sweeper can never disagree.

History: pre-2026-06-08 this function (and GoPackage) emitted compact form (separators stripped — "admin_server" → "adminserver"). The compact convention collided with the universal snake_case dir layout projects actually use (protoc-gen-go emits snake for multi-word proto packages, KCL package names use snake, every existing forge project on disk had snake handler dirs). The bug surface was a duplicate-dir failure where `forge generate` created `handlers/adminserver/` alongside the existing `handlers/admin_server/`, then regenerated wire_gen.go to reference the compact form while user-owned bootstrap.go still referenced the snake form — broken build.

func ToExportedFieldName

func ToExportedFieldName(pkg string) string

ToExportedFieldName converts a lowercase package/field name to an exported Go identifier, respecting Go initialisms. Examples: "api" -> "API", "db" -> "DB", "orders" -> "Orders"

func ToKebabCase

func ToKebabCase(s string) string

ToKebabCase converts a name to kebab-case, treating known initialisms (LLM, API, URL, JSON, etc. — see GoInitialisms) as a single segment so that "LLMGateway" → "llm-gateway" rather than "l-l-m-gateway".

Accepts input in PascalCase, camelCase, snake_case, or already-kebab- case form; the output is always lowercase, hyphen-separated, with no runs of multiple hyphens. This is the canonical kebab function for every site that emits filenames, slugs, route paths, or import paths that need to round-trip with proto Go names — keep it the single source of truth so frontend hooks files, navigation slugs, and re- export indexers can never disagree on whether "LLMGateway" splits as "l-l-m-gateway" or "llm-gateway".

func ToPascalCase

func ToPascalCase(s string) string

ToPascalCase converts a hyphenated, underscored, or camelCase name to PascalCase. It handles both '-' and '_' as word separators and capitalizes Go initialisms. e.g. "api-gateway" -> "APIGateway", "user_service" -> "UserService", "http_client" -> "HTTPClient".

func ToSnakeCase

func ToSnakeCase(s string) string

ToSnakeCase converts a string (camelCase, PascalCase, UPPER_CASE, etc.) to snake_case. e.g. "firstName" -> "first_name", "HTTPStatus" -> "http_status", "UPPER_CASE" -> "upper_case".

func ToSnakeCaseWords added in v1.0.0

func ToSnakeCaseWords(s string) string

ToSnakeCaseWords is ToSnakeCase for labels a person reads — graph node names in logs, errors, the readiness report and the start plan — where the split must follow the WORDS of a Go identifier, not just its case changes:

GitHubAccessLoop → github_access_loop   (ToSnakeCase: git_hub_access_loop)
OAuthClient      → oauth_client         (o_auth_client)
APIURL           → api_url              (apiurl)
LLMMCPBridge     → llm_mcp_bridge       (llmmcp_bridge)
UserIDs          → user_ids             (user_i_ds)

Three rules on top of ToSnakeCase's:

  • a word written with inner capitals (CompoundWords: GitHub, OAuth, OpenAI, …) is one word, with any digits that follow it (OAuth2);
  • a run of capitals that is a sequence of known initialisms (GoInitialisms, plus SnakeInitialisms) splits into them;
  • a known initialism's plural (IDs, URLs, APIs) is one word.

Every other identifier gets exactly ToSnakeCase's answer, so the only names that change are the ones these rules name.

It is deliberately NOT ToSnakeCase itself. ToSnakeCase names wire and schema things — proto fields, columns, entity fields — whose spelling is a compatibility surface; a label is not.

func TripsReviveVarNaming added in v0.1.46

func TripsReviveVarNaming(goIdent string) bool

TripsReviveVarNaming reports whether revive's var-naming rule would want a different spelling of goIdent (e.g. CreateApiKey → CreateAPIKey). Proto / connect-generated handler method names must match the Handler interface exactly, so scaffolded stubs keep the generator spelling and suppress the lint instead of renaming.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL