Refactor main.go — a clean display and a real check
The menu loop stays exactly as it was — we add two functions and one check. Everything else in your main.go is untouched.
1. A correct title check — validateTitle. It counts characters (runes), not bytes:
func validateTitle(title string) string {
title = strings.TrimSpace(title)
n := utf8.RuneCountInString(title)
if n == 0 {
return "Title cannot be empty."
}
if n > 60 {
return fmt.Sprintf("Title is too long: %d characters (max 60).", n)
}
return ""
}
Had we written len(title) > 60, a Lithuanian title with diacritics would be rejected too early — len counts bytes. This one function is step 2's gotcha, applied. (Add "unicode/utf8" to the imports.)
2. Use it in "Set title". One check added inside case "2" — validate before storing:
case "2":
fmt.Print("New title: ")
title := readLine()
if msg := validateTitle(title); msg != "" {
fmt.Println(msg)
continue
}
it.Title = title
fmt.Println("Title set to:", title)
3. A clean, ALIGNED display — formatItem. %-30s left-aligns the title in a 30-column field, %3d/%-3d pads the numbers, so rows line up:
func formatItem(it Item) string {
status := "reading"
if it.Read {
status = "done"
}
return fmt.Sprintf("%-30s %3d/%-3d [%s]", it.Title, it.PagesRead, it.Pages, status)
}
printItem now just calls it — fmt.Println(formatItem(it)). In lesson 8, with many records, the alignment pays off: a tidy table for free.
Verify — set a multi-word Lithuanian title, then show it:
> 2
New title: Ąžuolas ir uosis
Title set to: Ąžuolas ir uosis
> 1
Ąžuolas ir uosis 0/380 [reading]
The title is read in full (spaces included — readLine from lesson 5), and the columns line up.
Gotcha (
fmt.Scanlnand spaces). If you read the title withfmt.Scanln(&title), "Ąžuolas ir uosis" becomes just "Ąžuolas" — the rest of the line stays in the buffer and scrambles the next read. For multi-word text, always read the whole line withreadLine, notScanln.