Go documentation for all public functions
This commit is contained in:
parent
1a1832b77d
commit
6cfa6622f3
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// Commit represents a git commit.
|
||||
type Commit struct {
|
||||
Repo *Repository
|
||||
|
||||
|
@ -22,15 +23,22 @@ type Commit struct {
|
|||
Message string
|
||||
}
|
||||
|
||||
// ParentHash returns a ref name usable to reach the commit's parent.
|
||||
func (c *Commit) ParentHash() string {
|
||||
return c.Hash + "^"
|
||||
}
|
||||
|
||||
// ShortMessage returns the first line of the commit message.
|
||||
func (c *Commit) ShortMessage() string {
|
||||
short, _, _ := strings.Cut(c.Message, "\n")
|
||||
return short
|
||||
}
|
||||
|
||||
// RestOfMessage returns all but the first line of the commit message.
|
||||
//
|
||||
// It will trim any newline prefixes however, so be aware that
|
||||
// c.ShortMessage + "\n" + c.RestOfMessage may not produce the original
|
||||
// commit message. For that use c.Message.
|
||||
func (c *Commit) RestOfMessage() string {
|
||||
_, rest, _ := strings.Cut(c.Message, "\n")
|
||||
return strings.TrimPrefix(rest, "\n")
|
||||
|
|
31
gemini.go
31
gemini.go
|
@ -19,6 +19,37 @@ const (
|
|||
reponamekey = "syw_reponame"
|
||||
)
|
||||
|
||||
// GeminiRouter builds a router that will handle requests into a directory of git repositories.
|
||||
//
|
||||
// The routes it defines are:
|
||||
// / gemtext listing of the repositories in the directory
|
||||
// /:syw_reponame[/] gemtext overview of the repository
|
||||
// /:syw_reponame/branches gemtext list of branches/heads
|
||||
// /:syw_reponame/tags gemtext listing of tags
|
||||
// /:syw_reponame/refs/:ref/ gemtext overview of a ref
|
||||
// /:syw_reponame/refs/:ref/tree/*path gemtext listing of directories, raw files
|
||||
// /:syw_reponame/diffstat/:fromref/:toref text/plain diffstat between two refs
|
||||
// /:syw_reponame/diff/:fromref/:toref text/x-diff between two refs
|
||||
//
|
||||
// The overrides argument can provide templates to define the behavior of nearly all of the above
|
||||
// routes. All of them have default implementations so the argument can even be nil, but otherwise
|
||||
// the template names used are:
|
||||
// repo_root.gmi gemtext at /
|
||||
// repo_home.gmi gemtext at /:syw_reponame/
|
||||
// branch_list.gmi gemtext at /:syw_reponame/branches
|
||||
// tag_list.gmi gemtext at /:syw_reponame/tags
|
||||
// ref.gmi gemtext at /:syw_reponame/refs/:ref/
|
||||
// tree.gmi gemtext for directories requested under /:syw_reponame/refs/:ref/tree/*path
|
||||
// (file paths return the raw files without any template involved)
|
||||
// diffstat.gmi the plaintext diffstat at /:syw_reponame/diffstat/:fromref/:toref
|
||||
// diff.gmi the text/x-diff at /:syw_reponame/diff/:fromref/:toref
|
||||
//
|
||||
// Most of the templates above are rendered with an object with 3 fields:
|
||||
// Ctx: the context.Context from the request
|
||||
// Repo: a *syw.Repository object corresponding to <repodir>/:syw_reponame
|
||||
// Params: a map[string]string of the route parameters
|
||||
//
|
||||
// The only exception is repo_root.gmi, which is rendered with a slice of the repo names instead.
|
||||
func GeminiRouter(repodir string, overrides *template.Template) *sliderule.Router {
|
||||
tmpl, err := addTemplates(geminiTemplate, overrides)
|
||||
if err != nil {
|
||||
|
|
41
gopher.go
41
gopher.go
|
@ -14,6 +14,47 @@ import (
|
|||
"tildegit.org/tjp/sliderule/gopher"
|
||||
)
|
||||
|
||||
// GopherRouter builds a router that will handle gopher requests in a directory of git repositories.
|
||||
//
|
||||
// The routes it defines are:
|
||||
// / gophermap listing of the repositories in the directory
|
||||
// /:syw_reponame gophermap overview of the repository
|
||||
// /:syw_reponame/branches gophermap list of branches/head
|
||||
// /:syw_reponame/tags gophermap listing of tags
|
||||
// /:syw_reponame/refs/:ref gophermap overview of a ref
|
||||
// /:syw_reponame/refs/:ref/tree gophermap listing of a ref's root directory
|
||||
// /:syw_reponame/refs/:ref/tree/*path for directories: gophermap list of contents
|
||||
// for files: raw files (guessed item type text/binary/image/etc)
|
||||
// /:syw_reponame/diffstat/:fromref/:toref text diffstat between two refs
|
||||
// /:syw_reponame/diff/:fromref/:toref text diff between two refs
|
||||
//
|
||||
// The overrides argument can provide templates to define the behavior of nearly all of the above routes.
|
||||
// All of them have default implementations, so the argument can be nil, but otherwise the template names
|
||||
// used are:
|
||||
// repo_root.gophermap gophermap at /
|
||||
// repo_home.gophermap gophermap at /:syw_reponame
|
||||
// branch_list.gophermap gophermap at /:syw_reponame/branches
|
||||
// tag_list.gophermap gophermap at /:syw_reponame/tags
|
||||
// ref.gophermap gophermap at /:syw_reponame/refs/:ref
|
||||
// tree.gophermap gophermap at direcotry paths under /:syw_reponame/refs/:ref/tree/*path
|
||||
// (file paths return the raw files without any template involved)
|
||||
// diffstat.gophertext plain text diffstat at /:syw_reponame/diffstat/:fromref/:toref
|
||||
// diff.gophertext plain text diff at /:syw_reponame/diff/:fromref/:toref
|
||||
//
|
||||
// Most of the templates above are rendered with an object with 6 fields:
|
||||
// Ctx: the context.Context from the request
|
||||
// Repo: a *syw.Repository corresponding to <repodir>/:syw_reponame
|
||||
// Params: the map[string]string of the route parameters
|
||||
// Host: the hostname of the running server
|
||||
// Port: the port number of the running server
|
||||
// Selector: the selector in the current request
|
||||
//
|
||||
// The only exception is repo_root.gophermap, which is instead rendered with a slice of the repo names.
|
||||
//
|
||||
// All templates have 3 additional functions made available to them:
|
||||
// combine: func(string, ...string) string - successively combines paths using url.URL.ResolveReference
|
||||
// join: func(string, ...string) string - successively joins path segments
|
||||
// rawtext: func(selector, host, port, text string) string renders text lines as gopher info-message lines.
|
||||
func GopherRouter(repodir string, overrides *template.Template) *sliderule.Router {
|
||||
tmpl, err := addTemplates(gopherTemplate, overrides)
|
||||
if err != nil {
|
||||
|
|
2
refs.go
2
refs.go
|
@ -2,6 +2,7 @@ package syw
|
|||
|
||||
import "strings"
|
||||
|
||||
// Ref is an object representing a commit in a repository.
|
||||
type Ref struct {
|
||||
Repo *Repository
|
||||
Name string
|
||||
|
@ -11,6 +12,7 @@ type Ref struct {
|
|||
func (r Ref) IsBranch() bool { return strings.HasPrefix(r.Name, "refs/heads/") }
|
||||
func (r Ref) IsTag() bool { return strings.HasPrefix(r.Name, "refs/tags/") }
|
||||
|
||||
// ShortName returns the branch or tag name with "refs/[heads|tags]/" trimmed off.
|
||||
func (r Ref) ShortName() string {
|
||||
if r.IsBranch() {
|
||||
return r.Name[11:]
|
||||
|
|
30
repo.go
30
repo.go
|
@ -14,8 +14,16 @@ import (
|
|||
"tildegit.org/tjp/sliderule/logging"
|
||||
)
|
||||
|
||||
// Repository represents a git repository.
|
||||
type Repository string
|
||||
|
||||
// Open produces a git repository from a directory path.
|
||||
//
|
||||
// It will also try a few variations (dirpath.git, dirpath/.git) and use the first
|
||||
// path found to be a git repository.
|
||||
//
|
||||
// It returns nil if neither dirpath nor any of its variations are a valid git
|
||||
// repository.
|
||||
func Open(dirpath string) *Repository {
|
||||
check := []string{dirpath}
|
||||
if !strings.HasSuffix(dirpath, ".git") {
|
||||
|
@ -38,6 +46,7 @@ func Open(dirpath string) *Repository {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Name is the repository name, defined by the directory path.
|
||||
func (r *Repository) Name() string {
|
||||
name := filepath.Base(string(*r))
|
||||
if name == ".git" {
|
||||
|
@ -46,6 +55,7 @@ func (r *Repository) Name() string {
|
|||
return strings.TrimSuffix(name, ".git")
|
||||
}
|
||||
|
||||
// NameBytes returns a byte slice of the repository name.
|
||||
func (r *Repository) NameBytes() []byte {
|
||||
return []byte(r.Name())
|
||||
}
|
||||
|
@ -64,6 +74,7 @@ func (r *Repository) cmd(ctx context.Context, cmdname string, args ...string) (*
|
|||
return result, err
|
||||
}
|
||||
|
||||
// Type returns the result of "git cat-file -t <hash>".
|
||||
func (r *Repository) Type(ctx context.Context, hash string) (string, error) {
|
||||
res, err := r.cmd(ctx, "cat-file", "-t", hash)
|
||||
if err != nil {
|
||||
|
@ -76,6 +87,7 @@ func (r *Repository) Type(ctx context.Context, hash string) (string, error) {
|
|||
return strings.Trim(res.out.String(), "\n"), nil
|
||||
}
|
||||
|
||||
// Refs returns a list of branch and tag references.
|
||||
func (r *Repository) Refs(ctx context.Context) ([]Ref, error) {
|
||||
res, err := r.cmd(ctx, "show-ref", "--head", "--heads", "--tags")
|
||||
if err != nil {
|
||||
|
@ -101,6 +113,7 @@ func (r *Repository) Refs(ctx context.Context) ([]Ref, error) {
|
|||
|
||||
var badRevListOutput = errors.New("unexpected 'git rev-list' output")
|
||||
|
||||
// Commits lists commits backwards from a given head.
|
||||
func (r *Repository) Commits(ctx context.Context, head string, count int) ([]Commit, error) {
|
||||
res, err := r.cmd(ctx, "rev-list",
|
||||
"--format=%an%n%ae%n%aI%n%cn%n%ce%n%cI%n%P%n%B$$END$$",
|
||||
|
@ -176,6 +189,7 @@ func (r *Repository) Commits(ctx context.Context, head string, count int) ([]Com
|
|||
return commits, nil
|
||||
}
|
||||
|
||||
// Commit gathers a single commit by a reference string.
|
||||
func (r *Repository) Commit(ctx context.Context, ref string) (*Commit, error) {
|
||||
commits, err := r.Commits(ctx, ref, 1)
|
||||
if err != nil {
|
||||
|
@ -184,6 +198,7 @@ func (r *Repository) Commit(ctx context.Context, ref string) (*Commit, error) {
|
|||
return &commits[0], nil
|
||||
}
|
||||
|
||||
// Diffstat produces a diffstat of two trees by their references.
|
||||
func (r *Repository) Diffstat(ctx context.Context, fromref, toref string) (string, error) {
|
||||
res, err := r.cmd(ctx, "diff-tree", "-r", "--stat", fromref, toref)
|
||||
if err != nil {
|
||||
|
@ -195,6 +210,7 @@ func (r *Repository) Diffstat(ctx context.Context, fromref, toref string) (strin
|
|||
return res.out.String(), nil
|
||||
}
|
||||
|
||||
// Diff produces a diff of two trees by their references.
|
||||
func (r *Repository) Diff(ctx context.Context, fromref, toref string) (string, error) {
|
||||
res, err := r.cmd(ctx, "diff-tree", "-r", "-p", "-u", fromref, toref)
|
||||
if err != nil {
|
||||
|
@ -206,19 +222,27 @@ func (r *Repository) Diff(ctx context.Context, fromref, toref string) (string, e
|
|||
return res.out.String(), nil
|
||||
}
|
||||
|
||||
// Readme represents a README file.
|
||||
type Readme struct {
|
||||
Filename string
|
||||
RawContents string
|
||||
}
|
||||
|
||||
// GeminiEscapedContent produces the file contents with any ```-leading lines prefixed with a space.
|
||||
func (r Readme) GeminiEscapedContents() string {
|
||||
return strings.ReplaceAll(r.RawContents, "\n```", "\n ```")
|
||||
body := r.RawContents
|
||||
if strings.HasPrefix(body, "```") {
|
||||
body = " " + body
|
||||
}
|
||||
return strings.ReplaceAll(body, "\n```", "\n ```")
|
||||
}
|
||||
|
||||
// GopherEscapedContent produces the file formatted as gophermap with every line an info-message line.
|
||||
func (r Readme) GopherEscapedContents(selector, host, port string) string {
|
||||
return gopherRawtext(selector, host, port, r.RawContents)
|
||||
}
|
||||
|
||||
// Readme finds a README blob in the root path under a ref string.
|
||||
func (r *Repository) Readme(ctx context.Context, ref string) (*Readme, error) {
|
||||
dir, err := r.Tree(ctx, ref, "")
|
||||
if err != nil {
|
||||
|
@ -248,6 +272,7 @@ func (r *Repository) Readme(ctx context.Context, ref string) (*Readme, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// Description reads the "description" file from in the git repository.
|
||||
func (r *Repository) Description() string {
|
||||
f, err := os.Open(filepath.Join(string(*r), "description"))
|
||||
if err != nil {
|
||||
|
@ -263,6 +288,7 @@ func (r *Repository) Description() string {
|
|||
return strings.TrimRight(string(b), "\n")
|
||||
}
|
||||
|
||||
// Blob returns the contents of a blob at a given ref (commit) and path.
|
||||
func (r *Repository) Blob(ctx context.Context, ref, path string) ([]byte, error) {
|
||||
res, err := r.cmd(ctx, "cat-file", "blob", ref+":"+path)
|
||||
switch {
|
||||
|
@ -277,6 +303,7 @@ func (r *Repository) Blob(ctx context.Context, ref, path string) ([]byte, error)
|
|||
}
|
||||
}
|
||||
|
||||
// ObjectDescription represents an object within a git tree (directory).
|
||||
type ObjectDescription struct {
|
||||
Mode int
|
||||
Type string
|
||||
|
@ -285,6 +312,7 @@ type ObjectDescription struct {
|
|||
Path string
|
||||
}
|
||||
|
||||
// Tree lists the contents of a given directory (path) in a commit (ref).
|
||||
func (r *Repository) Tree(ctx context.Context, ref, path string) ([]ObjectDescription, error) {
|
||||
pattern := ref
|
||||
if path != "" && path != "." {
|
||||
|
|
Loading…
Reference in New Issue