You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
3.6 KiB

package main
import (
"embed"
"html/template"
"io"
"log"
"math/rand"
"net/http"
"strconv"
)
//go:embed views/*.html
var tmplFS embed.FS
//go:embed public/*
var publicFS embed.FS
type Template struct {
templates *template.Template
}
func New() *Template {
templates := template.Must(template.New("").ParseFS(tmplFS, "views/*.html"))
return &Template{
templates: templates,
}
}
func (t *Template) Render(w io.Writer, name string, data any) error {
tmpl := template.Must(t.templates.Clone())
tmpl = template.Must(tmpl.ParseFS(tmplFS, "views/"+name))
return tmpl.ExecuteTemplate(w, "layout.html", data)
}
type Todo struct {
Id string
Title string
Completed bool
}
2 years ago
type ViewModel struct {
2 years ago
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
2 years ago
}
2 years ago
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() {
2 years ago
todos := make([]Todo, 0, 10)
t := New()
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 })
}
2 years ago
incomplete := countSlice(todos, func(todo Todo) bool { return !todo.Completed })
2 years ago
vm := ViewModel{
2 years ago
Todos: renderTodos,
Only: only,
Incomplete: incomplete,
}
2 years ago
err := t.Render(w, "list.html", vm)
if err != nil {
log.Println(err)
}
})
2 years ago
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()),
2 years ago
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)
})
2 years ago
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)
})
2 years ago
log.Print("Running on 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}