GitRoot

Craft your forge, Build your project, Grow your community freely
  1// SPDX-FileCopyrightText: 2025 Romain Maneschi <romain@gitroot.dev>
  2//
  3// SPDX-License-Identifier: EUPL-1.2
  4
  5package main
  6
  7import (
  8	"bytes"
  9	"errors"
 10	"fmt"
 11	"io/fs"
 12	"regexp"
 13	"slices"
 14	"strings"
 15)
 16
 17type CacheLine struct {
 18	fp      string
 19	toPath  string
 20	title   string
 21	selects []string
 22}
 23
 24func (p *Plugin) extractCacheLines(fp string) ([]CacheLine, []CacheLine) {
 25	res := []CacheLine{}
 26	toDelete := []CacheLine{}
 27	for _, conf := range p.config {
 28		if conf.glob.Match(fp) {
 29			content, err := fs.ReadFile(p.server.Worktree(), fp)
 30			if err != nil {
 31				p.server.Log("extractCacheLines ReadFile " + fp + " == " + err.Error())
 32				continue
 33			}
 34			if bytes.Contains(content, []byte(conf.Where)) {
 35				res = append(res, p.extractCacheLine(fp, conf, content))
 36			} else {
 37				toDelete = append(toDelete, CacheLine{fp: fp, toPath: conf.To})
 38			}
 39		}
 40	}
 41	return res, toDelete
 42}
 43func (p *Plugin) extractCacheLine(fp string, c Conf, content []byte) CacheLine {
 44	res := CacheLine{
 45		fp:      fp,
 46		toPath:  c.To,
 47		title:   findTitle(fp, content),
 48		selects: make([]string, len(c.selections)),
 49	}
 50	remaningFind := slices.Clone(c.selections)
 51	for line := range bytes.SplitSeq(content, []byte("\n")) {
 52		for i, selection := range remaningFind {
 53			if selection == nil {
 54				continue
 55			}
 56			t := selection.FindSubmatch(line)
 57			if len(t) > 1 {
 58				toAppend := ""
 59				for _, a := range t[1:] {
 60					if toAppend == "" {
 61						toAppend = string(a)
 62					} else {
 63						toAppend = fmt.Sprintf("%s %s", toAppend, a)
 64					}
 65				}
 66				res.selects[i] = toAppend
 67				remaningFind[i] = nil
 68			}
 69		}
 70		if !slices.ContainsFunc(remaningFind, func(f *regexp.Regexp) bool { return f != nil }) {
 71			break
 72		}
 73	}
 74	return res
 75}
 76
 77func (c CacheLine) Marshal() string {
 78	return fmt.Sprintf("%s||%s||%s||%s", c.fp, c.toPath, c.title, strings.Join(c.selects, ";;"))
 79}
 80
 81func Unmarshal(s string) CacheLine {
 82	data := strings.Split(s, "||")
 83	return CacheLine{
 84		fp:      data[0],
 85		toPath:  data[1],
 86		title:   data[2],
 87		selects: slices.DeleteFunc(strings.Split(data[3], ";;"), func(s string) bool { return s == "" }),
 88	}
 89}
 90
 91func (p *Plugin) fromCache() []CacheLine {
 92	cache, err := fs.ReadFile(p.server.Cache(), CACHE_PATH)
 93	if err != nil && errors.Is(err, fs.ErrNotExist) {
 94		p.server.Log("cache not existing")
 95		return []CacheLine{}
 96	} else if err != nil {
 97		p.server.LogError("can't read cache in fromCache", err)
 98		return []CacheLine{}
 99	}
100	res := []CacheLine{}
101	for line := range bytes.SplitSeq(cache, []byte("\n")) {
102		if len(line) > 0 {
103			res = append(res, Unmarshal(string(line)))
104		}
105	}
106	return res
107}
108
109func (p *Plugin) toCache() string {
110	var buf bytes.Buffer
111	for _, cl := range p.cache {
112		buf.WriteString(cl.Marshal())
113		buf.WriteString("\n")
114	}
115	return buf.String()
116}
117
118func (p *Plugin) deleteInCache(fp string) {
119	res := make([]CacheLine, 0)
120	for _, cl := range p.cache {
121		if cl.fp != fp {
122			res = append(res, cl)
123		}
124	}
125	p.cache = res
126}
127
128func (p *Plugin) deleteInCacheConf(cacheLine CacheLine) {
129	res := make([]CacheLine, 0)
130	for _, cl := range p.cache {
131		if cl.fp != cacheLine.fp || cl.toPath != cacheLine.toPath {
132			res = append(res, cl)
133		}
134	}
135	p.cache = res
136}
137
138func (p *Plugin) appendInCache(cacheLine CacheLine) {
139	found := false // check if not already stored, in case inactive/active and wortree pass
140	for _, cl := range p.cache {
141		if cl.fp == cacheLine.fp && cl.toPath == cacheLine.toPath {
142			found = true
143			break
144		}
145	}
146	p.server.Log(fmt.Sprintf("fp %s is found %t with %s", cacheLine.fp, found, cacheLine.Marshal()))
147	if !found {
148		p.cache = append(p.cache, cacheLine)
149	} else {
150		p.modifyInCache(cacheLine.fp, cacheLine)
151	}
152}
153
154func (p *Plugin) modifyInCache(oldFp string, cacheLine CacheLine) {
155	res := make([]CacheLine, len(p.cache))
156	for i, cl := range p.cache {
157		if cl.fp == oldFp && cl.toPath == cacheLine.toPath {
158			res[i] = cacheLine
159		} else {
160			res[i] = cl
161		}
162	}
163	p.cache = res
164}