Mercurial > public > mercurial-scm > hg-stable
comparison rust/hg-core/src/repo.rs @ 47986:fc208d6faed3
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct
More components of `Repo` will be added following the same pattern.
Differential Revision: https://phab.mercurial-scm.org/D11405
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Mon, 13 Sep 2021 13:16:10 +0200 |
parents | 81aedf1fc897 |
children | 21d25e9ee58e |
comparison
equal
deleted
inserted
replaced
47985:d44740725b95 | 47986:fc208d6faed3 |
---|---|
21 store: PathBuf, | 21 store: PathBuf, |
22 requirements: HashSet<String>, | 22 requirements: HashSet<String>, |
23 config: Config, | 23 config: Config, |
24 // None means not known/initialized yet | 24 // None means not known/initialized yet |
25 dirstate_parents: Cell<Option<DirstateParents>>, | 25 dirstate_parents: Cell<Option<DirstateParents>>, |
26 dirstate_map: RefCell<Option<OwningDirstateMap>>, | 26 dirstate_map: LazyCell<OwningDirstateMap, DirstateError>, |
27 } | 27 } |
28 | 28 |
29 #[derive(Debug, derive_more::From)] | 29 #[derive(Debug, derive_more::From)] |
30 pub enum RepoError { | 30 pub enum RepoError { |
31 NotFound { | 31 NotFound { |
194 working_directory, | 194 working_directory, |
195 store: store_path, | 195 store: store_path, |
196 dot_hg, | 196 dot_hg, |
197 config: repo_config, | 197 config: repo_config, |
198 dirstate_parents: Cell::new(None), | 198 dirstate_parents: Cell::new(None), |
199 dirstate_map: RefCell::new(None), | 199 dirstate_map: LazyCell::new(Self::new_dirstate_map), |
200 }; | 200 }; |
201 | 201 |
202 requirements::check(&repo)?; | 202 requirements::check(&repo)?; |
203 | 203 |
204 Ok(repo) | 204 Ok(repo) |
300 } | 300 } |
301 | 301 |
302 pub fn dirstate_map( | 302 pub fn dirstate_map( |
303 &self, | 303 &self, |
304 ) -> Result<Ref<OwningDirstateMap>, DirstateError> { | 304 ) -> Result<Ref<OwningDirstateMap>, DirstateError> { |
305 let mut borrowed = self.dirstate_map.borrow(); | 305 self.dirstate_map.get_or_init(self) |
306 } | |
307 | |
308 pub fn dirstate_map_mut( | |
309 &self, | |
310 ) -> Result<RefMut<OwningDirstateMap>, DirstateError> { | |
311 self.dirstate_map.get_mut_or_init(self) | |
312 } | |
313 } | |
314 | |
315 /// Lazily-initialized component of `Repo` with interior mutability | |
316 /// | |
317 /// This differs from `OnceCell` in that the value can still be "deinitialized" | |
318 /// later by setting its inner `Option` to `None`. | |
319 struct LazyCell<T, E> { | |
320 value: RefCell<Option<T>>, | |
321 // `Fn`s that don’t capture environment are zero-size, so this box does | |
322 // not allocate: | |
323 init: Box<dyn Fn(&Repo) -> Result<T, E>>, | |
324 } | |
325 | |
326 impl<T, E> LazyCell<T, E> { | |
327 fn new(init: impl Fn(&Repo) -> Result<T, E> + 'static) -> Self { | |
328 Self { | |
329 value: RefCell::new(None), | |
330 init: Box::new(init), | |
331 } | |
332 } | |
333 | |
334 fn get_or_init(&self, repo: &Repo) -> Result<Ref<T>, E> { | |
335 let mut borrowed = self.value.borrow(); | |
306 if borrowed.is_none() { | 336 if borrowed.is_none() { |
307 drop(borrowed); | 337 drop(borrowed); |
308 // Only use `borrow_mut` if it is really needed to avoid panic in | 338 // Only use `borrow_mut` if it is really needed to avoid panic in |
309 // case there is another outstanding borrow but mutation is not | 339 // case there is another outstanding borrow but mutation is not |
310 // needed. | 340 // needed. |
311 *self.dirstate_map.borrow_mut() = Some(self.new_dirstate_map()?); | 341 *self.value.borrow_mut() = Some((self.init)(repo)?); |
312 borrowed = self.dirstate_map.borrow() | 342 borrowed = self.value.borrow() |
313 } | 343 } |
314 Ok(Ref::map(borrowed, |option| option.as_ref().unwrap())) | 344 Ok(Ref::map(borrowed, |option| option.as_ref().unwrap())) |
315 } | 345 } |
316 | 346 |
317 pub fn dirstate_map_mut( | 347 pub fn get_mut_or_init(&self, repo: &Repo) -> Result<RefMut<T>, E> { |
318 &self, | 348 let mut borrowed = self.value.borrow_mut(); |
319 ) -> Result<RefMut<OwningDirstateMap>, DirstateError> { | |
320 let mut borrowed = self.dirstate_map.borrow_mut(); | |
321 if borrowed.is_none() { | 349 if borrowed.is_none() { |
322 *borrowed = Some(self.new_dirstate_map()?); | 350 *borrowed = Some((self.init)(repo)?); |
323 } | 351 } |
324 Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap())) | 352 Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap())) |
325 } | 353 } |
326 } | 354 } |
327 | 355 |