Use a more general Option parameter

git-svn-id: file:///srv/svn/repo/toyohime/trunk@85 922d331f-388e-da47-97a9-ad700dc0b8b9
This commit is contained in:
jonbetti 2018-06-06 03:28:38 +00:00
parent 7669f77df1
commit f68c3c6bf4

112
vanity.go
View File

@ -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("<meta name=\"go-import\" content=\"" + importPath + " " +
vcs + " " + vcsRoot + "\">")
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("<meta name=\"go-source\" content=\"" + prefix + " " + home +
" " + directory + " " + file + "\">")
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(`<!DOCTYPE html>
<html>
@ -63,9 +100,16 @@ Nothing to see here; <a href="{{ . }}">move along</a>.
</html>
`, 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; <a href="{{ . }}">move along</a>.
}
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"),
)
}