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} 85