Stable APIs for Go. https://go.code.as
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
7.0 KiB

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "html/template"
  6. "log"
  7. "net/http"
  8. "net/url"
  9. "os"
  10. "sort"
  11. "strings"
  12. )
  13. const packageTemplateString = `<!DOCTYPE html>
  14. <html >
  15. <head>
  16. <meta charset="utf-8">
  17. <title>{{.Repo.PackageName}}.{{.Repo.MajorVersion}}{{.Repo.SubPath}} - {{.Repo.GopkgPath}}</title>
  18. <link href='//fonts.googleapis.com/css?family=Ubuntu+Mono|Ubuntu' rel='stylesheet' >
  19. <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet" >
  20. <link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" >
  21. <style>
  22. html,
  23. body {
  24. height: 100%;
  25. }
  26. @media (min-width: 1200px) {
  27. .container {
  28. width: 970px;
  29. }
  30. }
  31. body {
  32. font-family: 'Ubuntu', sans-serif;
  33. }
  34. pre {
  35. font-family: 'Ubuntu Mono', sans-serif;
  36. }
  37. .main {
  38. padding-top: 20px;
  39. }
  40. .buttons a {
  41. width: 100%;
  42. text-align: left;
  43. margin-bottom: 5px;
  44. }
  45. .getting-started div {
  46. padding-top: 12px;
  47. }
  48. .getting-started p, .synopsis p {
  49. font-size: 1.3em;
  50. }
  51. .getting-started pre {
  52. font-size: 15px;
  53. }
  54. .versions {
  55. font-size: 1.3em;
  56. }
  57. .versions div {
  58. padding-top: 5px;
  59. }
  60. .versions a {
  61. font-weight: bold;
  62. }
  63. .versions a.current {
  64. color: black;
  65. font-decoration: none;
  66. }
  67. /* wrapper for page content to push down footer */
  68. #wrap {
  69. min-height: 100%;
  70. height: auto !important;
  71. height: 100%;
  72. /* negative indent footer by it's height */
  73. margin: 0 auto -40px;
  74. }
  75. /* footer styling */
  76. #footer {
  77. height: 40px;
  78. background-color: #eee;
  79. padding-top: 8px;
  80. text-align: center;
  81. }
  82. /* footer fixes for mobile devices */
  83. @media (max-width: 767px) {
  84. #footer {
  85. margin-left: -20px;
  86. margin-right: -20px;
  87. padding-left: 20px;
  88. padding-right: 20px;
  89. }
  90. }
  91. </style>
  92. </head>
  93. <body>
  94. <script type="text/javascript">
  95. // If there's a URL fragment, assume it's an attempt to read a specific documentation entry.
  96. if (window.location.hash.length > 1) {
  97. window.location = "http://godoc.org/{{.Repo.GopkgPath}}" + window.location.hash;
  98. }
  99. </script>
  100. <div id="wrap" >
  101. <div class="container" >
  102. <div class="row" >
  103. <div class="col-sm-12" >
  104. <div class="page-header">
  105. <h1>{{.Repo.GopkgPath}}</h1>
  106. {{.Synopsis}}
  107. </div>
  108. </div>
  109. </div>
  110. <div class="row" >
  111. <div class="col-sm-12" >
  112. <a class="btn btn-lg btn-info" href="https://{{.Repo.GitHubRoot}}/tree/{{if .Repo.AllVersions}}{{.FullVersion}}{{else}}master{{end}}{{.Repo.SubPath}}" ><i class="fa fa-github"></i> Source Code</a>
  113. <a class="btn btn-lg btn-info" href="http://godoc.org/{{.Repo.GopkgPath}}" ><i class="fa fa-info-circle"></i> API Documentation</a>
  114. </div>
  115. </div>
  116. <div class="row main" >
  117. <div class="col-sm-8 info" >
  118. <div class="getting-started" >
  119. <h2>Getting started</h2>
  120. <div>
  121. <p>To get the package, execute:</p>
  122. <pre>go get {{.Repo.GopkgPath}}</pre>
  123. </div>
  124. <div>
  125. <p>To import this package, add the following line to your code:</p>
  126. <pre>import "{{.Repo.GopkgPath}}"</pre>
  127. {{if .CleanPackageName}}<p>Refer to it as <i>{{.CleanPackageName}}</i>.{{end}}
  128. </div>
  129. <div>
  130. <p>For more details, see the API documentation.</p>
  131. </div>
  132. </div>
  133. </div>
  134. <div class="col-sm-3 col-sm-offset-1 versions" >
  135. <h2>Versions</h2>
  136. {{ if .LatestVersions }}
  137. {{ range .LatestVersions }}
  138. <div>
  139. <a href="//{{gopkgVersionRoot $.Repo .}}{{$.Repo.SubPath}}" {{if eq .Major $.Repo.MajorVersion.Major}}class="current"{{end}} >v{{.Major}}</a>
  140. &rarr;
  141. <span class="label label-default">{{.}}</span>
  142. </div>
  143. {{ end }}
  144. {{ else }}
  145. <div>
  146. <a href="//{{$.Repo.GopkgPath}}" class="current">v0</a>
  147. &rarr;
  148. <span class="label label-default">master</span>
  149. </div>
  150. {{ end }}
  151. </div>
  152. </div>
  153. </div>
  154. </div>
  155. <div id="footer">
  156. <div class="container">
  157. <div class="row">
  158. <div class="col-sm-12">
  159. <p class="text-muted credit"><a href="https://gopkg.in">gopkg.in<a></p>
  160. </div>
  161. </div>
  162. </div>
  163. </div>
  164. <!--<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>-->
  165. <!--<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>-->
  166. </body>
  167. </html>`
  168. var packageTemplate *template.Template
  169. func gopkgVersionRoot(repo *Repo, version Version) string {
  170. return repo.GopkgVersionRoot(version)
  171. }
  172. var packageFuncs = template.FuncMap{
  173. "gopkgVersionRoot": gopkgVersionRoot,
  174. }
  175. func init() {
  176. var err error
  177. packageTemplate, err = template.New("page").Funcs(packageFuncs).Parse(packageTemplateString)
  178. if err != nil {
  179. fmt.Fprintf(os.Stderr, "fatal: parsing package template failed: %s\n", err)
  180. os.Exit(1)
  181. }
  182. }
  183. type packageData struct {
  184. Repo *Repo
  185. LatestVersions VersionList // Contains only the latest version for each major
  186. FullVersion Version // Version that the major requested resolves to
  187. CleanPackageName string
  188. Synopsis string
  189. }
  190. // SearchResults is used with the GDDO (godoc.org) search API
  191. type SearchResults struct {
  192. Results []struct {
  193. Path string `json:"path"`
  194. Synopsis string `json:"synopsis"`
  195. } `json:"results"`
  196. }
  197. func renderPackagePage(resp http.ResponseWriter, req *http.Request, repo *Repo) {
  198. data := &packageData{
  199. Repo: repo,
  200. }
  201. // calculate version mapping
  202. latestVersionsMap := make(map[int]Version)
  203. for _, v := range repo.AllVersions {
  204. v2, exists := latestVersionsMap[v.Major]
  205. if !exists || v2.Less(v) {
  206. latestVersionsMap[v.Major] = v
  207. }
  208. }
  209. data.FullVersion = latestVersionsMap[repo.MajorVersion.Major]
  210. data.LatestVersions = make(VersionList, 0, len(latestVersionsMap))
  211. for _, v := range latestVersionsMap {
  212. data.LatestVersions = append(data.LatestVersions, v)
  213. }
  214. sort.Sort(sort.Reverse(data.LatestVersions))
  215. // find clean package name
  216. data.CleanPackageName = repo.PackageName
  217. if strings.HasPrefix(data.CleanPackageName, "go-") {
  218. data.CleanPackageName = data.CleanPackageName[3:]
  219. }
  220. if strings.HasSuffix(data.CleanPackageName, "-go") {
  221. data.CleanPackageName = data.CleanPackageName[:len(data.CleanPackageName)-3]
  222. }
  223. for i, c := range data.CleanPackageName {
  224. if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
  225. continue
  226. }
  227. if i > 0 && (c == '_' || c >= '0' && c <= '9') {
  228. continue
  229. }
  230. data.CleanPackageName = ""
  231. break
  232. }
  233. // retrieve synopsis
  234. str := `http://api.godoc.org/search?q=` + url.QueryEscape(repo.GopkgPath())
  235. fmt.Println(str)
  236. searchResp, err := http.Get(str)
  237. if err == nil {
  238. searchResults := &SearchResults{}
  239. err = json.NewDecoder(searchResp.Body).Decode(&searchResults)
  240. searchResp.Body.Close()
  241. if err == nil {
  242. gopkgPath := repo.GopkgPath()
  243. for _, apiPkg := range searchResults.Results {
  244. if apiPkg.Path == gopkgPath {
  245. data.Synopsis = apiPkg.Synopsis
  246. break
  247. }
  248. }
  249. }
  250. }
  251. err = packageTemplate.Execute(resp, data)
  252. if err != nil {
  253. log.Printf("error executing tmplPackage: %s\n", err)
  254. }
  255. }