treeboot_core/
discovery.rs1use std::path::{Path, PathBuf};
2
3use crate::context::resolve_worktree_path;
4use crate::{Error, Result, Worktree};
5
6const INIT_SCRIPT_PATHS: &[&str] = &[".treeboot.sh", ".treebootrc", ".config/treeboot/init"];
7const CONFIG_PATHS: &[&str] = &[
8 ".treeboot.toml",
9 "treeboot.toml",
10 ".config/treeboot/config.toml",
11];
12
13#[derive(Debug, Clone, Default, PartialEq, Eq)]
15pub struct InitScriptDiscovery {
16 pub executable: Option<PathBuf>,
18 pub ignored: Vec<PathBuf>,
21}
22
23impl InitScriptDiscovery {
24 #[must_use]
26 pub fn discover(context: &Worktree) -> Self {
27 discover_scripts(&context.worktree_path)
28 }
29}
30
31pub(crate) fn discover_scripts(worktree_path: &Path) -> InitScriptDiscovery {
32 let mut ignored = Vec::new();
33
34 for relative in INIT_SCRIPT_PATHS {
35 let path = worktree_path.join(relative);
36
37 if !path.is_file() {
38 continue;
39 }
40
41 if is_executable(&path) {
42 return InitScriptDiscovery {
43 executable: Some(path),
44 ignored,
45 };
46 }
47
48 ignored.push(path);
49 }
50
51 InitScriptDiscovery {
52 executable: None,
53 ignored,
54 }
55}
56
57pub(crate) fn discover_config(
58 worktree_path: &Path,
59 requested_config: Option<&Path>,
60) -> Result<Option<PathBuf>> {
61 if let Some(path) = requested_config {
62 let path = resolve_worktree_path(worktree_path, path);
63
64 if path.is_file() {
65 return Ok(Some(path));
66 }
67
68 return Err(Error::ConfigNotFound(path));
69 }
70
71 Ok(CONFIG_PATHS
72 .iter()
73 .map(|relative| worktree_path.join(relative))
74 .find(|path| path.is_file()))
75}
76
77#[cfg(unix)]
78fn is_executable(path: &Path) -> bool {
79 use std::os::unix::fs::PermissionsExt;
80
81 path.metadata()
82 .map(|metadata| metadata.permissions().mode() & 0o111 != 0)
83 .unwrap_or(false)
84}
85
86#[cfg(not(unix))]
87fn is_executable(path: &Path) -> bool {
88 path.is_file()
89}