package uncanny import ( "log" "fmt" "net/http" "path" "strconv" ) const ( // highest slot number (range: 0..maxSlot) maxSlot = 4 ) type Server struct { *http.ServeMux can *Can } func NewServer(can *Can) *Server { ret := &Server{ ServeMux: http.NewServeMux(), can: can, } ret.registerHandlers() return ret } func (s *Server) registerHandlers() { s.HandleFunc("/", http.NotFound) s.HandleFunc("/dispense/", func(rw http.ResponseWriter, r *http.Request) { if matched, _ := path.Match("/dispense/[0-9]?[0-9]*", r.URL.Path); matched { slot, err := strconv.ParseUint(path.Base(r.URL.Path), 10, 32) if err != nil { log.Printf("Error decoding slot number: %v", err) http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } else if slot < 0 || slot > maxSlot { log.Printf("Invalid slot number: %u", slot) http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } else { err = s.can.Dispense(int(slot)) if err != nil { log.Printf("Error sending dispense command: %v", err) rw.Header().Set("Content-Type", "text/plain") rw.WriteHeader(http.StatusServiceUnavailable) fmt.Fprintf(rw, "%u error", slot) } else { rw.Header().Set("Content-Type", "text/plain") rw.WriteHeader(http.StatusOK) fmt.Fprintf(rw, "%u dispense", slot) } } } else { http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } }) s.HandleFunc("/stop", func(rw http.ResponseWriter, r *http.Request) { err := s.can.Cancel() if err != nil { log.Printf("Error sending stop command: %v", err) rw.Header().Set("Content-Type", "text/plain") rw.WriteHeader(http.StatusServiceUnavailable) fmt.Fprintf(rw, "error") } else { rw.Header().Set("Content-Type", "text/plain") rw.WriteHeader(http.StatusOK) fmt.Fprintf(rw, "stopped") } }) s.HandleFunc("/level/", func(rw http.ResponseWriter, r *http.Request) { if matched, _ := path.Match("/level/[0-9]?[0-9]*", r.URL.Path); matched { slot, err := strconv.ParseUint(path.Base(r.URL.Path), 10, 32) if err != nil { log.Printf("Error decoding slot number: %v", err) http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } else if slot < 0 || slot > maxSlot { log.Printf("Invalid slot number: %u", slot) http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } else { rw.Header().Set("Content-Type", "text/plain") rw.WriteHeader(http.StatusOK) if s.can.IsEmpty(int(slot)) { fmt.Fprintf(rw, "%u empty", slot) } else { fmt.Fprintf(rw, "%u full", slot) } } } else { http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } }) s.HandleFunc("/active/", func(rw http.ResponseWriter, r *http.Request) { if matched, _ := path.Match("/active/[0-9]?[0-9]*", r.URL.Path); matched { slot, err := strconv.ParseUint(path.Base(r.URL.Path), 10, 32) if err != nil { log.Printf("Error decoding slot number: %v", err) http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } else if slot < 0 || slot > maxSlot { log.Printf("Invalid slot number: %u", slot) http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } else { rw.Header().Set("Content-Type", "text/plain") rw.WriteHeader(http.StatusOK) if s.can.IsDispensing(int(slot)) { fmt.Fprintf(rw, "%u dispensing", slot) } else { fmt.Fprintf(rw, "%u off", slot) } } } else { http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } }) } func (s *Server) ListenAndServe(addr string) error { return http.ListenAndServe(addr, s) }