// Copyright 2020 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. #![allow(missing_docs)] use std::collections::hash_map::Entry;use std::collections::HashMap;use std::collections::HashSet;use std::fmt::Debug;use std::fmt::Formatter;use std::fs;use std::path::Path;use std::slice;use std::sync::Arc; use itertools::Itertools as _;use once_cell::sync::OnceCell;use thiserror::Error;use tracing::instrument; use self::dirty_cell::DirtyCell;use crate::backend::Backend;use crate::backend::BackendError;use crate::backend::BackendInitError;use crate::backend::BackendLoadError;use crate::backend::BackendResult;use crate::backend::ChangeId;use crate::backend::CommitId;use crate::backend::MergedTreeId;use crate::commit::Commit;use crate::commit::CommitByCommitterTimestamp;use crate::commit_builder::CommitBuilder;use crate::commit_builder::DetachedCommitBuilder;use crate::dag_walk;use crate::default_index::DefaultIndexStore;use crate::default_index::DefaultMutableIndex;use crate::default_submodule_store::DefaultSubmoduleStore;use crate::file_util::IoResultExt as _;use crate::file_util::PathError;use crate::index::ChangeIdIndex;use crate::index::Index;use crate::index::IndexReadError;use crate::index::IndexStore;use crate::index::MutableIndex;use crate::index::ReadonlyIndex;use crate::merge::MergeBuilder;use crate::object_id::HexPrefix;use crate::object_id::ObjectId as _;use crate::object_id::PrefixResolution;use crate::op_heads_store;use crate::op_heads_store::OpHeadResolutionError;use crate::op_heads_store::OpHeadsStore;use crate::op_heads_store::OpHeadsStoreError;use crate::op_store;use crate::op_store::OpStore;use crate::op_store::OpStoreError;use crate::op_store::OpStoreResult;use crate::op_store::OperationId;use crate::op_store::RefTarget;use crate::op_store::RemoteRef;use crate::op_store::RemoteRefState;use crate::op_store::RootOperationData;use crate::operation::Operation;use crate::ref_name::GitRefName;use crate::ref_name::RefName;use crate::ref_name::RemoteName;use crate::ref_name::RemoteRefSymbol;use crate::ref_name::WorkspaceName;use crate::ref_name::WorkspaceNameBuf;use crate::refs::diff_named_ref_targets;use crate::refs::diff_named_remote_refs;use crate::refs::merge_ref_targets;use crate::refs::merge_remote_refs;use crate::revset;use crate::revset::RevsetExpression;use crate::revset::RevsetIteratorExt as _;use crate::rewrite::merge_commit_trees;use crate::rewrite::rebase_commit_with_options;use crate::rewrite::CommitRewriter;use crate::rewrite::RebaseOptions;use crate::rewrite::RebasedCommit;use crate::rewrite::RewriteRefsOptions;use crate::settings::UserSettings;use crate::signing::SignInitError;use crate::signing::Signer;use crate::simple_backend::SimpleBackend;use crate::simple_op_heads_store::SimpleOpHeadsStore;use crate::simple_op_store::SimpleOpStore;use crate::store::Store;use crate::submodule_store::SubmoduleStore;use crate::transaction::Transaction;use crate::view::RenameWorkspaceError;use crate::view::View; pub trait Repo { /// Base repository that contains all committed data. Returns `self` if this /// is a `ReadonlyRepo`, fn base_repo(&self) -> &ReadonlyRepo; fn store(&self) -> &Arc<Store>; fn op_store(&self) -> &Arc<dyn OpStore>; fn index(&self) -> &dyn Index; fn view(&self) -> &View; fn submodule_store(&self) -> &Arc<dyn SubmoduleStore>; fn resolve_change_id(&self, change_id: &ChangeId) -> Option<Vec<CommitId>> { // Replace this if we added more efficient lookup method. let prefix = HexPrefix::from_bytes(change_id.as_bytes()); match self.resolve_change_id_prefix(&prefix) { PrefixResolution::NoMatch => None, PrefixResolution::SingleMatch(entries) => Some(entries), PrefixResolution::AmbiguousMatch => panic!("complete change_id should be unambiguous"), } } fn resolve_change_id_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<Vec<CommitId>>; fn shortest_unique_change_id_prefix_len(&self, target_id_bytes: &ChangeId) -> usize;} pub struct ReadonlyRepo { loader: RepoLoader, operation: Operation, index: Box<dyn ReadonlyIndex>, change_id_index: OnceCell<Box<dyn ChangeIdIndex>>, // TODO: This should eventually become part of the index and not be stored fully in memory. view: View,} impl Debug for ReadonlyRepo { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { f.debug_struct("ReadonlyRepo") .field("store", &self.loader.store) .finish_non_exhaustive() }} #[derive(Error, Debug)]pub enum RepoInitError { #[error(transparent)] Backend(#[from] BackendInitError), #[error(transparent)] OpHeadsStore(#[from] OpHeadsStoreError), #[error(transparent)] Path(#[from] PathError),} impl ReadonlyRepo { pub fn default_op_store_initializer() -> &'static OpStoreInitializer<'static> { &|_settings, store_path, root_data| { Ok(Box::new(SimpleOpStore::init(store_path, root_data)?)) } } pub fn default_op_heads_store_initializer() -> &'static OpHeadsStoreInitializer<'static> { &|_settings, store_path| Ok(Box::new(SimpleOpHeadsStore::init(store_path)?)) } pub fn default_index_store_initializer() -> &'static IndexStoreInitializer<'static> { &|_settings, store_path| Ok(Box::new(DefaultIndexStore::init(store_path)?)) } pub fn default_submodule_store_initializer() -> &'static SubmoduleStoreInitializer<'static> { &|_settings, store_path| Ok(Box::new(DefaultSubmoduleStore::init(store_path))) } #[expect(clippy::too_many_arguments)] pub fn init( settings: &UserSettings, repo_path: &Path, backend_initializer: &BackendInitializer, signer: Signer, op_store_initializer: &OpStoreInitializer, op_heads_store_initializer: &OpHeadsStoreInitializer, index_store_initializer: &IndexStoreInitializer, submodule_store_initializer: &SubmoduleStoreInitializer, ) -> Result<Arc<ReadonlyRepo>, RepoInitError> { let repo_path = dunce::canonicalize(repo_path).context(repo_path)?; let store_path = repo_path.join("store"); fs::create_dir(&store_path).context(&store_path)?; let backend = backend_initializer(settings, &store_path)?; let backend_path = store_path.join("type"); fs::write(&backend_path, backend.name()).context(&backend_path)?; let store = Store::new(backend, signer); let op_store_path = repo_path.join("op_store"); fs::create_dir(&op_store_path).context(&op_store_path)?; let root_op_data = RootOperationData { root_commit_id: store.root_commit_id().clone(), }; let op_store = op_store_initializer(settings, &op_store_path, root_op_data)?; let op_store_type_path = op_store_path.join("type"); fs::write(&op_store_type_path, op_store.name()).context(&op_store_type_path)?; let op_store: Arc<dyn OpStore> = Arc::from(op_store); let op_heads_path = repo_path.join("op_heads"); fs::create_dir(&op_heads_path).context(&op_heads_path)?; let op_heads_store = op_heads_store_initializer(settings, &op_heads_path)?; let op_heads_type_path = op_heads_path.join("type"); fs::write(&op_heads_type_path, op_heads_store.name()).context(&op_heads_type_path)?; op_heads_store.update_op_heads(&[], op_store.root_operation_id())?; let op_heads_store: Arc<dyn OpHeadsStore> = Arc::from(op_heads_store); let index_path = repo_path.join("index"); fs::create_dir(&index_path).context(&index_path)?; let index_store = index_store_initializer(settings, &index_path)?; let index_type_path = index_path.join("type"); fs::write(&index_type_path, index_store.name()).context(&index_type_path)?; let index_store: Arc<dyn IndexStore> = Arc::from(index_store); let submodule_store_path = repo_path.join("submodule_store"); fs::create_dir(&submodule_store_path).context(&submodule_store_path)?; let submodule_store = submodule_store_initializer(settings, &submodule_store_path)?; let submodule_store_type_path = submodule_store_path.join("type"); fs::write(&submodule_store_type_path, submodule_store.name()) .context(&submodule_store_type_path)?; let submodule_store = Arc::from(submodule_store); let loader = RepoLoader { settings: settings.clone(), store, op_store, op_heads_store, index_store, submodule_store, }; let root_operation = loader.root_operation(); let root_view = root_operation.view().expect("failed to read root view"); assert!(!root_view.heads().is_empty()); let index = loader .index_store .get_index_at_op(&root_operation, &loader.store) // If the root op index couldn't be read, the index backend wouldn't // be initialized properly. .map_err(|err| BackendInitError(err.into()))?; Ok(Arc::new(ReadonlyRepo { loader, operation: root_operation, index, change_id_index: OnceCell::new(), view: root_view, })) } pub fn loader(&self) -> &RepoLoader { &self.loader } pub fn op_id(&self) -> &OperationId { self.operation.id() } pub fn operation(&self) -> &Operation { &self.operation } pub fn view(&self) -> &View { &self.view } pub fn readonly_index(&self) -> &dyn ReadonlyIndex { self.index.as_ref() } fn change_id_index(&self) -> &dyn ChangeIdIndex { self.change_id_index .get_or_init(|| { self.readonly_index() .change_id_index(&mut self.view().heads().iter()) }) .as_ref() } pub fn op_heads_store(&self) -> &Arc<dyn OpHeadsStore> { self.loader.op_heads_store() } pub fn index_store(&self) -> &Arc<dyn IndexStore> { self.loader.index_store() } pub fn settings(&self) -> &UserSettings { self.loader.settings() } pub fn start_transaction(self: &Arc<ReadonlyRepo>) -> Transaction { let mut_repo = MutableRepo::new(self.clone(), self.readonly_index(), &self.view); Transaction::new(mut_repo, self.settings()) } pub fn reload_at_head(&self) -> Result<Arc<ReadonlyRepo>, RepoLoaderError> { self.loader().load_at_head() } #[instrument] pub fn reload_at(&self, operation: &Operation) -> Result<Arc<ReadonlyRepo>, RepoLoaderError> { self.loader().load_at(operation) }} impl Repo for ReadonlyRepo { fn base_repo(&self) -> &ReadonlyRepo { self } fn store(&self) -> &Arc<Store> { self.loader.store() } fn op_store(&self) -> &Arc<dyn OpStore> { self.loader.op_store() } fn index(&self) -> &dyn Index { self.readonly_index().as_index() } fn view(&self) -> &View { &self.view } fn submodule_store(&self) -> &Arc<dyn SubmoduleStore> { self.loader.submodule_store() } fn resolve_change_id_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<Vec<CommitId>> { self.change_id_index().resolve_prefix(prefix) } fn shortest_unique_change_id_prefix_len(&self, target_id: &ChangeId) -> usize { self.change_id_index().shortest_unique_prefix_len(target_id) }} pub type BackendInitializer<'a> = dyn Fn(&UserSettings, &Path) -> Result<Box<dyn Backend>, BackendInitError> + 'a;#[rustfmt::skip] // auto-formatted line would exceed the maximum widthpub type OpStoreInitializer<'a> = dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendInitError> + 'a;pub type OpHeadsStoreInitializer<'a> = dyn Fn(&UserSettings, &Path) -> Result<Box<dyn OpHeadsStore>, BackendInitError> + 'a;pub type IndexStoreInitializer<'a> = dyn Fn(&UserSettings, &Path) -> Result<Box<dyn IndexStore>, BackendInitError> + 'a;pub type SubmoduleStoreInitializer<'a> = dyn Fn(&UserSettings, &Path) -> Result<Box<dyn SubmoduleStore>, BackendInitError> + 'a; type BackendFactory = Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn Backend>, BackendLoadError>>;type OpStoreFactory = Box< dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendLoadError>,>;type OpHeadsStoreFactory = Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn OpHeadsStore>, BackendLoadError>>;type IndexStoreFactory = Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn IndexStore>, BackendLoadError>>;type SubmoduleStoreFactory = Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn SubmoduleStore>, BackendLoadError>>; pub fn merge_factories_map<F>(base: &mut HashMap<String, F>, ext: HashMap<String, F>) { for (name, factory) in ext { match base.entry(name) { Entry::Vacant(v) => { v.insert(factory); } Entry::Occupied(o) => { panic!("Conflicting factory definitions for '{}' factory", o.key()) } } }} pub struct StoreFactories { backend_factories: HashMap<String, BackendFactory>, op_store_factories: HashMap<String, OpStoreFactory>, op_heads_store_factories: HashMap<String, OpHeadsStoreFactory>, index_store_factories: HashMap<String, IndexStoreFactory>, submodule_store_factories: HashMap<String, SubmoduleStoreFactory>,} impl Default for StoreFactories { fn default() -> Self { let mut factories = StoreFactories::empty(); // Backends factories.add_backend( SimpleBackend::name(), Box::new(|_settings, store_path| Ok(Box::new(SimpleBackend::load(store_path)))), ); #[cfg(feature = "git")] factories.add_backend( crate::git_backend::GitBackend::name(), Box::new(|settings, store_path| { Ok(Box::new(crate::git_backend::GitBackend::load( settings, store_path, )?)) }), ); #[cfg(feature = "testing")] factories.add_backend( crate::secret_backend::SecretBackend::name(), Box::new(|settings, store_path| { Ok(Box::new(crate::secret_backend::SecretBackend::load( settings, store_path, )?)) }), ); // OpStores factories.add_op_store( SimpleOpStore::name(), Box::new(|_settings, store_path, root_data| { Ok(Box::new(SimpleOpStore::load(store_path, root_data))) }), ); // OpHeadsStores factories.add_op_heads_store( SimpleOpHeadsStore::name(), Box::new(|_settings, store_path| Ok(Box::new(SimpleOpHeadsStore::load(store_path)))), ); // Index factories.add_index_store( DefaultIndexStore::name(), Box::new(|_settings, store_path| Ok(Box::new(DefaultIndexStore::load(store_path)))), ); // SubmoduleStores factories.add_submodule_store( DefaultSubmoduleStore::name(), Box::new(|_settings, store_path| Ok(Box::new(DefaultSubmoduleStore::load(store_path)))), ); factories }} #[derive(Debug, Error)]pub enum StoreLoadError { #[error("Unsupported {store} backend type '{store_type}'")] UnsupportedType { store: &'static str, store_type: String, }, #[error("Failed to read {store} backend type")] ReadError { store: &'static str, source: PathError, }, #[error(transparent)] Backend(#[from] BackendLoadError), #[error(transparent)] Signing(#[from] SignInitError),} impl StoreFactories { pub fn empty() -> Self { StoreFactories { backend_factories: HashMap::new(), op_store_factories: HashMap::new(), op_heads_store_factories: HashMap::new(), index_store_factories: HashMap::new(), submodule_store_factories: HashMap::new(), } } pub fn merge(&mut self, ext: StoreFactories) { let StoreFactories { backend_factories, op_store_factories, op_heads_store_factories, index_store_factories, submodule_store_factories, } = ext; merge_factories_map(&mut self.backend_factories, backend_factories); merge_factories_map(&mut self.op_store_factories, op_store_factories); merge_factories_map(&mut self.op_heads_store_factories, op_heads_store_factories); merge_factories_map(&mut self.index_store_factories, index_store_factories); merge_factories_map( &mut self.submodule_store_factories, submodule_store_factories,Lines 1–500 of 1945. Load more
// Copyright 2020 The Jujutsu Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![allow(missing_docs)] use std::collections::hash_map::Entry; use std::collections::HashMap; use std::collections::HashSet; use std::fmt::Debug; use std::fmt::Formatter; use std::fs; use std::path::Path; use std::slice; use std::sync::Arc; use itertools::Itertools as _; use once_cell::sync::OnceCell; use thiserror::Error; use tracing::instrument; use self::dirty_cell::DirtyCell; use crate::backend::Backend; use crate::backend::BackendError; use crate::backend::BackendInitError; use crate::backend::BackendLoadError; use crate::backend::BackendResult; use crate::backend::ChangeId; use crate::backend::CommitId; use crate::backend::MergedTreeId; use crate::commit::Commit; use crate::commit::CommitByCommitterTimestamp; use crate::commit_builder::CommitBuilder; use crate::commit_builder::DetachedCommitBuilder; use crate::dag_walk; use crate::default_index::DefaultIndexStore; use crate::default_index::DefaultMutableIndex; use crate::default_submodule_store::DefaultSubmoduleStore; use crate::file_util::IoResultExt as _; use crate::file_util::PathError; use crate::index::ChangeIdIndex; use crate::index::Index; use crate::index::IndexReadError; use crate::index::IndexStore; use crate::index::MutableIndex; use crate::index::ReadonlyIndex; use crate::merge::MergeBuilder; use crate::object_id::HexPrefix; use crate::object_id::ObjectId as _; use crate::object_id::PrefixResolution; use crate::op_heads_store; use crate::op_heads_store::OpHeadResolutionError; use crate::op_heads_store::OpHeadsStore; use crate::op_heads_store::OpHeadsStoreError; use crate::op_store; use crate::op_store::OpStore; use crate::op_store::OpStoreError; use crate::op_store::OpStoreResult; use crate::op_store::OperationId; use crate::op_store::RefTarget; use crate::op_store::RemoteRef; use crate::op_store::RemoteRefState; use crate::op_store::RootOperationData; use crate::operation::Operation; use crate::ref_name::GitRefName; use crate::ref_name::RefName; use crate::ref_name::RemoteName; use crate::ref_name::RemoteRefSymbol; use crate::ref_name::WorkspaceName; use crate::ref_name::WorkspaceNameBuf; use crate::refs::diff_named_ref_targets; use crate::refs::diff_named_remote_refs; use crate::refs::merge_ref_targets; use crate::refs::merge_remote_refs; use crate::revset; use crate::revset::RevsetExpression; use crate::revset::RevsetIteratorExt as _; use crate::rewrite::merge_commit_trees; use crate::rewrite::rebase_commit_with_options; use crate::rewrite::CommitRewriter; use crate::rewrite::RebaseOptions; use crate::rewrite::RebasedCommit; use crate::rewrite::RewriteRefsOptions; use crate::settings::UserSettings; use crate::signing::SignInitError; use crate::signing::Signer; use crate::simple_backend::SimpleBackend; use crate::simple_op_heads_store::SimpleOpHeadsStore; use crate::simple_op_store::SimpleOpStore; use crate::store::Store; use crate::submodule_store::SubmoduleStore; use crate::transaction::Transaction; use crate::view::RenameWorkspaceError; use crate::view::View; pub trait Repo { /// Base repository that contains all committed data. Returns `self` if this /// is a `ReadonlyRepo`, fn base_repo(&self) -> &ReadonlyRepo; fn store(&self) -> &Arc<Store>; fn op_store(&self) -> &Arc<dyn OpStore>; fn index(&self) -> &dyn Index; fn view(&self) -> &View; fn submodule_store(&self) -> &Arc<dyn SubmoduleStore>; fn resolve_change_id(&self, change_id: &ChangeId) -> Option<Vec<CommitId>> { // Replace this if we added more efficient lookup method. let prefix = HexPrefix::from_bytes(change_id.as_bytes()); match self.resolve_change_id_prefix(&prefix) { PrefixResolution::NoMatch => None, PrefixResolution::SingleMatch(entries) => Some(entries), PrefixResolution::AmbiguousMatch => panic!("complete change_id should be unambiguous"), } } fn resolve_change_id_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<Vec<CommitId>>; fn shortest_unique_change_id_prefix_len(&self, target_id_bytes: &ChangeId) -> usize; } pub struct ReadonlyRepo { loader: RepoLoader, operation: Operation, index: Box<dyn ReadonlyIndex>, change_id_index: OnceCell<Box<dyn ChangeIdIndex>>, // TODO: This should eventually become part of the index and not be stored fully in memory. view: View, } impl Debug for ReadonlyRepo { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { f.debug_struct("ReadonlyRepo") .field("store", &self.loader.store) .finish_non_exhaustive() } } #[derive(Error, Debug)] pub enum RepoInitError { #[error(transparent)] Backend(#[from] BackendInitError), #[error(transparent)] OpHeadsStore(#[from] OpHeadsStoreError), #[error(transparent)] Path(#[from] PathError), } impl ReadonlyRepo { pub fn default_op_store_initializer() -> &'static OpStoreInitializer<'static> { &|_settings, store_path, root_data| { Ok(Box::new(SimpleOpStore::init(store_path, root_data)?)) } } pub fn default_op_heads_store_initializer() -> &'static OpHeadsStoreInitializer<'static> { &|_settings, store_path| Ok(Box::new(SimpleOpHeadsStore::init(store_path)?)) } pub fn default_index_store_initializer() -> &'static IndexStoreInitializer<'static> { &|_settings, store_path| Ok(Box::new(DefaultIndexStore::init(store_path)?)) } pub fn default_submodule_store_initializer() -> &'static SubmoduleStoreInitializer<'static> { &|_settings, store_path| Ok(Box::new(DefaultSubmoduleStore::init(store_path))) } #[expect(clippy::too_many_arguments)] pub fn init( settings: &UserSettings, repo_path: &Path, backend_initializer: &BackendInitializer, signer: Signer, op_store_initializer: &OpStoreInitializer, op_heads_store_initializer: &OpHeadsStoreInitializer, index_store_initializer: &IndexStoreInitializer, submodule_store_initializer: &SubmoduleStoreInitializer, ) -> Result<Arc<ReadonlyRepo>, RepoInitError> { let repo_path = dunce::canonicalize(repo_path).context(repo_path)?; let store_path = repo_path.join("store"); fs::create_dir(&store_path).context(&store_path)?; let backend = backend_initializer(settings, &store_path)?; let backend_path = store_path.join("type"); fs::write(&backend_path, backend.name()).context(&backend_path)?; let store = Store::new(backend, signer); let op_store_path = repo_path.join("op_store"); fs::create_dir(&op_store_path).context(&op_store_path)?; let root_op_data = RootOperationData { root_commit_id: store.root_commit_id().clone(), }; let op_store = op_store_initializer(settings, &op_store_path, root_op_data)?; let op_store_type_path = op_store_path.join("type"); fs::write(&op_store_type_path, op_store.name()).context(&op_store_type_path)?; let op_store: Arc<dyn OpStore> = Arc::from(op_store); let op_heads_path = repo_path.join("op_heads"); fs::create_dir(&op_heads_path).context(&op_heads_path)?; let op_heads_store = op_heads_store_initializer(settings, &op_heads_path)?; let op_heads_type_path = op_heads_path.join("type"); fs::write(&op_heads_type_path, op_heads_store.name()).context(&op_heads_type_path)?; op_heads_store.update_op_heads(&[], op_store.root_operation_id())?; let op_heads_store: Arc<dyn OpHeadsStore> = Arc::from(op_heads_store); let index_path = repo_path.join("index"); fs::create_dir(&index_path).context(&index_path)?; let index_store = index_store_initializer(settings, &index_path)?; let index_type_path = index_path.join("type"); fs::write(&index_type_path, index_store.name()).context(&index_type_path)?; let index_store: Arc<dyn IndexStore> = Arc::from(index_store); let submodule_store_path = repo_path.join("submodule_store"); fs::create_dir(&submodule_store_path).context(&submodule_store_path)?; let submodule_store = submodule_store_initializer(settings, &submodule_store_path)?; let submodule_store_type_path = submodule_store_path.join("type"); fs::write(&submodule_store_type_path, submodule_store.name()) .context(&submodule_store_type_path)?; let submodule_store = Arc::from(submodule_store); let loader = RepoLoader { settings: settings.clone(), store, op_store, op_heads_store, index_store, submodule_store, }; let root_operation = loader.root_operation(); let root_view = root_operation.view().expect("failed to read root view"); assert!(!root_view.heads().is_empty()); let index = loader .index_store .get_index_at_op(&root_operation, &loader.store) // If the root op index couldn't be read, the index backend wouldn't // be initialized properly. .map_err(|err| BackendInitError(err.into()))?; Ok(Arc::new(ReadonlyRepo { loader, operation: root_operation, index, change_id_index: OnceCell::new(), view: root_view, })) } pub fn loader(&self) -> &RepoLoader { &self.loader } pub fn op_id(&self) -> &OperationId { self.operation.id() } pub fn operation(&self) -> &Operation { &self.operation } pub fn view(&self) -> &View { &self.view } pub fn readonly_index(&self) -> &dyn ReadonlyIndex { self.index.as_ref() } fn change_id_index(&self) -> &dyn ChangeIdIndex { self.change_id_index .get_or_init(|| { self.readonly_index() .change_id_index(&mut self.view().heads().iter()) }) .as_ref() } pub fn op_heads_store(&self) -> &Arc<dyn OpHeadsStore> { self.loader.op_heads_store() } pub fn index_store(&self) -> &Arc<dyn IndexStore> { self.loader.index_store() } pub fn settings(&self) -> &UserSettings { self.loader.settings() } pub fn start_transaction(self: &Arc<ReadonlyRepo>) -> Transaction { let mut_repo = MutableRepo::new(self.clone(), self.readonly_index(), &self.view); Transaction::new(mut_repo, self.settings()) } pub fn reload_at_head(&self) -> Result<Arc<ReadonlyRepo>, RepoLoaderError> { self.loader().load_at_head() } #[instrument] pub fn reload_at(&self, operation: &Operation) -> Result<Arc<ReadonlyRepo>, RepoLoaderError> { self.loader().load_at(operation) } } impl Repo for ReadonlyRepo { fn base_repo(&self) -> &ReadonlyRepo { self } fn store(&self) -> &Arc<Store> { self.loader.store() } fn op_store(&self) -> &Arc<dyn OpStore> { self.loader.op_store() } fn index(&self) -> &dyn Index { self.readonly_index().as_index() } fn view(&self) -> &View { &self.view } fn submodule_store(&self) -> &Arc<dyn SubmoduleStore> { self.loader.submodule_store() } fn resolve_change_id_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<Vec<CommitId>> { self.change_id_index().resolve_prefix(prefix) } fn shortest_unique_change_id_prefix_len(&self, target_id: &ChangeId) -> usize { self.change_id_index().shortest_unique_prefix_len(target_id) } } pub type BackendInitializer<'a> = dyn Fn(&UserSettings, &Path) -> Result<Box<dyn Backend>, BackendInitError> + 'a; #[rustfmt::skip] // auto-formatted line would exceed the maximum width pub type OpStoreInitializer<'a> = dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendInitError> + 'a; pub type OpHeadsStoreInitializer<'a> = dyn Fn(&UserSettings, &Path) -> Result<Box<dyn OpHeadsStore>, BackendInitError> + 'a; pub type IndexStoreInitializer<'a> = dyn Fn(&UserSettings, &Path) -> Result<Box<dyn IndexStore>, BackendInitError> + 'a; pub type SubmoduleStoreInitializer<'a> = dyn Fn(&UserSettings, &Path) -> Result<Box<dyn SubmoduleStore>, BackendInitError> + 'a; type BackendFactory = Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn Backend>, BackendLoadError>>; type OpStoreFactory = Box< dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendLoadError>, >; type OpHeadsStoreFactory = Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn OpHeadsStore>, BackendLoadError>>; type IndexStoreFactory = Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn IndexStore>, BackendLoadError>>; type SubmoduleStoreFactory = Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn SubmoduleStore>, BackendLoadError>>; pub fn merge_factories_map<F>(base: &mut HashMap<String, F>, ext: HashMap<String, F>) { for (name, factory) in ext { match base.entry(name) { Entry::Vacant(v) => { v.insert(factory); } Entry::Occupied(o) => { panic!("Conflicting factory definitions for '{}' factory", o.key()) } } } } pub struct StoreFactories { backend_factories: HashMap<String, BackendFactory>, op_store_factories: HashMap<String, OpStoreFactory>, op_heads_store_factories: HashMap<String, OpHeadsStoreFactory>, index_store_factories: HashMap<String, IndexStoreFactory>, submodule_store_factories: HashMap<String, SubmoduleStoreFactory>, } impl Default for StoreFactories { fn default() -> Self { let mut factories = StoreFactories::empty(); // Backends factories.add_backend( SimpleBackend::name(), Box::new(|_settings, store_path| Ok(Box::new(SimpleBackend::load(store_path)))), ); #[cfg(feature = "git")] factories.add_backend( crate::git_backend::GitBackend::name(), Box::new(|settings, store_path| { Ok(Box::new(crate::git_backend::GitBackend::load( settings, store_path, )?)) }), ); #[cfg(feature = "testing")] factories.add_backend( crate::secret_backend::SecretBackend::name(), Box::new(|settings, store_path| { Ok(Box::new(crate::secret_backend::SecretBackend::load( settings, store_path, )?)) }), ); // OpStores factories.add_op_store( SimpleOpStore::name(), Box::new(|_settings, store_path, root_data| { Ok(Box::new(SimpleOpStore::load(store_path, root_data))) }), ); // OpHeadsStores factories.add_op_heads_store( SimpleOpHeadsStore::name(), Box::new(|_settings, store_path| Ok(Box::new(SimpleOpHeadsStore::load(store_path)))), ); // Index factories.add_index_store( DefaultIndexStore::name(), Box::new(|_settings, store_path| Ok(Box::new(DefaultIndexStore::load(store_path)))), ); // SubmoduleStores factories.add_submodule_store( DefaultSubmoduleStore::name(), Box::new(|_settings, store_path| Ok(Box::new(DefaultSubmoduleStore::load(store_path)))), ); factories } } #[derive(Debug, Error)] pub enum StoreLoadError { #[error("Unsupported {store} backend type '{store_type}'")] UnsupportedType { store: &'static str, store_type: String, }, #[error("Failed to read {store} backend type")] ReadError { store: &'static str, source: PathError, }, #[error(transparent)] Backend(#[from] BackendLoadError), #[error(transparent)] Signing(#[from] SignInitError), } impl StoreFactories { pub fn empty() -> Self { StoreFactories { backend_factories: HashMap::new(), op_store_factories: HashMap::new(), op_heads_store_factories: HashMap::new(), index_store_factories: HashMap::new(), submodule_store_factories: HashMap::new(), } } pub fn merge(&mut self, ext: StoreFactories) { let StoreFactories { backend_factories, op_store_factories, op_heads_store_factories, index_store_factories, submodule_store_factories, } = ext; merge_factories_map(&mut self.backend_factories, backend_factories); merge_factories_map(&mut self.op_store_factories, op_store_factories); merge_factories_map(&mut self.op_heads_store_factories, op_heads_store_factories); merge_factories_map(&mut self.index_store_factories, index_store_factories); merge_factories_map( &mut self.submodule_store_factories, submodule_store_factories,