Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-vcs/src/lib.rs')
| -rw-r--r-- | helix-vcs/src/lib.rs | 127 |
1 files changed, 23 insertions, 104 deletions
diff --git a/helix-vcs/src/lib.rs b/helix-vcs/src/lib.rs index 4c5f2036..97320d32 100644 --- a/helix-vcs/src/lib.rs +++ b/helix-vcs/src/lib.rs @@ -1,13 +1,9 @@ -//! `helix_vcs` provides types for working with diffs from a Version Control System (VCS). -//! Currently `git` is the only supported provider for diffs, but this architecture allows -//! for other providers to be added in the future. +use std::path::Path; -use anyhow::{anyhow, bail, Result}; -use arc_swap::ArcSwap; -use std::{ - path::{Path, PathBuf}, - sync::Arc, -}; +#[cfg(feature = "git")] +pub use git::Git; +#[cfg(not(feature = "git"))] +pub use Dummy as Git; #[cfg(feature = "git")] mod git; @@ -16,64 +12,31 @@ mod diff; pub use diff::{DiffHandle, Hunk}; -mod status; +pub trait DiffProvider { + /// Returns the data that a diff should be computed against + /// if this provider is used. + /// The data is returned as raw byte without any decoding or encoding performed + /// to ensure all file encodings are handled correctly. + fn get_diff_base(&self, file: &Path) -> Option<Vec<u8>>; +} -pub use status::FileChange; +#[doc(hidden)] +pub struct Dummy; +impl DiffProvider for Dummy { + fn get_diff_base(&self, _file: &Path) -> Option<Vec<u8>> { + None + } +} -/// Contains all active diff providers. Diff providers are compiled in via features. Currently -/// only `git` is supported. -#[derive(Clone)] pub struct DiffProviderRegistry { - providers: Vec<DiffProvider>, + providers: Vec<Box<dyn DiffProvider>>, } impl DiffProviderRegistry { - /// Get the given file from the VCS. This provides the unedited document as a "base" - /// for a diff to be created. pub fn get_diff_base(&self, file: &Path) -> Option<Vec<u8>> { self.providers .iter() - .find_map(|provider| match provider.get_diff_base(file) { - Ok(res) => Some(res), - Err(err) => { - log::debug!("{err:#?}"); - log::debug!("failed to open diff base for {}", file.display()); - None - } - }) - } - - /// Get the current name of the current [HEAD](https://stackoverflow.com/questions/2304087/what-is-head-in-git). - pub fn get_current_head_name(&self, file: &Path) -> Option<Arc<ArcSwap<Box<str>>>> { - self.providers - .iter() - .find_map(|provider| match provider.get_current_head_name(file) { - Ok(res) => Some(res), - Err(err) => { - log::debug!("{err:#?}"); - log::debug!("failed to obtain current head name for {}", file.display()); - None - } - }) - } - - /// Fire-and-forget changed file iteration. Runs everything in a background task. Keeps - /// iteration until `on_change` returns `false`. - pub fn for_each_changed_file( - self, - cwd: PathBuf, - f: impl Fn(Result<FileChange>) -> bool + Send + 'static, - ) { - tokio::task::spawn_blocking(move || { - if self - .providers - .iter() - .find_map(|provider| provider.for_each_changed_file(&cwd, &f).ok()) - .is_none() - { - f(Err(anyhow!("no diff provider returns success"))); - } - }); + .find_map(|provider| provider.get_diff_base(file)) } } @@ -81,52 +44,8 @@ impl Default for DiffProviderRegistry { fn default() -> Self { // currently only git is supported // TODO make this configurable when more providers are added - let providers = vec![ - #[cfg(feature = "git")] - DiffProvider::Git, - DiffProvider::None, - ]; + let git: Box<dyn DiffProvider> = Box::new(Git); + let providers = vec![git]; DiffProviderRegistry { providers } } } - -/// A union type that includes all types that implement [DiffProvider]. We need this type to allow -/// cloning [DiffProviderRegistry] as `Clone` cannot be used in trait objects. -/// -/// `Copy` is simply to ensure the `clone()` call is the simplest it can be. -#[derive(Copy, Clone)] -enum DiffProvider { - #[cfg(feature = "git")] - Git, - None, -} - -impl DiffProvider { - fn get_diff_base(&self, file: &Path) -> Result<Vec<u8>> { - match self { - #[cfg(feature = "git")] - Self::Git => git::get_diff_base(file), - Self::None => bail!("No diff support compiled in"), - } - } - - fn get_current_head_name(&self, file: &Path) -> Result<Arc<ArcSwap<Box<str>>>> { - match self { - #[cfg(feature = "git")] - Self::Git => git::get_current_head_name(file), - Self::None => bail!("No diff support compiled in"), - } - } - - fn for_each_changed_file( - &self, - cwd: &Path, - f: impl Fn(Result<FileChange>) -> bool, - ) -> Result<()> { - match self { - #[cfg(feature = "git")] - Self::Git => git::for_each_changed_file(cwd, f), - Self::None => bail!("No diff support compiled in"), - } - } -} |