Refactor main.go — the app holds MANY records
The second milestone — your app goes from one record to a shelf of them. The menu loop's skeleton stays; it becomes a slice, and the actions become Add / List / Mark.
One value becomes a slice:
var items []Item // a whole SHELF of records now, not just one
The menu pivots to three list actions:
fmt.Println("\n1) Add 2) List 3) Mark read q) Quit")
1) Add — validate the title (your validateTitle from lesson 7), read the pages, then append:
case "1":
fmt.Print("Title: ")
title := readLine()
if msg := validateTitle(title); msg != "" {
fmt.Println(msg)
continue
}
pages := readInt("Pages: ")
if pages <= 0 {
fmt.Println("Pages must be a positive number.")
continue
}
items = append(items, Item{Title: title, Pages: pages})
fmt.Println("Added:", title)
3) Mark read — reads a number and changes the element through the index, not a copy:
case "3":
n := readInt("Item number: ")
if n < 1 || n > len(items) {
fmt.Println("No item with that number.")
continue
}
items[n-1].Read = true // the REAL element, not a range copy
fmt.Println("Marked as read:", items[n-1].Title)
2) List — a new listItems that ranges the slice; your aligned formatItem finally pays off as a table:
func listItems(items []Item) {
if len(items) == 0 {
fmt.Println("No items yet.")
return
}
for i, it := range items {
fmt.Printf("%2d. %s\n", i+1, formatItem(it))
}
}
Two single-item pieces step aside — deliberately, not by accident: printItem retires (listItems took over its job, calling your formatItem directly), and progressPercent waits — it works on ONE item, so in the sample it has nothing to point at. In YOUR app, keep it: apply it to an item picked by number, the same way "Mark read" picks one (that's part of the homework).
Verify — add two multi-word titles, then list:
> 1
Title: War and Peace
Pages: 1225
Added: War and Peace
> 1
Title: Dune
Pages: 412
Added: Dune
> 2
1. War and Peace 0/1225 [reading]
2. Dune 0/412 [reading]
The titles read in full (spaces included), and formatItem's columns line up into a table — exactly what lesson 7's alignment was for.
Gotcha (the range copy).
for _, it := range items { it.Read = true }compiles and does nothing —itis a copy of each element. To change the real one, reach it by index:items[n-1].Read = true. The same copy trap as the struct in lesson 4, now inside a loop.