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}