Documentation
¶
Overview ¶
Package scs (String Case Style) converts identifiers between naming conventions: camelCase, PascalCase, snake_case, kebab-case, SCREAMING_SNAKE_CASE, dot.case and Title Case.
Model ¶
Every conversion is built on one universal tokenizer. Split breaks any input into normalized words by honoring separators, case transitions and acronym boundaries; each style is then a different rendering of those words:
scs.ToSnake("HTTPServerID") // "http_server_id"
scs.ToCamel("user_id") // "userId"
scs.ToKebab("HelloWorld") // "hello-world"
Because a single tokenizer feeds every renderer, the converters are total: they never return an error and never need to know the input's original style. Any string maps to a well-defined result.
Initialisms ¶
By default words are Title-cased ("Id", "Url", "Http"), which always round-trips. To follow the Go convention of all-caps initialisms, build a Caser with preserved acronyms:
c := scs.New(scs.WithAcronyms("ID", "URL", "HTTP"))
c.ToPascal("user_id") // "UserID"
Numbers ¶
Digits attach to neighboring letters and never split a word on their own, so identifier fragments stay intact ("utf8", "sha256", "oauth2"). Only an explicit separator turns a number into its own word ("web 2 print" -> "web_2_print").
Thread safety ¶
All package-level functions and all *Caser methods are safe for concurrent use; a Caser is immutable once built.
Index ¶
- func Convert(to Style, s string) string
- func Is(style Style, s string) bool
- func IsCamel(s string) bool
- func IsDot(s string) bool
- func IsKebab(s string) bool
- func IsPascal(s string) bool
- func IsScreamingSnake(s string) bool
- func IsSentence(s string) bool
- func IsSnake(s string) bool
- func IsTitle(s string) bool
- func Split(s string) []string
- func ToCamel(s string) string
- func ToDot(s string) string
- func ToKebab(s string) string
- func ToPascal(s string) string
- func ToScreamingSnake(s string) string
- func ToSentence(s string) string
- func ToSnake(s string) string
- func ToTitle(s string) string
- func Words(s string) iter.Seq[string]
- type Caser
- func (c *Caser) Convert(to Style, s string) string
- func (c *Caser) ToCamel(s string) string
- func (c *Caser) ToDot(s string) string
- func (c *Caser) ToKebab(s string) string
- func (c *Caser) ToPascal(s string) string
- func (c *Caser) ToScreamingSnake(s string) string
- func (c *Caser) ToSentence(s string) string
- func (c *Caser) ToSnake(s string) string
- func (c *Caser) ToTitle(s string) string
- type Option
- type Style
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Convert ¶
Convert renders s in the requested style using the default configuration. An Unknown or out-of-range style returns s unchanged, so Convert is total and never panics.
scs.Convert(scs.Kebab, "userID") // "user-id"
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
for _, style := range []scs.Style{scs.Snake, scs.Kebab, scs.Camel} {
fmt.Printf("%-6s %s\n", style, scs.Convert(style, "HTTPServerID"))
}
}
Output: snake http_server_id kebab http-server-id camel httpServerId
func Is ¶
Is reports whether s is already canonical for the given style. An invalid style always reports false.
func IsCamel ¶
IsCamel reports whether s is already in canonical camelCase, i.e. it is non-empty and ToCamel leaves it unchanged.
Note that a single lowercase word such as "api" is canonical in several styles at once (camel, snake, kebab, dot); use Detect when you need a single unambiguous answer.
func IsScreamingSnake ¶
IsScreamingSnake reports whether s is already in canonical SCREAMING_SNAKE_CASE.
func IsSentence ¶
IsSentence reports whether s is already in canonical Sentence case.
func Split ¶
Split returns the normalized (lowercased) words of s, as detected by the package's universal tokenizer. It is the building block every converter shares: ToSnake(s) is the words joined by "_", ToCamel(s) is the words rendered camelCase, and so on.
The result is empty for an empty string or a string of separators only.
scs.Split("HTTPServerID") // ["http", "server", "id"]
scs.Split("user_id") // ["user", "id"]
scs.Split("web2print") // ["web2print"]
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
fmt.Println(scs.Split("HTTPServerID"))
fmt.Println(scs.Split("web2print"))
}
Output: [http server id] [web2print]
func ToCamel ¶
ToCamel converts s to camelCase using the default configuration.
scs.ToCamel("hello_world") // "helloWorld"
scs.ToCamel("HelloWorld") // "helloWorld"
scs.ToCamel("HTTP server") // "httpServer"
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
fmt.Println(scs.ToCamel("hello_world"))
fmt.Println(scs.ToCamel("HelloWorld"))
fmt.Println(scs.ToCamel("HTTP server"))
}
Output: helloWorld helloWorld httpServer
func ToDot ¶
ToDot converts s to dot.case.
scs.ToDot("helloWorld") // "hello.world"
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
fmt.Println(scs.ToDot("HelloWorld"))
}
Output: hello.world
func ToKebab ¶
ToKebab converts s to kebab-case.
scs.ToKebab("helloWorld") // "hello-world"
scs.ToKebab("HelloWorld") // "hello-world"
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
fmt.Println(scs.ToKebab("helloWorld"))
}
Output: hello-world
func ToPascal ¶
ToPascal converts s to PascalCase using the default configuration.
scs.ToPascal("hello_world") // "HelloWorld"
scs.ToPascal("helloWorld") // "HelloWorld"
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
fmt.Println(scs.ToPascal("hello-world"))
fmt.Println(scs.ToPascal("userID"))
}
Output: HelloWorld UserId
func ToScreamingSnake ¶
ToScreamingSnake converts s to SCREAMING_SNAKE_CASE, the usual style for constants and environment variables.
scs.ToScreamingSnake("helloWorld") // "HELLO_WORLD"
scs.ToScreamingSnake("userID") // "USER_ID"
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
fmt.Println(scs.ToScreamingSnake("userID"))
}
Output: USER_ID
func ToSentence ¶
ToSentence converts s to Sentence case (only the first word capitalized).
scs.ToSentence("hello_world") // "Hello world"
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
fmt.Println(scs.ToSentence("hello_world"))
fmt.Println(scs.ToSentence("parseHTTPResponse"))
}
Output: Hello world Parse http response
func ToSnake ¶
ToSnake converts s to snake_case.
scs.ToSnake("helloWorld") // "hello_world"
scs.ToSnake("HTTPServer") // "http_server"
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
fmt.Println(scs.ToSnake("helloWorld"))
fmt.Println(scs.ToSnake("HTTPServer"))
}
Output: hello_world http_server
func ToTitle ¶
ToTitle converts s to Title Case.
scs.ToTitle("hello_world") // "Hello World"
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
fmt.Println(scs.ToTitle("hello_world"))
}
Output: Hello World
func Words ¶
Words returns an iterator over the normalized words of s. It yields exactly the same tokens as Split without materializing a slice, which lets callers build custom renderings cheaply.
for w := range scs.Words("HTTPServerID") {
fmt.Println(w) // http, server, id
}
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
for w := range scs.Words("parseJSONResponse") {
fmt.Println(w)
}
}
Output: parse json response
Types ¶
type Caser ¶
type Caser struct {
// contains filtered or unexported fields
}
Caser is a reusable, immutable converter configuration. The package-level functions (ToCamel, ToSnake, ...) use a zero-configuration Caser that always renders Title-cased words; build a custom one with New when you need extra behavior such as preserved initialisms.
A Caser is safe for concurrent use by multiple goroutines: it is never mutated after New returns.
func New ¶
New returns a Caser configured by the given options. With no options it is equivalent to the package-level functions.
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
// Opt in to Go-style initialisms.
c := scs.New(scs.WithAcronyms("ID", "URL", "HTTP"))
fmt.Println(c.ToPascal("user_id"))
fmt.Println(c.ToCamel("http_url_builder"))
}
Output: UserID httpURLBuilder
func (*Caser) Convert ¶
Convert renders s in the requested style. An Unknown or out-of-range style returns s unchanged, so Convert is always total.
func (*Caser) ToScreamingSnake ¶
ToScreamingSnake renders s as SCREAMING_SNAKE_CASE.
func (*Caser) ToSentence ¶
ToSentence renders s as Sentence case: the first word is capitalized, the rest stay lowercase (initialisms excepted), joined by single spaces.
type Option ¶
type Option func(*Caser)
Option configures a Caser. Options are applied by New in order.
func WithAcronyms ¶
WithAcronyms makes the Caser render the given words as all-caps initialisms in the camelCase, PascalCase and Title styles, matching the Go convention of writing "ID", "URL" or "HTTP" instead of "Id", "Url" or "Http". Matching is case-insensitive on word boundaries detected by the tokenizer.
c := scs.New(scs.WithAcronyms("ID", "URL", "HTTP"))
c.ToPascal("user_id_url") // "UserIDURL"
c.ToCamel("http_server") // "httpServer" (first word stays lowercase)
Initialisms are an opt-in convenience, not the default, because adjacent all-caps acronyms cannot always be split back apart: "HTTPAPI" is ambiguous between [HTTP, API] and other partitions. The zero-configuration Caser keeps Title casing, which always round-trips.
type Style ¶
type Style uint8
Style enumerates the naming conventions the package can render to.
The zero value is Unknown and is never produced by a converter; it is returned by Detect when the input does not map unambiguously to a single style. Use Valid to test whether a Style names a real convention.
const ( // Unknown is the zero value: not a real style. Detect returns it for // inputs that match no style or match several at once. Unknown Style = iota // Camel is camelCase: words joined, first word lowercase, every // subsequent word capitalized (e.g. "helloWorld"). Camel // Pascal is PascalCase: words joined, every word capitalized including // the first (e.g. "HelloWorld"). Pascal // Snake is snake_case: lowercase words joined by underscores // (e.g. "hello_world"). Snake // Kebab is kebab-case: lowercase words joined by hyphens // (e.g. "hello-world"). Kebab // ScreamingSnake is SCREAMING_SNAKE_CASE: uppercase words joined by // underscores (e.g. "HELLO_WORLD"). Common for constants and env vars. ScreamingSnake // Dot is dot.case: lowercase words joined by dots (e.g. "hello.world"). Dot // Title is Title Case: capitalized words joined by single spaces // (e.g. "Hello World"). Title // Sentence is Sentence case: only the first word is capitalized, the rest // stay lowercase, joined by single spaces (e.g. "Hello world"). Sentence )
func Detect ¶
Detect returns the single style for which s is already canonical. If s is empty, matches no style, or matches more than one style (for example the bare word "api", which is valid camel, snake, kebab and dot at once), it returns Unknown and false.
scs.Detect("user_id") // Snake, true
scs.Detect("userId") // Camel, true
scs.Detect("USER_ID") // ScreamingSnake, true
scs.Detect("api") // Unknown, false (ambiguous)
scs.Detect("Hello_World") // Unknown, false (no canonical style)
The input is scanned once for its separator so that only the styles which could possibly match are probed; a string mixing two separators, or carrying any other punctuation, is rejected without conversion.
Example ¶
package main
import (
"fmt"
"github.com/goloop/scs/v2"
)
func main() {
for _, s := range []string{"user_id", "userId", "API", "api"} {
style, ok := scs.Detect(s)
fmt.Printf("%q -> %v (%v)\n", s, style, ok)
}
}
Output: "user_id" -> snake (true) "userId" -> camel (true) "API" -> screaming_snake (true) "api" -> unknown (false)
func ParseStyle ¶
ParseStyle maps a style identifier (as produced by Style.String) to its Style. The match is exact and case-sensitive. The second result is false for unrecognized names, in which case the Style is Unknown.
A few common aliases are accepted: "constant" and "screaming" for ScreamingSnake.