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: MIT
  4
  5package gitroot
  6
  7// #include <stdlib.h>
  8import "C"
  9
 10import (
 11	"encoding/json"
 12	"errors"
 13	"io/fs"
 14	"os"
 15	"runtime"
 16
 17	"github.com/tidwall/gjson"
 18	"gitroot.dev/libs/golang/plugin/model"
 19)
 20
 21type PluginFactory = func(server Server) Plugin
 22
 23type Plugin interface {
 24	Init(repoName string, confHasChanged bool, serializedConf string) error
 25	StartCommit(commit model.Commit) error
 26	AddFile(path string) error
 27	ModFile(fromPath string, toPath string) error
 28	DelFile(path string) error
 29	EndCommit(commit model.Commit) error
 30	Finish() error
 31}
 32
 33type Server interface {
 34	Worktree() fs.FS
 35	Webcontent() fs.FS
 36	Cache() fs.FS
 37	ModifyContent(filepath, content string)
 38	ModifyWebContent(filepath, content string)
 39	ModifyCacheContent(filepath, content string)
 40	CommitAllIfNeeded(message string)
 41	DiffWithParent(hash string, oldFilepath string, newFilepath string) (string, error)
 42	Log(message string)
 43	LogError(message string, err error)
 44	Merge(from string, to string)
 45	Commits(from string, to string) ([]model.Commit, error)
 46}
 47
 48type pServer struct {
 49	plugin           Plugin
 50	run              string
 51	atLeastOneChange bool
 52}
 53
 54var server = &pServer{
 55	atLeastOneChange: false,
 56}
 57
 58func Register(run []model.PluginRun, p PluginFactory) {
 59	if j, err := json.Marshal(run); err == nil {
 60		server.run = string(j)
 61	} else {
 62		server.LogError("can't marshal conf", err)
 63	}
 64	server.plugin = p(server)
 65}
 66
 67func (s *pServer) Worktree() fs.FS {
 68	return os.DirFS("/worktree")
 69}
 70
 71func (s *pServer) Webcontent() fs.FS {
 72	return os.DirFS("/webcontent")
 73}
 74
 75func (s *pServer) Cache() fs.FS {
 76	return os.DirFS("/cache")
 77}
 78
 79func (s *pServer) ModifyContent(filepath, content string) {
 80	s.atLeastOneChange = true
 81	ptr, size := stringToPtr(filepath)
 82	ptr2, size2 := stringToPtr(content)
 83	_modifyContent(ptr, size, ptr2, size2)
 84	runtime.KeepAlive(filepath)
 85	runtime.KeepAlive(content)
 86}
 87
 88func (s *pServer) ModifyWebContent(filepath, content string) {
 89	ptr, size := stringToPtr(filepath)
 90	ptr2, size2 := stringToPtr(content)
 91	_modifyWebContent(ptr, size, ptr2, size2)
 92	runtime.KeepAlive(filepath)
 93	runtime.KeepAlive(content)
 94}
 95
 96func (s *pServer) ModifyCacheContent(filepath, content string) {
 97	ptr, size := stringToPtr(filepath)
 98	ptr2, size2 := stringToPtr(content)
 99	_modifyCacheContent(ptr, size, ptr2, size2)
100	runtime.KeepAlive(filepath)
101	runtime.KeepAlive(content)
102}
103
104func (s *pServer) CommitAllIfNeeded(message string) {
105	if s.atLeastOneChange {
106		ptr, size := stringToPtr(message)
107		_commitAll(ptr, size)
108		runtime.KeepAlive(message)
109	}
110}
111
112func (s *pServer) DiffWithParent(hash string, oldFilepath string, newFilePath string) (string, error) {
113	ptrHash, sizeHash := stringToPtr(hash)
114	ptrOldFile, sizeOldFile := stringToPtr(oldFilepath)
115	ptrNewFile, sizeNewFile := stringToPtr(newFilePath)
116	ptrSizeDiff := _diffWithParent(ptrHash, sizeHash, ptrOldFile, sizeOldFile, ptrNewFile, sizeNewFile)
117	if ptrSizeDiff == 0 {
118		return "", errors.New("can't make diff")
119	}
120	ptrDiff := uint32(ptrSizeDiff >> 32)
121	sizeDiff := uint32(ptrSizeDiff)
122	return ptrToString(ptrDiff, sizeDiff), nil
123}
124
125func (s *pServer) Log(message string) {
126	ptr, size := stringToPtr(message)
127	_log(ptr, size)
128	runtime.KeepAlive(message)
129}
130
131func (s *pServer) LogError(message string, err error) {
132	ptr, size := stringToPtr(message)
133	errPtr, errSize := stringToPtr(err.Error())
134	_logError(ptr, size, errPtr, errSize)
135	runtime.KeepAlive(message)
136	runtime.KeepAlive(err)
137}
138
139func (s *pServer) Merge(from string, to string) {
140	fromPtr, fromSize := stringToPtr(from)
141	toPtr, toSize := stringToPtr(to)
142	_merge(fromPtr, fromSize, toPtr, toSize)
143	runtime.KeepAlive(from)
144	runtime.KeepAlive(to)
145}
146
147func (s *pServer) Commits(from string, to string) ([]model.Commit, error) {
148	fromPtr, fromSize := stringToPtr(from)
149	toPtr, toSize := stringToPtr(to)
150	ptrSizeDiff := _commits(fromPtr, fromSize, toPtr, toSize)
151	runtime.KeepAlive(from)
152	runtime.KeepAlive(to)
153	if ptrSizeDiff == 0 {
154		return nil, errors.New("can't get commits")
155	}
156	ptrDiff := uint32(ptrSizeDiff >> 32)
157	sizeDiff := uint32(ptrSizeDiff)
158	jsonCommits := gjson.Parse(ptrToString(ptrDiff, sizeDiff)).Array()
159	res := make([]model.Commit, len(jsonCommits))
160	for i, jsonCommit := range jsonCommits {
161		res[i] = model.Commit{
162			Branch:     jsonCommit.Get("Branch").String(),
163			Hash:       jsonCommit.Get("Hash").String(),
164			Message:    jsonCommit.Get("Message").String(),
165			Date:       jsonCommit.Get("Date").Time(),
166			Committer:  jsonCommit.Get("Committer").String(),
167			ParentHash: jsonCommit.Get("ParentHash").String(),
168		}
169	}
170	return res, nil
171}