Files
mesh-catalog/modules/mesh-knowledge/cmd/mesh-knowledge/hq.go
T

153 lines
4.7 KiB
Go

package main
// hq as the knowledge base reads it: through the records module over the bus (to-be 55 §1: "it holds no
// checkout of this repository; the import reads it through the records module, as the issue tracker's import
// does"), or a checkout on disk for the terminal's dry run and the tests.
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
stdio "git.novox.be/novox/mesh-sdk/go"
)
// Records is hq as the records module serves it: where its checkout stands, what a folder holds, and one
// document whole with the commit it was read at.
type Records interface {
Standing() (string, error)
List(folder string) (folders, documents []string, err error)
Read(path string) (content, commit string, err error)
}
// HeadReader is Records that can read only the first part of a document: a caller that needs a frontmatter or a
// title reads a long document through it, bounded, never whole (novox/hq issue 1947).
type HeadReader interface {
ReadHead(path string, bytes int) (content, commit string, err error)
}
// busRecords is Records over the bus: the records module's tools, called as this module.
type busRecords struct{}
func (busRecords) Standing() (string, error) {
raw, err := stdio.Ask("records.records_status", map[string]any{})
if err != nil {
return "", err
}
var s struct {
Commit string `json:"commit"`
LastError string `json:"lastError"`
}
if err := json.Unmarshal(raw, &s); err != nil {
return "", fmt.Errorf("records_status answered something else: %v", err)
}
if s.Commit == "" {
return "", fmt.Errorf("the record's checkout stands at no commit yet (%s)", s.LastError)
}
return s.Commit, nil
}
func (busRecords) List(folder string) ([]string, []string, error) {
raw, err := stdio.Ask("records.records_list", map[string]any{"folder": folder})
if err != nil {
return nil, nil, err
}
var f struct {
Folders []string `json:"folders"`
Documents []string `json:"documents"`
}
if err := json.Unmarshal(raw, &f); err != nil {
return nil, nil, fmt.Errorf("records_list answered something else: %v", err)
}
return f.Folders, f.Documents, nil
}
func (busRecords) Read(path string) (string, string, error) { return busRead(path, 0) }
func (busRecords) ReadHead(path string, bytes int) (string, string, error) {
return busRead(path, bytes)
}
func busRead(path string, head int) (string, string, error) {
args := map[string]any{"path": path}
if head > 0 {
args["head"] = head
}
raw, err := stdio.Ask("records.records_read", args)
if err != nil {
return "", "", err
}
var d struct {
Content string `json:"content"`
Commit string `json:"commit"`
Truncated bool `json:"truncated"`
}
if err := json.Unmarshal(raw, &d); err != nil {
return "", "", fmt.Errorf("records_read answered something else: %v", err)
}
if d.Truncated {
return "", "", fmt.Errorf("%s is longer than the records module answers whole", path)
}
return d.Content, d.Commit, nil
}
// dirRecords is Records over a checkout on disk. Its commit is the file COMMIT at its root when there is one (a
// fixture copied from hq at a commit), else the checkout's HEAD, else `dir:` and the directory.
type dirRecords struct{ dir string }
func (d dirRecords) Standing() (string, error) {
if st, err := os.Stat(d.dir); err != nil || !st.IsDir() {
return "", fmt.Errorf("%s is not a checkout", d.dir)
}
if raw, err := os.ReadFile(filepath.Join(d.dir, "COMMIT")); err == nil {
return strings.TrimSpace(string(raw)), nil
}
if out, err := os.ReadFile(filepath.Join(d.dir, ".git", "HEAD")); err == nil {
ref := strings.TrimSpace(string(out))
if strings.HasPrefix(ref, "ref: ") {
if sha, err := os.ReadFile(filepath.Join(d.dir, ".git", strings.TrimPrefix(ref, "ref: "))); err == nil {
return strings.TrimSpace(string(sha)), nil
}
} else if ref != "" {
return ref, nil
}
}
return "dir:" + d.dir, nil
}
func (d dirRecords) List(folder string) ([]string, []string, error) {
entries, err := os.ReadDir(filepath.Join(d.dir, filepath.FromSlash(folder)))
if err != nil {
return nil, nil, err
}
var folders, docs []string
for _, e := range entries {
switch {
case e.IsDir():
folders = append(folders, e.Name())
case strings.HasSuffix(e.Name(), ".md"):
docs = append(docs, e.Name())
}
}
return folders, docs, nil
}
func (d dirRecords) Read(path string) (string, string, error) { return d.ReadHead(path, 0) }
func (d dirRecords) ReadHead(path string, head int) (string, string, error) {
raw, err := os.ReadFile(filepath.Join(d.dir, filepath.FromSlash(path)))
if err != nil {
return "", "", err
}
if head > 0 && head < len(raw) {
raw = raw[:head]
if i := strings.LastIndexByte(string(raw), '\n'); i > 0 {
raw = raw[:i+1]
}
}
commit, _ := d.Standing()
return string(raw), commit, nil
}