Skip to main content

framework_startup/
config.rs

1use std::env;
2use std::path::PathBuf;
3
4use anyhow::{Context, Result};
5
6/// 开机自启的注册配置;三个操作系统共用同一份配置。
7#[derive(Debug, Clone)]
8pub struct StartupConfig {
9    /// 自启名称,同时作为 Windows 计划任务名、systemd 单元名与 launchd 标签。
10    pub name: String,
11    /// 自启描述。
12    pub description: String,
13    /// 要启动的可执行文件绝对路径;为空表示当前运行中的可执行文件。
14    pub program: PathBuf,
15    /// 启动参数。
16    pub args: Vec<String>,
17    /// 工作目录;为空表示由系统决定。
18    pub working_directory: Option<PathBuf>,
19    /// 自启未注册时 `start`、`stop`、`is_run`、`restart` 与 `uninstall` 的行为:
20    /// 为真时直接返回成功或假,为假时返回错误。默认为真,便于把这些操作当作清理手段重复调用。
21    pub ignore_not_installed: bool,
22    /// 是否以最高可用权限启动,仅 Windows 生效。
23    ///
24    /// 为真时用户级计划任务追加 `/rl highest`:登录时以当前用户身份、用提权后的令牌启动,
25    /// 不触发 UAC 弹窗;账号不在管理员组时注册或启动会失败。
26    /// 系统级任务固定以 `SYSTEM` 身份运行,该字段对它无意义。
27    #[cfg(target_os = "windows")]
28    pub highest: bool,
29}
30
31impl StartupConfig {
32    /// 创建配置;启动目标默认为当前运行中的可执行文件,未注册时忽略。
33    pub fn new(name: impl Into<String>) -> Self {
34        Self {
35            name: name.into(),
36            description: String::new(),
37            program: PathBuf::new(),
38            args: Vec::new(),
39            working_directory: None,
40            ignore_not_installed: true,
41            #[cfg(target_os = "windows")]
42            highest: false,
43        }
44    }
45
46    /// 补全启动目标:未指定可执行文件时取当前运行中的可执行文件。
47    pub(crate) fn resolve(mut self) -> Result<Self> {
48        if self.program.as_os_str().is_empty() {
49            self.program = env::current_exe().context("获取当前可执行文件路径失败")?;
50        }
51
52        Ok(self)
53    }
54
55    /// 完整的启动命令行:可执行文件绝对路径加启动参数,含空格的路径加引号;
56    /// 供 Windows 计划任务与 systemd 单元使用,launchd 需要拆成独立的参数元素。
57    #[cfg(any(target_os = "windows", target_os = "linux"))]
58    pub(crate) fn command_line(&self) -> String {
59        let mut line = quote(&self.program.to_string_lossy());
60        for arg in &self.args {
61            line.push(' ');
62            line.push_str(&quote(arg));
63        }
64
65        line
66    }
67}
68
69/// 命令行参数加引号;参数本身含双引号时原样返回,避免破坏用户输入。
70#[cfg(any(target_os = "windows", target_os = "linux"))]
71fn quote(value: &str) -> String {
72    if value.contains('"') || !value.contains(' ') {
73        return value.to_string();
74    }
75
76    format!("\"{value}\"")
77}