#!/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