Show More
@@ -16,7 +16,6 b' use std::env;' | |||
|
16 | 16 | use std::path::{Path, PathBuf}; |
|
17 | 17 | |
|
18 | 18 | use crate::errors::{HgResultExt, IoResultExt}; |
|
19 | use crate::repo::Repo; | |
|
20 | 19 | |
|
21 | 20 | /// Holds the config values for the current repository |
|
22 | 21 | /// TODO update this docstring once we support more sources |
@@ -196,12 +195,28 b' impl Config {' | |||
|
196 | 195 | Ok(Config { layers }) |
|
197 | 196 | } |
|
198 | 197 | |
|
199 | /// Loads the local config. In a future version, this will also load the | |
|
200 | /// `$HOME/.hgrc` and more to mirror the Python implementation. | |
|
201 | pub fn load_for_repo(repo: &Repo) -> Result<Self, ConfigError> { | |
|
202 | Ok(Self::load_from_explicit_sources(vec![ | |
|
203 | ConfigSource::AbsPath(repo.hg_vfs().join("hgrc")), | |
|
204 | ])?) | |
|
198 | /// Loads the per-repository config into a new `Config` which is combined | |
|
199 | /// with `self`. | |
|
200 | pub(crate) fn combine_with_repo( | |
|
201 | &self, | |
|
202 | repo_config_files: &[PathBuf], | |
|
203 | ) -> Result<Self, ConfigError> { | |
|
204 | let (cli_layers, other_layers) = self | |
|
205 | .layers | |
|
206 | .iter() | |
|
207 | .cloned() | |
|
208 | .partition(ConfigLayer::is_from_command_line); | |
|
209 | ||
|
210 | let mut repo_config = Self { | |
|
211 | layers: other_layers, | |
|
212 | }; | |
|
213 | for path in repo_config_files { | |
|
214 | // TODO: check if this file should be trusted: | |
|
215 | // `mercurial/ui.py:427` | |
|
216 | repo_config.add_trusted_file(path)?; | |
|
217 | } | |
|
218 | repo_config.layers.extend(cli_layers); | |
|
219 | Ok(repo_config) | |
|
205 | 220 | } |
|
206 | 221 | |
|
207 | 222 | /// Returns an `Err` if the first value found is not a valid boolean. |
@@ -297,8 +312,6 b' mod tests {' | |||
|
297 | 312 | let config = Config::load_from_explicit_sources(sources) |
|
298 | 313 | .expect("expected valid config"); |
|
299 | 314 | |
|
300 | dbg!(&config); | |
|
301 | ||
|
302 | 315 | let (_, value) = config.get_inner(b"section", b"item").unwrap(); |
|
303 | 316 | assert_eq!( |
|
304 | 317 | value, |
@@ -51,6 +51,15 b' impl ConfigLayer {' | |||
|
51 | 51 | } |
|
52 | 52 | } |
|
53 | 53 | |
|
54 | /// Returns whether this layer comes from `--config` CLI arguments | |
|
55 | pub(crate) fn is_from_command_line(&self) -> bool { | |
|
56 | if let ConfigOrigin::CommandLine = self.origin { | |
|
57 | true | |
|
58 | } else { | |
|
59 | false | |
|
60 | } | |
|
61 | } | |
|
62 | ||
|
54 | 63 | /// Add an entry to the config, overwriting the old one if already present. |
|
55 | 64 | pub fn add( |
|
56 | 65 | &mut self, |
@@ -97,11 +106,13 b' impl ConfigLayer {' | |||
|
97 | 106 | if let Some(m) = INCLUDE_RE.captures(&bytes) { |
|
98 | 107 | let filename_bytes = &m[1]; |
|
99 | 108 | // `Path::parent` only fails for the root directory, |
|
100 |
// which `src` canβt be since weβve managed to open it as a |
|
|
109 | // which `src` canβt be since weβve managed to open it as a | |
|
110 | // file. | |
|
101 | 111 | let dir = src |
|
102 | 112 | .parent() |
|
103 | 113 | .expect("Path::parent fail on a file weβve read"); |
|
104 |
// `Path::join` with an absolute argument correctly ignores the |
|
|
114 | // `Path::join` with an absolute argument correctly ignores the | |
|
115 | // base path | |
|
105 | 116 | let filename = dir.join(&get_path_from_bytes(&filename_bytes)); |
|
106 | 117 | let data = std::fs::read(&filename).for_file(&filename)?; |
|
107 | 118 | layers.push(current_layer); |
@@ -200,9 +211,11 b' pub struct ConfigValue {' | |||
|
200 | 211 | |
|
201 | 212 | #[derive(Clone, Debug)] |
|
202 | 213 | pub enum ConfigOrigin { |
|
203 |
/// |
|
|
214 | /// From a configuration file | |
|
204 | 215 | File(PathBuf), |
|
205 | /// The value comes from the environment like `$PAGER` or `$EDITOR` | |
|
216 | /// From a `--config` CLI argument | |
|
217 | CommandLine, | |
|
218 | /// From environment variables like `$PAGER` or `$EDITOR` | |
|
206 | 219 | Environment(Vec<u8>), |
|
207 | 220 | /* TODO cli |
|
208 | 221 | * TODO defaults (configitems.py) |
@@ -216,6 +229,7 b' impl ConfigOrigin {' | |||
|
216 | 229 | pub fn to_bytes(&self) -> Vec<u8> { |
|
217 | 230 | match self { |
|
218 | 231 | ConfigOrigin::File(p) => get_bytes_from_path(p), |
|
232 | ConfigOrigin::CommandLine => b"--config".to_vec(), | |
|
219 | 233 | ConfigOrigin::Environment(e) => format_bytes!(b"${}", e), |
|
220 | 234 | } |
|
221 | 235 | } |
@@ -1,4 +1,4 b'' | |||
|
1 | use crate::config::Config; | |
|
1 | use crate::config::{Config, ConfigError, ConfigParseError}; | |
|
2 | 2 | use crate::errors::{HgError, IoResultExt}; |
|
3 | 3 | use crate::requirements; |
|
4 | 4 | use crate::utils::files::get_path_from_bytes; |
@@ -12,17 +12,29 b' pub struct Repo {' | |||
|
12 | 12 | dot_hg: PathBuf, |
|
13 | 13 | store: PathBuf, |
|
14 | 14 | requirements: HashSet<String>, |
|
15 | config: Config, | |
|
15 | 16 | } |
|
16 | 17 | |
|
17 | 18 | #[derive(Debug, derive_more::From)] |
|
18 |
pub enum Repo |
|
|
19 | NotFoundInCurrentDirectoryOrAncestors { | |
|
19 | pub enum RepoError { | |
|
20 | NotFound { | |
|
20 | 21 | current_directory: PathBuf, |
|
21 | 22 | }, |
|
22 | 23 | #[from] |
|
24 | ConfigParseError(ConfigParseError), | |
|
25 | #[from] | |
|
23 | 26 | Other(HgError), |
|
24 | 27 | } |
|
25 | 28 | |
|
29 | impl From<ConfigError> for RepoError { | |
|
30 | fn from(error: ConfigError) -> Self { | |
|
31 | match error { | |
|
32 | ConfigError::Parse(error) => error.into(), | |
|
33 | ConfigError::Other(error) => error.into(), | |
|
34 | } | |
|
35 | } | |
|
36 | } | |
|
37 | ||
|
26 | 38 | /// Filesystem access abstraction for the contents of a given "base" diretory |
|
27 | 39 | #[derive(Clone, Copy)] |
|
28 | 40 | pub(crate) struct Vfs<'a> { |
@@ -32,7 +44,7 b" pub(crate) struct Vfs<'a> {" | |||
|
32 | 44 | impl Repo { |
|
33 | 45 | /// Search the current directory and its ancestores for a repository: |
|
34 | 46 | /// a working directory that contains a `.hg` sub-directory. |
|
35 |
pub fn find(config: &Config) -> Result<Self, Repo |
|
|
47 | pub fn find(config: &Config) -> Result<Self, RepoError> { | |
|
36 | 48 | let current_directory = crate::utils::current_dir()?; |
|
37 | 49 | // ancestors() is inclusive: it first yields `current_directory` as-is. |
|
38 | 50 | for ancestor in current_directory.ancestors() { |
@@ -40,18 +52,20 b' impl Repo {' | |||
|
40 | 52 | return Ok(Self::new_at_path(ancestor.to_owned(), config)?); |
|
41 | 53 | } |
|
42 | 54 | } |
|
43 |
Err(Repo |
|
|
44 | current_directory, | |
|
45 | }) | |
|
55 | Err(RepoError::NotFound { current_directory }) | |
|
46 | 56 | } |
|
47 | 57 | |
|
48 | 58 | /// To be called after checking that `.hg` is a sub-directory |
|
49 | 59 | fn new_at_path( |
|
50 | 60 | working_directory: PathBuf, |
|
51 | 61 | config: &Config, |
|
52 |
) -> Result<Self, |
|
|
62 | ) -> Result<Self, RepoError> { | |
|
53 | 63 | let dot_hg = working_directory.join(".hg"); |
|
54 | 64 | |
|
65 | let mut repo_config_files = Vec::new(); | |
|
66 | repo_config_files.push(dot_hg.join("hgrc")); | |
|
67 | repo_config_files.push(dot_hg.join("hgrc-not-shared")); | |
|
68 | ||
|
55 | 69 | let hg_vfs = Vfs { base: &dot_hg }; |
|
56 | 70 | let mut reqs = requirements::load_if_exists(hg_vfs)?; |
|
57 | 71 | let relative = |
@@ -89,7 +103,8 b' impl Repo {' | |||
|
89 | 103 | return Err(HgError::corrupted(format!( |
|
90 | 104 | ".hg/sharedpath points to nonexistent directory {}", |
|
91 | 105 | shared_path.display() |
|
92 |
)) |
|
|
106 | )) | |
|
107 | .into()); | |
|
93 | 108 | } |
|
94 | 109 | |
|
95 | 110 | store_path = shared_path.join("store"); |
@@ -99,12 +114,15 b' impl Repo {' | |||
|
99 | 114 | .contains(requirements::SHARESAFE_REQUIREMENT); |
|
100 | 115 | |
|
101 | 116 | if share_safe && !source_is_share_safe { |
|
102 |
return Err(match config |
|
|
117 | return Err(match config | |
|
118 | .get(b"safe-mismatch", b"source-not-safe") | |
|
119 | { | |
|
103 | 120 | Some(b"abort") | None => HgError::abort( |
|
104 | "share source does not support share-safe requirement" | |
|
121 | "share source does not support share-safe requirement", | |
|
105 | 122 | ), |
|
106 | _ => HgError::unsupported("share-safe downgrade") | |
|
107 |
} |
|
|
123 | _ => HgError::unsupported("share-safe downgrade"), | |
|
124 | } | |
|
125 | .into()); | |
|
108 | 126 | } else if source_is_share_safe && !share_safe { |
|
109 | 127 | return Err( |
|
110 | 128 | match config.get(b"safe-mismatch", b"source-safe") { |
@@ -113,16 +131,24 b' impl Repo {' | |||
|
113 | 131 | functionality while the current share does not", |
|
114 | 132 | ), |
|
115 | 133 | _ => HgError::unsupported("share-safe upgrade"), |
|
116 |
} |
|
|
134 | } | |
|
135 | .into(), | |
|
117 | 136 | ); |
|
118 | 137 | } |
|
138 | ||
|
139 | if share_safe { | |
|
140 | repo_config_files.insert(0, shared_path.join("hgrc")) | |
|
141 | } | |
|
119 | 142 | } |
|
120 | 143 | |
|
144 | let repo_config = config.combine_with_repo(&repo_config_files)?; | |
|
145 | ||
|
121 | 146 | let repo = Self { |
|
122 | 147 | requirements: reqs, |
|
123 | 148 | working_directory, |
|
124 | 149 | store: store_path, |
|
125 | 150 | dot_hg, |
|
151 | config: repo_config, | |
|
126 | 152 | }; |
|
127 | 153 | |
|
128 | 154 | requirements::check(&repo)?; |
@@ -138,6 +164,10 b' impl Repo {' | |||
|
138 | 164 | &self.requirements |
|
139 | 165 | } |
|
140 | 166 | |
|
167 | pub fn config(&self) -> &Config { | |
|
168 | &self.config | |
|
169 | } | |
|
170 | ||
|
141 | 171 | /// For accessing repository files (in `.hg`), except for the store |
|
142 | 172 | /// (`.hg/store`). |
|
143 | 173 | pub(crate) fn hg_vfs(&self) -> Vfs<'_> { |
@@ -3,7 +3,7 b' use crate::ui::UiError;' | |||
|
3 | 3 | use format_bytes::format_bytes; |
|
4 | 4 | use hg::config::{ConfigError, ConfigParseError}; |
|
5 | 5 | use hg::errors::HgError; |
|
6 |
use hg::repo::Repo |
|
|
6 | use hg::repo::RepoError; | |
|
7 | 7 | use hg::revlog::revlog::RevlogError; |
|
8 | 8 | use hg::utils::files::get_bytes_from_path; |
|
9 | 9 | use std::convert::From; |
@@ -51,18 +51,17 b' impl From<UiError> for CommandError {' | |||
|
51 | 51 | } |
|
52 | 52 | } |
|
53 | 53 | |
|
54 |
impl From<Repo |
|
|
55 |
fn from(error: Repo |
|
|
54 | impl From<RepoError> for CommandError { | |
|
55 | fn from(error: RepoError) -> Self { | |
|
56 | 56 | match error { |
|
57 |
Repo |
|
|
58 | current_directory, | |
|
59 | } => CommandError::Abort { | |
|
57 | RepoError::NotFound { current_directory } => CommandError::Abort { | |
|
60 | 58 | message: format_bytes!( |
|
61 | 59 | b"no repository found in '{}' (.hg not found)!", |
|
62 | 60 | get_bytes_from_path(current_directory) |
|
63 | 61 | ), |
|
64 | 62 | }, |
|
65 |
Repo |
|
|
63 | RepoError::ConfigParseError(error) => error.into(), | |
|
64 | RepoError::Other(error) => error.into(), | |
|
66 | 65 | } |
|
67 | 66 | } |
|
68 | 67 | } |
@@ -70,33 +69,35 b' impl From<RepoFindError> for CommandErro' | |||
|
70 | 69 | impl From<ConfigError> for CommandError { |
|
71 | 70 | fn from(error: ConfigError) -> Self { |
|
72 | 71 | match error { |
|
73 |
ConfigError::Parse( |
|
|
74 | origin, | |
|
75 | line, | |
|
76 | bytes, | |
|
77 | }) => { | |
|
78 | let line_message = if let Some(line_number) = line { | |
|
79 | format_bytes!( | |
|
80 | b" at line {}", | |
|
81 | line_number.to_string().into_bytes() | |
|
82 | ) | |
|
83 | } else { | |
|
84 | Vec::new() | |
|
85 | }; | |
|
86 | CommandError::Abort { | |
|
87 | message: format_bytes!( | |
|
88 | b"config parse error in {}{}: '{}'", | |
|
89 | origin.to_bytes(), | |
|
90 | line_message, | |
|
91 | bytes | |
|
92 | ), | |
|
93 | } | |
|
94 | } | |
|
72 | ConfigError::Parse(error) => error.into(), | |
|
95 | 73 | ConfigError::Other(error) => error.into(), |
|
96 | 74 | } |
|
97 | 75 | } |
|
98 | 76 | } |
|
99 | 77 | |
|
78 | impl From<ConfigParseError> for CommandError { | |
|
79 | fn from(error: ConfigParseError) -> Self { | |
|
80 | let ConfigParseError { | |
|
81 | origin, | |
|
82 | line, | |
|
83 | bytes, | |
|
84 | } = error; | |
|
85 | let line_message = if let Some(line_number) = line { | |
|
86 | format_bytes!(b" at line {}", line_number.to_string().into_bytes()) | |
|
87 | } else { | |
|
88 | Vec::new() | |
|
89 | }; | |
|
90 | CommandError::Abort { | |
|
91 | message: format_bytes!( | |
|
92 | b"config parse error in {}{}: '{}'", | |
|
93 | origin.to_bytes(), | |
|
94 | line_message, | |
|
95 | bytes | |
|
96 | ), | |
|
97 | } | |
|
98 | } | |
|
99 | } | |
|
100 | ||
|
100 | 101 | impl From<(RevlogError, &str)> for CommandError { |
|
101 | 102 | fn from((err, rev): (RevlogError, &str)) -> CommandError { |
|
102 | 103 | match err { |
General Comments 0
You need to be logged in to leave comments.
Login now