summaryrefslogtreecommitdiff
path: root/contrib/fs/gopher.go
blob: 05947308e7a55b7004d96594cc7d717bc578ac19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package fs

import (
	"context"
	"io/fs"
	"strings"
	"text/template"

	sr "tildegit.org/tjp/sliderule"
	"tildegit.org/tjp/sliderule/gopher"
)

// GopherFileHandler builds a handler which serves up files from a file system.
//
// It only serves responses for paths which correspond to files, not directories.
func GopherFileHandler(fileSystem fs.FS) sr.Handler {
	return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response {
		filepath, file, err := ResolveFile(request, fileSystem)
		if err != nil {
			return gopher.Error(err).Response()
		}

		if file == nil {
			return nil
		}

		return gopher.File(gopher.GuessItemType(filepath), file)
	})
}

// GopherDirectoryDefault serves up default files for directory path requests.
//
// If any of the supported filenames are found in the requested directory, the
// contents of that file is returned as the gopher response.
//
// It returns nil for any paths which don't correspond to a directory.
//
// It requires that files from the provided fs.FS implement fs.ReadDirFile. If
// they don't, it will produce nil responses for all directory paths.
func GopherDirectoryDefault(fileSystem fs.FS, filenames ...string) sr.Handler {
	return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response {
		dirpath, dir, err := ResolveDirectory(request, fileSystem)
		if err != nil {
			return gopher.Error(err).Response()
		}
		if dir == nil {
			return nil
		}
		defer func() { _ = dir.Close() }()

		_, file, err := ResolveDirectoryDefault(fileSystem, dirpath, dir, filenames)
		if err != nil {
			return gopher.Error(err).Response()
		}
		if file == nil {
			return nil
		}

		return gopher.File(gopher.MenuType, file)
	})
}

// GopherDirectoryListing produces a listing of the contents of any requested directories.
//
// It returns nil for any paths which don't correspond to a filesystem directory.
//
// It requires that files from the provided fs.FS implement fs.ReadDirFile. If they
// don't, it will produce nil responses for any directory paths.
//
// A template may be nil, in which case DefaultGopherDirectoryList is used instead. The
// template is then processed with RenderDirectoryListing.
func GopherDirectoryListing(fileSystem fs.FS, tpl *template.Template) sr.Handler {
	return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response {
		dirpath, dir, err := ResolveDirectory(request, fileSystem)
		if err != nil {
			return gopher.Error(err).Response()
		}
		if dir == nil {
			return nil
		}
		defer func() { _ = dir.Close() }()

		if tpl == nil {
			tpl = DefaultGopherDirectoryList
		}
		body, err := RenderDirectoryListing(dirpath, dir, tpl, request.Server)
		if err != nil {
			return gopher.Error(err).Response()
		}

		return gopher.File(gopher.MenuType, body)
	})
}

// GopherTemplateFunctions is a map for templates providing useful functions for gophermaps.
//
// - GuessItemType: return a gopher item type for a file based on its path/name.
var GopherTemplateFunctions = template.FuncMap{
	"GuessItemType": func(filepath string) string {
		return string([]byte{byte(gopher.GuessItemType(filepath))})
	},
}

// DefaultGopherDirectoryList is a template which renders a directory listing as gophermap.
var DefaultGopherDirectoryList = template.Must(
	template.New("gopher_dirlist").Funcs(GopherTemplateFunctions).Parse(
		strings.ReplaceAll(
			`
{{ $root := .FullPath -}}
{{ if eq .FullPath "." }}{{ $root = "" }}{{ end -}}
{{ $hostname := .Hostname -}}
{{ $port := .Port -}}
i{{ .DirName }}		{{ $hostname }}	{{ $port }}
i		{{ $hostname }}	{{ $port }}
{{ range .Entries -}}
{{ if .IsDir -}}
1{{ .Name }}	{{ $root }}/{{ .Name }}	{{ $hostname }}	{{ $port }}
{{- else -}}
{{ GuessItemType .Name }}{{ .Name }}	{{ $root }}/{{ .Name }}	{{ $hostname }}	{{ $port }}
{{- end }}
{{ end -}}
.
`[1:],
			"\n",
			"\r\n",
		),
	),
)