Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove: platform specific parent directory when installing neovim versions #226

Merged
merged 13 commits into from
Jul 31, 2024
2 changes: 1 addition & 1 deletion src/handlers/install_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ async fn handle_rollback(config: &Config) -> Result<()> {
.collect();

info!("Creating rollback: nightly-{id}");
filesystem::copy_dir("nightly", format!("nightly-{id}")).await?;
filesystem::copy_dir_async("nightly", format!("nightly-{id}")).await?;

json_struct.tag_name += &format!("-{id}");

Expand Down
54 changes: 52 additions & 2 deletions src/helpers/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pub async fn remove_dir(directory: &str) -> Result<()> {
/// copy_dir(from, to).await;
/// ```
#[async_recursion(?Send)]
pub async fn copy_dir(
pub async fn copy_dir_async(
from: impl AsRef<Path> + 'static,
to: impl AsRef<Path> + 'static,
) -> Result<()> {
Expand All @@ -105,7 +105,7 @@ pub async fn copy_dir(

if path.is_dir() {
let new_dest = destination.join(path.file_name().unwrap());
copy_dir(path, new_dest).await?;
copy_dir_async(path, new_dest).await?;
} else {
let new_dest = destination.join(path.file_name().unwrap());
fs::copy(path, new_dest).await?;
Expand All @@ -114,3 +114,53 @@ pub async fn copy_dir(

Ok(())
}

/// Copies a directory from one location to another.
///
/// This function takes two arguments: the source directory and the destination directory. Both arguments are implemented as references to `Path` and are static.
/// It first creates the destination directory, then reads the entries of the source directory.
/// For each entry in the source directory, it checks if the entry is a directory or a file.
/// If the entry is a directory, it recursively calls `copy_dir` to copy the directory to the destination.
/// If the entry is a file, it copies the file to the destination.
///
/// # Arguments
///
/// * `from` - A reference to a `Path` representing the source directory.
/// * `to` - A reference to a `Path` representing the destination directory.
///
/// # Returns
///
/// This function returns a `Result` that indicates whether the operation was successful.
/// If the operation was successful, the function returns `Ok(())`.
/// If the operation failed, the function returns `Err` with a description of the error.
///
/// # Example
///
/// ```rust
/// let from = Path::new("/path/to/source");
/// let to = Path::new("/path/to/destination");
/// copy_dir(from, to).await;
/// ```
#[cfg(target_os = "linux")]
pub fn copy_dir(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
let original_path = from.as_ref().to_owned();
let destination = to.as_ref().to_owned();

std::fs::create_dir(&destination)?;

let entries = std::fs::read_dir(original_path)?;

for entry in entries {
let path = entry?.path();

if path.is_dir() {
let new_dest = destination.join(path.file_name().unwrap());
copy_dir(path, new_dest)?;
} else {
let new_dest = destination.join(path.file_name().unwrap());
std::fs::copy(path, new_dest)?;
}
}

Ok(())
}
22 changes: 17 additions & 5 deletions src/helpers/processes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,23 @@ pub async fn handle_nvim_process(config: &Config, args: &[String]) -> Result<()>
used_version
};

let location = downloads_dir
.join(new_version)
.join(platform)
.join("bin")
.join("nvim");
let mut location = downloads_dir.join(&new_version).join("bin").join("nvim");

if cfg!(windows) {
location = location.with_extension("exe");
}

if !location.exists() {
location = downloads_dir
.join(new_version)
.join(platform)
.join("bin")
.join("nvim");

if cfg!(windows) {
location = location.with_extension("exe");
}
}

let _term = Arc::new(AtomicBool::new(false));
#[cfg(unix)]
Expand Down
82 changes: 56 additions & 26 deletions src/helpers/unarchive.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use anyhow::{anyhow, Result};
use std::fs;
use std::{
fs,
path::{Path, PathBuf},
};

use super::version::types::LocalVersion;

Expand Down Expand Up @@ -103,9 +106,10 @@ pub async fn start(file: LocalVersion) -> Result<()> {
/// ```
#[cfg(target_os = "linux")]
fn expand(downloaded_file: LocalVersion) -> Result<()> {
use crate::helpers::filesystem::copy_dir;

use super::sync;
use std::env::set_current_dir;
use std::fs::{remove_file, rename};
use std::fs::remove_dir_all;
use std::os::unix::fs::PermissionsExt;
use std::process::Command;

Expand All @@ -123,18 +127,11 @@ fn expand(downloaded_file: LocalVersion) -> Result<()> {

sync::handle_subprocess(Command::new(file).arg("--appimage-extract"))?;

rename("squashfs-root", &downloaded_file.file_name)?;

set_current_dir(downloaded_file.file_name)?;

for x in ["AppRun", "nvim.desktop", "nvim.png", ".DirIcon"] {
remove_file(x)?;
}
let src_root = "squashfs-root";
let dest = downloaded_file.file_name;

rename("usr", "nvim-linux64")?;

let parent_dir = std::env::current_dir()?.parent().unwrap().to_path_buf();
std::env::set_current_dir(parent_dir)?;
copy_dir(Path::new(src_root).join("usr"), Path::new(&dest))?;
remove_dir_all(src_root)?;

Ok(())
}
Expand Down Expand Up @@ -210,10 +207,11 @@ fn expand(downloaded_file: LocalVersion) -> Result<()> {
std::fs::create_dir(downloaded_file.file_name.clone())?;

let mut downloaded: u64 = 0;

for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let temp = &format!("{}/{}", downloaded_file.file_name, file.name());
let outpath = Path::new(temp);
let file_path = remove_base_parent(&file.mangled_name()).unwrap();
let outpath = Path::new(&downloaded_file.file_name).join(file_path);

if file.is_dir() {
fs::create_dir_all(outpath)?;
Expand Down Expand Up @@ -279,7 +277,6 @@ fn expand(downloaded_file: LocalVersion) -> Result<()> {
/// ```
#[cfg(target_os = "macos")] // I don't know if its worth making both expand functions into one function, but the API difference will cause so much if statements
fn expand(downloaded_file: LocalVersion) -> Result<()> {
use crate::helpers;
use flate2::read::GzDecoder;
use indicatif::{ProgressBar, ProgressStyle};
use std::cmp::min;
Expand Down Expand Up @@ -325,7 +322,8 @@ fn expand(downloaded_file: LocalVersion) -> Result<()> {
Ok(mut file) => {
let mut outpath = PathBuf::new();
outpath.push(&downloaded_file.file_name);
outpath.push(file.path()?.to_str().unwrap());
let no_parent_file = remove_base_parent(&file.path().unwrap()).unwrap();
outpath.push(no_parent_file);

let file_name = format!("{}", file.path()?.display()); // file.path()?.is_dir() always returns false... weird
if file_name.ends_with('/') {
Expand All @@ -350,16 +348,48 @@ fn expand(downloaded_file: LocalVersion) -> Result<()> {
"Finished expanding to {}/{}",
downloaded_file.path, downloaded_file.file_name
));
if fs::metadata(format!("{}/nvim-osx64", downloaded_file.file_name)).is_ok() {
fs::rename(
format!("{}/nvim-osx64", downloaded_file.file_name),
format!("{}/nvim-macos", downloaded_file.file_name),
)?;
}
let platform = helpers::get_platform_name(&downloaded_file.semver);
let file = &format!("{}/{platform}/bin/nvim", downloaded_file.file_name);

let file = &format!("{}/bin/nvim", downloaded_file.file_name);
let mut perms = fs::metadata(file)?.permissions();
perms.set_mode(0o551);
fs::set_permissions(file, perms)?;
Ok(())
}

/// Removes the base parent from a given path.
///
/// This function takes a path and removes its base parent component. For example, on Windows,
/// if the path is "D:\\test.txt", this function will return "test.txt", effectively removing
/// the drive letter and the root directory.
///
/// # Arguments
///
/// * `path` - A reference to a `Path` from which the base parent will be removed.
///
/// # Returns
///
/// This function returns an `Option<PathBuf>`. If the path has a base parent that can be
/// removed, it returns `Some(PathBuf)` with the modified path. If the path does not have
/// a base parent or cannot be modified, it may return `None`, although in the current
/// implementation, it always returns `Some(PathBuf)` even if the path is unchanged.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::path::{Path, PathBuf};
/// use your_crate_name::remove_base_parent; // Adjust the use path according to your crate's structure
///
/// let path = Path::new("D:\\test.txt");
/// let new_path = remove_base_parent(path).unwrap();
/// assert_eq!(new_path, PathBuf::from("test.txt"));
/// ```
#[allow(dead_code)]
fn remove_base_parent(path: &Path) -> Option<PathBuf> {
let mut components = path.components();

components.next();

Some(components.as_path().to_path_buf())
}
Loading