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
  7// compile cmd
  8// tinygo build -o silo-0.0.1.wasm -scheduler=none --no-debug -target=wasi ./
  9
 10import (
 11	_ "embed"
 12	"fmt"
 13	"math"
 14	"path/filepath"
 15	"slices"
 16	"strconv"
 17	"strings"
 18
 19	gitroot "gitroot.dev/libs/golang/plugin"
 20	"gitroot.dev/libs/golang/plugin/model"
 21)
 22
 23const cachePath string = "boards"
 24
 25type Plugin struct {
 26	server      model.Server
 27	config      []conf
 28	commitsHash []string
 29	cache       []CacheLine
 30}
 31
 32func (p *Plugin) Init(repoName string, confHasChanged bool, serializedConf string) error {
 33	if confHasChanged {
 34		p.server.Log("confHasChanged don't use cache")
 35		p.cache = []CacheLine{}
 36	} else {
 37		p.cache = p.fromCache()
 38	}
 39	p.commitsHash = []string{}
 40	p.config = p.unmarshalConf(serializedConf)
 41	return nil
 42}
 43
 44func (p *Plugin) StartCommit(commit model.Commit) error {
 45	p.commitsHash = append(p.commitsHash, commit.Hash)
 46	return nil
 47}
 48
 49func (p *Plugin) AddFile(fp model.File) error {
 50	toAdd, toDel := p.extractCacheLines(fp.Path)
 51	for _, line := range toAdd {
 52		p.appendInCache(line)
 53	}
 54	for _, line := range toDel {
 55		p.deleteInCacheConf(line)
 56	}
 57	return nil
 58}
 59
 60func (p *Plugin) DelFile(fp model.File) error {
 61	p.deleteInCache(fp.Path)
 62	return nil
 63}
 64
 65func (p *Plugin) ModFile(fp model.File) error {
 66	if fp.OldPath != fp.Path {
 67		p.DelFile(model.File{Path: fp.OldPath, FileHash: fp.OldFileHash})
 68		p.AddFile(fp)
 69		return nil
 70	}
 71
 72	toMod, toDel := p.extractCacheLines(fp.Path)
 73	for _, line := range toMod {
 74		p.modifyInCache(fp.OldPath, line)
 75	}
 76	for _, line := range toDel {
 77		p.deleteInCacheConf(line)
 78	}
 79	return nil
 80}
 81
 82func (p *Plugin) EndCommit(commit model.Commit) error {
 83	return nil
 84}
 85
 86func (p *Plugin) Finish() error {
 87	confs := make(map[string]conf)
 88	for _, conf := range p.config {
 89		confs[conf.To] = conf
 90	}
 91	cachedFiles := make(map[string][]CacheLine)
 92	for _, cachedLine := range p.cache {
 93		if _, ok := confs[cachedLine.toPath]; ok {
 94			if oldContent, ok := cachedFiles[cachedLine.toPath]; !ok {
 95				cachedFiles[cachedLine.toPath] = []CacheLine{cachedLine}
 96			} else {
 97				cachedFiles[cachedLine.toPath] = append(oldContent, cachedLine)
 98			}
 99		}
100	}
101
102	contents := make(map[string]string)
103	if len(confs) > len(cachedFiles) {
104		for _, c := range confs {
105			found := false
106			for cache := range cachedFiles {
107				if c.To == cache {
108					found = true
109					break
110				}
111			}
112			if !found {
113				contents[c.To] = fmt.Sprintf("%s\n%s", c.initFile(), "Nothing here.")
114			}
115		}
116	}
117
118	for confTo, cachedFile := range cachedFiles {
119		if conf, ok := confs[confTo]; ok {
120			slices.SortFunc(cachedFile, func(a CacheLine, b CacheLine) int {
121				res := 0
122				if conf.Sort == "select[0]" && len(a.selects) > 0 && len(b.selects) > 0 {
123					i1, err := strconv.Atoi(a.selects[0])
124					i2, err2 := strconv.Atoi(b.selects[0])
125					if err != nil || err2 != nil {
126						res = strings.Compare(a.selects[0], b.selects[0])
127					} else {
128						res = i1 - i2
129					}
130				} else if conf.Sort == "select[1]" && len(a.selects) > 1 && len(b.selects) > 1 {
131					i1, err := strconv.Atoi(a.selects[1])
132					i2, err2 := strconv.Atoi(b.selects[1])
133					if err != nil || err2 != nil {
134						res = strings.Compare(a.selects[1], b.selects[1])
135					} else {
136						res = i1 - i2
137					}
138				}
139				// if nothing has been picked to sort or if sort == "title"
140				if res == 0 {
141					res = strings.Compare(a.title, b.title)
142				}
143				if conf.SortOrder == "desc" {
144					return res * -1
145				}
146				return res
147			})
148			currentFile := confTo
149			dir, file := filepath.Split(confTo)
150			confToNoExt, _ := strings.CutSuffix(file, ".md")
151			nbPages := int(math.Ceil(float64(len(cachedFile)) / float64(conf.Paginator)))
152			for nbLine, cachedLine := range cachedFile {
153				newLine := conf.makeLink(p, cachedLine)
154				curPage := int(math.Ceil(float64(nbLine+1) / float64(conf.Paginator)))
155				if conf.Paginator != -1 {
156					if curPage > 1 {
157						currentFile = fmt.Sprintf("%s%s_%d.md", dir, confToNoExt, curPage)
158					}
159				}
160				if oldContent, ok := contents[currentFile]; !ok {
161					contents[currentFile] = fmt.Sprintf("%s\n%s", conf.initFile(), newLine)
162				} else {
163					contents[currentFile] = fmt.Sprintf("%s\n%s", oldContent, newLine)
164				}
165				if conf.Paginator != -1 && nbPages > 1 {
166					nextPage := int(math.Ceil(float64(nbLine+2) / float64(conf.Paginator)))
167					if nextPage != curPage || nbLine+1 >= len(cachedFile) {
168						buttonPages := ""
169						for i := range nbPages {
170							if i == 0 && curPage > 1 {
171								buttonPages = fmt.Sprintf("[%d](./%s.md)\n", i+1, confToNoExt)
172							} else if i == 0 {
173								buttonPages = fmt.Sprintf("%d\n", i+1)
174							} else if curPage != i+1 {
175								buttonPages = fmt.Sprintf("%s[%d](./%s_%d.md)\n", buttonPages, i+1, confToNoExt, i+1)
176							} else {
177								buttonPages = fmt.Sprintf("%s%d\n", buttonPages, i+1)
178							}
179						}
180						contents[currentFile] = fmt.Sprintf("%s\n\n%s", contents[currentFile], buttonPages)
181					}
182				}
183			}
184		}
185	}
186	newCache := p.toCache()
187	p.server.ModifyCacheContent(cachePath, newCache)
188	for pathTo, content := range contents {
189		p.server.ModifyContent(pathTo, content)
190	}
191	p.server.CommitAllIfNeeded(fmt.Sprintf("silo plugin\n\nfor %s", strings.Join(p.commitsHash, ", ")))
192	p.cache = nil
193	p.config = nil
194	return nil
195}
196
197func Build(server model.Server) model.Plugin {
198	return &Plugin{
199		server: server,
200	}
201}
202
203//go:wasmexport install
204func main() {
205	gitroot.Register(defaultRun, Build)
206}