Skip to main content

upstream_rs/services/integration/
permission_handler.rs

1use anyhow::{Context, Result};
2
3#[cfg(unix)]
4use std::os::unix::fs::PermissionsExt;
5
6use std::path::Path;
7use std::{fs, path::PathBuf};
8
9/// Sets executable permissions on a file for user, group, and others.
10#[cfg(unix)]
11pub fn make_executable(exec_path: &Path) -> Result<()> {
12    if !exec_path.exists() {
13        anyhow::bail!("Invalid executable path: {}", exec_path.to_string_lossy());
14    }
15
16    match fs::metadata(exec_path) {
17        Ok(metadata) => {
18            let mut permissions = metadata.permissions();
19            let mode = permissions.mode();
20
21            permissions.set_mode(mode | 0o111);
22
23            fs::set_permissions(exec_path, permissions)
24                .context("Failed to set executable permissions")?;
25        }
26        Err(e) => {
27            return Err(e).context("Failed to read metadata");
28        }
29    }
30
31    Ok(())
32}
33
34#[cfg(windows)]
35pub fn make_executable(exec_path: &Path) -> Result<()> {
36    Ok(())
37}
38
39/// Finds any potential executables in a directory.
40pub fn find_executable(directory_path: &Path, name: &str) -> Option<PathBuf> {
41    #[cfg(windows)]
42    let name = &format!("{}.exe", name);
43
44    // 1. bin/<name>
45    let bin_path = directory_path.join("bin").join(name);
46    if bin_path.is_file() {
47        return Some(bin_path);
48    }
49
50    // 2. directoryPath/<name>
51    let direct_path = directory_path.join(name);
52    if direct_path.is_file() {
53        return Some(direct_path);
54    }
55
56    // 3. directory name is the executable name
57    //    e.g. cool-app-x86_64/cool-app-x86_64
58    if let Some(dir_name) = directory_path.file_name() {
59        let derived_path = directory_path.join(dir_name);
60        if derived_path.is_file() {
61            return Some(derived_path);
62        }
63    }
64
65    // 4. As a fallback, search for any file starting with name
66    //    e.g. "cool-app" -> "cool-app-x86_64", "cool-app-v1"
67    if let Ok(entries) = fs::read_dir(directory_path) {
68        for entry in entries.flatten() {
69            if let Ok(file_type) = entry.file_type()
70                && file_type.is_file()
71                && let Some(file_name) = entry.file_name().to_str()
72                && file_name.to_lowercase().starts_with(&name.to_lowercase())
73            {
74                return Some(entry.path());
75            }
76        }
77    }
78
79    None
80}