internal/manpages/manpages.go (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | package manpages
import (
"context"
"encoding/json"
"fmt"
"io"
"time"
"alin.ovh/searchix/internal/config"
"alin.ovh/searchix/internal/fetcher/http"
"alin.ovh/searchix/internal/file"
"alin.ovh/x/log"
"github.com/Southclaws/fault"
"github.com/Southclaws/fault/fmsg"
)
const basename = "manpage-urls.json"
type URLMap struct {
root *file.Root
mtime time.Time
logger *log.Logger
urlMap map[string]string
}
type Options struct {
Logger *log.Logger
Root *file.Root
}
func New(opts *Options) *URLMap {
return &URLMap{
logger: opts.Logger,
root: opts.Root,
}
}
func (m *URLMap) Update(
ctx context.Context,
source *config.Source,
) error {
if !source.Manpages.Enable {
return fault.New("manpages not enabled for this source")
}
if source.Manpages.Path == "" {
return fault.New("manpages repo source path not configured")
}
url, err := makeManpageURL(source)
if err != nil {
return fault.Wrap(err, fmsg.With("failed to join manpages URL"))
}
m.logger.Debug("fetching manpages URL map", "url", url)
r, mtime, err := http.FetchFileIfNeeded(ctx, m.logger.Named("http"), m.mtime, url)
if err != nil {
return fault.Wrap(err, fmsg.With("failed to fetch manpages"))
}
defer r.Close()
if err := m.save(r); err != nil {
return fault.Wrap(err, fmsg.With("failed to save manpages"))
}
m.mtime = mtime
return nil
}
// Open loads the manpage URLs from the JSON file
func (m *URLMap) Open() error {
m.logger.Debug("opening manpages file", "path", basename)
stat, err := m.root.Stat(basename)
if err != nil {
return fault.Wrap(err, fmsg.Withf("failed to stat manpages file: %s", basename))
}
data, err := m.root.ReadFile(basename)
if err != nil {
return fault.Wrap(err, fmsg.With("failed to read manpages file"))
}
m.mtime = stat.ModTime()
m.urlMap = make(map[string]string)
if err := json.Unmarshal(data, &m.urlMap); err != nil {
return fault.Wrap(err, fmsg.With("failed to parse manpages JSON"))
}
m.logger.Debug("loaded manpages data", "urls", len(m.urlMap))
return nil
}
func (m *URLMap) save(r io.Reader) error {
m.logger.Debug("saving manpages file", "path", basename)
f, err := m.root.Create(basename)
if err != nil {
return fault.Wrap(err, fmsg.With("failed to create manpages file"))
}
defer f.Close()
if _, err := io.Copy(f, r); err != nil {
return fault.Wrap(err, fmsg.With("failed to write manpages file"))
}
return nil
}
func (m *URLMap) Get(section string, page string) (string, bool) {
key := fmt.Sprintf("%s(%s)", page, section)
m.logger.Debug("getting manpage URL", "key", key)
url, ok := m.urlMap[key]
if !ok {
return "", false
}
return url, true
}
func makeManpageURL(source *config.Source) (string, error) {
url, err := source.Repo.GetRawFileURL(source.Manpages.Path)
if err != nil {
return "", fault.Wrap(err, fmsg.With("failed to join manpage URL"))
}
return url, nil
}
|