package main import ( "embed" "log" "math/rand" "net/http" "strconv" "jxs.me/todogo/views" ) //go:embed public/* var publicFS embed.FS type Todo struct { Id string Title string Completed bool } type ViewModel struct { Todos []Todo Only string Incomplete int } func filterSlice[T any](xs []T, pred func(T) bool) []T { result := make([]T, 0, len(xs)) for _, element := range xs { if pred(element) { result = append(result, element) } } return result } func countSlice[T any](xs []T, pred func(T) bool) int { count := 0 for _, element := range xs { if pred(element) { count += 1 } } return count } func main() { todos := make([]Todo, 0, 10) t := views.New() list := t.Renderer("list.html") http.Handle("/public/", http.FileServer(http.FS(publicFS))) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() renderTodos := todos only := q.Get("only") if only == "active" { renderTodos = filterSlice(todos, func(todo Todo) bool { return !todo.Completed }) } if only == "completed" { renderTodos = filterSlice(todos, func(todo Todo) bool { return todo.Completed }) } incomplete := countSlice(todos, func(todo Todo) bool { return !todo.Completed }) vm := ViewModel{ Todos: renderTodos, Only: only, Incomplete: incomplete, } list(w, r, vm) }) http.HandleFunc("/todo", func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(http.StatusMethodNotAllowed) return } if err := r.ParseForm(); err != nil { w.WriteHeader(http.StatusBadRequest) return } title := r.Form.Get("title") todos = append(todos, Todo{ Id: strconv.Itoa(rand.Int()), Title: title, }) w.Header().Set("Location", "/") w.WriteHeader(http.StatusSeeOther) }) http.HandleFunc("/toggle", func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(http.StatusMethodNotAllowed) return } if err := r.ParseForm(); err != nil { w.WriteHeader(http.StatusBadRequest) return } id := r.Form.Get("id") for i, todo := range todos { if todo.Id == id { todo.Completed = !todo.Completed todos[i] = todo } } w.Header().Set("Location", "/") w.WriteHeader(http.StatusSeeOther) }) http.HandleFunc("/destroy", func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(http.StatusMethodNotAllowed) return } if err := r.ParseForm(); err != nil { w.WriteHeader(http.StatusBadRequest) return } id := r.Form.Get("id") todos = filterSlice(todos, func(t Todo) bool { return t.Id != id }) w.Header().Set("Location", "/") w.WriteHeader(http.StatusSeeOther) }) http.HandleFunc("/clear", func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(http.StatusMethodNotAllowed) return } if err := r.ParseForm(); err != nil { w.WriteHeader(http.StatusBadRequest) return } todos = filterSlice(todos, func(t Todo) bool { return !t.Completed }) w.Header().Set("Location", "/") w.WriteHeader(http.StatusSeeOther) }) log.Print("Running on 8080") log.Fatal(http.ListenAndServe(":8080", nil)) }