~sbinet/griket

0eba05bad785de7959d09318b81e99b8e66ca863 — Sebastien Binet 1 year, 1 month ago main
all: first import

Signed-off-by: Sebastien Binet <s@sbinet.org>
6 files changed, 365 insertions(+), 0 deletions(-)

A LICENSE
A README.md
A cmd/griket-srv/main.go
A cmd/griket-srv/writer.go
A go.mod
A go.sum
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=