Go Go F-ing Strings

2024-10-07

f-string pic

Golang is really daft.

Keep it simple means it is frigging dumb.
Why can’t the bloody compiler validate templates of strings?

Golang is really great.

Not for a hacker’s flow for sure, but for the type obsessed Jvm Droids and Sea Sharpies, it feels o so good! to be able to fall over that ctrl-spacebar and have the ide complete your scentences for you.

Make a function the language shoulda had

Being firmly in the camp of “wouldn’t it be nice if a language is your bitch and not the other way around”
I find it daft to have to write crap like this, but this is go yo… Repeat yourself in every project, again and again and again.

package main

import (
	"bytes"
	"log"
	"text/template"
)

func fstring(theTemplate string, vars interface{}) (string, error) {
	temp, err := template.New("foop").Parse(theTemplate)
	if err != nil {
		log.Fatal(err)
	}
	var buffy bytes.Buffer
	err = temp.Execute(&buffy, vars)
	if err != nil {
		log.Fatal(err)
	}
	return buffy.String()
}

type mapi map[string]interface{}
type strs []string

func main() {
	println(fstring(`
mother {{ .who }}
{{- range .bar }}
- {{ . }}
{{- end }}
		`, map[string]interface{}{
		"who": "dearest",
		"bar": []string{
			"a", "b",
		},
	}))
}

Use it somewhere

If you want to use golang as a scripting language you want it to execute some shit.

func bashIt(command string) (stdout string, stderr string, returnCode int, err error) {
	cmd := exec.Command("bash", "-c", command)
	var stdoutBuf, stderrBuf bytes.Buffer
	cmd.Stdout = &stdoutBuf
	cmd.Stderr = &stderrBuf
	err = cmd.Run()
	stdout = stdoutBuf.String()
	stderr = stderrBuf.String()
	if exitError, ok := err.(*exec.ExitError); ok {
		returnCode = exitError.ExitCode()
	} else if err == nil {
		returnCode = 0
	} else {
		returnCode = -1
	}
	return stdout, stderr, returnCode, err
}

func main() {
	o, _, _, _ := bashIt(fstring(`echo whos a bad boi? {{.}}`, "me"))
	println(o)
	o, _, _, _ = bashIt(fstring(`echo whos a bad boi? {{.me}}`, map[string]string{"me": "no, you."}))
	println(o)
	o, _, _, _ = bashIt(fstring(`echo whos a bad boi? {{.You}}`, struct{ You string } { You: "no, you."}))
	println(o)
}

or some aws cli scrape commands

func main() {
	allRepos := []map[string]interface{}{}

	for _, profileReg := range []struct {
		Profile string
		Region  string
	}{
		{Profile: "loadpi", Region: "eu-west-1"},
		{Profile: "loadpi", Region: "eu-west-2"},
	} {
		o, _, _, _ = bashIt(fstring(`
			export AWS_PROFILE={{.Profile}}
			aws codecommit list-repositories
		`, profileReg))
		repos := map[string][]map[string]interface{}{}
		json.Unmarshal([]byte(o), &repos)
		for _, repo := range repos["repositories"] {
			repo["profile"] = profileReg.Profile
			repo["region"] = profileReg.Region
			allRepos = append(allRepos, repo)
		}
	}

	println(func(obj interface{}) string {
		jsonBytes, err := json.Marshal(obj)
		if err != nil {
			log.Fatal(err)
		}
		return string(jsonBytes)
	}(allRepos))
}

other places I’ve used fstrings:

  • going levels deep on kfc (kubes-for-cattle) and jq master pieces llm’s clobbered together
  • terror farms of taffy and kustomize in gitlab pipeline generators, merging config with target clusters / accounts

for a script junkie like me - string interpolation feels very mia.

I mean hell, even Java is getting the message, right next to the old go killer rust.