caps

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2022 License: MIT Imports: 5 Imported by: 18

README

caps

Go Reference GoReportCard example GitHub go.mod Go version of a Go module

caps is a unicode aware (with support for special cases) case conversion library for Go. It was built with the following priorites in mind: configurability, consistency, correctness, ergonomic, and reasonable performance; in that order.

Out of the box, the following case conversion are supported:

  • Camel Case (e.g. CamelCase)
  • Lower Camel Case (e.g. lowerCamelCase)
  • Snake Case (e.g. snake_case)
  • Screaming Snake Case (e.g. SCREAMING_SNAKE_CASE)
  • Kebab Case (e.g. kebab-case)
  • Screaming Kebab Case(e.g. SCREAMING-KEBAB-CASE)
  • Dot Notation Case (e.g. dot.notation.case)
  • Screaming Dot Notation Case (e.g. DOT.NOTATION.CASE)
  • Title Case (e.g. Title Case)
  • Other deliminations

Word boundaries are determined by the caps.Converter. The provided implementation, caps.ConverterImpl, delegates the boundary detection to caps.Tokenizer. The provided implementation, caps.TokenizerImpl, uses the following runes as delimiters: " _.!?:;$-(){}[]#@&+~".

caps.StdConverter also allows users to register caps.Replacements for acronym replacement. The default list is:

{"Acl", "ACL"}
{"Api", "API"}
{"Ascii", "ASCII"}
{"Cpu", "CPU"}
{"Css", "CSS"}
{"Dns", "DNS"}
{"Eof", "EOF"}
{"Guid", "GUID"}
{"Html", "HTML"}
{"Http", "HTTP"}
{"Https", "HTTPS"}
{"Id", "ID"}
{"Ip", "IP"}
{"Json", "JSON"}
{"Lhs", "LHS"}
{"Qps", "QPS"}
{"Ram", "RAM"}
{"Rhs", "RHS"}
{"Rpc", "RPC"}
{"Sla", "SLA"}
{"Smtp", "SMTP"}
{"Sql", "SQL"}
{"Ssh", "SSH"}
{"Tcp", "TCP"}
{"Tls", "TLS"}
{"Ttl", "TTL"}
{"Udp", "UDP"}
{"Ui", "UI"}
{"Uid", "UID"}
{"Uuid", "UUID"}
{"Uri", "URI"}
{"Url", "URL"}
{"Utf8", "UTF8"}
{"Vm", "VM"}
{"Xml", "XML"}
{"Xmpp", "XMPP"}
{"Xsrf", "XSRF"}
{"Xss", "XSS"}

If you would like to add or remove entries from that list, you have a few options.

You can pass a new instance of caps.StdConverter with a new set of caps.Replacement (likely preferred).

You can create your own caps.Converter. This could be as simple as implementing the single Convert method, calling caps.DefaultConverter.Convert, and then modifying the result.

Finally, if you are so inclined, you can update caps.DefaultConverter. Just be aware that the module was not built with thread-safety in mind so you should set it once. Otherwise, you'll need guard your usage of the library accordingly.

Support for special case unicode (Turkish, Azeri)

caps supports Turkish and Azeri through the use of special casers.

For example, to use Turkish, you would need to instantiate a few variables:

package main
import (
    "github.com/chanced/caps"
    "github.com/chanced/token"

)
func main() {
    tokenizer := caps.NewTokenizer(caps.DEFAULT_DELIMITERS, token.TurkishCaser)
    // I suppose these would need to be specific to Turkish?
    // if not, you can just use caps.DefaultReplacements
    replacements := []caps.Replacement{
        { Camel: "Http", Screaming: "HTTP" }, // just an example
    }
    turkish := caps.NewConverter(replacements, tokenizer, token.TurkishCaser)

    // to use this as your default throughout your application
    // you can set it **BEFORE YOU USE IT** (caps.ConverterImpl is not guarded for thread-safety)
    //
    // caps.DefaultConverter = turkish
    //
    // otherwise, you can pass in the converter to the config for each call:
    caps.ToCamel("An example", caps.Opts{Converter: turkish})
}



## License

MIT

Documentation

Index

Examples

Constants

View Source
const (
	ReplaceStyleNotSpecified = iota
	ReplaceStyleCamel        // Text should be replaced with the Camel variant (e.g. "Json").
	ReplaceStyleScreaming    // Text should be replaced with the screaming variant (e.g. "JSON").
	ReplaceStyleLower        // Text should be replaced with the lowercase variant (e.g. "json").
)
View Source
const (
	// DEFAULT_DELIMITERS is the default set of delimiters in string convert.
	DEFAULT_DELIMITERS string = " _.!?:;$-(){}[]#@&+~"
)

Variables

View Source
var (
	// DefaultTokenizer is the default Tokenizer.
	DefaultTokenizer TokenizerImpl = NewTokenizer(DEFAULT_DELIMITERS, token.DefaultCaser)

	// DefaultReplacements is the list of Replacements passed to DefaultConverter.
	// 	{"Acl", "ACL"},
	// 	{"Api", "API"},
	// 	{"Ascii", "ASCII"},
	// 	{"Cpu", "CPU"},
	// 	{"Css", "CSS"},
	// 	{"Dns", "DNS"},
	// 	{"Eof", "EOF"},
	// 	{"Guid", "GUID"},
	// 	{"Html", "HTML"},
	// 	{"Http", "HTTP"},
	// 	{"Https", "HTTPS"},
	// 	{"Id", "ID"},
	// 	{"Ip", "IP"},
	// 	{"Json", "JSON"},
	// 	{"Lhs", "LHS"},
	// 	{"Qps", "QPS"},
	// 	{"Ram", "RAM"},
	// 	{"Rhs", "RHS"},
	// 	{"Rpc", "RPC"},
	// 	{"Sla", "SLA"},
	// 	{"Smtp", "SMTP"},
	// 	{"Sql", "SQL"},
	// 	{"Ssh", "SSH"},
	// 	{"Tcp", "TCP"},
	// 	{"Tls", "TLS"},
	// 	{"Ttl", "TTL"},
	// 	{"Udp", "UDP"},
	// 	{"Ui", "UI"},
	// 	{"Uid", "UID"},
	// 	{"Uuid", "UUID"},
	// 	{"Uri", "URI"},
	// 	{"Url", "URL"},
	// 	{"Utf8", "UTF8"},
	// 	{"Vm", "VM"},
	// 	{"Xml", "XML"},
	// 	{"Xmpp", "XMPP"},
	// 	{"Xsrf", "XSRF"},
	// 	{"Xss", "XSS"},
	DefaultReplacements []Replacement = []Replacement{
		{"Acl", "ACL"},
		{"Api", "API"},
		{"Ascii", "ASCII"},
		{"Cpu", "CPU"},
		{"Css", "CSS"},
		{"Dns", "DNS"},
		{"Eof", "EOF"},
		{"Guid", "GUID"},
		{"Html", "HTML"},
		{"Http", "HTTP"},
		{"Https", "HTTPS"},
		{"Id", "ID"},
		{"Ip", "IP"},
		{"Json", "JSON"},
		{"Lhs", "LHS"},
		{"Qps", "QPS"},
		{"Ram", "RAM"},
		{"Rhs", "RHS"},
		{"Rpc", "RPC"},
		{"Sla", "SLA"},
		{"Smtp", "SMTP"},
		{"Sql", "SQL"},
		{"Ssh", "SSH"},
		{"Tcp", "TCP"},
		{"Tls", "TLS"},
		{"Ttl", "TTL"},
		{"Udp", "UDP"},
		{"Ui", "UI"},
		{"Uid", "UID"},
		{"Uuid", "UUID"},
		{"Uri", "URI"},
		{"Url", "URL"},
		{"Utf8", "UTF8"},
		{"Vm", "VM"},
		{"Xml", "XML"},
		{"Xmpp", "XMPP"},
		{"Xsrf", "XSRF"},
		{"Xss", "XSS"},
	}

	//
	// replacements:
	//  DefaultReplacements
	//
	// tokenizer:
	//  DefaultTokenizer
	DefaultConverter = NewConverter(DefaultReplacements, DefaultTokenizer, token.DefaultCaser)
)

Functions

func FormatIndexedReplacement added in v0.3.0

func FormatIndexedReplacement(style Style, replaceStyle ReplaceStyle, index int, rep index.IndexedReplacement) string

func FormatToken

func FormatToken(style Style, index int, tok token.Token) string

FormatToken formats the token with the desired style.

func LowerFirst

func LowerFirst[T ~string](str T) T

LowerFirst converts the first rune of str to lowercase.

func ToCamel

func ToCamel[T ~string](str T, options ...Opts) T

ToCamel transforms the case of str into Camel Case (e.g. AnExampleString) using either the provided Converter or the DefaultConverter otherwise.

The default Converter detects case so that "AN_EXAMPLE_STRING" becomes "AnExampleString". It also has a configurable set of replacements, such that "some_json" becomes "SomeJSON" so long as opts.ReplacementStyle is set to ReplaceStyleScreaming. A ReplaceStyle of ReplaceStyleCamel would result in "SomeJson".

caps.ToCamel("This is [an] {example}${id32}.") // ThisIsAnExampleID32
caps.ToCamel("AN_EXAMPLE_STRING", ) // AnExampleString
Example
package main

import (
	"fmt"

	"github.com/chanced/caps"
)

func main() {
	fmt.Println(caps.ToCamel("This is [an] {example}${id32}."))
	fmt.Println(caps.ToCamel("AN_EXAMPLE_STRING"))
}
Output:

ThisIsAnExampleID32
AnExampleString

func ToDelimited

func ToDelimited[T ~string](str T, delimiter rune, lowercase bool, options ...Opts) T

ToDelimited transforms the case of str into a string separated by delimiter, using either the provided Converter or the DefaultConverter otherwise.

If lowercase is false, the output will be all uppercase.

See Options for more information on available configuration

Example

caps.ToDelimited("This is [an] {example}${id}.#32", '.', true) // this.is.an.example.id.32
caps.ToDelimited("This is [an] {example}${id}.break32", '.', false) // THIS.IS.AN.EXAMPLE.ID.BREAK.32
caps.ToDelimited("This is [an] {example}${id}.v32", '.', true, caps.Opts{AllowedSymbols: "$"}) // this.is.an.example.$.id.v32
Example
package main

import (
	"fmt"

	"github.com/chanced/caps"
)

func main() {
	fmt.Println(caps.ToDelimited("This is [an] {example}${id}.#32", '.', true))
	fmt.Println(caps.ToDelimited("This is [an] {example}${id}.break32", '.', false))
	fmt.Println(caps.ToDelimited("This is [an] {example}${id}.v32", '.', true, caps.Opts{AllowedSymbols: "$"}))

}
Output:

this.is.an.example.id.32
THIS.IS.AN.EXAMPLE.ID.BREAK.32
this.is.an.example.$.id.v32

func ToDotNotation added in v0.3.0

func ToDotNotation[T ~string](str T, options ...Opts) T

ToDotNotation transforms the case of str into Lower Dot Notation Case (e.g. an.example.string) using either the provided Converter or the DefaultConverter otherwise.

caps.ToDotNotation("This is [an] {example}${id32}.") // this.is.an.example.id.32
Example
package main

import (
	"fmt"

	"github.com/chanced/caps"
)

func main() {
	fmt.Println(caps.ToDotNotation("This is [an] {example}${id32}."))
}
Output:

this.is.an.example.id.32

func ToKebab

func ToKebab[T ~string](str T, options ...Opts) T

ToKebab transforms the case of str into Lower Kebab Case (e.g. an-example-string) using either the provided Converter or the DefaultConverter otherwise.

caps.ToKebab("This is [an] {example}${id32}.") // this-is-an-example-id-32
Example
package main

import (
	"fmt"

	"github.com/chanced/caps"
)

func main() {
	fmt.Println(caps.ToKebab("This is [an] {example}${id32}."))
}
Output:

this-is-an-example-id-32

func ToLower added in v0.2.3

func ToLower[T ~string](str T) T

ToLower returns s with all Unicode letters mapped to their lower case.

func ToLowerCamel

func ToLowerCamel[T ~string](str T, options ...Opts) T

ToLowerCamel transforms the case of str into Lower Camel Case (e.g. anExampleString) using either the provided Converter or the DefaultConverter otherwise.

The default Converter detects case so that "AN_EXAMPLE_STRING" becomes "anExampleString". It also has a configurable set of replacements, such that "some_json" becomes "someJSON" so long as opts.ReplacementStyle is set to ReplaceStyleScreaming. A ReplaceStyle of ReplaceStyleCamel would result in "someJson".

caps.ToLowerCamel("This is [an] {example}${id32}.") // thisIsAnExampleID32
Example
package main

import (
	"fmt"

	"github.com/chanced/caps"
)

func main() {
	fmt.Println(caps.ToLowerCamel("This is [an] {example}${id32}."))
}
Output:

thisIsAnExampleID32

func ToScreamingDotNotation added in v0.3.0

func ToScreamingDotNotation[T ~string](str T, options ...Opts) T

ToScreamingDotNotation transforms the case of str into Screaming Kebab Case (e.g. AN.EXAMPLE.STRING) using either the provided Converter or the DefaultConverter otherwise.

caps.ToScreamingDotNotation("This is [an] {example}${id32}.") // THIS.IS.AN.EXAMPLE.ID.32
Example
package main

import (
	"fmt"

	"github.com/chanced/caps"
)

func main() {
	fmt.Println(caps.ToScreamingDotNotation("This is [an] {example}${id32}."))
}
Output:

THIS.IS.AN.EXAMPLE.ID.32

func ToScreamingKebab

func ToScreamingKebab[T ~string](str T, options ...Opts) T

ToScreamingKebab transforms the case of str into Screaming Kebab Snake (e.g. AN-EXAMPLE-STRING) using either the provided Converter or the DefaultConverter otherwise.

caps.ToScreamingKebab("This is [an] {example}${id32}.") // THIS-IS-AN-EXAMPLE-ID-32
Example
package main

import (
	"fmt"

	"github.com/chanced/caps"
)

func main() {
	fmt.Println(caps.ToScreamingKebab("This is [an] {example}${id32}."))
}
Output:

THIS-IS-AN-EXAMPLE-ID-32

func ToScreamingSnake

func ToScreamingSnake[T ~string](str T, options ...Opts) T

ToScreamingSnake transforms the case of str into Screaming Snake Case (e.g. AN_EXAMPLE_STRING) using either the provided Converter or the DefaultConverter otherwise.

caps.ToScreamingSnake("This is [an] {example}${id32}.") // THIS_IS_AN_EXAMPLE_ID_32
Example
package main

import (
	"fmt"

	"github.com/chanced/caps"
)

func main() {
	fmt.Println(caps.ToScreamingSnake("This is [an] {example}${id32}."))
}
Output:

THIS_IS_AN_EXAMPLE_ID_32

func ToSnake

func ToSnake[T ~string](str T, options ...Opts) T

ToSnake transforms the case of str into Lower Snake Case (e.g. an_example_string) using either the provided Converter or the DefaultConverter otherwise.

caps.ToSnake("This is [an] {example}${id32}.") // this_is_an_example_id_32
Example
package main

import (
	"fmt"

	"github.com/chanced/caps"
)

func main() {
	fmt.Println(caps.ToSnake("This is [an] {example}${id32}."))
	fmt.Println(caps.ToSnake("v3.2.2"))
}
Output:

this_is_an_example_id_32
v3_2_2

func ToTitle

func ToTitle[T ~string](str T, options ...Opts) T

ToTitle transforms the case of str into Title Case (e.g. An Example String) using either the provided Converter or the DefaultConverter otherwise.

caps.ToTitle("This is [an] {example}${id32}.") // This Is An Example ID 32
Example
package main

import (
	"fmt"

	"github.com/chanced/caps"
)

func main() {
	fmt.Println(caps.ToTitle("This is [an] {example}${id32}."))
}
Output:

This Is An Example ID 32

func ToUpper added in v0.2.3

func ToUpper[T ~string](str T) T

ToUpper returns s with all Unicode letters mapped to their upper case.

func UpperFirst

func UpperFirst[T ~string](str T) T

UpperFirst converts the first rune of str to unicode upper case.

This method does not support special cases (such as Turkish and Azeri)

func WithoutNumbers

func WithoutNumbers[T ~string](s T) T

Without numbers returns the string with all numeric runes removed.

It does not currently use any logic to determine if a rune (e.g. '.') is part of a number. This may change in the future.

Types

type Converter added in v0.2.0

type Converter interface {
	Convert(
		style Style,
		repStyle ReplaceStyle,
		input string,
		join string,
		allowedSymbols []rune,
		numberRules map[rune]func(index int, r rune, val []rune) bool,
	) string
}

Converter is an interface satisfied by types which can convert the case of a string.

ConverterImpl is provided as a default implementation. If you have edge cases which require custom formatting, you can implement your own Converter by wrapping ConverterImpl:

type MyConverter struct {}
func(MyConverter) Convert(style Style, repStyle ReplaceStyle, input string, join string, allowedSymbols []rune, numberRules map[rune]func(index int, r rune, val []rune) bool) string{
	formatted := caps.DefaultConverter.Convert(style, repStyle, input, join, allowedSymbols, numberRules)
	if formatted == "something_unusual" {
	  	return "replaced"
 	}
 	return formatted
}

Parameters

style:          Expected output caps.Style of the string.
repStyle:       The caps.ReplaceStyle to use if a word needs to be replaced.
join:           The delimiter to use when joining the words. For Camel, this is an empty string.
allowedSymbols: The set of allowed symbols. If set, these should take precedence over any delimiters
numberRules:    Any custom rules dictating how to handle special characters in numbers.

type ConverterImpl added in v0.2.0

type ConverterImpl struct {
	// contains filtered or unexported fields
}

ConverterImpl contains a table of words to their desired replacement. Tokens will be compared against the keys of this table to determine if the string should be replaced with the value of the table.

This is primarily designed for acronyms but it could be used for other purposes.

The default Replacements can be found in the DefaultReplacements variable.

func NewConverter added in v0.2.0

func NewConverter(replacements []Replacement, tokenizer Tokenizer, caser token.Caser) ConverterImpl

NewConverter creates a new Converter which is used to convert the input text to the desired output.

replacements are used to make replacements of tokens to the specified formatting (e.g. { "Json", "JSON"}).

tokenizer is used to tokenize the input text.

func (ConverterImpl) Contains added in v0.2.0

func (ci ConverterImpl) Contains(key string) bool

Contains reports whether a key is in the Converter's replacement table.

func (ConverterImpl) Convert added in v0.2.0

func (ci ConverterImpl) Convert(style Style, repStyle ReplaceStyle, input string, join string, allowedSymbols []rune, numberRules map[rune]func(index int, r rune, val []rune) bool) string

Convert formats the string with the desired style.

func (*ConverterImpl) Delete added in v0.2.0

func (ci *ConverterImpl) Delete(key string)

Remove deletes the key from the map. Either variant is sufficient.

func (ConverterImpl) Index added in v0.3.0

func (ci ConverterImpl) Index() index.Index

func (ConverterImpl) Replacements added in v0.2.0

func (ci ConverterImpl) Replacements() []Replacement

Replacements returns a slice of Replacement in the lookup trie.

func (*ConverterImpl) Set added in v0.2.0

func (ci *ConverterImpl) Set(key, value string)

Set adds the key/value pair to the table.

type Opts

type Opts struct {
	// Any characters within this string will be allowed in the output.
	//
	// Default:
	// 	""
	AllowedSymbols string
	// The Converter to use.
	//
	// Default:
	// 	DefaultConverter
	Converter Converter

	// Styles overwrites the way words are replaced.
	//
	// A typical call to ToLowerCamel for "ServeJSON" with a Replacer that
	// contains {"Json": "JSON"} would result in "serveJSON" by using the
	// StyleScreaming variant. If ReplacementStyle was set to
	// ReplaceStyleUpperCamel, on the call to ToLowerCamel then the result would
	// be "serveHttp".
	//
	// The default replacement style is dependent upon the target casing.
	ReplaceStyle ReplaceStyle
	// NumberRules are used by the DefaultTokenizer to augment the standard
	// rules for determining if a rune is part of a number.
	//
	// Note, if you add special characters here, they must be present in the
	// AllowedSymbols string for them to be part of the output.
	NumberRules map[rune]func(index int, r rune, val []rune) bool
}

Opts include configurable options for case conversion.

See the documentation for the individual fields for more information.

type ReplaceStyle

type ReplaceStyle uint8

ReplaceStyle is used to indicate the case style the text should be transformed to when seeking replacement text in a Converter.

When a Replacer is configured, the expected input is:

caps.Replacement{ Camel: "Json", Screaming: "JSON" }

If the ReplaceStyle equals ReplaceStyleScreaming then an input of "MarshalJson" will return "MarshaalJSON" with the above caps.Replacement.

type Replacement

type Replacement struct {
	// Camel case variant of the word which should be replaced.
	// e.g. "Http"
	Camel string
	// Screaming (all upper case) representation of the word to replace.
	// e.g. "HTTP"
	Screaming string
}

type Style

type Style uint8
const (
	StyleLower      Style = iota // The output should be lowercase (e.g. "an_example")
	StyleScreaming               // The output should be screaming (e.g. "AN_EXAMPLE")
	StyleCamel                   // The output should be camel case (e.g. "AnExample")
	StyleLowerCamel              // The output should be lower camel case (e.g. "anExample")
)

type Tokenizer

type Tokenizer interface {
	Tokenize(value string, allowedSymbols []rune, numberRules map[rune]func(index int, r rune, val []rune) bool) []token.Token
}

Tokenizer is an interface satisfied by tyeps which can

type TokenizerImpl

type TokenizerImpl struct {
	// contains filtered or unexported fields
}

TokenizerImpl is the provided implementation of the Tokenizer interface.

TokenizerImpl tokenizes the input text into token.Tokens based on a set of delimiters (runes).

If you need custom logic, consider wrapping the logic by implementing Tokenizer and then calling a TokenizerImpl's Tokenize method.

Example:

func NewTokenizer

func NewTokenizer(delimiters string, caser token.Caser) TokenizerImpl

NewTokenizer creates and returns a new TokenizerImpl which implements the Tokenizer interface.

Tokenizers are used by ConverterImpl to tokenize the input text into token.Tokens that are then formatted.

func (TokenizerImpl) Tokenize

func (ti TokenizerImpl) Tokenize(str string, allowedSymbols []rune, numberRules map[rune]func(index int, r rune, val []rune) bool) []token.Token

Tokenize splits a string into a list of token.Tokens based on the case of each rune, it's delimiters, and the specified allowedSymbols.

For example:

t.Tokenize("ASnakecaseVariable", nil) -> ["A", "Snakecase", "Variable"]

Tokenizer attempts to detect formatting, such that a screaming snakecase str will be inferred accordingly.

For example:

t.Tokenize("A_SCREAMING_SNAKECASE_VARIABLE", nil) -> ["A", "SCREAMING", "SNAKECASE", "VARIABLE"]

If allowedSymbols is not nil, then those symbols will be treated as non-delimiters even if they are in the delimiters list.

For example:

t := caps.token.Newizer("_")
t.Tokenize("A_SCREAMING_SNAKECASE_VARIABLE", []rune{'_'}) -> ["A_SCREAMING_SNAKECASE_VARIABLE"]

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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