workspace: combine working copy functions into a trait

This commit is contained in:
Daniel Ploch 2024-01-25 11:30:11 -05:00 committed by Daniel Ploch
parent c2da98471b
commit cb889f0b45
7 changed files with 130 additions and 88 deletions

View file

@ -32,11 +32,9 @@ use jj_lib::signing::Signer;
use jj_lib::store::Store;
use jj_lib::working_copy::{
CheckoutError, CheckoutStats, LockedWorkingCopy, ResetError, SnapshotError, SnapshotOptions,
WorkingCopy, WorkingCopyStateError,
};
use jj_lib::workspace::{
default_working_copy_factories, WorkingCopyInitializer, Workspace, WorkspaceInitError,
WorkingCopy, WorkingCopyFactory, WorkingCopyStateError,
};
use jj_lib::workspace::{default_working_copy_factories, Workspace, WorkspaceInitError};
#[derive(clap::Parser, Clone, Debug)]
enum CustomCommand {
@ -67,7 +65,7 @@ fn run_custom_command(
&ReadonlyRepo::default_op_heads_store_initializer(),
&ReadonlyRepo::default_index_store_initializer(),
&ReadonlyRepo::default_submodule_store_initializer(),
&ConflictsWorkingCopy::initializer(),
&ConflictsWorkingCopyFactory {},
WorkspaceId::default(),
)?;
Ok(())
@ -79,13 +77,7 @@ fn main() -> std::process::ExitCode {
let mut working_copy_factories = default_working_copy_factories();
working_copy_factories.insert(
ConflictsWorkingCopy::name().to_owned(),
Box::new(|store, working_copy_path, state_path| {
Box::new(ConflictsWorkingCopy::load(
store.clone(),
working_copy_path.to_owned(),
state_path.to_owned(),
))
}),
Box::new(ConflictsWorkingCopyFactory {}),
);
CliRunner::init()
.set_working_copy_factories(working_copy_factories)
@ -113,8 +105,8 @@ impl ConflictsWorkingCopy {
store: Arc<Store>,
working_copy_path: PathBuf,
state_path: PathBuf,
workspace_id: WorkspaceId,
operation_id: OperationId,
workspace_id: WorkspaceId,
) -> Result<Self, WorkingCopyStateError> {
let inner = LocalWorkingCopy::init(
store,
@ -128,21 +120,6 @@ impl ConflictsWorkingCopy {
})
}
fn initializer() -> Box<WorkingCopyInitializer<'static>> {
Box::new(
|store, working_copy_path, state_path, workspace_id, operation_id| {
let wc = Self::init(
store,
working_copy_path,
state_path,
workspace_id,
operation_id,
)?;
Ok(Box::new(wc))
},
)
}
fn load(store: Arc<Store>, working_copy_path: PathBuf, state_path: PathBuf) -> Self {
let inner = LocalWorkingCopy::load(store, working_copy_path, state_path);
ConflictsWorkingCopy {
@ -189,6 +166,40 @@ impl WorkingCopy for ConflictsWorkingCopy {
}
}
struct ConflictsWorkingCopyFactory {}
impl WorkingCopyFactory for ConflictsWorkingCopyFactory {
fn init_working_copy(
&self,
store: Arc<Store>,
working_copy_path: PathBuf,
state_path: PathBuf,
operation_id: OperationId,
workspace_id: WorkspaceId,
) -> Result<Box<dyn WorkingCopy>, WorkingCopyStateError> {
Ok(Box::new(ConflictsWorkingCopy::init(
store,
working_copy_path,
state_path,
operation_id,
workspace_id,
)?))
}
fn load_working_copy(
&self,
store: Arc<Store>,
working_copy_path: PathBuf,
state_path: PathBuf,
) -> Box<dyn WorkingCopy> {
Box::new(ConflictsWorkingCopy::load(
store,
working_copy_path,
state_path,
))
}
}
struct LockedConflictsWorkingCopy {
wc_path: PathBuf,
inner: Box<dyn LockedWorkingCopy>,

View file

@ -63,11 +63,11 @@ use jj_lib::transaction::Transaction;
use jj_lib::tree::TreeMergeError;
use jj_lib::working_copy::{
CheckoutStats, LockedWorkingCopy, ResetError, SnapshotError, SnapshotOptions, WorkingCopy,
WorkingCopyStateError,
WorkingCopyFactory, WorkingCopyStateError,
};
use jj_lib::workspace::{
default_working_copy_factories, LockedWorkspace, WorkingCopyFactory, Workspace,
WorkspaceInitError, WorkspaceLoadError, WorkspaceLoader,
default_working_copy_factories, LockedWorkspace, Workspace, WorkspaceInitError,
WorkspaceLoadError, WorkspaceLoader,
};
use jj_lib::{dag_walk, file_util, git, op_walk, revset};
use once_cell::unsync::OnceCell;
@ -515,7 +515,7 @@ pub struct CommandHelper {
layered_configs: LayeredConfigs,
maybe_workspace_loader: Result<WorkspaceLoader, CommandError>,
store_factories: StoreFactories,
working_copy_factories: HashMap<String, WorkingCopyFactory>,
working_copy_factories: HashMap<String, Box<dyn WorkingCopyFactory>>,
}
impl CommandHelper {
@ -530,7 +530,7 @@ impl CommandHelper {
layered_configs: LayeredConfigs,
maybe_workspace_loader: Result<WorkspaceLoader, CommandError>,
store_factories: StoreFactories,
working_copy_factories: HashMap<String, WorkingCopyFactory>,
working_copy_factories: HashMap<String, Box<dyn WorkingCopyFactory>>,
) -> Self {
// `cwd` is canonicalized for consistency with `Workspace::workspace_root()` and
// to easily compute relative paths between them.
@ -2775,7 +2775,7 @@ pub struct CliRunner {
app: Command,
extra_configs: Option<config::Config>,
store_factories: Option<StoreFactories>,
working_copy_factories: Option<HashMap<String, WorkingCopyFactory>>,
working_copy_factories: Option<HashMap<String, Box<dyn WorkingCopyFactory>>>,
dispatch_fn: CliDispatchFn,
process_global_args_fns: Vec<ProcessGlobalArgsFn>,
}
@ -2822,7 +2822,7 @@ impl CliRunner {
/// Replaces working copy factories to be used.
pub fn set_working_copy_factories(
mut self,
working_copy_factories: HashMap<String, WorkingCopyFactory>,
working_copy_factories: HashMap<String, Box<dyn WorkingCopyFactory>>,
) -> Self {
self.working_copy_factories = Some(working_copy_factories);
self
@ -2928,7 +2928,7 @@ impl CliRunner {
let settings = UserSettings::from_config(config);
let working_copy_factories = self
.working_copy_factories
.unwrap_or_else(|| default_working_copy_factories());
.unwrap_or_else(default_working_copy_factories);
let command_helper = CommandHelper::new(
self.app,
cwd,

View file

@ -23,7 +23,7 @@ use jj_lib::object_id::ObjectId;
use jj_lib::op_store::WorkspaceId;
use jj_lib::repo::Repo;
use jj_lib::rewrite::merge_commit_trees;
use jj_lib::workspace::{default_working_copy_initializer, Workspace};
use jj_lib::workspace::{default_working_copy_factory, Workspace};
use tracing::instrument;
use crate::cli_util::{
@ -153,7 +153,7 @@ fn cmd_workspace_add(
command.settings(),
&destination_path,
repo,
default_working_copy_initializer(),
&*default_working_copy_factory(),
workspace_id,
)?;
writeln!(

View file

@ -64,7 +64,7 @@ use crate::store::Store;
use crate::tree::Tree;
use crate::working_copy::{
CheckoutError, CheckoutStats, LockedWorkingCopy, ResetError, SnapshotError, SnapshotOptions,
SnapshotProgress, WorkingCopy, WorkingCopyStateError,
SnapshotProgress, WorkingCopy, WorkingCopyFactory, WorkingCopyStateError,
};
#[cfg(unix)]
@ -1653,6 +1653,36 @@ impl LocalWorkingCopy {
}
}
pub struct LocalWorkingCopyFactory {}
impl WorkingCopyFactory for LocalWorkingCopyFactory {
fn init_working_copy(
&self,
store: Arc<Store>,
working_copy_path: PathBuf,
state_path: PathBuf,
operation_id: OperationId,
workspace_id: WorkspaceId,
) -> Result<Box<dyn WorkingCopy>, WorkingCopyStateError> {
Ok(Box::new(LocalWorkingCopy::init(
store,
working_copy_path,
state_path,
operation_id,
workspace_id,
)?))
}
fn load_working_copy(
&self,
store: Arc<Store>,
working_copy_path: PathBuf,
state_path: PathBuf,
) -> Box<dyn WorkingCopy> {
Box::new(LocalWorkingCopy::load(store, working_copy_path, state_path))
}
}
/// A working copy that's locked on disk. The lock is held until you call
/// `finish()` or `discard()`.
pub struct LockedLocalWorkingCopy {

View file

@ -30,6 +30,7 @@ use crate::merged_tree::MergedTree;
use crate::op_store::{OperationId, WorkspaceId};
use crate::repo_path::{RepoPath, RepoPathBuf};
use crate::settings::HumanByteSize;
use crate::store::Store;
/// The trait all working-copy implementations must implement.
pub trait WorkingCopy {
@ -63,6 +64,27 @@ pub trait WorkingCopy {
fn start_mutation(&self) -> Result<Box<dyn LockedWorkingCopy>, WorkingCopyStateError>;
}
/// The factory which creates and loads a specific type of working copy.
pub trait WorkingCopyFactory {
/// Create a new working copy from scratch.
fn init_working_copy(
&self,
store: Arc<Store>,
working_copy_path: PathBuf,
state_path: PathBuf,
operation_id: OperationId,
workspace_id: WorkspaceId,
) -> Result<Box<dyn WorkingCopy>, WorkingCopyStateError>;
/// Load an existing working copy.
fn load_working_copy(
&self,
store: Arc<Store>,
working_copy_path: PathBuf,
state_path: PathBuf,
) -> Box<dyn WorkingCopy>;
}
/// A working copy that's being modified.
pub trait LockedWorkingCopy {
/// Should return `self`. For down-casting purposes.

View file

@ -28,7 +28,7 @@ use crate::commit::Commit;
use crate::file_util::{self, IoResultExt as _, PathError};
use crate::git_backend::{canonicalize_git_repo_path, GitBackend};
use crate::local_backend::LocalBackend;
use crate::local_working_copy::LocalWorkingCopy;
use crate::local_working_copy::{LocalWorkingCopy, LocalWorkingCopyFactory};
use crate::op_store::{OperationId, WorkspaceId};
use crate::repo::{
read_store_type_compat, BackendInitializer, CheckOutCommitError, IndexStoreInitializer,
@ -39,7 +39,8 @@ use crate::settings::UserSettings;
use crate::signing::{SignInitError, Signer};
use crate::store::Store;
use crate::working_copy::{
CheckoutError, CheckoutStats, LockedWorkingCopy, WorkingCopy, WorkingCopyStateError,
CheckoutError, CheckoutStats, LockedWorkingCopy, WorkingCopy, WorkingCopyFactory,
WorkingCopyStateError,
};
#[derive(Error, Debug)]
@ -102,7 +103,7 @@ fn init_working_copy(
repo: &Arc<ReadonlyRepo>,
workspace_root: &Path,
jj_dir: &Path,
working_copy_initializer: &WorkingCopyInitializer,
working_copy_factory: &dyn WorkingCopyFactory,
workspace_id: WorkspaceId,
) -> Result<(Box<dyn WorkingCopy>, Arc<ReadonlyRepo>), WorkspaceInitError> {
let working_copy_state_path = jj_dir.join("working_copy");
@ -116,12 +117,12 @@ fn init_working_copy(
)?;
let repo = tx.commit(format!("add workspace '{}'", workspace_id.as_str()));
let working_copy = working_copy_initializer(
let working_copy = working_copy_factory.init_working_copy(
repo.store().clone(),
workspace_root.to_path_buf(),
working_copy_state_path.clone(),
workspace_id,
repo.op_id().clone(),
workspace_id,
)?;
let working_copy_type_path = working_copy_state_path.join("type");
fs::write(&working_copy_type_path, working_copy.name()).context(&working_copy_type_path)?;
@ -243,7 +244,7 @@ impl Workspace {
op_heads_store_initializer: &OpHeadsStoreInitializer,
index_store_initializer: &IndexStoreInitializer,
submodule_store_initializer: &SubmoduleStoreInitializer,
working_copy_initializer: &WorkingCopyInitializer,
working_copy_factory: &dyn WorkingCopyFactory,
workspace_id: WorkspaceId,
) -> Result<(Self, Arc<ReadonlyRepo>), WorkspaceInitError> {
let jj_dir = create_jj_dir(workspace_root)?;
@ -269,7 +270,7 @@ impl Workspace {
&repo,
workspace_root,
&jj_dir,
working_copy_initializer,
working_copy_factory,
workspace_id,
)?;
let repo_loader = repo.loader();
@ -297,7 +298,7 @@ impl Workspace {
ReadonlyRepo::default_op_heads_store_initializer(),
ReadonlyRepo::default_index_store_initializer(),
ReadonlyRepo::default_submodule_store_initializer(),
default_working_copy_initializer(),
&*default_working_copy_factory(),
WorkspaceId::default(),
)
}
@ -306,7 +307,7 @@ impl Workspace {
user_settings: &UserSettings,
workspace_root: &Path,
repo: &Arc<ReadonlyRepo>,
working_copy_initializer: &WorkingCopyInitializer,
working_copy_factory: &dyn WorkingCopyFactory,
workspace_id: WorkspaceId,
) -> Result<(Self, Arc<ReadonlyRepo>), WorkspaceInitError> {
let jj_dir = create_jj_dir(workspace_root)?;
@ -328,7 +329,7 @@ impl Workspace {
repo,
workspace_root,
&jj_dir,
working_copy_initializer,
working_copy_factory,
workspace_id,
)?;
let workspace = Workspace::new(workspace_root, working_copy, repo.loader())?;
@ -339,7 +340,7 @@ impl Workspace {
user_settings: &UserSettings,
workspace_path: &Path,
store_factories: &StoreFactories,
working_copy_factories: &HashMap<String, WorkingCopyFactory>,
working_copy_factories: &HashMap<String, Box<dyn WorkingCopyFactory>>,
) -> Result<Self, WorkspaceLoadError> {
let loader = WorkspaceLoader::init(workspace_path)?;
let workspace = loader.load(user_settings, store_factories, working_copy_factories)?;
@ -475,7 +476,7 @@ impl WorkspaceLoader {
&self,
user_settings: &UserSettings,
store_factories: &StoreFactories,
working_copy_factories: &HashMap<String, WorkingCopyFactory>,
working_copy_factories: &HashMap<String, Box<dyn WorkingCopyFactory>>,
) -> Result<Workspace, WorkspaceLoadError> {
let repo_loader = RepoLoader::init(user_settings, &self.repo_dir, store_factories)?;
let working_copy = self.load_working_copy(repo_loader.store(), working_copy_factories)?;
@ -486,7 +487,7 @@ impl WorkspaceLoader {
fn load_working_copy(
&self,
store: &Arc<Store>,
working_copy_factories: &HashMap<String, WorkingCopyFactory>,
working_copy_factories: &HashMap<String, Box<dyn WorkingCopyFactory>>,
) -> Result<Box<dyn WorkingCopy>, StoreLoadError> {
// For compatibility with existing repos. TODO: Delete default in 0.17+
let working_copy_type = read_store_type_compat(
@ -501,46 +502,24 @@ impl WorkspaceLoader {
store: "working copy",
store_type: working_copy_type.to_string(),
})?;
let working_copy =
working_copy_factory(store, &self.workspace_root, &self.working_copy_state_path);
Ok(working_copy)
Ok(working_copy_factory.load_working_copy(
store.clone(),
self.workspace_root.to_owned(),
self.working_copy_state_path.to_owned(),
))
}
}
pub fn default_working_copy_initializer() -> &'static WorkingCopyInitializer<'static> {
&|store: Arc<Store>, working_copy_path, state_path, workspace_id, operation_id| {
let wc = LocalWorkingCopy::init(
store,
working_copy_path,
state_path,
operation_id,
workspace_id,
)?;
Ok(Box::new(wc))
}
}
pub fn default_working_copy_factories() -> HashMap<String, WorkingCopyFactory> {
let mut factories: HashMap<String, WorkingCopyFactory> = HashMap::new();
pub fn default_working_copy_factories() -> HashMap<String, Box<dyn WorkingCopyFactory>> {
let mut factories: HashMap<String, Box<dyn WorkingCopyFactory>> = HashMap::new();
factories.insert(
LocalWorkingCopy::name().to_owned(),
Box::new(|store, working_copy_path, store_path| {
Box::new(LocalWorkingCopy::load(
store.clone(),
working_copy_path.to_owned(),
store_path.to_owned(),
))
}),
Box::new(LocalWorkingCopyFactory {}),
);
factories
}
pub type WorkingCopyInitializer<'a> = dyn Fn(
Arc<Store>,
PathBuf,
PathBuf,
WorkspaceId,
OperationId,
) -> Result<Box<dyn WorkingCopy>, WorkingCopyStateError>
+ 'a;
pub type WorkingCopyFactory = Box<dyn Fn(&Arc<Store>, &Path, &Path) -> Box<dyn WorkingCopy>>;
pub fn default_working_copy_factory() -> Box<dyn WorkingCopyFactory> {
Box::new(LocalWorkingCopyFactory {})
}

View file

@ -16,7 +16,7 @@ use assert_matches::assert_matches;
use jj_lib::op_store::WorkspaceId;
use jj_lib::repo::Repo;
use jj_lib::workspace::{
default_working_copy_factories, default_working_copy_initializer, Workspace, WorkspaceLoadError,
default_working_copy_factories, default_working_copy_factory, Workspace, WorkspaceLoadError,
};
use testutils::{TestRepo, TestWorkspace};
@ -51,7 +51,7 @@ fn test_init_additional_workspace() {
&settings,
&ws2_root,
&test_workspace.repo,
default_working_copy_initializer(),
&*default_working_copy_factory(),
ws2_id.clone(),
)
.unwrap();