package main
import (
"bufio"
"bytes"
"fmt"
"io/fs"
"net/url"
"sort"
"strings"
"sync"
"time"
)
type entryMetadata struct {
Filename string
Date time.Time
Title []byte
}
type entry struct {
Metadata *entryMetadata
Body []byte
}
type gemlog struct {
gemlogFS fs.FS
entrySet map[string]*entryMetadata
entryList []*entryMetadata
mu sync.RWMutex
}
func (g *gemlog) list() ([]*entryMetadata, error) {
g.mu.Lock()
defer g.mu.Unlock()
if err := g.populate(); err != nil {
return nil, err
}
return g.entryList, nil
}
func (g *gemlog) entry(path string) (*entry, bool) {
g.mu.RLock()
defer g.mu.RUnlock()
path, err := url.QueryUnescape(strings.TrimPrefix(path, "/"))
if err != nil {
return nil, false
}
metadata, ok := g.entrySet[path]
if !ok {
return nil, false
}
f, err := fs.ReadFile(g.gemlogFS, path)
if err != nil {
return nil, false
}
return &entry{
Metadata: metadata,
Body: bytes.TrimSpace(f[bytes.Index(f, []byte{'\n'})+1:]),
}, true
}
func (g *gemlog) populate() error {
dirInfo, err := fs.ReadDir(g.gemlogFS, "gemlog")
if err != nil {
return fmt.Errorf("cannot read gemlog dir: %w", err)
}
if len(g.entryList) == len(dirInfo) {
return nil
}
g.entryList = []*entryMetadata{}
g.entrySet = map[string]*entryMetadata{}
if err := fs.WalkDir(g.gemlogFS, "gemlog", g.walkFS); err != nil {
return fmt.Errorf("cannot walk gemlog dir: %w", err)
}
sort.Stable(g)
return nil
}
func (g *gemlog) Len() int { return len(g.entryList) }
func (g *gemlog) Swap(i, j int) { g.entryList[i], g.entryList[j] = g.entryList[j], g.entryList[i] }
func (g *gemlog) Less(i, j int) bool { return g.entryList[i].Filename >= g.entryList[j].Filename }
func (g *gemlog) walkFS(path string, d fs.DirEntry, e error) error {
if e != nil {
return e
}
if d.IsDir() {
return nil
}
t, err := time.Parse(time.RFC3339[:10], d.Name()[:10])
if err != nil {
return fmt.Errorf("unable to parse time stamp: %w", err)
}
f, err := g.gemlogFS.Open(path)
if err != nil {
return fmt.Errorf("unable to open file: %w", err)
}
title, err := bufio.NewReader(f).ReadBytes('\n')
if err != nil {
return fmt.Errorf("unable to read title: %w", err)
}
metadata := &entryMetadata{
Filename: d.Name(),
Date: t,
Title: bytes.TrimSpace(bytes.TrimPrefix(title, []byte{'#'})),
}
g.entrySet[path] = metadata
g.entryList = append(g.entryList, metadata)
return nil
}
text/plain
This content has been proxied by September (ba2dc).