use std::{
path::{Path, PathBuf},
sync::Arc,
};
use crate::RobloxStudioResult;
use crate::task::RobloxStudioTask;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "windows")]
mod windows;
#[derive(Debug, Clone)]
pub struct RobloxStudioPaths {
inner: Arc<RobloxStudioPathsInner>,
}
impl RobloxStudioPaths {
pub fn new() -> RobloxStudioResult<Self> {
RobloxStudioPathsInner::new().map(Self::from)
}
#[must_use]
pub fn exe(&self) -> &Path {
self.inner.exe.as_path()
}
#[must_use]
pub fn launcher(&self) -> Option<&Path> {
self.inner.launcher.as_deref()
}
#[must_use]
pub(crate) fn exe_for_task(&self, task: Option<RobloxStudioTask>) -> &Path {
if cfg!(target_os = "windows")
&& task.is_some_and(RobloxStudioTask::needs_launcher)
&& self.inner.launcher.is_some()
{
self.inner
.launcher
.as_deref()
.expect("launcher path should exist")
} else {
self.exe()
}
}
#[must_use]
pub fn content(&self) -> &Path {
self.inner.content.as_path()
}
#[must_use]
pub fn user_plugins(&self) -> &Path {
self.inner.plugins_user.as_path()
}
#[must_use]
pub fn built_in_plugins(&self) -> &Path {
self.inner.plugins_builtin.as_path()
}
#[must_use]
pub fn global_settings(&self) -> Option<PathBuf> {
let dir = self.inner.settings.as_ref()?;
let mut best: Option<(u32, PathBuf)> = None;
for entry in std::fs::read_dir(dir).ok()?.flatten() {
let file_name = entry.file_name();
let Some(name) = file_name.to_str() else {
continue;
};
let Some(version) = name
.strip_prefix("GlobalSettings_")
.and_then(|rest| rest.strip_suffix(".xml"))
.and_then(|version| version.parse::<u32>().ok())
else {
continue;
};
if best
.as_ref()
.is_none_or(|(best_version, _)| version > *best_version)
{
best = Some((version, entry.path()));
}
}
best.map(|(_, path)| path)
}
}
#[derive(Debug, Clone)]
struct RobloxStudioPathsInner {
exe: PathBuf,
launcher: Option<PathBuf>,
content: PathBuf,
plugins_user: PathBuf,
plugins_builtin: PathBuf,
settings: Option<PathBuf>,
}
impl From<RobloxStudioPathsInner> for RobloxStudioPaths {
fn from(inner: RobloxStudioPathsInner) -> Self {
Self {
inner: Arc::new(inner),
}
}
}