A => LICENSE +23 -0
@@ 1,23 @@
+Copyright ©2021 The griket Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the griket project nor the names of its authors and
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
A => README.md +12 -0
@@ 1,12 @@
+# griket
+
+`griket` is a set of tools for Gemini and Go.
+
+- `cmd/griket-srv`: a Godoc-like server for Gemini
+
+## Examples
+
+```
+$> griket-srv -gen-cert
+$> griket-srv
+```
A => cmd/griket-srv/main.go +99 -0
@@ 1,99 @@
+// Copyright ©2021 The griket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "context"
+ "flag"
+ "log"
+ "net/http"
+ "os"
+ "time"
+
+ "git.sr.ht/~adnano/go-gemini"
+ "git.sr.ht/~adnano/go-gemini/certificate"
+ godoc "github.com/golang/gddo/doc"
+)
+
+func main() {
+ log.SetPrefix("griket: ")
+ log.SetFlags(0)
+
+ var (
+ doGenCert = flag.Bool("gen-cert", false, "generate a certificate")
+ certHost = flag.String("host", "localhost", "server hostname for certificate")
+ )
+
+ flag.Parse()
+
+ if *doGenCert {
+ genCert(*certHost)
+ }
+
+ serve()
+}
+
+func genCert(name string) {
+ opts := certificate.CreateOptions{
+ DNSNames: []string{name},
+ Duration: 365 * 3600 * time.Second,
+ }
+
+ cert, err := certificate.Create(opts)
+ if err != nil {
+ log.Fatalf("could not create certificate: %+v", err)
+ }
+
+ _ = os.Mkdir("./certs", 0755)
+
+ err = certificate.Write(cert, "./certs/cert.pem", "key.pem")
+ if err != nil {
+ log.Fatalf("could not save certificate: %+v", err)
+ }
+
+ os.Exit(0)
+}
+
+func serve() {
+ certs := &certificate.Store{}
+ certs.Register("localhost")
+ if err := certs.Load("./certs"); err != nil {
+ log.Fatal(err)
+ }
+
+ mux := &gemini.Mux{}
+ mux.HandleFunc("/", pkgHandler)
+
+ server := &gemini.Server{
+ Handler: gemini.LoggingMiddleware(mux),
+ ReadTimeout: 30 * time.Second,
+ WriteTimeout: 1 * time.Minute,
+ GetCertificate: certs.Get,
+ }
+
+ err := server.ListenAndServe(context.Background())
+ if err != nil {
+ log.Fatalf("could not run gmni server: %+v", err)
+ }
+}
+
+func pkgHandler(ctx context.Context, w gemini.ResponseWriter, r *gemini.Request) {
+ name := r.URL.Path[1:] // drop leading '/'
+ pkg, err := godoc.Get(ctx, http.DefaultClient, name, "")
+ if err != nil {
+ log.Printf("could not retrieve doc for package %q: %+v", name, err)
+ return
+ }
+
+ showExamples := r.URL.Query().Get("ex") == "1"
+ err = Generate(w, pkg, showExamples)
+ if err != nil {
+ log.Printf(
+ "could not encode package %q documentation to text/gemini: %+v",
+ pkg.ImportPath, err,
+ )
+ return
+ }
+}
A => cmd/griket-srv/writer.go +179 -0
@@ 1,179 @@
+// Copyright ©2021 The griket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "time"
+
+ "git.sr.ht/~adnano/go-gemini"
+ godoc "github.com/golang/gddo/doc"
+)
+
+func Generate(w io.Writer, pkg *godoc.Package, showExamples bool) error {
+ return newWriter(w, showExamples).write(pkg)
+}
+
+type writer struct {
+ w io.Writer
+ err error
+
+ Examples bool
+}
+
+func newWriter(w io.Writer, showExamples bool) *writer {
+ return &writer{w: w, Examples: showExamples}
+}
+
+func (w *writer) write(pkg *godoc.Package) error {
+ pkgType := "Package"
+ if pkg.IsCmd {
+ pkgType = "Command"
+ }
+ w.h1(pkgType + " " + pkg.Name)
+ w.code("import \"" + pkg.ImportPath + "\"")
+ w.text(pkg.Doc)
+
+ if !w.Examples {
+ w.line(gemini.LineLink{
+ Name: "Show examples",
+ URL: "/" + pkg.ImportPath + "?ex=1",
+ })
+ }
+
+ if w.Examples && len(pkg.Examples) > 0 {
+ w.h2("Examples")
+ for _, v := range pkg.Examples {
+ w.genExample(v)
+ }
+ }
+
+ if len(pkg.Consts) > 0 {
+ w.h2("Constants")
+ for _, v := range pkg.Consts {
+ w.code(v.Decl.Text)
+ w.text(v.Doc)
+ }
+ }
+
+ if len(pkg.Vars) > 0 {
+ w.h2("Variables")
+ for _, v := range pkg.Vars {
+ w.code(v.Decl.Text)
+ w.text(v.Doc)
+ }
+ }
+
+ if len(pkg.Funcs) > 0 {
+ for _, v := range pkg.Funcs {
+ w.genFunc(pkg, v)
+ }
+ }
+
+ if len(pkg.Types) > 0 {
+ for _, v := range pkg.Types {
+ w.genType(pkg, v)
+ }
+ }
+
+ w.h2("Files")
+ for _, f := range pkg.Files {
+ w.line(gemini.LineLink{
+ Name: f.Name,
+ URL: fmt.Sprintf(pkg.LineFmt, f.URL, 1),
+ })
+ }
+
+ if len(pkg.Subdirectories) > 0 {
+ w.h2("Directories")
+ for _, dir := range pkg.Subdirectories {
+ log.Printf("name: %q -- %s", dir, pkg.Name+"/"+dir)
+ w.line(gemini.LineLink{
+ URL: pkg.Name + "/" + dir,
+ Name: pkg.ImportPath + "/" + dir,
+ })
+ }
+ }
+ w.text(fmt.Sprintf("%s %s imports %d packages", pkgType, pkg.Name, len(pkg.Imports)))
+ w.text("")
+ w.text("---")
+ w.text("generated at: " + time.Now().UTC().Format(time.RFC3339Nano))
+
+ return w.err
+}
+
+func (w *writer) line(v gemini.Line) {
+ if w.err != nil {
+ return
+ }
+ _, w.err = w.w.Write([]byte(v.String() + "\n"))
+}
+
+func (w *writer) h1(v string) { w.line(gemini.LineHeading1(v)) }
+func (w *writer) h2(v string) { w.line(gemini.LineHeading2(v)) }
+func (w *writer) h3(v string) { w.line(gemini.LineHeading3(v)) }
+func (w *writer) text(v string) { w.line(gemini.LineText(v)) }
+func (w *writer) code(v string) {
+ w.line(gemini.LinePreformattingToggle(""))
+ w.line(gemini.LinePreformattedText(v))
+ w.line(gemini.LinePreformattingToggle(""))
+}
+
+func (w *writer) link(pkg *godoc.Package, pos godoc.Pos) {
+ var (
+ file = pkg.Files[pos.File]
+ url = fmt.Sprintf(pkg.LineFmt, file.URL, pos.Line)
+ )
+ w.line(gemini.LineLink{URL: url})
+}
+
+func (w *writer) genExample(ex *godoc.Example) {
+ w.h3("Example " + ex.Name)
+ w.code(ex.Code.Text)
+ if ex.Output != "" {
+ w.text("Output:")
+ w.code(ex.Output)
+ }
+ w.text(ex.Doc)
+}
+
+func (w *writer) genFunc(pkg *godoc.Package, fct *godoc.Func) {
+ recv := fct.Recv
+ if recv != "" {
+ recv = " (" + recv + ") "
+ }
+ w.h3("func " + recv + fct.Name)
+ w.code(fct.Decl.Text)
+ w.text(fct.Doc)
+ w.link(pkg, fct.Pos)
+
+ if w.Examples && len(fct.Examples) > 0 {
+ for _, ex := range fct.Examples {
+ w.genExample(ex)
+ }
+ }
+}
+
+func (w *writer) genType(pkg *godoc.Package, typ *godoc.Type) {
+ w.h2("type " + typ.Name)
+ w.code(typ.Decl.Text)
+ w.text(typ.Doc)
+
+ for _, f := range typ.Funcs {
+ w.genFunc(pkg, f)
+ }
+
+ for _, m := range typ.Methods {
+ w.genFunc(pkg, m)
+ }
+
+ if w.Examples && len(typ.Examples) > 0 {
+ for _, v := range typ.Examples {
+ w.genExample(v)
+ }
+ }
+}
A => go.mod +10 -0
@@ 1,10 @@
+module git.sr.ht/~sbinet/griket
+
+go 1.15
+
+require (
+ git.sr.ht/~adnano/go-gemini v0.2.0
+ github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f
+)
+
+replace github.com/golang/gddo => git.sr.ht/~sircmpwn/gddo v0.0.0-20210324120513-1227dec19e53
A => go.sum +42 -0
@@ 1,42 @@
+git.sr.ht/~adnano/go-gemini v0.2.0 h1:24rGvovyXhUX8X51EJq8JeHMxxbO362KzCBtz6QNH7o=
+git.sr.ht/~adnano/go-gemini v0.2.0/go.mod h1:hQ75Y0i5jSFL+FQ7AzWVAYr5LQsaFC7v3ZviNyj46dY=
+git.sr.ht/~sircmpwn/gddo v0.0.0-20210324120513-1227dec19e53 h1:kGHwQKUnwLVol2UwuJO7sljFNZqgsjTbiFHvMroS10o=
+git.sr.ht/~sircmpwn/gddo v0.0.0-20210324120513-1227dec19e53/go.mod h1:MA66WIu1HWTT51zhVU/GNxsZinel/5+sbdR14oeSy1U=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
+github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
+github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=