tag : break apart into smaller files of related function
This commit is contained in:
parent
211bf38758
commit
bab61c31a3
6 changed files with 431 additions and 399 deletions
124
tag/parse.go
Normal file
124
tag/parse.go
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
package tag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/brtholomy/um/go/pipe"
|
||||
)
|
||||
|
||||
const (
|
||||
// TODO: source in common with last.go
|
||||
GLOB = "[0-9]*.md"
|
||||
|
||||
// The layout string must be a representation of:
|
||||
// Jan 2 15:04:05 2006 MST
|
||||
// 1 2 3 4 5 6 -7
|
||||
DATE_FORMAT = "2006.01.02"
|
||||
|
||||
// ^: YYYY.DD.MM$
|
||||
DATE_REGEXP = `(?m)^\: ([\.0-9]+?)$`
|
||||
|
||||
// ^+ tag$
|
||||
TAG_REGEXP = `(?m)^\+ (.+)$`
|
||||
)
|
||||
|
||||
// regexp.Compile is by far the most expensive operation of ParseContent:
|
||||
var dateRegexp *regexp.Regexp = regexp.MustCompile(DATE_REGEXP)
|
||||
var tagRegexp *regexp.Regexp = regexp.MustCompile(TAG_REGEXP)
|
||||
|
||||
type Entry struct {
|
||||
filename string
|
||||
date time.Time
|
||||
content string
|
||||
tags []string
|
||||
}
|
||||
|
||||
func parseHeader(content *string) string {
|
||||
// returns complete string if not found:
|
||||
header, _, _ := strings.Cut(*content, "\n\n")
|
||||
return header
|
||||
}
|
||||
|
||||
func parseTags(content *string) (tags []string) {
|
||||
res := tagRegexp.FindAllStringSubmatch(*content, -1)
|
||||
for i := range res {
|
||||
// group submatch is indexed at 1:
|
||||
// this shouldn't ever fail if there's a result:
|
||||
tags = append(tags, res[i][1])
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func parseDate(content *string) (time.Time, error) {
|
||||
res := dateRegexp.FindStringSubmatch(*content)
|
||||
if len(res) < 2 {
|
||||
return time.Time{}, errors.New("failed to find date string")
|
||||
}
|
||||
return time.Parse(DATE_FORMAT, res[1])
|
||||
}
|
||||
|
||||
func parseContent(filename string, content *string) Entry {
|
||||
base := filepath.Base(filename)
|
||||
header := parseHeader(content)
|
||||
date, _ := parseDate(&header)
|
||||
tags := parseTags(&header)
|
||||
return Entry{
|
||||
base,
|
||||
date,
|
||||
*content,
|
||||
tags,
|
||||
}
|
||||
}
|
||||
|
||||
// reads files from stdin if present, otherwise from the glob pattern:
|
||||
func getFilelist(glob string) []string {
|
||||
filelist, err := pipe.GetStdin()
|
||||
// otherwise get from the glob:
|
||||
if err != nil {
|
||||
filelist, err = filepath.Glob(glob)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
return filelist
|
||||
}
|
||||
|
||||
// create []Entry representing qualifying files in current directory or from stdin
|
||||
func entriesGlobOrStdin() []Entry {
|
||||
filelist := getFilelist(GLOB)
|
||||
|
||||
// NOTE: size 0, capacity specified:
|
||||
entries := make([]Entry, 0, len(filelist))
|
||||
for _, f := range filelist {
|
||||
dat, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Errorf("gag: error opening file: %s\n%w", f, err))
|
||||
}
|
||||
s := string(dat)
|
||||
e := parseContent(f, &s)
|
||||
entries = append(entries, e)
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
// maps tags to a set of filenames
|
||||
func makeTagmap(entries []Entry) map[string]Set {
|
||||
tagmap := map[string]Set{}
|
||||
for _, e := range entries {
|
||||
for _, tag := range e.tags {
|
||||
// allocate submap if necessary:
|
||||
if _, ok := tagmap[tag]; !ok {
|
||||
tagmap[tag] = Set{}
|
||||
}
|
||||
tagmap[tag].Add(e.filename)
|
||||
}
|
||||
}
|
||||
return tagmap
|
||||
}
|
||||
Loading…
Reference in a new issue