package todo import "strconv" // filter is which slice of the list a page shows. Its name is what forms // post back as `filter`; its path is where the 303 goes. type filter string const ( filterAll filter = "all" filterActive filter = "active" filterCompleted filter = "completed" ) // parseFilter reads a posted filter. Missing or unknown is "all". func parseFilter(s string) filter { switch f := filter(s); f { case filterActive, filterCompleted: return f } return filterAll } func (f filter) path() string { if f == filterAll { return "/" } return "/" + string(f) } func (f filter) shows(t Todo) bool { switch f { case filterActive: return !t.Completed case filterCompleted: return t.Completed } return true } // view is everything the page template draws. type view struct { Filter filter Items []item Total int Left int Completed int Editing bool } type item struct { ID int64 Title string Completed bool Editing bool EditHref string } // newView sorts the list into what the page shows. Counts cover the // whole list whichever filter is on; editing is the item whose editor is // open, or 0. func newView(todos []Todo, f filter, editing int64) view { v := view{Filter: f, Total: len(todos)} for _, t := range todos { if t.Completed { v.Completed++ } else { v.Left++ } if !f.shows(t) { continue } it := item{ID: t.ID, Title: t.Title, Completed: t.Completed, Editing: t.ID == editing, EditHref: f.path() + "?edit=" + strconv.FormatInt(t.ID, 10)} v.Editing = v.Editing || it.Editing v.Items = append(v.Items, it) } return v } // Class is the li's class attribute: completed, editing, both or none. func (it item) Class() string { switch { case it.Completed && it.Editing: return "completed editing" case it.Completed: return "completed" case it.Editing: return "editing" } return "" } // AllCompleted is toggle-all's checked state. func (v view) AllCompleted() bool { return v.Total > 0 && v.Left == 0 }