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