1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
pub mod traits;
use std::{
    fs, io,
    os::unix::fs::PermissionsExt,
    path::{Path, PathBuf},
};

#[derive(Debug)]
pub enum CopyFileError {
    GetParent,
    CreateDirStructure(std::io::Error),
    BootstrapBinary(std::io::Error),
}

impl std::fmt::Display for CopyFileError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            CopyFileError::GetParent => write!(f, "Error getting parent directory"),
            CopyFileError::CreateDirStructure(e) => {
                write!(f, "Error creating directory structure: {}", e)
            }
            CopyFileError::BootstrapBinary(e) => write!(f, "Error bootstrapping binary: {}", e),
        }
    }
}

impl std::error::Error for CopyFileError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            CopyFileError::CreateDirStructure(e) => Some(e),
            CopyFileError::BootstrapBinary(e) => Some(e),
            _ => None,
        }
    }
}

fn create_dir_structure<P: AsRef<Path>>(path: P) -> Result<(), CopyFileError> {
    println!("Creating directory: {:?}", path.as_ref());

    fs::create_dir_all(path).map_err(CopyFileError::CreateDirStructure)
}

pub fn copy_file<S, D>(src: S, dst: D) -> Result<u64, CopyFileError>
where
    S: AsRef<Path>,
    D: AsRef<Path>,
{
    dst.as_ref()
        .parent()
        .ok_or(CopyFileError::GetParent)
        .and_then(create_dir_structure)?;

    println!("Copying {:?} to {:?}", src.as_ref(), dst.as_ref());
    fs::copy(src, dst).map_err(CopyFileError::BootstrapBinary)
}

pub fn get_executable_files(path: &String) -> Result<Vec<PathBuf>, io::Error> {
    let entries = fs::read_dir(path)?
        .filter_map(|res| res.ok())
        .map(|entry| entry.path())
        .filter(|path| path.is_file())
        .filter(|file| {
            fs::metadata(file)
                .map(|metadata| metadata.permissions().mode())
                .map(|mode| mode & 0o111 != 0)
                .unwrap_or(false)
        })
        .collect::<Vec<_>>();

    Ok(entries)
}

pub fn get_filenames(paths: &[PathBuf]) -> Vec<String> {
    paths
        .iter()
        .filter_map(|path| {
            path.file_name()
                .map(|name| name.to_string_lossy().to_string())
        })
        .collect()
}

pub fn build_paths(filenames: &[String], build_path: &str) -> Vec<String> {
    filenames
        .iter()
        .map(|f| format!("{build_path}/{f}/bootstrap"))
        .collect()
}