mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? This PR aims to avoid usage of [`strings.Split`](https://pkg.go.dev/strings#Split) where possible in favor of better string splitting techniques, speficially: [`strings.SplitN`](https://pkg.go.dev/strings#SplitN) and [`strings.SplitSeq`](https://pkg.go.dev/strings#SplitSeq) where appropriate. There was also a [`strings.Fields`](https://pkg.go.dev/strings#Fields) change I made to use [`strings.FieldsSeq`](https://pkg.go.dev/strings#FieldsSeq) instead, and another for S3 to use the [`path`](https://pkg.go.dev/path) package instead of [`strings.Split`](https://pkg.go.dev/strings#Split). ## Why? [`strings.SplitN`](https://pkg.go.dev/strings#SplitN) and [`strings.SplitSeq`](https://pkg.go.dev/strings#SplitSeq) are often better options in many cases, and can be _partially_ detected using [`modernize`](https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/modernize): > `stringsseq`: replace Split in "for range strings.Split(...)" by go1.24's more efficient `SplitSeq`, or `Fields` with `FieldSeq`. ## How did you test it? - [X] built - [X] run locally and tested manually - [x] covered by existing tests - [x] added new unit test(s) - [ ] added new functional test(s) ## Potential risks There are lots of potentially subtle behaviors from the `strings.Split` (and `strings.Fields`) usage that should be accounted for. If our existing tests don't cover those subtleties, there's risk for introducing an unintended bug. More intricate handling/parsing previously using the `strings` package should get extra attention from reviewers. I've attempted to break up my changes into logical commit chunks to aid in review / help spot potentially concerning changes.
97 lines
2.1 KiB
Go
97 lines
2.1 KiB
Go
package codegen
|
|
|
|
import (
|
|
"go/parser"
|
|
"go/token"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"golang.org/x/text/cases"
|
|
"golang.org/x/text/language"
|
|
)
|
|
|
|
func GenerateToFile[T any](
|
|
generator func(io.Writer, T) error,
|
|
generatorArg T,
|
|
outPath string,
|
|
outFileName string,
|
|
) {
|
|
filename := filepath.Join(outPath, outFileName+"_gen.go")
|
|
w, err := os.Create(filename)
|
|
FatalIfErr(err)
|
|
defer func() {
|
|
FatalIfErr(w.Close())
|
|
_, err = parser.ParseFile(token.NewFileSet(), filename, nil, parser.SkipObjectResolution)
|
|
FatalIfErr(err)
|
|
}()
|
|
err = generator(w, generatorArg)
|
|
FatalIfErr(err)
|
|
}
|
|
|
|
func GenerateTemplateToWriter(tmpl string, data any, w io.Writer) error {
|
|
t, err := template.New("code").Parse(tmpl)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return t.Execute(w, data)
|
|
}
|
|
|
|
func GenerateTemplateToFile(
|
|
tmpl string,
|
|
data any,
|
|
outPath string,
|
|
outFileName string,
|
|
) {
|
|
GenerateToFile(func(w io.Writer, data any) error {
|
|
return GenerateTemplateToWriter(tmpl, data, w)
|
|
}, data, outPath, outFileName)
|
|
}
|
|
|
|
func FatalIfErr(err error) {
|
|
if err != nil {
|
|
//nolint:revive // okay to call Fatal here since this is part of the build process, not the server.
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func Fatalf(format string, v ...any) {
|
|
//nolint:revive // okay to call Fatal here since this is part of the build process, not the server.
|
|
log.Fatalf(format, v...)
|
|
}
|
|
|
|
func CamelCaseToSnakeCase(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
t := make([]rune, 0, len(s)+5)
|
|
for i, c := range s {
|
|
if IsASCIIUpper(c) {
|
|
if i != 0 {
|
|
t = append(t, '_')
|
|
}
|
|
c ^= ' ' // Make it a lower letter.
|
|
}
|
|
t = append(t, c)
|
|
}
|
|
return string(t)
|
|
}
|
|
|
|
func SnakeCaseToPascalCase(s string) string {
|
|
var b strings.Builder
|
|
// Capitalize the first letter of each word split by underscore
|
|
for word := range strings.SplitSeq(s, "_") {
|
|
// Convert first rune to upper and the rest to lower case
|
|
b.WriteString(cases.Title(language.AmericanEnglish).String(strings.ToLower(word)))
|
|
}
|
|
// Join them back into a single string
|
|
return b.String()
|
|
}
|
|
|
|
func IsASCIIUpper(c rune) bool {
|
|
return 'A' <= c && c <= 'Z'
|
|
}
|