summaryrefslogtreecommitdiff
path: root/contrib/fs/file.go
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/fs/file.go')
-rw-r--r--contrib/fs/file.go55
1 files changed, 55 insertions, 0 deletions
diff --git a/contrib/fs/file.go b/contrib/fs/file.go
new file mode 100644
index 0000000..cdcd1a9
--- /dev/null
+++ b/contrib/fs/file.go
@@ -0,0 +1,55 @@
+package fs
+
+import (
+ "context"
+ "io/fs"
+ "mime"
+ "strings"
+
+ "tildegit.org/tjp/gus/gemini"
+)
+
+// FileHandler builds a handler function which serves up a file system.
+func FileHandler(fileSystem fs.FS) gemini.Handler {
+ return func(ctx context.Context, req *gemini.Request) *gemini.Response {
+ file, err := fileSystem.Open(strings.TrimPrefix(req.Path, "/"))
+ if isNotFound(err) {
+ return gemini.NotFound("Resource does not exist.")
+ }
+ if err != nil {
+ return gemini.Failure(err)
+ }
+
+ isDir, err := fileIsDir(file)
+ if err != nil {
+ return gemini.Failure(err)
+ }
+
+ if isDir {
+ return gemini.NotFound("Resource does not exist.")
+ }
+
+ return gemini.Success(mediaType(req.Path), file)
+ }
+}
+
+func mediaType(filePath string) string {
+ if strings.HasSuffix(filePath, ".gmi") {
+ // This may not be present in the listings searched by mime.TypeByExtension,
+ // so provide a dedicated fast path for it here.
+ return "text/gemini"
+ }
+
+ slashIdx := strings.LastIndex(filePath, "/")
+ dotIdx := strings.LastIndex(filePath[slashIdx+1:], ".")
+ if dotIdx == -1 {
+ return "application/octet-stream"
+ }
+ ext := filePath[slashIdx+dotIdx:]
+
+ mtype := mime.TypeByExtension(ext)
+ if mtype == "" {
+ return "application/octet-stream"
+ }
+ return mtype
+}