Skip to main content

carch_core/
lib.rs

1use crate::error::{CarchError, Result};
2use include_dir::{Dir, include_dir};
3use std::fs;
4use std::os::unix::fs::PermissionsExt;
5use std::path::Path;
6
7pub mod error;
8pub mod ui;
9pub mod version;
10
11static EMBEDDED_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/src/modules");
12
13pub const VALID_THEMES: &[&str] = &["catppuccin-mocha", "dracula", "gruvbox", "nord", "rose-pine"];
14
15#[must_use]
16pub fn is_valid_theme(theme: &str) -> bool {
17    VALID_THEMES.contains(&theme)
18}
19
20const EXECUTABLE_MODE: u32 = 0o755;
21
22/// True when the current process is running as root.
23#[must_use]
24#[cfg(unix)]
25pub fn is_running_as_root() -> bool {
26    // SAFETY: geteuid has no preconditions.
27    unsafe { libc::geteuid() == 0 }
28}
29
30#[must_use]
31#[cfg(not(unix))]
32pub fn is_running_as_root() -> bool {
33    false
34}
35
36pub fn extract_scripts(temp_path: &Path) -> Result<()> {
37    let modules_dir = temp_path.join("modules");
38    fs::create_dir_all(&modules_dir)
39        .map_err(|e| CarchError::CreateDir(modules_dir.display().to_string(), e))?;
40
41    extract_dir_recursive(&EMBEDDED_DIR, &modules_dir)?;
42
43    let preview_link = temp_path.join("preview_scripts");
44    let _ = fs::remove_file(&preview_link);
45
46    std::os::unix::fs::symlink(&modules_dir, &preview_link).map_err(|e| {
47        CarchError::Symlink(format!(
48            "{} -> {}: {}",
49            modules_dir.display(),
50            preview_link.display(),
51            e
52        ))
53    })?;
54
55    Ok(())
56}
57
58fn extract_dir_recursive(dir: &Dir, target_path: &Path) -> Result<()> {
59    fs::create_dir_all(target_path)
60        .map_err(|e| CarchError::CreateDir(target_path.display().to_string(), e))?;
61
62    for entry in dir.entries() {
63        match entry {
64            include_dir::DirEntry::File(file) => {
65                let file_path = target_path.join(file.path().file_name().unwrap_or_default());
66                fs::write(&file_path, file.contents())
67                    .map_err(|e| CarchError::WriteFile(file_path.display().to_string(), e))?;
68
69                if file_path.extension().is_some_and(|ext| ext == "sh") {
70                    set_executable(&file_path)?;
71                }
72            }
73            include_dir::DirEntry::Dir(subdir) => {
74                let subdir_path = target_path.join(subdir.path().file_name().unwrap_or_default());
75                extract_dir_recursive(subdir, &subdir_path)?;
76            }
77        }
78    }
79
80    Ok(())
81}
82
83fn set_executable(path: &Path) -> Result<()> {
84    let mut perms = fs::metadata(path)
85        .map_err(|e| CarchError::Metadata(path.display().to_string(), e))?
86        .permissions();
87    perms.set_mode(EXECUTABLE_MODE);
88    fs::set_permissions(path, perms)
89        .map_err(|e| CarchError::SetPermissions(path.display().to_string(), e))?;
90    Ok(())
91}