Pinball dot-matrix clock animation thingy
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.
 
 

149 lines
3.1 KiB

package matrix
import (
"fmt"
"html/template"
"io"
"log"
"net"
"net/http"
"path/filepath"
"github.com/labstack/echo/v4/middleware"
"maze.io/matrix/driver"
"github.com/labstack/echo/v4"
)
var defaultServerConfig = &ServerConfig{
Listen: ":8025",
Template: "template",
Static: "static",
}
type ServerConfig struct {
Listen string
TLS bool
Certificate string
Key string
Template string
Static string
}
type Server struct {
router *echo.Echo
config *ServerConfig
loader *Loader
matrix driver.Matrix
}
func NewServer(config *ServerConfig, options ...ServerOption) (*Server, error) {
if _, err := net.ResolveTCPAddr("tcp", config.Listen); err != nil {
return nil, fmt.Errorf("matrix: invalid server listen %q: %w", config.Listen, err)
}
s := &Server{
router: echo.New(),
config: config,
}
var err error
if s.router.Renderer, err = newTemplateRenderer(config.Template); err != nil {
return nil, err
}
for _, option := range options {
if err := option(s); err != nil {
return nil, err
}
}
s.routes()
return s, nil
}
func (s *Server) Start() error {
if s.config.TLS {
if s.config.Certificate == "" {
return s.router.StartAutoTLS(s.config.Listen)
}
return s.router.StartTLS(s.config.Listen, s.config.Certificate, s.config.Key)
}
return s.router.Start(s.config.Listen)
}
func (s *Server) routes() {
s.router.HideBanner = true
s.router.HidePort = true
s.router.Use(middleware.Recover())
s.router.Use(middleware.Logger())
s.router.GET("/", s.handleIndex)
s.router.GET("/animation/set/:set", s.handleAnimationSetIndex)
s.router.GET("/animation/set/:set/:name", s.handleAnimationSetAnimation)
}
func (s *Server) handleIndex(c echo.Context) error {
return c.Render(http.StatusOK, "index.html", map[string]interface{}{
"Loader": s.loader,
"Matrix": s.matrix,
})
}
func (s *Server) handleAnimationSetIndex(c echo.Context) error {
set, ok := s.loader.BySet[c.Param("set")]
if !ok {
log.Printf("server: animation set %q not found", c.Param("set"))
return c.NoContent(http.StatusNotFound)
}
return c.Render(http.StatusOK, "animation_set.html", map[string]interface{}{
"Loader": s.loader,
"Matrix": s.matrix,
"Name": c.Param("set"),
"Animations": set,
})
}
func (s *Server) handleAnimationSetAnimation(context echo.Context) error {
return nil
}
type ServerOption func(*Server) error
func WithLoader(l *Loader) ServerOption {
return func(s *Server) error {
s.loader = l
return nil
}
}
func WithOutput(m driver.Matrix) ServerOption {
return func(s *Server) error {
s.matrix = m
return nil
}
}
type templateRenderer struct {
t *template.Template
}
func (r templateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
// Add global methods if data is a map
if viewContext, isMap := data.(map[string]interface{}); isMap {
viewContext["reverse"] = c.Echo().Reverse
}
return r.t.ExecuteTemplate(w, name, data)
}
func newTemplateRenderer(path string) (echo.Renderer, error) {
t, err := template.ParseGlob(filepath.Join(path, "*.html"))
if err != nil {
return nil, err
}
return templateRenderer{
t: t,
}, nil
}