radicle: canonicalize home path

If a path was passed to `Home` in non-canonical form it would not use
the right path for any initialisation or use.

The aim is to use the `canonicalize` function for paths. However, this
may return an error if none of the paths exist. To ensure that the
path exists the `new` constructor for `Home` creates the directory and
canonicalizes the path.

Since `new` now initialises the home directory, it would be useful if
it also initialised all the necessary subdirectories -- which removes
the need for the `init` method.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
Fintan Halpenny 2023-02-16 10:11:32 +00:00 committed by Alexis Sellier
parent a7d2fa95cd
commit 8e26e4c046
No known key found for this signature in database
3 changed files with 60 additions and 14 deletions

View File

@ -71,9 +71,7 @@ impl Environment {
/// Create a new profile in this environment.
/// This should be used when a running node is not required.
pub fn profile(&mut self, name: &str) -> Profile {
let home = Home::new(self.tmp().join("home").join(name))
.init()
.unwrap();
let home = Home::new(self.tmp().join("home").join(name)).unwrap();
let storage = Storage::open(home.storage()).unwrap();
let keystore = Keystore::new(&home.keys());
let keypair = KeyPair::from_seed(Seed::from([!(self.users as u8); 32]));
@ -232,7 +230,7 @@ impl Node<MockSigner> {
.take(8)
.collect::<String>(),
);
let home = Home::new(home).init().unwrap();
let home = Home::new(home).unwrap();
let signer = MockSigner::default();
let storage = Storage::open(home.storage()).unwrap();

View File

@ -67,7 +67,6 @@ pub struct Profile {
impl Profile {
pub fn init(home: Home, passphrase: impl Into<Passphrase>) -> Result<Self, Error> {
let home = home.init()?;
let storage = Storage::open(home.storage())?;
let keystore = Keystore::new(&home.keys());
let public_key = keystore.init("radicle", passphrase)?;
@ -142,9 +141,9 @@ impl Profile {
/// Get the path to the radicle home folder.
pub fn home() -> Result<Home, io::Error> {
if let Some(home) = env::var_os(env::RAD_HOME) {
Ok(Home::new(PathBuf::from(home)))
Ok(Home::new(PathBuf::from(home))?)
} else if let Some(home) = env::var_os("HOME") {
Ok(Home::new(PathBuf::from(home).join(".radicle")))
Ok(Home::new(PathBuf::from(home).join(".radicle"))?)
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
@ -166,14 +165,33 @@ impl From<PathBuf> for Home {
}
impl Home {
pub fn init(self) -> Result<Self, io::Error> {
fs::create_dir_all(self.node()).ok();
/// Creates the Radicle Home directories.
///
/// The `home` path is used as the base directory for all
/// necessary subdirectories.
///
/// If `home` does not already exist then it and any
/// subdirectories are created using [`fs::create_dir_all`].
///
/// The `home` path is also canonicalized using [`fs::canonicalize`].
///
/// All necessary subdirectories are also created.
pub fn new(home: impl Into<PathBuf>) -> Result<Self, io::Error> {
let path = home.into();
if !path.exists() {
fs::create_dir_all(path.clone())?;
}
let home = Self {
path: path.canonicalize()?,
};
Ok(self)
}
for dir in &[home.storage(), home.keys(), home.node()] {
if !dir.exists() {
fs::create_dir_all(dir)?;
}
}
pub fn new(home: impl Into<PathBuf>) -> Self {
Self { path: home.into() }
Ok(home)
}
pub fn path(&self) -> &Path {
@ -198,3 +216,33 @@ impl Home {
.unwrap_or_else(|| self.node().join(node::DEFAULT_SOCKET_NAME))
}
}
#[cfg(test)]
mod test {
use std::fs;
use super::Home;
// Checks that if we have:
// '/run/user/1000/.tmpqfK6ih/../.tmpqfK6ih/Radicle/Home'
//
// that it gets normalized to:
// '/run/user/1000/.tmpqfK6ih/Radicle/Home'
#[test]
fn canonicalize_home() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("Home").join("Radicle");
fs::create_dir_all(path.clone()).unwrap();
let last = tmp.path().components().last().unwrap();
let home = Home::new(
tmp.path()
.join("..")
.join(last)
.join("Home")
.join("Radicle"),
)
.unwrap();
assert_eq!(home.path, path);
}
}

View File

@ -19,7 +19,7 @@ pub mod setup {
let mut rng = fastrand::Rng::new();
let signer = MockSigner::new(&mut rng);
let home = tmp.path().join("home");
let paths = Home::new(home.as_path());
let paths = Home::new(home.as_path()).unwrap();
let storage = Storage::open(paths.storage()).unwrap();
let (id, _, _, _) = fixtures::project(tmp.path().join("copy"), &storage, &signer).unwrap();
let project = storage.repository(id).unwrap();