Capstone: CLI tip calculator
End of the line for Unit 1. Everything so far has been one idea at a time. This capstone joins them up into a small but real program: a command-line tip calculator for splitting a restaurant bill.
$ tip 48.50 good 3
Bill £48.50
Tip £6.06 (12.5%)
Total £54.56
Each £18.19 (3 people, 1p over)
It takes the bill, the tip (a percentage, or poor, good or great), and optionally how many people are paying. If anything's wrong, it prints a usage message instead of guessing.
How it's put together
There are two packages, as in 1.11:
tip(in tip/tip.go) does the sums: parsing a bill, picking a percentage, working out the tip, splitting the total and formatting money. It knows nothing about the command line.main(in main.go) reads the arguments, callstip, and lays out the result. The layout is already written. You add the argument checks.
flowchart TB accTitle: How the tip calculator runs accDescr: main passes the arguments to run. run checks there are two or three of them, then parses the bill with tip.ParsePounds, the tip with tip.Percent, and, if given, the number of people with strconv.Atoi. Any problem returns the usage message straight away. Otherwise run works out the tip, the total and each person's share, and returns the receipt for main to print. a["run(os.Args[1:])"] --> n["2 or 3 arguments?"] n --> b["tip.ParsePounds"] --> p["tip.Percent"] --> ppl["people: 1 to 50?"] ppl --> calc["tip.Tip, tip.Split"] --> out(["the receipt"]) n & b & p & ppl -.->|"no"| u(["usage"])
The work lives in run, not main. run takes the arguments and returns the output as a string, so the tests can call it with any arguments they like. main is one line: it passes os.Args[1:], which is PHP's $argv without the script name, and prints whatever comes back.
Laravel corner
In Laravel this would be an Artisan command with $signature = 'tip {bill} {tip} {people=1}', and Artisan (built on Symfony Console) would check the arguments and print the usage for you. Go's standard library has the flag package for -name=value options (lesson 7.5), and Cobra is the usual choice for big CLIs. For three positional arguments, a few ifs in run are all you need, and that's what you'll write.
What you'll use
Nearly every station in Unit 1 turns up somewhere:
| You'll need | From |
|---|---|
Money in pence as an int, formatted with %d.%02d |
1.6 |
float64(bill) before multiplying, and math.Round |
1.3 |
| Rounding up with whole-number division | 0.6 |
Functions that return a value and an ok |
1.2 |
A switch on the tip word |
1.10 |
Trimming £ and % with the strings package |
1.5 |
| Early returns for every bad argument | 1.8 |
| Exported functions in a second package | 1.11 |
There's one small new thing: turning text into numbers. strconv.ParseFloat and strconv.Atoi return the number and an error, and the Reference panel under the task shows the one-line check you need. Errors get a whole unit of their own later (Unit 4). For now, an error just means "that wasn't a number".
Tip
Work through it a function at a time. The tests are grouped by function, so TestFormat and TestTip can go green while TestRun is still red. Start at the bottom of tip/tip.go, where the functions are simplest, and work up. Leave run until last.
Mind the gap
Where PHP is better: number_format($pence / 100, 2) and round($x, 2) make money formatting one call in PHP. Go makes you choose between pence and floats, and say exactly how to round. That's more typing. The payoff is that every sum is exact, and the split never leaves the table a penny short.
When it's all green, Unit 1 is done. Unit 2 takes PHP's do-everything array apart into slices, maps and structs.