Skip to content

Commit

Permalink
bundles all of the params to Converter into a struct
Browse files Browse the repository at this point in the history
  • Loading branch information
chanced committed Aug 31, 2022
1 parent 33714cf commit acaf276
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 56 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,9 @@ import (
"github.com/chanced/caps"
)
type MyConverter struct{}
func (MyConverter) Convert(style caps.Style, repStyle caps.ReplaceStyle, input string, join string, allowedSymbols []rune, numberRules map[rune]func(index int, r rune, val []rune) bool) string {
res := caps.DefaultConverter.Convert(style, repStyle, input, join, allowedSymbols, numberRules)
if style == caps.StyleLowerCamel && repStyle == caps.ReplaceStyleCamel && res == "id" {
func (MyConverter) Convert(req caps.ConvertRequest) string {
res := caps.DefaultConverter.Convert(req)
if req.Style == caps.StyleLowerCamel && req.ReplaceStyle == caps.ReplaceStyleCamel && res == "id" {
return "_id"
}
return res
Expand Down
37 changes: 33 additions & 4 deletions caps.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,14 @@ func WithoutNumbers[T ~string](s T) T {
// caps.ToCamel("AN_EXAMPLE_STRING", ) // AnExampleString
func ToCamel[T ~string](str T, options ...Opts) T {
opts := loadOpts(options)
return T(opts.Converter.Convert(StyleCamel, opts.ReplaceStyle, string(str), "", []rune(opts.AllowedSymbols), opts.NumberRules))
return T(opts.Converter.Convert(ConvertRequest{
Style: StyleCamel,
ReplaceStyle: opts.ReplaceStyle,
Input: string(str),
Join: "",
AllowedSymbols: []rune(opts.AllowedSymbols),
NumberRules: opts.NumberRules,
}))
}

// ToLowerCamel transforms the case of str into Lower Camel Case (e.g. anExampleString) using
Expand All @@ -71,7 +78,15 @@ func ToCamel[T ~string](str T, options ...Opts) T {
// caps.ToLowerCamel("This is [an] {example}${id32}.") // thisIsAnExampleID32
func ToLowerCamel[T ~string](str T, options ...Opts) T {
opts := loadOpts(options)
return T(opts.Converter.Convert(StyleLowerCamel, opts.ReplaceStyle, string(str), "", []rune(opts.AllowedSymbols), opts.NumberRules))

return T(opts.Converter.Convert(ConvertRequest{
Style: StyleLowerCamel,
ReplaceStyle: opts.ReplaceStyle,
Input: string(str),
Join: "",
AllowedSymbols: []rune(opts.AllowedSymbols),
NumberRules: opts.NumberRules,
}))
}

// ToSnake transforms the case of str into Lower Snake Case (e.g. an_example_string) using
Expand Down Expand Up @@ -131,7 +146,14 @@ func ToScreamingDotNotation[T ~string](str T, options ...Opts) T {
// caps.ToTitle("This is [an] {example}${id32}.") // This Is An Example ID 32
func ToTitle[T ~string](str T, options ...Opts) T {
opts := loadOpts(options)
return T(opts.Converter.Convert(StyleCamel, opts.ReplaceStyle, string(str), " ", []rune(opts.AllowedSymbols), opts.NumberRules))
return T(opts.Converter.Convert(ConvertRequest{
Style: StyleCamel,
ReplaceStyle: opts.ReplaceStyle,
Input: string(str),
Join: " ",
AllowedSymbols: []rune(opts.AllowedSymbols),
NumberRules: opts.NumberRules,
}))
}

// ToDelimited transforms the case of str into a string separated by delimiter,
Expand All @@ -157,7 +179,14 @@ func ToDelimited[T ~string](str T, delimiter rune, lowercase bool, options ...Op
style = StyleScreaming
replacementStyle = ReplaceStyleScreaming
}
return T(opts.Converter.Convert(style, replacementStyle, string(str), string(delimiter), []rune(opts.AllowedSymbols), opts.NumberRules))
return T(opts.Converter.Convert(ConvertRequest{
Style: style,
ReplaceStyle: replacementStyle,
Input: string(str),
Join: string(delimiter),
AllowedSymbols: []rune(opts.AllowedSymbols),
NumberRules: opts.NumberRules,
}))
}

// ToLower returns s with all Unicode letters mapped to their lower case.
Expand Down
100 changes: 52 additions & 48 deletions converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ var DefaultConverter = NewConverter(DefaultReplacements, DefaultTokenizer, token
// 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 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
Convert(req ConvertRequest) string
}

type ConvertRequest struct {
Style Style
ReplaceStyle ReplaceStyle
Input string
Join string
AllowedSymbols []rune
NumberRules map[rune]func(index int, r rune, val []rune) bool
}

// NewConverter creates a new Converter which is used to convert the input text to the desired output.
Expand All @@ -51,15 +53,15 @@ type Converter interface {
//
// tokenizer is used to tokenize the input text.
func NewConverter(replacements []Replacement, tokenizer Tokenizer, caser token.Caser) StdConverter {
ci := StdConverter{
sc := StdConverter{
index: index.New(caser),
tokenizer: tokenizer,
caser: token.CaserOrDefault(caser),
}
for _, v := range replacements {
ci.set(v.Camel, v.Screaming)
sc.set(v.Camel, v.Screaming)
}
return ci
return sc
}

// StdConverter contains a table of words to their desired replacement. Tokens
Expand All @@ -76,60 +78,62 @@ type StdConverter struct {
caser token.Caser
}

func (ci StdConverter) Index() index.Index {
return *ci.index
func (sc StdConverter) Index() index.Index {
return *sc.index
}

// Contains reports whether a key is in the Converter's replacement table.
func (ci StdConverter) Contains(key string) bool {
return ci.index.Contains(token.FromString(ci.caser, key))
func (sc StdConverter) Contains(key string) bool {
return sc.index.Contains(token.FromString(sc.caser, key))
}

// Replacements returns a slice of Replacement in the lookup trie.
func (ci StdConverter) Replacements() []Replacement {
indexedVals := ci.index.Values()
func (sc StdConverter) Replacements() []Replacement {
indexedVals := sc.index.Values()
res := make([]Replacement, len(indexedVals))
for i, v := range indexedVals {
res[i] = Replacement{
Camel: v.Camel.Value(),
Screaming: v.Screaming.Value(),
}
}
b := strings.Builder{}
b.WriteByte('.')
return res
}

func (ci *StdConverter) set(key, value string) {
ci.index.Add(token.FromString(ci.caser, key), token.FromString(ci.caser, value))
func (sc *StdConverter) set(key, value string) {
sc.index.Add(token.FromString(sc.caser, key), token.FromString(sc.caser, value))
}

// Set adds the key/value pair to the table.
func (ci *StdConverter) Set(key, value string) {
func (sc *StdConverter) Set(key, value string) {
kstr, keyHasLower := lowerAndCheck(key)
vstr, valueHasLower := lowerAndCheck(value)
ci.Delete(kstr)
ci.Delete(vstr)
sc.Delete(kstr)
sc.Delete(vstr)

// checking to see if we need to swap these.
if !keyHasLower && valueHasLower {
ci.set(value, key)
sc.set(value, key)
} else {
ci.set(key, value)
sc.set(key, value)
}
}

// Remove deletes the key from the map. Either variant is sufficient.
func (ci *StdConverter) Delete(key string) {
tok := token.FromString(ci.caser, key)
ci.index.Delete(tok)
func (sc *StdConverter) Delete(key string) {
tok := token.FromString(sc.caser, key)
sc.index.Delete(tok)
}

// Convert formats the string with the desired style.
func (ci StdConverter) Convert(style Style, repStyle ReplaceStyle, input string, join string, allowedSymbols []rune, numberRules map[rune]func(index int, r rune, val []rune) bool) string {
tokens := ci.tokenizer.Tokenize(input, allowedSymbols, numberRules)
func (sc StdConverter) Convert(req ConvertRequest) string {
tokens := sc.tokenizer.Tokenize(req.Input, req.AllowedSymbols, req.NumberRules)
var parts []string
var ok bool
var addedAsNumber bool
idx := ci.Index()
idx := sc.Index()
for i, tok := range tokens {
switch tok.Len() {
case 0:
Expand All @@ -139,72 +143,72 @@ func (ci StdConverter) Convert(style Style, repStyle ReplaceStyle, input string,
if idx, ok = idx.Match(tok); !ok {
if idx.LastMatch().HasValue() {
// appending the last match
parts = append(parts, formatIndexedReplacement(style, repStyle, len(parts), idx.LastMatch()))
parts = append(parts, formatIndexedReplacement(req.Style, req.ReplaceStyle, len(parts), idx.LastMatch()))
}
if idx.HasPartialMatches() {
// checking to make sure it isn't a number
accum := token.Append(ci.caser, tok, idx.PartialMatches()...)
accum := token.Append(sc.caser, tok, idx.PartialMatches()...)
if accum.IsNumber() {
parts = append(parts, FormatToken(style, len(parts), accum))
parts = append(parts, FormatToken(req.Style, len(parts), accum))
addedAsNumber = true
} else {
for _, partok := range idx.PartialMatches() {
parts = append(parts, FormatToken(style, len(parts), partok))
parts = append(parts, FormatToken(req.Style, len(parts), partok))
}
addedAsNumber = false
}
}
if !addedAsNumber {
parts = append(parts, FormatToken(style, len(parts), tok))
parts = append(parts, FormatToken(req.Style, len(parts), tok))
}
// resetting the index
idx = ci.Index()
idx = sc.Index()
}
default:
if idx.HasMatch() {
parts = append(parts, formatIndexedReplacement(style, repStyle, len(parts), idx.LastMatch()))
parts = append(parts, formatIndexedReplacement(req.Style, req.ReplaceStyle, len(parts), idx.LastMatch()))
}
if idx.HasPartialMatches() {
for _, partok := range idx.PartialMatches() {
parts = append(parts, FormatToken(style, len(parts), partok))
parts = append(parts, FormatToken(req.Style, len(parts), partok))
}
}
if idx.HasMatch() || idx.HasPartialMatches() {
// resetting index
idx = ci.Index()
idx = sc.Index()
}
if rep, ok := idx.Get(tok); ok {
parts = append(parts, formatIndexedReplacement(style, repStyle, len(parts), rep))
parts = append(parts, formatIndexedReplacement(req.Style, req.ReplaceStyle, len(parts), rep))
} else if isNextTokenNumber(tokens, i) {
if idx, ok = idx.Match(tok); !ok {
parts = append(parts, FormatToken(style, len(parts), tok))
idx = ci.Index()
parts = append(parts, FormatToken(req.Style, len(parts), tok))
idx = sc.Index()
}
} else {
parts = append(parts, FormatToken(style, len(parts), tok))
parts = append(parts, FormatToken(req.Style, len(parts), tok))
}
}
}
result := strings.Builder{}
result.Grow(len(input))
result.Grow(len(req.Input))

shouldWriteDelimiter := false
if idx.HasMatch() {
parts = append(parts, formatIndexedReplacement(style, repStyle, len(parts), idx.LastMatch()))
parts = append(parts, formatIndexedReplacement(req.Style, req.ReplaceStyle, len(parts), idx.LastMatch()))
}

if idx.HasPartialMatches() {
for _, partok := range idx.PartialMatches() {
parts = append(parts, FormatToken(style, len(parts), partok))
parts = append(parts, FormatToken(req.Style, len(parts), partok))
}
}
for _, part := range parts {
if shouldWriteDelimiter {
result.WriteString(join)
result.WriteString(req.Join)
}
result.WriteString(part)
if !shouldWriteDelimiter {
shouldWriteDelimiter = len(part) > 0 && len(join) > 0
shouldWriteDelimiter = len(part) > 0 && len(req.Join) > 0
}
}

Expand Down
11 changes: 10 additions & 1 deletion converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,16 @@ func TestConverterConvert(t *testing.T) {

for _, test := range tests {
t.Run(test.input, func(t *testing.T) {
output := converter.Convert(test.style, test.repStyle, test.input, test.join, test.allowedSymbols, test.numberRules)
params := caps.ConvertRequest{
Style: test.style,
ReplaceStyle: test.repStyle,
Input: string(test.input),
Join: test.join,
AllowedSymbols: test.allowedSymbols,
NumberRules: test.numberRules,
}

output := converter.Convert(params)
if output != test.expected {
t.Errorf("expected \"%s\", got \"%s\"", test.expected, output)
}
Expand Down
16 changes: 16 additions & 0 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,19 @@ func ExampleToTitle() {
// Output:
// This Is An Example ID 32
}

type MyConverter struct{}

func (MyConverter) Convert(req caps.ConvertRequest) string {
res := caps.DefaultConverter.Convert(req)
if req.Style == caps.StyleLowerCamel && req.ReplaceStyle == caps.ReplaceStyleCamel && res == "id" {
return "_id"
}
return res
}

func ExampleWithConverter() {
fmt.Println(caps.ToLowerCamel("id", caps.WithConverter(MyConverter{}), caps.WithReplaceStyle(caps.ReplaceStyleCamel)))
// Output:
// _id
}

0 comments on commit acaf276

Please sign in to comment.