openvcs_core/
lib.rs

1//! OpenVCS Core: shared types for OpenVCS plugins and the OpenVCS client.
2
3pub mod backend_id;
4pub mod models;
5
6pub use crate::backend_id::BackendId;
7
8#[doc(hidden)]
9pub use log as __log;
10
11#[cfg(feature = "backend-registry")]
12pub mod backend_descriptor;
13
14#[cfg(feature = "plugin-protocol")]
15pub mod plugin_protocol;
16
17#[cfg(feature = "plugin-protocol")]
18pub mod plugin_stdio;
19
20#[cfg(feature = "plugin-protocol")]
21pub mod host;
22
23#[cfg(feature = "plugin-protocol")]
24pub mod plugin_runtime;
25
26#[cfg(feature = "plugin-protocol")]
27pub mod events;
28
29#[cfg(feature = "plugin-protocol")]
30mod plugin_logging;
31
32#[doc(hidden)]
33pub fn __ensure_plugin_logging_initialized() {
34    #[cfg(feature = "plugin-protocol")]
35    crate::plugin_logging::ensure_initialized();
36}
37
38#[macro_export]
39macro_rules! trace {
40    (target: $target:expr, $($arg:tt)+) => {{
41        $crate::__ensure_plugin_logging_initialized();
42        $crate::__log::trace!(target: $target, $($arg)+);
43    }};
44    ($($arg:tt)+) => {{
45        $crate::__ensure_plugin_logging_initialized();
46        $crate::__log::trace!($($arg)+);
47    }};
48}
49
50#[macro_export]
51macro_rules! debug {
52    (target: $target:expr, $($arg:tt)+) => {{
53        $crate::__ensure_plugin_logging_initialized();
54        $crate::__log::debug!(target: $target, $($arg)+);
55    }};
56    ($($arg:tt)+) => {{
57        $crate::__ensure_plugin_logging_initialized();
58        $crate::__log::debug!($($arg)+);
59    }};
60}
61
62#[macro_export]
63macro_rules! info {
64    (target: $target:expr, $($arg:tt)+) => {{
65        $crate::__ensure_plugin_logging_initialized();
66        $crate::__log::info!(target: $target, $($arg)+);
67    }};
68    ($($arg:tt)+) => {{
69        $crate::__ensure_plugin_logging_initialized();
70        $crate::__log::info!($($arg)+);
71    }};
72}
73
74#[macro_export]
75macro_rules! warn {
76    (target: $target:expr, $($arg:tt)+) => {{
77        $crate::__ensure_plugin_logging_initialized();
78        $crate::__log::warn!(target: $target, $($arg)+);
79    }};
80    ($($arg:tt)+) => {{
81        $crate::__ensure_plugin_logging_initialized();
82        $crate::__log::warn!($($arg)+);
83    }};
84}
85
86#[macro_export]
87macro_rules! error {
88    (target: $target:expr, $($arg:tt)+) => {{
89        $crate::__ensure_plugin_logging_initialized();
90        $crate::__log::error!(target: $target, $($arg)+);
91    }};
92    ($($arg:tt)+) => {{
93        $crate::__ensure_plugin_logging_initialized();
94        $crate::__log::error!($($arg)+);
95    }};
96}
97
98#[cfg(feature = "plugin-protocol")]
99pub use crate::plugin_protocol::{PluginMessage, RpcRequest, RpcResponse};
100
101#[cfg(feature = "vcs")]
102pub use crate::models::{Capabilities, FetchOptions, OnEvent};
103
104#[cfg(feature = "vcs")]
105use std::path::{Path, PathBuf};
106
107#[cfg(feature = "vcs")]
108#[derive(thiserror::Error, Debug)]
109pub enum VcsError {
110    #[error("not a repository: {0}")]
111    NotARepo(String),
112    #[error("branch not found: {0}")]
113    NoSuchBranch(String),
114    #[error("no upstream configured")]
115    NoUpstream,
116    #[error("nothing to commit")]
117    NothingToCommit,
118    #[error("non-fast-forward; merge or rebase required")]
119    NonFastForward,
120    #[error("unsupported backend: {0}")]
121    Unsupported(BackendId),
122    #[error("io: {0}")]
123    Io(#[from] std::io::Error),
124    #[error("{backend}: {msg}")]
125    Backend { backend: BackendId, msg: String },
126}
127
128#[cfg(feature = "vcs")]
129pub type Result<T> = std::result::Result<T, VcsError>;
130
131/// The single trait every VCS backend implements.
132#[cfg(feature = "vcs")]
133pub trait Vcs: Send + Sync {
134    fn id(&self) -> BackendId;
135    fn caps(&self) -> models::Capabilities;
136
137    // lifecycle
138    fn open(path: &Path) -> Result<Self>
139    where
140        Self: Sized;
141    fn clone(url: &str, dest: &Path, on: Option<models::OnEvent>) -> Result<Self>
142    where
143        Self: Sized;
144
145    // context
146    fn workdir(&self) -> &Path;
147
148    // common ops
149    fn current_branch(&self) -> Result<Option<String>>;
150    fn branches(&self) -> Result<Vec<models::BranchItem>>;
151
152    #[deprecated(
153        since = "0.1.0",
154        note = "This function is being replaced by `branches`."
155    )]
156    fn local_branches(&self) -> Result<Vec<String>>;
157    fn create_branch(&self, name: &str, checkout: bool) -> Result<()>;
158    fn checkout_branch(&self, name: &str) -> Result<()>;
159
160    // network
161    fn ensure_remote(&self, name: &str, url: &str) -> Result<()>;
162    fn list_remotes(&self) -> Result<Vec<(String, String)>>;
163    fn remove_remote(&self, name: &str) -> Result<()>;
164    fn fetch(&self, remote: &str, refspec: &str, on: Option<models::OnEvent>) -> Result<()>;
165    fn fetch_with_options(
166        &self,
167        remote: &str,
168        refspec: &str,
169        _opts: models::FetchOptions,
170        on: Option<models::OnEvent>,
171    ) -> Result<()> {
172        self.fetch(remote, refspec, on)
173    }
174    fn push(&self, remote: &str, refspec: &str, on: Option<models::OnEvent>) -> Result<()>;
175    fn pull_ff_only(&self, remote: &str, branch: &str, on: Option<models::OnEvent>) -> Result<()>;
176
177    // content
178    fn commit(&self, message: &str, name: &str, email: &str, paths: &[PathBuf]) -> Result<String>;
179    fn commit_index(&self, message: &str, name: &str, email: &str) -> Result<String>;
180    fn status_summary(&self) -> Result<models::StatusSummary>;
181    fn status_payload(&self) -> Result<models::StatusPayload>;
182    fn log_commits(&self, query: &models::LogQuery) -> Result<Vec<models::CommitItem>>;
183    fn diff_file(&self, path: &Path) -> Result<Vec<String>>;
184    fn diff_commit(&self, rev: &str) -> Result<Vec<String>>;
185
186    // conflicts
187    fn conflict_details(&self, _path: &Path) -> Result<models::ConflictDetails> {
188        Err(VcsError::Unsupported(self.id()))
189    }
190    fn checkout_conflict_side(&self, _path: &Path, _side: models::ConflictSide) -> Result<()> {
191        Err(VcsError::Unsupported(self.id()))
192    }
193    fn write_merge_result(&self, _path: &Path, _content: &[u8]) -> Result<()> {
194        Err(VcsError::Unsupported(self.id()))
195    }
196
197    fn stage_patch(&self, patch: &str) -> Result<()>;
198    fn discard_paths(&self, paths: &[PathBuf]) -> Result<()>;
199    fn apply_reverse_patch(&self, patch: &str) -> Result<()>;
200
201    // branches
202    fn delete_branch(&self, name: &str, force: bool) -> Result<()>;
203    fn rename_branch(&self, old: &str, new: &str) -> Result<()>;
204    fn merge_into_current(&self, name: &str) -> Result<()>;
205    fn merge_into_current_with_message(&self, name: &str, message: Option<&str>) -> Result<()> {
206        let _ = message;
207        self.merge_into_current(name)
208    }
209    fn merge_abort(&self) -> Result<()> {
210        Err(VcsError::Unsupported(self.id()))
211    }
212    fn merge_continue(&self) -> Result<()> {
213        Err(VcsError::Unsupported(self.id()))
214    }
215    fn merge_in_progress(&self) -> Result<bool> {
216        Ok(false)
217    }
218    fn set_branch_upstream(&self, _branch: &str, _upstream: &str) -> Result<()> {
219        Err(VcsError::Unsupported(self.id()))
220    }
221    fn branch_upstream(&self, _branch: &str) -> Result<Option<String>> {
222        Err(VcsError::Unsupported(self.id()))
223    }
224
225    // recovery
226    fn hard_reset_head(&self) -> Result<()>;
227    fn reset_soft_to(&self, _rev: &str) -> Result<()> {
228        Err(VcsError::Unsupported(self.id()))
229    }
230
231    // config
232    fn get_identity(&self) -> Result<Option<(String, String)>>;
233    fn set_identity_local(&self, name: &str, email: &str) -> Result<()>;
234
235    // stash
236    fn stash_list(&self) -> Result<Vec<models::StashItem>> {
237        Err(VcsError::Unsupported(self.id()))
238    }
239    fn stash_push(
240        &self,
241        _message: &str,
242        _include_untracked: bool,
243        _paths: &[PathBuf],
244    ) -> Result<()> {
245        Err(VcsError::Unsupported(self.id()))
246    }
247    fn stash_apply(&self, _selector: &str) -> Result<()> {
248        Err(VcsError::Unsupported(self.id()))
249    }
250    fn stash_pop(&self, _selector: &str) -> Result<()> {
251        Err(VcsError::Unsupported(self.id()))
252    }
253    fn stash_drop(&self, _selector: &str) -> Result<()> {
254        Err(VcsError::Unsupported(self.id()))
255    }
256    fn stash_show(&self, _selector: &str) -> Result<Vec<String>> {
257        Err(VcsError::Unsupported(self.id()))
258    }
259
260    // lfs
261    fn lfs_fetch(&self) -> Result<()> {
262        Err(VcsError::Unsupported(self.id()))
263    }
264    fn lfs_pull(&self) -> Result<()> {
265        Err(VcsError::Unsupported(self.id()))
266    }
267    fn lfs_prune(&self) -> Result<()> {
268        Err(VcsError::Unsupported(self.id()))
269    }
270    fn lfs_track(&self, _paths: &[PathBuf]) -> Result<()> {
271        Err(VcsError::Unsupported(self.id()))
272    }
273    fn lfs_untrack(&self, _paths: &[PathBuf]) -> Result<()> {
274        Err(VcsError::Unsupported(self.id()))
275    }
276    fn lfs_is_tracked(&self, _path: &Path) -> Result<bool> {
277        Err(VcsError::Unsupported(self.id()))
278    }
279
280    // history operations
281    fn cherry_pick(&self, _rev: &str) -> Result<()> {
282        Err(VcsError::Unsupported(self.id()))
283    }
284    fn revert_commit(&self, _rev: &str, _no_edit: bool) -> Result<()> {
285        Err(VcsError::Unsupported(self.id()))
286    }
287}