cargo-reclaim 0.2.1

Safe Cargo cleanup for target directories, stale artifacts, and Cargo home caches
Documentation
use std::path::PathBuf;

use super::model::{DEFAULT_SCHEDULER_INSTANCE_NAME, SchedulerPlatform, SchedulerRequest};

pub(crate) struct SchedulerPaths {
    pub(crate) instance_name: String,
    pub(crate) systemd_service_name: String,
    pub(crate) systemd_timer_name: String,
    pub(crate) launchd_label: String,
    pub(crate) task_name: String,
    pub(crate) state_dir: PathBuf,
    pub(crate) log_dir: PathBuf,
    pub(crate) runner_path: PathBuf,
    pub(crate) log_path: PathBuf,
}

impl SchedulerPaths {
    pub(crate) fn new(request: &SchedulerRequest) -> Self {
        let is_default_instance = request.instance_name == DEFAULT_SCHEDULER_INSTANCE_NAME;
        let state_dir = request.state_dir.clone().unwrap_or_else(|| {
            if is_default_instance {
                default_state_dir(request.platform)
            } else {
                default_instance_state_dir(request.platform, &request.instance_name)
            }
        });
        let log_dir = request.log_dir.clone().unwrap_or_else(|| {
            if is_default_instance {
                default_log_dir(request.platform)
            } else {
                default_instance_log_dir(request.platform, &request.instance_name)
            }
        });
        let runner_name = match request.platform {
            SchedulerPlatform::SystemdUser | SchedulerPlatform::Launchd => {
                if is_default_instance {
                    "scheduler-runner.sh".to_string()
                } else {
                    format!("scheduler-runner-{}.sh", request.instance_name)
                }
            }
            SchedulerPlatform::TaskScheduler => {
                if is_default_instance {
                    "scheduler-runner.ps1".to_string()
                } else {
                    format!("scheduler-runner-{}.ps1", request.instance_name)
                }
            }
        };
        let runner_path = match request.platform {
            SchedulerPlatform::SystemdUser
            | SchedulerPlatform::Launchd
            | SchedulerPlatform::TaskScheduler => state_dir.join(runner_name),
        };
        Self {
            instance_name: request.instance_name.clone(),
            systemd_service_name: instance_file_name(
                is_default_instance,
                "cargo-reclaim.service",
                &format!("cargo-reclaim-{}.service", request.instance_name),
            ),
            systemd_timer_name: instance_file_name(
                is_default_instance,
                "cargo-reclaim.timer",
                &format!("cargo-reclaim-{}.timer", request.instance_name),
            ),
            launchd_label: instance_file_name(
                is_default_instance,
                "com.cargo-reclaim",
                &format!("com.cargo-reclaim.{}", request.instance_name),
            ),
            task_name: instance_file_name(
                is_default_instance,
                r"\cargo-reclaim",
                &format!(r"\cargo-reclaim\{}", request.instance_name),
            ),
            log_path: log_dir.join("scheduler.log"),
            state_dir,
            log_dir,
            runner_path,
        }
    }

    pub(crate) fn task_scheduler_xml_path(&self) -> PathBuf {
        if self.instance_name == DEFAULT_SCHEDULER_INSTANCE_NAME {
            self.state_dir.join("cargo-reclaim.xml")
        } else {
            self.state_dir
                .join(format!("cargo-reclaim-{}.xml", self.instance_name))
        }
    }

    pub(crate) fn systemd_service_path(&self) -> PathBuf {
        home_dir()
            .map(|home| {
                home.join(".config/systemd/user")
                    .join(&self.systemd_service_name)
            })
            .unwrap_or_else(|| {
                PathBuf::from(".config/systemd/user").join(&self.systemd_service_name)
            })
    }

    pub(crate) fn systemd_timer_path(&self) -> PathBuf {
        home_dir()
            .map(|home| {
                home.join(".config/systemd/user")
                    .join(&self.systemd_timer_name)
            })
            .unwrap_or_else(|| PathBuf::from(".config/systemd/user").join(&self.systemd_timer_name))
    }

    pub(crate) fn launchd_plist_path(&self) -> PathBuf {
        home_dir()
            .map(|home| {
                home.join("Library/LaunchAgents")
                    .join(format!("{}.plist", self.launchd_label))
            })
            .unwrap_or_else(|| {
                PathBuf::from("Library/LaunchAgents").join(format!("{}.plist", self.launchd_label))
            })
    }
}

fn instance_file_name(is_default_instance: bool, default_name: &str, named: &str) -> String {
    if is_default_instance {
        default_name.to_string()
    } else {
        named.to_string()
    }
}

pub fn default_state_dir(platform: SchedulerPlatform) -> PathBuf {
    match platform {
        SchedulerPlatform::SystemdUser => home_dir()
            .map(|home| home.join(".local/state/cargo-reclaim"))
            .unwrap_or_else(|| PathBuf::from(".cargo-reclaim/state")),
        SchedulerPlatform::Launchd => home_dir()
            .map(|home| home.join("Library/Application Support/cargo-reclaim"))
            .unwrap_or_else(|| PathBuf::from("cargo-reclaim/Application Support")),
        SchedulerPlatform::TaskScheduler => local_app_data_dir()
            .map(|local_app_data| local_app_data.join("cargo-reclaim"))
            .unwrap_or_else(|| PathBuf::from(r"cargo-reclaim")),
    }
}

pub fn default_log_dir(platform: SchedulerPlatform) -> PathBuf {
    match platform {
        SchedulerPlatform::SystemdUser => default_state_dir(platform).join("logs"),
        SchedulerPlatform::Launchd => home_dir()
            .map(|home| home.join("Library/Logs/cargo-reclaim"))
            .unwrap_or_else(|| PathBuf::from("cargo-reclaim/logs")),
        SchedulerPlatform::TaskScheduler => default_state_dir(platform).join("logs"),
    }
}

pub fn default_instance_state_dir(platform: SchedulerPlatform, instance_name: &str) -> PathBuf {
    default_state_dir(platform).join(instance_name)
}

pub fn default_instance_log_dir(platform: SchedulerPlatform, instance_name: &str) -> PathBuf {
    default_log_dir(platform).join(instance_name)
}

pub(crate) fn home_dir() -> Option<PathBuf> {
    std::env::var_os("HOME")
        .or_else(|| std::env::var_os("USERPROFILE"))
        .map(PathBuf::from)
}

pub(crate) fn local_app_data_dir() -> Option<PathBuf> {
    std::env::var_os("LOCALAPPDATA").map(PathBuf::from)
}