mmv1/google/string_utils.go (101 lines of code) (raw):
// Copyright 2024 Google Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google
import (
"fmt"
"log"
"regexp"
"strings"
"unicode"
)
// // Helper class to process and mutate strings.
// class StringUtils
// Converts string from camel case to underscore
func Underscore(source string) string {
tmp := regexp.MustCompile(`([A-Z]+)([A-Z][a-z])`).ReplaceAllString(source, "${1}_${2}")
tmp = regexp.MustCompile(`([a-z\d])([A-Z])`).ReplaceAllString(tmp, "${1}_${2}")
tmp = strings.Replace(tmp, "-", "_", 1)
tmp = strings.Replace(tmp, ".", "_", 1)
tmp = strings.ToLower(tmp)
return tmp
}
// Converts from PascalCase to Space Separated
// For example, converts "AccessApproval" to "Access approval"
func SpaceSeparated(source string) string {
tmp := regexp.MustCompile(`([A-Z]+)([A-Z][a-z])`).ReplaceAllString(source, "${1} ${2}")
tmp = regexp.MustCompile(`([a-z\d])([A-Z])`).ReplaceAllString(tmp, "${1} ${2}")
tmp = strings.ToLower(tmp)
// Capitalize the first letter
if len(tmp) != 0 {
r := []rune(tmp)
r[0] = unicode.ToUpper(r[0])
tmp = string(r)
}
return tmp
}
// Converts a string to space-separated capitalized words
func SpaceSeparatedTitle(source string) string {
ss := SpaceSeparated(source)
return strings.Title(ss)
}
// Returns all the characters up until the period (.) or returns text
// unchanged if there is no period.
func FirstSentence(text string) string {
re := regexp.MustCompile(`[.?!]`)
periodPos := re.FindStringIndex(text)
if periodPos == nil {
return text
}
return text[:periodPos[0]+1]
}
// Returns the plural form of a word
func Plural(source string) string {
// policies -> policies
// indices -> indices
if strings.HasSuffix(source, "ies") || strings.HasSuffix(source, "es") {
return source
}
// index -> indices
if strings.HasSuffix(source, "ex") {
re := regexp.MustCompile("ex$")
result := re.ReplaceAllString(source, "")
return fmt.Sprintf("%sices", result)
}
// mesh -> meshes
if strings.HasSuffix(source, "esh") {
return fmt.Sprintf("%ses", source)
}
// key -> keys
// gateway -> gateways
if strings.HasSuffix(source, "ey") || strings.HasSuffix(source, "ay") {
return fmt.Sprintf("%ss", source)
}
// policy -> policies
if strings.HasSuffix(source, "y") {
re := regexp.MustCompile("y$")
result := re.ReplaceAllString(source, "")
return fmt.Sprintf("%sies", result)
}
return fmt.Sprintf("%ss", source)
}
func Camelize(term string, firstLetter string) string {
if firstLetter != "upper" && firstLetter != "lower" {
log.Fatalf("Invalid option, use either upper or lower")
}
res := term
if firstLetter == "upper" {
res = regexp.MustCompile(`^[a-z\d]*`).ReplaceAllStringFunc(res, func(match string) string {
return strings.Title(match)
})
} else {
if len(res) != 0 {
r := []rune(res)
r[0] = unicode.ToLower(r[0])
res = string(r)
}
}
// handle snake case
re := regexp.MustCompile(`(?:_)([a-z\d]*)`)
res = re.ReplaceAllStringFunc(res, func(match string) string {
word := match[1:]
word = strings.Title(word)
return word
})
return res
}
/*
Transforms a format string with field markers to a regex string with capture groups.
For instance,
projects/{{project}}/global/networks/{{name}}
is transformed to
projects/(?P<project>[^/]+)/global/networks/(?P<name>[^/]+)
Values marked with % are URL-encoded, and will match any number of /'s.
Note: ?P indicates a Python-compatible named capture group. Named groups
aren't common in JS-based regex flavours, but are in Perl-based ones
*/
func Format2Regex(format string) string {
re := regexp.MustCompile(`\{\{%([[:word:]]+)\}\}`)
result := re.ReplaceAllStringFunc(format, func(match string) string {
// TODO rewrite: the trims may not be needed with more effecient regex
word := strings.TrimPrefix(match, "{{")
word = strings.TrimSuffix(word, "}}")
word = strings.ReplaceAll(word, "%", "")
return fmt.Sprintf("(?P<%s>.+)", word)
})
re = regexp.MustCompile(`\{\{([[:word:]]+)\}\}`)
result = re.ReplaceAllStringFunc(result, func(match string) string {
word := strings.TrimPrefix(match, "{{")
word = strings.TrimSuffix(word, "}}")
return fmt.Sprintf("(?P<%s>[^/]+)", word)
})
return result
}