summaryrefslogtreecommitdiff
path: root/gemini.go
diff options
context:
space:
mode:
authortjp <tjp@ctrl-c.club>2023-11-14 15:44:00 -0700
committertjp <tjp@ctrl-c.club>2023-11-14 15:44:00 -0700
commit105022b22d4b297057a7a946e3d6e1202bc6d1d5 (patch)
tree840eea2a699f28aac1443b2275f9a05abb098fb5 /gemini.go
parente8122362a0af5a499dcba182d6e7b41bd4b3eccf (diff)
Protocol refactor
Diffstat (limited to 'gemini.go')
-rw-r--r--gemini.go116
1 files changed, 20 insertions, 96 deletions
diff --git a/gemini.go b/gemini.go
index 878b2b8..390a0fc 100644
--- a/gemini.go
+++ b/gemini.go
@@ -1,24 +1,13 @@
package syw
import (
- "bytes"
"context"
- "mime"
- "os"
- "path"
- "path/filepath"
- "strings"
"text/template"
"tildegit.org/tjp/sliderule"
"tildegit.org/tjp/sliderule/gemini"
)
-const (
- repokey = "syw_repo"
- reponamekey = "repository"
-)
-
// GeminiRouter builds a router that will handle requests into a directory of git repositories.
//
// The routes it defines are:
@@ -52,7 +41,8 @@ const (
// Repo: a *syw.Repository object corresponding to <repodir>/:repository
// Params: a map[string]string of the route parameters
//
-// The only exception is repo_root.gmi, which is rendered with a slice of the repo names instead.
+// The only exception is repo_root.gmi, which is rendered with an object containing a single name
+// "Repos", which is a slice of the repository names.
func GeminiRouter(repodir string, overrides *template.Template) *sliderule.Router {
tmpl, err := addTemplates(geminiTemplate, overrides)
if err != nil {
@@ -61,99 +51,33 @@ func GeminiRouter(repodir string, overrides *template.Template) *sliderule.Route
repoRouter := &sliderule.Router{}
repoRouter.Use(assignRepo(repodir))
- repoRouter.Route("/", gmiTemplate(tmpl, "repo_home.gmi"))
- repoRouter.Route("/branches", gmiTemplate(tmpl, "branch_list.gmi"))
- repoRouter.Route("/tags", gmiTemplate(tmpl, "tag_list.gmi"))
- repoRouter.Route("/refs/:ref/", gmiTemplate(tmpl, "ref.gmi"))
- repoRouter.Route("/refs/:ref/tree/*path", geminiTreePath(tmpl))
- repoRouter.Route("/diffstat/:fromref/:toref", runGemiTemplate(tmpl, "diffstat.gmi.txt", "text/plain"))
- repoRouter.Route("/diff/:fromref/:toref", runGemiTemplate(tmpl, "diff.gmi.txt", "text/x-diff"))
+ repoRouter.Route("/", repoRouteHandler(geminiProto, tmpl, "repo_home.gmi"))
+ repoRouter.Route("/branches", repoRouteHandler(geminiProto, tmpl, "branch_list.gmi"))
+ repoRouter.Route("/tags", repoRouteHandler(geminiProto, tmpl, "tag_list.gmi"))
+ repoRouter.Route("/refs/:ref/", repoRouteHandler(geminiProto, tmpl, "ref.gmi"))
+ repoRouter.Route("/refs/:ref/tree/*path", treePathHandler(geminiProto, tmpl, "tree.gmi"))
+ repoRouter.Route("/diffstat/:fromref/:toref", repoRouteHandler(geminiProto, tmpl, "diffstat.gmi.txt"))
+ repoRouter.Route("/diff/:fromref/:toref", repoRouteHandler(geminiProto, tmpl, "diff.gmi.txt"))
router := &sliderule.Router{}
- router.Route("/", geminiRoot(repodir, tmpl))
+ router.Route("/", rootDirHandler(geminiProto, repodir, tmpl, "repo_root.gmi"))
router.Mount("/:"+reponamekey, repoRouter)
return router
}
-func assignRepo(repodir string) sliderule.Middleware {
- return func(h sliderule.Handler) sliderule.Handler {
- return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
- repo := Open(filepath.Join(repodir, sliderule.RouteParams(ctx)[reponamekey]))
- return h.Handle(context.WithValue(ctx, repokey, repo), request)
- })
- }
-}
-
-func geminiRoot(repodir string, tmpl *template.Template) sliderule.Handler {
- return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
- entries, err := os.ReadDir(repodir)
- if err != nil {
- return gemini.Failure(err)
- }
+type geminiProtocol struct{ sliderule.ServerProtocol }
- names := []string{}
- for _, item := range entries {
- if Open(filepath.Join(repodir, item.Name())) != nil {
- names = append(names, item.Name())
- }
- }
-
- buf := &bytes.Buffer{}
- if err := tmpl.ExecuteTemplate(buf, "repo_root.gmi", names); err != nil {
- return gemini.Failure(err)
- }
-
- return gemini.Success("text/gemini; charset=utf-8", buf)
- })
+func (geminiProtocol) TemplateBaseData(_ context.Context, _ *sliderule.Request) map[string]any {
+ return map[string]any{}
}
-func geminiTreePath(tmpl *template.Template) sliderule.Handler {
- return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
- params := sliderule.RouteParams(ctx)
- if params["path"] == "" || strings.HasSuffix(params["path"], "/") {
- return gmiTemplate(tmpl, "tree.gmi").Handle(ctx, request)
- }
-
- repo := ctx.Value(repokey).(*Repository)
-
- body, err := repo.Blob(ctx, params["ref"], params["path"])
- if err != nil {
- return gemini.Failure(err)
- }
-
- mediaType := ""
- ext := path.Ext(params["path"])
- if ext == ".gmi" {
- mediaType = "text/gemini; charset=utf-8"
- } else {
- mediaType = mime.TypeByExtension(ext)
- }
- if mediaType == "" {
- mediaType = "application/octet-stream"
- }
-
- return gemini.Success(mediaType, bytes.NewBuffer(body))
- })
-}
-
-func gmiTemplate(tmpl *template.Template, name string) sliderule.Handler {
- return runGemiTemplate(tmpl, name, "text/gemini; charset=utf-8")
+func (geminiProtocol) TemplateRepoData(ctx context.Context, request *sliderule.Request) map[string]any {
+ return map[string]any{
+ "Ctx": ctx,
+ "Repo": ctx.Value(repokey),
+ "Params": sliderule.RouteParams(ctx),
+ }
}
-func runGemiTemplate(tmpl *template.Template, name, mimetype string) sliderule.Handler {
- return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
- obj := map[string]any{
- "Ctx": ctx,
- "Repo": ctx.Value(repokey),
- "Params": sliderule.RouteParams(ctx),
- }
- buf := &bytes.Buffer{}
-
- if err := tmpl.ExecuteTemplate(buf, name, obj); err != nil {
- return gemini.Failure(err)
- }
-
- return gemini.Success(mimetype, buf)
- })
-}
+var geminiProto = geminiProtocol{gemini.ServerProtocol}