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