Vectrum
Sign In
82 lines · 1.6 KB · Shell
#!/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