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
  5use std::{
  6    collections::HashMap,
  7    sync::{OnceLock, RwLock},
  8};
  9
 10use crate::{
 11    fs::{Fs, FsBase},
 12    imports::{self, call, can_call},
 13    model::{Call, Commit, Exec, ExecStatus, ForgeConf, PluginRun, ReportLevel, ReportToGitroot},
 14};
 15
 16pub type CallbackFn =
 17    Box<dyn Fn(HashMap<String, String>) -> Result<HashMap<String, String>, String> + Send + Sync>;
 18
 19pub struct Server {
 20    pub run: Vec<PluginRun>,
 21    exported_funcs: RwLock<HashMap<String, CallbackFn>>,
 22    worktree_fs: OnceLock<Fs>,
 23    webcontent_fs: OnceLock<Fs>,
 24    cache_fs: OnceLock<Fs>,
 25}
 26
 27impl Server {
 28    pub fn new(run: Vec<PluginRun>) -> Self {
 29        Server {
 30            run,
 31            exported_funcs: RwLock::new(HashMap::new()),
 32            worktree_fs: OnceLock::new(),
 33            webcontent_fs: OnceLock::new(),
 34            cache_fs: OnceLock::new(),
 35        }
 36    }
 37
 38    pub fn forge_conf(&self) -> Result<ForgeConf, String> {
 39        imports::forge_conf()
 40    }
 41
 42    pub fn worktree(&self) -> &Fs {
 43        self.worktree_fs.get_or_init(|| Fs::new(FsBase::WORKTREE))
 44    }
 45
 46    pub fn webcontent(&self) -> &Fs {
 47        self.webcontent_fs
 48            .get_or_init(|| Fs::new(FsBase::WEBCONTENT))
 49    }
 50
 51    pub fn cache(&self) -> &Fs {
 52        self.cache_fs.get_or_init(|| Fs::new(FsBase::CACHE))
 53    }
 54
 55    pub fn commit_all_if_needed(&self, message: String) {
 56        imports::commit_all(message);
 57    }
 58
 59    pub fn diff_with_parent(
 60        &self,
 61        hash: String,
 62        old_filepath: String,
 63        new_filepath: String,
 64    ) -> Result<String, String> {
 65        imports::diff_with_parent(hash, old_filepath, new_filepath)
 66    }
 67
 68    pub fn log<S>(&self, message: S)
 69    where
 70        S: AsRef<str>,
 71    {
 72        imports::log(message.as_ref());
 73    }
 74
 75    pub fn log_error<S, K>(&self, message: S, err: K)
 76    where
 77        S: AsRef<str>,
 78        K: AsRef<str>,
 79    {
 80        imports::log_error(message.as_ref(), err.as_ref());
 81    }
 82
 83    pub fn merge(&self, from: String, to: String) {
 84        imports::merge(from, to);
 85    }
 86
 87    pub fn commits(&self, from: String, to: String) -> Result<Vec<Commit>, String> {
 88        imports::commits(from, to)
 89    }
 90
 91    pub fn exec(&self, cmd: &Exec) -> Result<ExecStatus, String> {
 92        imports::exec(cmd)
 93    }
 94
 95    pub fn report(&self, level: ReportLevel, content: Vec<String>) {
 96        imports::report_to_gitroot(ReportToGitroot { level, content })
 97    }
 98
 99    pub fn export_func<S, F>(&self, name: S, callback: F)
100    where
101        S: Into<String>,
102        F: Fn(HashMap<String, String>) -> Result<HashMap<String, String>, String>
103            + Send
104            + Sync
105            + 'static,
106    {
107        let mut cb_map = self.exported_funcs.write().unwrap();
108        cb_map.insert(name.into(), Box::new(callback));
109    }
110
111    pub(crate) fn call_internal_func(&self, c: Call) -> Result<HashMap<String, String>, String> {
112        let cb_map = self.exported_funcs.read().unwrap();
113        match cb_map.get(&c.name) {
114            Some(func) => func(c.args),
115            None => Err("func not found".to_string()),
116        }
117    }
118
119    pub fn can_call_func(&self, c: Call) -> bool {
120        can_call(&c) != 0
121    }
122
123    pub fn call_func(&self, c: Call) -> Result<HashMap<String, String>, String> {
124        call(&c).map(|r| r.res)
125    }
126}