github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/util/camelcase/camelcase.go (about)

     1  // Package camelcase is a micro package to split the words of a camelcase type
     2  // string into a slice of words.
     3  package camelcase
     4  
     5  import (
     6  	"unicode"
     7  	"unicode/utf8"
     8  )
     9  
    10  // Split splits the camelcase word and returns a list of words. It also
    11  // supports digits. Both lower camel case and upper camel case are supported.
    12  // For more info please check: https://en.wikipedia.org/wiki/CamelCase
    13  //
    14  // Examples
    15  //
    16  //   "" =>                     [""]
    17  //   "lowercase" =>            ["lowercase"]
    18  //   "Class" =>                ["Class"]
    19  //   "MyClass" =>              ["My", "Class"]
    20  //   "MyC" =>                  ["My", "C"]
    21  //   "HTML" =>                 ["HTML"]
    22  //   "PDFLoader" =>            ["PDF", "Loader"]
    23  //   "AString" =>              ["A", "String"]
    24  //   "SimpleXMLParser" =>      ["Simple", "XML", "Parser"]
    25  //   "vimRPCPlugin" =>         ["vim", "RPC", "Plugin"]
    26  //   "GL11Version" =>          ["GL", "11", "Version"]
    27  //   "99Bottles" =>            ["99", "Bottles"]
    28  //   "May5" =>                 ["May", "5"]
    29  //   "BFG9000" =>              ["BFG", "9000"]
    30  //   "BöseÜberraschung" =>     ["Böse", "Überraschung"]
    31  //   "Two  spaces" =>          ["Two", "  ", "spaces"]
    32  //   "BadUTF8\xe2\xe2\xa1" =>  ["BadUTF8\xe2\xe2\xa1"]
    33  //
    34  // Splitting rules
    35  //
    36  //  1) If string is not valid UTF-8, return it without splitting as
    37  //     single item array.
    38  //  2) Assign all unicode characters into one of 4 sets: lower case
    39  //     letters, upper case letters, numbers, and all other characters.
    40  //  3) Iterate through characters of string, introducing splits
    41  //     between adjacent characters that belong to different sets.
    42  //  4) Iterate through array of split strings, and if a given string
    43  //     is upper case:
    44  //       if subsequent string is lower case:
    45  //         move last character of upper case string to beginning of
    46  //         lower case string
    47  func Split(src string) (entries []string) {
    48  	// don't split invalid utf8
    49  	if !utf8.ValidString(src) {
    50  		return []string{src}
    51  	}
    52  	entries = []string{}
    53  	var runes [][]rune
    54  	var lastClass int
    55  	// split into fields based on class of unicode character
    56  	for _, r := range src {
    57  		var class int
    58  		switch {
    59  		case unicode.IsLower(r):
    60  			class = 1
    61  		case unicode.IsUpper(r):
    62  			class = 2
    63  		case unicode.IsDigit(r):
    64  			class = 3
    65  		default:
    66  			class = 4
    67  		}
    68  		if class == lastClass {
    69  			runes[len(runes)-1] = append(runes[len(runes)-1], r)
    70  		} else {
    71  			runes = append(runes, []rune{r})
    72  		}
    73  		lastClass = class
    74  	}
    75  	// handle upper case -> lower case sequences, e.g.
    76  	// "PDFL", "oader" -> "PDF", "Loader"
    77  	for i := 0; i < len(runes)-1; i++ {
    78  		if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) {
    79  			runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
    80  			runes[i] = runes[i][:len(runes[i])-1]
    81  		}
    82  	}
    83  	// construct []string from results
    84  	for _, s := range runes {
    85  		if len(s) > 0 {
    86  			entries = append(entries, string(s))
    87  		}
    88  	}
    89  
    90  	return entries
    91  }