Show More
@@ -1,5 +1,6 b'' | |||||
1 | use crate::errors::{HgError, HgResultExt}; |
|
1 | use crate::errors::{HgError, HgResultExt}; | |
2 | use crate::repo::{Repo, Vfs}; |
|
2 | use crate::repo::{Repo, Vfs}; | |
|
3 | use crate::utils::join_display; | |||
3 | use std::collections::HashSet; |
|
4 | use std::collections::HashSet; | |
4 |
|
5 | |||
5 | fn parse(bytes: &[u8]) -> Result<HashSet<String>, HgError> { |
|
6 | fn parse(bytes: &[u8]) -> Result<HashSet<String>, HgError> { | |
@@ -42,34 +43,48 b' pub(crate) fn load_if_exists(hg_vfs: Vfs' | |||||
42 | } |
|
43 | } | |
43 |
|
44 | |||
44 | pub(crate) fn check(repo: &Repo) -> Result<(), HgError> { |
|
45 | pub(crate) fn check(repo: &Repo) -> Result<(), HgError> { | |
45 | for feature in repo.requirements() { |
|
46 | let unknown: Vec<_> = repo | |
46 | if !SUPPORTED.contains(&feature.as_str()) { |
|
47 | .requirements() | |
47 | // TODO: collect and all unknown features and include them in the |
|
48 | .iter() | |
48 | // error message? |
|
49 | .map(String::as_str) | |
49 | return Err(HgError::UnsupportedFeature(format!( |
|
50 | // .filter(|feature| !ALL_SUPPORTED.contains(feature.as_str())) | |
50 | "repository requires feature unknown to this Mercurial: {}", |
|
51 | .filter(|feature| { | |
51 | feature |
|
52 | !REQUIRED.contains(feature) && !SUPPORTED.contains(feature) | |
52 |
|
|
53 | }) | |
53 | } |
|
54 | .collect(); | |
|
55 | if !unknown.is_empty() { | |||
|
56 | return Err(HgError::unsupported(format!( | |||
|
57 | "repository requires feature unknown to this Mercurial: {}", | |||
|
58 | join_display(&unknown, ", ") | |||
|
59 | ))); | |||
|
60 | } | |||
|
61 | let missing: Vec<_> = REQUIRED | |||
|
62 | .iter() | |||
|
63 | .filter(|&&feature| !repo.requirements().contains(feature)) | |||
|
64 | .collect(); | |||
|
65 | if !missing.is_empty() { | |||
|
66 | return Err(HgError::unsupported(format!( | |||
|
67 | "repository is missing feature required by this Mercurial: {}", | |||
|
68 | join_display(&missing, ", ") | |||
|
69 | ))); | |||
54 | } |
|
70 | } | |
55 | Ok(()) |
|
71 | Ok(()) | |
56 | } |
|
72 | } | |
57 |
|
73 | |||
58 | // TODO: set this to actually-supported features |
|
74 | /// rhg does not support repositories that are *missing* any of these features | |
|
75 | const REQUIRED: &[&str] = &["revlogv1", "store", "fncache", "dotencode"]; | |||
|
76 | ||||
|
77 | /// rhg supports repository with or without these | |||
59 | const SUPPORTED: &[&str] = &[ |
|
78 | const SUPPORTED: &[&str] = &[ | |
60 | "dotencode", |
|
|||
61 | "fncache", |
|
|||
62 | "generaldelta", |
|
79 | "generaldelta", | |
63 | "revlogv1", |
|
|||
64 | SHARED_REQUIREMENT, |
|
80 | SHARED_REQUIREMENT, | |
65 | SHARESAFE_REQUIREMENT, |
|
81 | SHARESAFE_REQUIREMENT, | |
66 | SPARSEREVLOG_REQUIREMENT, |
|
82 | SPARSEREVLOG_REQUIREMENT, | |
67 | RELATIVE_SHARED_REQUIREMENT, |
|
83 | RELATIVE_SHARED_REQUIREMENT, | |
68 | "store", |
|
|||
69 | // As of this writing everything rhg does is read-only. |
|
84 | // As of this writing everything rhg does is read-only. | |
70 | // When it starts writing to the repository, itβll need to either keep the |
|
85 | // When it starts writing to the repository, itβll need to either keep the | |
71 | // persistent nodemap up to date or remove this entry: |
|
86 | // persistent nodemap up to date or remove this entry: | |
72 | "persistent-nodemap", |
|
87 | NODEMAP_REQUIREMENT, | |
73 | ]; |
|
88 | ]; | |
74 |
|
89 | |||
75 | // Copied from mercurial/requirements.py: |
|
90 | // Copied from mercurial/requirements.py: |
@@ -11,6 +11,8 b' use crate::errors::{HgError, IoErrorCont' | |||||
11 | use crate::utils::hg_path::HgPath; |
|
11 | use crate::utils::hg_path::HgPath; | |
12 | use im_rc::ordmap::DiffItem; |
|
12 | use im_rc::ordmap::DiffItem; | |
13 | use im_rc::ordmap::OrdMap; |
|
13 | use im_rc::ordmap::OrdMap; | |
|
14 | use std::cell::Cell; | |||
|
15 | use std::fmt; | |||
14 | use std::{io::Write, ops::Deref}; |
|
16 | use std::{io::Write, ops::Deref}; | |
15 |
|
17 | |||
16 | pub mod files; |
|
18 | pub mod files; | |
@@ -378,3 +380,43 b' where' | |||||
378 | right |
|
380 | right | |
379 | } |
|
381 | } | |
380 | } |
|
382 | } | |
|
383 | ||||
|
384 | /// Join items of the iterable with the given separator, similar to Pythonβs | |||
|
385 | /// `separator.join(iter)`. | |||
|
386 | /// | |||
|
387 | /// Formatting the return value consumes the iterator. | |||
|
388 | /// Formatting it again will produce an empty string. | |||
|
389 | pub fn join_display( | |||
|
390 | iter: impl IntoIterator<Item = impl fmt::Display>, | |||
|
391 | separator: impl fmt::Display, | |||
|
392 | ) -> impl fmt::Display { | |||
|
393 | JoinDisplay { | |||
|
394 | iter: Cell::new(Some(iter.into_iter())), | |||
|
395 | separator, | |||
|
396 | } | |||
|
397 | } | |||
|
398 | ||||
|
399 | struct JoinDisplay<I, S> { | |||
|
400 | iter: Cell<Option<I>>, | |||
|
401 | separator: S, | |||
|
402 | } | |||
|
403 | ||||
|
404 | impl<I, T, S> fmt::Display for JoinDisplay<I, S> | |||
|
405 | where | |||
|
406 | I: Iterator<Item = T>, | |||
|
407 | T: fmt::Display, | |||
|
408 | S: fmt::Display, | |||
|
409 | { | |||
|
410 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |||
|
411 | if let Some(mut iter) = self.iter.take() { | |||
|
412 | if let Some(first) = iter.next() { | |||
|
413 | first.fmt(f)?; | |||
|
414 | } | |||
|
415 | for value in iter { | |||
|
416 | self.separator.fmt(f)?; | |||
|
417 | value.fmt(f)?; | |||
|
418 | } | |||
|
419 | } | |||
|
420 | Ok(()) | |||
|
421 | } | |||
|
422 | } |
General Comments 0
You need to be logged in to leave comments.
Login now