164 lines
4.2 KiB
Go
164 lines
4.2 KiB
Go
package website
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"io"
|
|
"io/fs"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.handmade.network/hmn/hmn/src/hmnurl"
|
|
"git.handmade.network/hmn/hmn/src/oops"
|
|
"git.handmade.network/hmn/hmn/src/templates"
|
|
"git.handmade.network/hmn/hmn/src/utils"
|
|
)
|
|
|
|
// This will skip the common path prefix for fishbowl files.
|
|
// We unfortunately need to do this because we want to use http.FileServer,
|
|
// but _that_ needs an http.FS, but _that_ needs an fs.FS...
|
|
var fishbowlFS = utils.Must1(fs.Sub(templates.FishbowlFS, "src/fishbowls"))
|
|
var fishbowlHTTPFS = http.StripPrefix("/fishbowl", http.FileServer(http.FS(fishbowlFS)))
|
|
|
|
type fishbowlInfo struct {
|
|
Slug string
|
|
Title string
|
|
Month time.Month
|
|
Year int
|
|
ContentsPath string
|
|
}
|
|
|
|
var fishbowls = [...]fishbowlInfo{
|
|
{
|
|
Slug: "internet-os",
|
|
Title: "The future of operating systems in an Internet world",
|
|
Month: time.May, Year: 2020,
|
|
},
|
|
{
|
|
Slug: "metaprogramming",
|
|
Title: "Compile-time introspection and metaprogramming",
|
|
Month: time.June, Year: 2020,
|
|
},
|
|
{
|
|
Slug: "lisp-jam",
|
|
Title: "Lessons from the LISP Jam",
|
|
Month: time.August, Year: 2020,
|
|
},
|
|
{
|
|
Slug: "parallel-programming",
|
|
Title: "Approaches to parallel programming",
|
|
Month: time.November, Year: 2020,
|
|
},
|
|
{
|
|
Slug: "skimming",
|
|
Title: "Code skimmability as the root cause for bad code structure decisions", // real snappy, this one
|
|
Month: time.January, Year: 2021,
|
|
},
|
|
{
|
|
Slug: "config",
|
|
Title: "How to design to avoid configuration",
|
|
Month: time.March, Year: 2021,
|
|
},
|
|
{
|
|
Slug: "simplicity-performance",
|
|
Title: "The relationship of simplicity and performance",
|
|
Month: time.May, Year: 2021,
|
|
},
|
|
{
|
|
Slug: "teaching-software",
|
|
Title: "How software development is taught",
|
|
Month: time.June, Year: 2021,
|
|
},
|
|
{
|
|
Slug: "flexible-software",
|
|
Title: "How to design flexible software",
|
|
Month: time.December, Year: 2021,
|
|
},
|
|
{
|
|
Slug: "oop",
|
|
Title: "What, if anything, is OOP?",
|
|
Month: time.May, Year: 2022,
|
|
ContentsPath: "oop/OOP.html",
|
|
},
|
|
}
|
|
|
|
func FishbowlIndex(c *RequestContext) ResponseData {
|
|
type fishbowlTmpl struct {
|
|
Date string
|
|
Title string
|
|
Url string
|
|
}
|
|
|
|
var fishbowlTmpls []fishbowlTmpl
|
|
for _, f := range fishbowls {
|
|
fishbowlTmpls = append(fishbowlTmpls, fishbowlTmpl{
|
|
Date: fmt.Sprintf("%s %d", f.Month.String(), f.Year),
|
|
Title: f.Title,
|
|
Url: hmnurl.BuildFishbowl(f.Slug),
|
|
})
|
|
}
|
|
|
|
var res ResponseData
|
|
err := res.WriteTemplate("fishbowl_index.html", getBaseData(c, "Fishbowls", nil), c.Perf)
|
|
if err != nil {
|
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render fishbowl index page"))
|
|
}
|
|
return res
|
|
}
|
|
|
|
func Fishbowl(c *RequestContext) ResponseData {
|
|
slug := c.PathParams["slug"]
|
|
var info fishbowlInfo
|
|
|
|
// Only serve up valid fishbowls (with content)
|
|
exists := false
|
|
for _, fishbowl := range fishbowls {
|
|
if fishbowl.Slug == slug && fishbowl.ContentsPath != "" {
|
|
exists = true
|
|
info = fishbowl
|
|
}
|
|
}
|
|
if !exists {
|
|
return FourOhFour(c)
|
|
}
|
|
|
|
// Ensure trailing slash (it matters for relative URLs in the HTML)
|
|
if !strings.HasSuffix(c.URL().Path, "/") {
|
|
return c.Redirect(c.URL().Path+"/", http.StatusFound)
|
|
}
|
|
|
|
type FishbowlData struct {
|
|
templates.BaseData
|
|
Slug string
|
|
Info fishbowlInfo
|
|
Contents template.HTML
|
|
}
|
|
|
|
contentsFile := utils.Must1(fishbowlFS.Open(info.ContentsPath))
|
|
|
|
tmpl := FishbowlData{
|
|
BaseData: getBaseData(c, info.Title, []templates.Breadcrumb{
|
|
{Name: "Fishbowls", Url: hmnurl.BuildFishbowlIndex()},
|
|
{Name: info.Title, Url: hmnurl.BuildFishbowl(slug)},
|
|
}),
|
|
Slug: slug,
|
|
Info: info,
|
|
Contents: template.HTML(utils.Must1(io.ReadAll(contentsFile))),
|
|
}
|
|
|
|
var res ResponseData
|
|
err := res.WriteTemplate("fishbowl.html", tmpl, c.Perf)
|
|
if err != nil {
|
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render fishbowl index page"))
|
|
}
|
|
return res
|
|
}
|
|
|
|
func FishbowlFiles(c *RequestContext) ResponseData {
|
|
var res ResponseData
|
|
fishbowlHTTPFS.ServeHTTP(&res, c.Req)
|
|
AddCORSHeaders(c, &res)
|
|
return res
|
|
}
|