// SPDX-FileCopyrightText: 2025 Romain Maneschi // // SPDX-License-Identifier: MIT package gitroot // #include import "C" import ( "encoding/json" "errors" "runtime" "github.com/tidwall/gjson" "gitroot.dev/libs/golang/plugin/model" ) type PluginFactory = func(server model.Server) model.Plugin type pServer struct { worktree *model.GrFs webcontent *model.GrFs cache *model.GrFs plugin model.Plugin pluginOpts model.PluginOption run []model.PluginRun atLeastOneChange bool exportedFuncs map[string]func(args map[string]string) (map[string]string, error) } var server = &pServer{ atLeastOneChange: false, pluginOpts: model.PluginOption{}, exportedFuncs: map[string]func(args map[string]string) (map[string]string, error){}, } func Register(run []model.PluginRun, p PluginFactory) { server.run = run server.plugin = p(server) } func (s *pServer) PluginOption(opts ...model.PluginOptionWith) { for _, opt := range opts { server.pluginOpts = opt(server.pluginOpts) } } func (s *pServer) ForgeConf() (model.ForgeConf, error) { forgeConf := model.ForgeConf{} ptrSize := _forgeConf() if ptrSize == 0 { return forgeConf, errors.New("can't make diff") } ptrDiff := uint32(ptrSize >> 32) sizeDiff := uint32(ptrSize) forgeConfJson := gjson.Parse(ptrToString(ptrDiff, sizeDiff)) return model.ForgeConf{ Domain: forgeConfJson.Get("domain").String(), ExternalSshAddr: forgeConfJson.Get("externalSshAddr").String(), ExternalHttpAddr: forgeConfJson.Get("externalHttpAddr").String(), RootRepositoryName: forgeConfJson.Get("rootRepositoryName").String(), }, nil } func (s *pServer) Worktree() *model.GrFs { if s.worktree == nil { s.worktree = model.NewGrFs(model.FS_BASE_WORKTREE, s) } return s.worktree } func (s *pServer) Webcontent() *model.GrFs { if s.webcontent == nil { s.webcontent = model.NewGrFs(model.FS_BASE_WEBCONTENT, s) } return s.webcontent } func (s *pServer) Cache() *model.GrFs { if s.cache == nil { s.cache = model.NewGrFs(model.FS_BASE_CACHE, s) } return s.cache } // TODO delete after v0.4 is released // Deprecated: Use Worktree().WriteContent(filepath, content string) instead func (s *pServer) ModifyContent(filepath, content string) { s.atLeastOneChange = true ptr, size := stringToPtr(filepath) ptr2, size2 := stringToPtr(content) _modifyContent(ptr, size, ptr2, size2) runtime.KeepAlive(filepath) runtime.KeepAlive(content) } // TODO delete after v0.4 is released // Deprecated: Use Webcontent().WriteContent(filepath, content string) instead func (s *pServer) ModifyWebContent(filepath, content string) { ptr, size := stringToPtr(filepath) ptr2, size2 := stringToPtr(content) _modifyWebContent(ptr, size, ptr2, size2) runtime.KeepAlive(filepath) runtime.KeepAlive(content) } // TODO delete after v0.4 is released // Deprecated: Use Webcontent().ReplaceContent(filepath string, oldContent string, content string) instead func (s *pServer) ReplaceWebContent(filepath, oldContent, content string) { ptr, size := stringToPtr(filepath) ptr2, size2 := stringToPtr(oldContent) ptr3, size3 := stringToPtr(content) _replaceWebContent(ptr, size, ptr2, size2, ptr3, size3) runtime.KeepAlive(filepath) runtime.KeepAlive(oldContent) runtime.KeepAlive(content) } // TODO delete after v0.4 is released // Deprecated: Use Cache().WriteContent(filepath, content string) instead func (s *pServer) ModifyCacheContent(filepath, content string) { ptr, size := stringToPtr(filepath) ptr2, size2 := stringToPtr(content) _modifyCacheContent(ptr, size, ptr2, size2) runtime.KeepAlive(filepath) runtime.KeepAlive(content) } // CopyFile implements [model.ServerNeeded]. func (s *pServer) CopyFile(fromFs model.FsBase, fromPath string, toFs model.FsBase, toPath string) error { ptr, size := stringToPtr(string(fromFs)) ptr2, size2 := stringToPtr(fromPath) ptr3, size3 := stringToPtr(string(toFs)) ptr4, size4 := stringToPtr(toPath) resPtrSize := _copyFile(ptr, size, ptr2, size2, ptr3, size3, ptr4, size4) runtime.KeepAlive(fromFs) runtime.KeepAlive(fromPath) runtime.KeepAlive(toFs) runtime.KeepAlive(toPath) return ptrSizeToError(resPtrSize) } // DeleteFile implements [model.ServerNeeded]. func (s *pServer) DeleteFile(fromFs model.FsBase, filename string) error { ptr, size := stringToPtr(string(fromFs)) ptr2, size2 := stringToPtr(filename) resPtrSize := _deleteFile(ptr, size, ptr2, size2) runtime.KeepAlive(fromFs) runtime.KeepAlive(filename) return ptrSizeToError(resPtrSize) } // MoveFile implements [model.ServerNeeded]. func (s *pServer) MoveFile(fromFs model.FsBase, fromPath string, toFs model.FsBase, toPath string) error { ptr, size := stringToPtr(string(fromFs)) ptr2, size2 := stringToPtr(fromPath) ptr3, size3 := stringToPtr(string(toFs)) ptr4, size4 := stringToPtr(toPath) resPtrSize := _moveFile(ptr, size, ptr2, size2, ptr3, size3, ptr4, size4) runtime.KeepAlive(fromFs) runtime.KeepAlive(fromPath) runtime.KeepAlive(toFs) runtime.KeepAlive(toPath) return ptrSizeToError(resPtrSize) } // ReplaceContent implements [model.ServerNeeded]. func (s *pServer) ReplaceContent(fromFs model.FsBase, filepath string, oldContent string, content string) error { ptr, size := stringToPtr(string(fromFs)) ptr2, size2 := stringToPtr(filepath) ptr3, size3 := stringToPtr(oldContent) ptr4, size4 := stringToPtr(content) resPtrSize := _replaceContent(ptr, size, ptr2, size2, ptr3, size3, ptr4, size4) runtime.KeepAlive(fromFs) runtime.KeepAlive(filepath) runtime.KeepAlive(oldContent) runtime.KeepAlive(content) return ptrSizeToError(resPtrSize) } // WriteContent implements [model.ServerNeeded]. func (s *pServer) WriteContent(fromFs model.FsBase, filepath string, content string) error { ptr, size := stringToPtr(string(fromFs)) ptr2, size2 := stringToPtr(filepath) ptr3, size3 := stringToPtr(content) resPtrSize := _writeContent(ptr, size, ptr2, size2, ptr3, size3) runtime.KeepAlive(fromFs) runtime.KeepAlive(filepath) runtime.KeepAlive(content) return ptrSizeToError(resPtrSize) } func (s *pServer) CommitAllIfNeeded(message string) { if s.atLeastOneChange { ptr, size := stringToPtr(message) _commitAll(ptr, size) runtime.KeepAlive(message) } } func (s *pServer) DiffWithParent(hash string, oldFilepath string, newFilePath string) (string, error) { ptrHash, sizeHash := stringToPtr(hash) ptrOldFile, sizeOldFile := stringToPtr(oldFilepath) ptrNewFile, sizeNewFile := stringToPtr(newFilePath) ptrSizeDiff := _diffWithParent(ptrHash, sizeHash, ptrOldFile, sizeOldFile, ptrNewFile, sizeNewFile) if ptrSizeDiff == 0 { return "", errors.New("can't make diff") } ptrDiff := uint32(ptrSizeDiff >> 32) sizeDiff := uint32(ptrSizeDiff) return ptrToString(ptrDiff, sizeDiff), nil } func (s *pServer) Log(message string) { ptr, size := stringToPtr(message) _log(ptr, size) runtime.KeepAlive(message) } func (s *pServer) LogError(message string, err error) { ptr, size := stringToPtr(message) errPtr, errSize := stringToPtr(err.Error()) _logError(ptr, size, errPtr, errSize) runtime.KeepAlive(message) runtime.KeepAlive(err) } func (s *pServer) Merge(from string, to string) { fromPtr, fromSize := stringToPtr(from) toPtr, toSize := stringToPtr(to) _merge(fromPtr, fromSize, toPtr, toSize) runtime.KeepAlive(from) runtime.KeepAlive(to) } func (s *pServer) Commits(from string, to string) ([]model.Commit, error) { fromPtr, fromSize := stringToPtr(from) toPtr, toSize := stringToPtr(to) ptrSizeDiff := _commits(fromPtr, fromSize, toPtr, toSize) runtime.KeepAlive(from) runtime.KeepAlive(to) if ptrSizeDiff == 0 { return nil, errors.New("can't get commits") } ptrDiff := uint32(ptrSizeDiff >> 32) sizeDiff := uint32(ptrSizeDiff) return model.CommitsFromString(ptrToString(ptrDiff, sizeDiff)), nil } func (s *pServer) Exec(exec model.Exec) (model.ExecStatus, error) { execMarshalled, err := json.Marshal(exec) if err != nil { return model.ExecStatus{}, err } ptr, size := stringToPtr(string(execMarshalled)) ptrSize := _exec(ptr, size) if ptrSize == 0 { return model.ExecStatus{}, errors.New("can't exec") } ptrLog := uint32(ptrSize >> 32) sizeLog := uint32(ptrSize) return model.ExecStatusFromString(ptrToString(ptrLog, sizeLog)), nil } func (s *pServer) ReportToPlugin(report model.Report) { if s.pluginOpts.Reporter != nil { if err := s.pluginOpts.Reporter(report); err != nil { s.LogError("plugin reporter err", err) } } } func (s *pServer) Report(level model.ReportLevel, content []string) error { reportMarshalled, err := json.Marshal(model.ReportToGitroot{Level: level, Content: content}) if err != nil { return err } ptr, size := stringToPtr(string(reportMarshalled)) _report(ptr, size) return nil } func (s *pServer) ExportFunc(name string, callback func(args map[string]string) (map[string]string, error)) { s.exportedFuncs[name] = callback } func (s *pServer) CanCallFunc(plugin string, name string, args map[string]string) bool { call := model.Call{ Plugin: plugin, Name: name, Args: args, } callJson, err := json.Marshal(call) if err != nil { s.LogError("can't serialize canCall", err) return false } ptr, size := stringToPtr(string(callJson)) ptrSize := _canCall(ptr, size) if ptrSize == 0 { return false } return true } func (s *pServer) CallFunc(plugin string, name string, args map[string]string) (map[string]string, error) { call := model.Call{ Plugin: plugin, Name: name, Args: args, } callJson, err := json.Marshal(call) if err != nil { return nil, err } ptr, size := stringToPtr(string(callJson)) ptrSize := _call(ptr, size) if ptrSize == 0 { return nil, errors.New("can't call") } ptrRes := uint32(ptrSize >> 32) sizeRes := uint32(ptrSize) resCall := model.CallResFromString(ptrToString(ptrRes, sizeRes)) if resCall.Err != "" { return nil, errors.New(resCall.Err) } return resCall.Res, nil }