💾 Archived View for source.community › ckaznocha › cliftons-capsule › raw › main › gemlog.go captured on 2021-12-17 at 13:26:06.

View Raw

More Information

-=-=-=-=-=-=-

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
}