#!/usr/bin/env bash set -euo pipefail mkdir -p internal/money internal/report cat > go.mod <<'EOF' module billing go 1.22 EOF cat > internal/money/money.go <<'EOF' package money // Cents is an amount in minor units. type Cents int64 // Add sums amounts. func Add(xs ...Cents) Cents { var total Cents for _, x := range xs { total += x } return total } EOF cat > internal/money/format.go <<'EOF' package money import "fmt" // String renders an amount as major units with two decimal places. func String(c Cents) string { return fmt.Sprintf("%d.%02d", c/100, c%10) } EOF cat > internal/report/report.go <<'EOF' package report import ( "fmt" "strings" "billing/internal/money" ) // Line is one row of an invoice. type Line struct { Label string Amount money.Cents } // Render produces the invoice body and its total. func Render(lines []Line) string { var b strings.Builder var amounts []money.Cents for _, l := range lines { fmt.Fprintf(&b, "%s: %s\n", l.Label, money.String(l.Amount)) amounts = append(amounts, l.Amount) } fmt.Fprintf(&b, "TOTAL: %s", money.String(money.Add(amounts...))) return b.String() } EOF cat > internal/report/report_test.go <<'EOF' package report import ( "testing" "billing/internal/money" ) func TestRender(t *testing.T) { got := Render([]Line{ {Label: "widget", Amount: money.Cents(1250)}, {Label: "shipping", Amount: money.Cents(499)}, }) want := "widget: 12.50\nshipping: 4.99\nTOTAL: 17.49" if got != want { t.Errorf("Render() =\n%q\nwant\n%q", got, want) } } EOF git init -q . && git add -A && git -c user.email=e@x -c user.name=n commit -qm base