1package goose
2
3import (
4 "bytes"
5 "strings"
6 "unicode"
7 "unicode/utf8"
8)
9
10type camelSnakeStateMachine int
11
12const ( // _$$_This is some text, OK?!
13 idle camelSnakeStateMachine = iota // 0 ↑ ↑ ↑
14 firstAlphaNum // 1 ↑ ↑ ↑ ↑ ↑
15 alphaNum // 2 ↑↑↑ ↑ ↑↑↑ ↑↑↑ ↑
16 delimiter // 3 ↑ ↑ ↑ ↑ ↑
17)
18
19func (s camelSnakeStateMachine) next(r rune) camelSnakeStateMachine {
20 switch s {
21 case idle:
22 if isAlphaNum(r) {
23 return firstAlphaNum
24 }
25 case firstAlphaNum:
26 if isAlphaNum(r) {
27 return alphaNum
28 }
29 return delimiter
30 case alphaNum:
31 if !isAlphaNum(r) {
32 return delimiter
33 }
34 case delimiter:
35 if isAlphaNum(r) {
36 return firstAlphaNum
37 }
38 return idle
39 }
40 return s
41}
42
43func camelCase(str string) string {
44 var b strings.Builder
45
46 stateMachine := idle
47 for i := 0; i < len(str); {
48 r, size := utf8.DecodeRuneInString(str[i:])
49 i += size
50 stateMachine = stateMachine.next(r)
51 switch stateMachine {
52 case firstAlphaNum:
53 b.WriteRune(unicode.ToUpper(r))
54 case alphaNum:
55 b.WriteRune(unicode.ToLower(r))
56 }
57 }
58 return b.String()
59}
60
61func snakeCase(str string) string {
62 var b bytes.Buffer
63
64 stateMachine := idle
65 for i := 0; i < len(str); {
66 r, size := utf8.DecodeRuneInString(str[i:])
67 i += size
68 stateMachine = stateMachine.next(r)
69 switch stateMachine {
70 case firstAlphaNum, alphaNum:
71 b.WriteRune(unicode.ToLower(r))
72 case delimiter:
73 b.WriteByte('_')
74 }
75 }
76 if stateMachine == idle {
77 return string(bytes.TrimSuffix(b.Bytes(), []byte{'_'}))
78 }
79 return b.String()
80}
81
82func isAlphaNum(r rune) bool {
83 return unicode.IsLetter(r) || unicode.IsNumber(r)
84}