summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Paul <n@nc0.fr>2023-05-26 23:19:10 +0200
committerNicolas Paul <n@nc0.fr>2023-05-26 23:19:27 +0200
commit9287bcd3b5fbefd260c0202ada4573d4a93961a1 (patch)
tree143a2c56b2c85f442fd3749bfff3116f813d63cd
parent39c37ae2178ba9fb69c4d51c773c27708729ce7b (diff)
Add HTML templates
These templates generate HTML documents that informs visitors and the Go toolchain about the location of the module's source and documentation page (on pkg.go.dev) Signed-off-by: Nicolas Paul <n@nc0.fr>
-rw-r--r--templates.go82
-rw-r--r--templates_test.go158
2 files changed, 240 insertions, 0 deletions
diff --git a/templates.go b/templates.go
new file mode 100644
index 0000000..1024325
--- /dev/null
+++ b/templates.go
@@ -0,0 +1,82 @@
+package main
+
+import (
+ "fmt"
+ "html/template"
+ "io"
+)
+
+// HTML document templates
+var (
+ // indexTmpl is the HTML template for the root of the static site.
+ // It redirects to a site configured by the user.
+ //
+ // {{.Count}} corresponds to the timer—in seconds— before redirecting.
+ // {{.Redirect}} is the URL to redirect visitors to.
+ indexTmpl *template.Template = template.Must(
+ template.New("index").Parse(`<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta http-equiv="refresh" content="{{.Count}}; url='{{.Redirect}}'">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<p>Nothing to see, redirecting <a href="{{.Redirect}}">here</a>.`))
+
+ // moduleTmpl is the HTML template for module-specific documents.
+ // They include information for the Go toolchain to find and download
+ // the module source code. It also redirects users to the module's
+ // documentation on pkg.go.dev.
+ //
+ // {{.Prefix}} is the module's prefix, a.k.a. import path,
+ // e.g. example.com/foo
+ // {{.Vcs}} is the version control system used in the codebase
+ // {{.Home}} is the repository's home.
+ // {{.Dir}} is a URL template to a page listing the files inside a package
+ // {{.File}} is a URL template listing the lines of a file
+ // {{.Doc}} is the URL of the module's documentation on pkg.go.dev
+ //
+ // Templates support a specific set of substitutions which are documented
+ // here: https://github.com/golang/gddo/wiki/Source-Code-Links
+ moduleTmpl *template.Template = template.Must(
+ template.New("module").Parse(`<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta name="go-import" content="{{.Prefix}} {{.Vcs}} {{.Home}}">
+<meta name="go-source" content="{{.Prefix}} {{.Home}} {{.Dir}} {{.File}}">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<p>There is nothing to see, redirecting <a href="{{.Doc}}">here</a>.`))
+)
+
+// ExecIndex constructs an HTML document for the index of the generated site
+// in the given writer "w."
+// The document redirects visitor to the specified "url" after "count" seconds.
+func ExecIndex(w io.Writer, url string, count uint8) error {
+ return indexTmpl.Execute(w, struct {
+ Redirect string
+ Count uint8
+ }{
+ Redirect: url,
+ Count: count,
+ })
+}
+
+// ExecModule constructs an HTML document for a module indexed on the domain
+// in the given writer "w." The "prefix" corresponds to the import path of the
+// module, "vcs" to the version control system used — git, bazaar..., "home"
+// is the repository's home and "dir"/"file" are URL templates as documented
+// by GoDoc: https://github.com/golang/gddo/wiki/Source-Code-Links.
+func ExecModule(w io.Writer, prefix, vcs,
+ home, dir, file string) error {
+ return moduleTmpl.Execute(w, struct {
+ Prefix string
+ Vcs string
+ Home string
+ Dir string
+ File string
+ Doc string
+ }{
+ Prefix: prefix,
+ Vcs: vcs,
+ Home: home,
+ Dir: dir,
+ File: file,
+ Doc: fmt.Sprintf("https://pkg.go.dev/%s", prefix),
+ })
+}
diff --git a/templates_test.go b/templates_test.go
new file mode 100644
index 0000000..680651f
--- /dev/null
+++ b/templates_test.go
@@ -0,0 +1,158 @@
+package main
+
+import (
+ "bytes"
+ "io"
+ "testing"
+)
+
+func TestExecIndex(t *testing.T) {
+ type args struct {
+ url string
+ count uint8
+ }
+ tests := []struct {
+ name string
+ args args
+ want *bytes.Buffer
+ wantErr bool
+ }{
+ {
+ name: "valid url waiting 5 seconds",
+ args: args{
+ url: "https://example.com",
+ count: 5,
+ },
+ want: bytes.NewBufferString(`<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta http-equiv="refresh" content="5; url='https://example.com'">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<p>Nothing to see, redirecting <a href="https://example.com">here</a>.`),
+ wantErr: false,
+ },
+ {
+ name: "valid url waiting 0 second",
+ args: args{
+ url: "https://example.com",
+ count: 0,
+ },
+ want: bytes.NewBufferString(`<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta http-equiv="refresh" content="0; url='https://example.com'">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<p>Nothing to see, redirecting <a href="https://example.com">here</a>.`),
+ wantErr: false,
+ },
+ {
+ name: "not the same url",
+ args: args{
+ url: "https://example.com",
+ count: 0,
+ },
+ want: bytes.NewBufferString(`<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta http-equiv="refresh" content="0; url='https://example2.com'">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<p>Nothing to see, redirecting <a href="https://example.com">here</a>.`),
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var buf bytes.Buffer
+ err := ExecIndex(&buf, tt.args.url, tt.args.count)
+
+ if !tt.wantErr && err != nil {
+ t.Errorf("ExecIndex() error = %v", err)
+ }
+
+ if !tt.wantErr && !bytes.Equal(buf.Bytes(), tt.want.Bytes()) {
+ t.Errorf("ExecIndex() = %v, want %v", buf, tt.want)
+ }
+ })
+ }
+}
+
+func TestExecModule(t *testing.T) {
+ type args struct {
+ w *io.Writer
+ prefix string
+ vcs string
+ home string
+ dir string
+ file string
+ }
+ tests := []struct {
+ name string
+ args args
+ want *bytes.Buffer
+ wantErr bool
+ }{
+ {
+ name: "module example.com/foo hosted on github",
+ args: args{
+ prefix: "example.com/foo",
+ vcs: "git",
+ home: "https://github.com/example/foo",
+ dir: "https://github.com/example/foo/tree/master{/dir}",
+ file: "https://github.com/example/foo/blob/master{/dir}/{file}#L{line}",
+ },
+ want: bytes.NewBufferString(`<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta name="go-import" content="example.com/foo git https://github.com/example/foo">
+<meta name="go-source" content="example.com/foo https://github.com/example/foo https://github.com/example/foo/tree/master{/dir} https://github.com/example/foo/blob/master{/dir}/{file}#L{line}">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<p>There is nothing to see, redirecting <a href="https://pkg.go.dev/example.com/foo">here</a>.`),
+ wantErr: false,
+ },
+ {
+ name: "module example.com/foo hosted on gitlab",
+ args: args{
+ prefix: "example.com/foo",
+ vcs: "git",
+ home: "https://gitlab.com/example/foo",
+ dir: "https://gitlab.com/example/foo/-/tree/master{/dir}",
+ file: "https://gitlab.com/example/foo/-/blob/master{/dir}/{file}#L{line}",
+ },
+ want: bytes.NewBufferString(`<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta name="go-import" content="example.com/foo git https://gitlab.com/example/foo">
+<meta name="go-source" content="example.com/foo https://gitlab.com/example/foo https://gitlab.com/example/foo/-/tree/master{/dir} https://gitlab.com/example/foo/-/blob/master{/dir}/{file}#L{line}">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<p>There is nothing to see, redirecting <a href="https://pkg.go.dev/example.com/foo">here</a>.`),
+ wantErr: false,
+ },
+ {
+ name: "module example.com/foo hosted on Source Hut with Mercurial",
+ args: args{
+ prefix: "example.com/foo",
+ vcs: "hg",
+ home: "https://hg.sr.ht/~example/foo",
+ dir: "https://hg.sr.ht/~example/foo/tree/master{/dir}",
+ file: "https://hg.sr.ht/~example/foo/browse/master{/dir}/{file}#{line}",
+ },
+ want: bytes.NewBufferString(`<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta name="go-import" content="example.com/foo hg https://hg.sr.ht/~example/foo">
+<meta name="go-source" content="example.com/foo https://hg.sr.ht/~example/foo https://hg.sr.ht/~example/foo/tree/master{/dir} https://hg.sr.ht/~example/foo/browse/master{/dir}/{file}#{line}">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<p>There is nothing to see, redirecting <a href="https://pkg.go.dev/example.com/foo">here</a>.`),
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var buf bytes.Buffer
+ err := ExecModule(&buf, tt.args.prefix, tt.args.vcs, tt.args.home,
+ tt.args.dir, tt.args.file)
+
+ if !tt.wantErr && err != nil {
+ t.Errorf("ExecIndex() error = %v", err)
+ }
+
+ if !tt.wantErr && !bytes.Equal(buf.Bytes(), tt.want.Bytes()) {
+ t.Errorf("ExecIndex() = %v, want %v", buf, tt.want)
+ }
+ })
+ }
+}