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:
parent
7669f77df1
commit
f68c3c6bf4
112
vanity.go
112
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("<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"),
|
||||
)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user