diff --git a/vanity.go b/vanity.go
index 6b5bfba..3538c38 100644
--- a/vanity.go
+++ b/vanity.go
@@ -7,21 +7,26 @@ import (
"strings"
)
-type Tag interface {
- get() string
+type config struct {
+ importTag *string
+ sourceTag *string
+ redir Redirector
}
-type strTag string
-
-func (t strTag) get() string {
- return string(t)
-}
+// Configures the Handler. The only required option is WithImport.
+type Option func(*config)
// Instructs the go tool where to fetch the repo at vcsRoot and the importPath
// that tree should be rooted at.
-func ImportTag(importPath, vcs, vcsRoot string) Tag {
- return strTag("")
+func WithImport(importPath, vcs, vcsRoot string) Option {
+ importTag := "meta name=\"go-import\" content=\"" + importPath + " " +
+ vcs + " " + vcsRoot + "\">"
+ return func(cfg *config) {
+ if cfg.importTag != nil {
+ panic(fmt.Sprintf("vanity: existing import tag: %s", *cfg.importTag))
+ }
+ cfg.importTag = &importTag
+ }
}
// Instructs gddo (godoc.org) how to direct browsers to browsable source code
@@ -33,22 +38,54 @@ func ImportTag(importPath, vcs, vcsRoot string) Tag {
//
// More information can be found at https://github.com/golang/gddo/wiki/Source-Code-Links.
//
-func SourceTag(prefix, home, directory, file string) Tag {
- return strTag("")
+func WithSource(prefix, home, directory, file string) Option {
+ sourceTag := "meta name=\"go-source\" content=\"" + prefix + " " +
+ home + " " + directory + " " + file + "\">"
+ return func(cfg *config) {
+ if cfg.sourceTag != nil {
+ panic(fmt.Sprintf("vanity: existing source tag: %s", *cfg.importTag))
+ }
+ cfg.sourceTag = &sourceTag
+ }
+}
+
+// When a browser navigates to the vanity URL of pkg, this function rewrites
+// pkg to a browsable URL.
+type Redirector func(pkg string) (url string)
+
+func WithRedirector(redir Redirector) Option {
+ return func(cfg *config) {
+ if cfg.redir != nil {
+ panic("vanity: existing Redirector")
+ }
+ cfg.redir = redir
+ }
}
// Returns an http.Handler that serves the vanity URL information for a single
-// repository. Each tag gives additional information to agents about the
-// repository and the packages it contains. An ImportTag is basically mandatory
-// since the go tool requires it to fetch the repository.
-func Handler(tags ...Tag) http.Handler {
+// repository. Each Option gives additional information to agents about the
+// repository or provides help to browsers that may have navigated to the vanity// URL. The WithImport Option is mandatory since the go tool requires it to
+// fetch the repository.
+func Handler(opts ...Option) http.Handler {
+ var redir Redirector
+
tpl := func() *template.Template {
- s := make([]string, len(tags))
- for i, t := range tags {
- s[i] = t.get()
+ // Process options.
+ var cfg config
+ for _, opt := range opts {
+ opt(&cfg)
}
- tagBlk := strings.Join(s, "\n")
+
+ // A WithImport is required.
+ if cfg.importTag == nil {
+ panic("vanity: WithImport is required")
+ }
+
+ tags := []string{*cfg.importTag}
+ if cfg.sourceTag != nil {
+ tags = append(tags, *cfg.sourceTag)
+ }
+ tagBlk := strings.Join(tags, "\n")
h := fmt.Sprintf(`
@@ -63,9 +100,16 @@ Nothing to see here; move along.
`, tagBlk)
+ redir = cfg.redir
return template.Must(template.New("").Parse(h))
}()
+ if redir == nil {
+ redir = func(pkg string) string {
+ return "https://godoc.org/" + pkg
+ }
+ }
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Redirect to https.
if r.URL.Scheme == "http" {
@@ -82,43 +126,45 @@ Nothing to see here; move along.
}
pkg := r.Host + r.URL.Path
+ redirURL := redir(pkg)
- // Redirect browsers to gddo.
+ // Issue an HTTP redirect if this is definitely a browser.
if r.FormValue("go-get") != "1" {
- url := "https://godoc.org/" + r.Host + r.URL.Path
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+ http.Redirect(w, r, redirURL, http.StatusTemporaryRedirect)
return
}
w.Header().Set("Cache-Control", "public, max-age=300")
- if err := tpl.ExecuteTemplate(w, "", pkg); err != nil {
+ if err := tpl.ExecuteTemplate(w, "", redirURL); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
}
+// Helpers for common VCSs.
+
// Redirects gddo to browsable source files for GitHub hosted repositories.
-func GitHubStyleSourceTag(importPath, repoPath, ref string) Tag {
+func WithGitHubStyleSource(importPath, repoPath, ref string) Option {
directory := repoPath + "/tree/" + ref + "{/dir}"
file := repoPath + "/blob/" + ref + "{/dir}/{file}#L{line}"
- return SourceTag(importPath, repoPath, directory, file)
+ return WithSource(importPath, repoPath, directory, file)
}
// Redirects gddo to browsable source files for Gogs hosted repositories.
-func GogsStyleSourceTag(importPath, repoPath, ref string) Tag {
+func WithGogsStyleSource(importPath, repoPath, ref string) Option {
directory := repoPath + "/src/" + ref + "{/dir}"
file := repoPath + "/src/" + ref + "{/dir}/{file}#L{line}"
- return SourceTag(importPath, repoPath, directory, file)
+ return WithSource(importPath, repoPath, directory, file)
}
// Creates a Handler that serves a GitHub repository at a specific importPath.
func GitHubHandler(importPath, user, repo, vcsScheme string) http.Handler {
ghImportPath := "github.com/" + user + "/" + repo
return Handler(
- ImportTag(importPath, "git", vcsScheme+"://"+ghImportPath),
- GitHubStyleSourceTag(importPath, "https://"+ghImportPath, "master"),
+ WithImport(importPath, "git", vcsScheme+"://"+ghImportPath),
+ WithGitHubStyleSource(importPath, "https://"+ghImportPath, "master"),
)
}
@@ -127,7 +173,7 @@ func GitHubHandler(importPath, user, repo, vcsScheme string) http.Handler {
func GogsHandler(importPath, host, user, repo, vcsScheme string) http.Handler {
gogsImportPath := host + "/" + user + "/" + repo
return Handler(
- ImportTag(importPath, "git", vcsScheme+"://"+gogsImportPath),
- GogsStyleSourceTag(importPath, "https://"+gogsImportPath, "master"),
+ WithImport(importPath, "git", vcsScheme+"://"+gogsImportPath),
+ WithGogsStyleSource(importPath, "https://"+gogsImportPath, "master"),
)
}