├── .gitignore ├── Readme.md ├── camel.go ├── camel_test.go └── circle.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # Emacs 27 | *~ 28 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # go-camelcase 2 | 3 | Fast camelcase implementation that avoids Go's regexps. Direct fork of our [snakecase](https://github.com/segmentio/go-snakecase) implementation. 4 | 5 | -- 6 | 7 | import "github.com/segmentio/go-camelcase" 8 | 9 | Convert strings to camelCase 10 | 11 | ## Usage 12 | 13 | #### func Camelcase 14 | 15 | ```go 16 | func Camelcase(str string) string 17 | ``` 18 | Camelcase representation of `str`. 19 | 20 | # License 21 | 22 | MIT -------------------------------------------------------------------------------- /camel.go: -------------------------------------------------------------------------------- 1 | // 2 | // Fast camel-case implementation. 3 | // 4 | package camelcase 5 | 6 | // Camelcase the given string. 7 | func Camelcase(s string) string { 8 | b := make([]byte, 0, 64) 9 | l := len(s) 10 | i := 0 11 | 12 | for i < l { 13 | 14 | // skip leading bytes that aren't letters or digits 15 | for i < l && !isWord(s[i]) { 16 | i++ 17 | } 18 | 19 | // set the first byte to uppercase if it needs to 20 | if i < l { 21 | c := s[i] 22 | 23 | // simply append contiguous digits 24 | if isDigit(c) { 25 | for i < l { 26 | if c = s[i]; !isDigit(c) { 27 | break 28 | } 29 | b = append(b, c) 30 | i++ 31 | } 32 | continue 33 | } 34 | 35 | // the sequence starts with and uppercase letter, we append 36 | // all following uppercase letters as equivalent lowercases 37 | if isUpper(c) { 38 | b = append(b, c) 39 | i++ 40 | 41 | for i < l { 42 | if c = s[i]; !isUpper(c) { 43 | break 44 | } 45 | b = append(b, toLower(c)) 46 | i++ 47 | } 48 | 49 | } else { 50 | b = append(b, toUpper(c)) 51 | i++ 52 | } 53 | 54 | // append all trailing lowercase letters 55 | for i < l { 56 | if c = s[i]; !isLower(c) { 57 | break 58 | } 59 | b = append(b, c) 60 | i++ 61 | } 62 | } 63 | } 64 | 65 | // the first byte must always be lowercase 66 | if len(b) != 0 { 67 | b[0] = toLower(b[0]) 68 | } 69 | 70 | return string(b) 71 | } 72 | 73 | func isWord(c byte) bool { 74 | return isLetter(c) || isDigit(c) 75 | } 76 | 77 | func isLetter(c byte) bool { 78 | return isLower(c) || isUpper(c) 79 | } 80 | 81 | func isUpper(c byte) bool { 82 | return c >= 'A' && c <= 'Z' 83 | } 84 | 85 | func isLower(c byte) bool { 86 | return c >= 'a' && c <= 'z' 87 | } 88 | 89 | func isDigit(c byte) bool { 90 | return c >= '0' && c <= '9' 91 | } 92 | 93 | func toLower(c byte) byte { 94 | if isUpper(c) { 95 | return c + ('a' - 'A') 96 | } 97 | return c 98 | } 99 | 100 | func toUpper(c byte) byte { 101 | if isLower(c) { 102 | return c - ('a' - 'A') 103 | } 104 | return c 105 | } 106 | -------------------------------------------------------------------------------- /camel_test.go: -------------------------------------------------------------------------------- 1 | package camelcase 2 | 3 | import "testing" 4 | 5 | var ops int = 1e6 6 | 7 | type sample struct { 8 | str, out string 9 | } 10 | 11 | func TestCamelcase(t *testing.T) { 12 | samples := []sample{ 13 | {"sample text", "sampleText"}, 14 | {"sample-text", "sampleText"}, 15 | {"sample_text", "sampleText"}, 16 | {"sample___text", "sampleText"}, 17 | {"sampleText", "sampleText"}, 18 | {"inviteYourCustomersAddInvites", "inviteYourCustomersAddInvites"}, 19 | {"sample 2 Text", "sample2Text"}, 20 | {" sample 2 Text ", "sample2Text"}, 21 | {" $#$sample 2 Text ", "sample2Text"}, 22 | {"SAMPLE 2 TEXT", "sample2Text"}, 23 | {"___$$Base64Encode", "base64Encode"}, 24 | {"FOO:BAR$BAZ", "fooBarBaz"}, 25 | {"FOO#BAR#BAZ", "fooBarBaz"}, 26 | {"something.com", "somethingCom"}, 27 | {"$something%", "something"}, 28 | {"something.com", "somethingCom"}, 29 | {"•¶§ƒ˚foo˙∆˚¬", "foo"}, 30 | } 31 | 32 | for _, sample := range samples { 33 | if out := Camelcase(sample.str); out != sample.out { 34 | t.Errorf("got %q from %q, expected %q", out, sample.str, sample.out) 35 | } 36 | } 37 | } 38 | 39 | func BenchmarkCamelcase(t *testing.B) { 40 | for i := 0; i < t.N; i++ { 41 | Camelcase("some sample text here_noething:too$amazing") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | services: 3 | - docker 4 | 5 | dependencies: 6 | override: 7 | - docker pull segment/golang:latest 8 | 9 | test: 10 | override: 11 | - > 12 | docker run 13 | $(env | grep -E '^CIRCLE_|^DOCKER_|^CIRCLECI=|^CI=' | sed 's/^/--env /g' | tr "\\n" " ") 14 | --rm 15 | --tty 16 | --interactive 17 | --name go 18 | --volume /var/run/docker.sock:/run/docker.sock 19 | --volume ${PWD}:/go/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} 20 | --workdir /go/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} 21 | segment/golang:latest 22 | --------------------------------------------------------------------------------