///|
/// Failures while resolving framework-owned application paths.
pub(all) suberror AppPathError {
InvalidIdentifier(identifier~ : String)
MissingHomeDirectory(platform~ : String)
PlatformProbe(status~ : Int, detail~ : String)
} derive(Debug, Eq)
///|
pub extend AppPathError with Eq::{not_equal, equal}
///|
pub extend AppPathError with Debug::{to_repr}
///|
pub fn AppPathError::message(self : AppPathError) -> String {
match self {
InvalidIdentifier(identifier~) =>
"invalid application identifier: " + identifier
MissingHomeDirectory(platform~) =>
"cannot resolve the application data directory on " + platform
PlatformProbe(status~, detail~) =>
"cannot determine the Proton platform (" +
status.to_string() +
"): " +
detail
}
}
///|
/// Resolves the absolute application resource directory.
///
/// `proton_cli dev` supplies the project resource directory explicitly. A
/// packaged application resolves the package resource directory beside its
/// executable. Directly launched code uses the current working directory.
pub fn resource_dir() -> String {
runtime_resource_dir()
}
///|
fn runtime_resource_dir() -> String {
match @env.get_env_var("PROTON_RESOURCE_DIR") {
Some(value) if value.trim().to_owned() != "" => {
let path : @mbpath.Path = value.trim().to_owned()
return path.resolve().to_string()
}
_ => ()
}
match packaged_resource_dir() {
Some(path) => path
None => {
let current : @mbpath.Path = "."
current.resolve().to_string()
}
}
}
///|
fn packaged_resource_dir() -> String? {
let args = @env.args()
guard args.length() > 0 else { return None }
let executable : @mbpath.Path = args[0]
let executable_dir = executable.resolve().dirname()
let candidate = if @mbpath.sep == '\\' {
executable_dir.join("Resources").normalize()
} else {
executable_dir.join("../Resources").normalize()
}
let packaged = @mbfs.is_file(
candidate.join("proton-package.json").to_string(),
) catch {
_ => false
}
guard packaged else { return None }
Some(candidate.to_string())
}
///|
/// Resolves the stable per-application directory for native persistent data.
///
/// The identifier should match the packaged bundle/application identifier.
/// This function resolves the path but does not create the directory.
pub fn app_data_dir(identifier : String) -> String raise AppPathError {
let info = @native.runtime_info() catch {
error => raise PlatformProbe(status=error.status(), detail=error.message())
}
let platform = info.platform
app_data_dir_for_platform(
platform,
identifier,
@env.get_env_var("HOME"),
@env.get_env_var("LOCALAPPDATA"),
@env.get_env_var("XDG_DATA_HOME"),
)
}
///|
fn browser_data_dir(identifier : String) -> String raise AppPathError {
@mbpath.Path(app_data_dir(identifier)).join("browser").normalize().to_string()
}
///|
fn app_data_dir_for_platform(
platform : String,
identifier : String,
home : String?,
local_app_data : String?,
xdg_data_home : String?,
) -> String raise AppPathError {
validate_app_identifier(identifier)
let base = match platform {
"macos" =>
@mbpath.Path(require_path_base(home, platform)).join(
"Library/Application Support",
)
"windows" => @mbpath.Path(require_path_base(local_app_data, platform))
"linux" =>
match non_empty_path(xdg_data_home) {
Some(path) => @mbpath.Path(path)
None =>
@mbpath.Path(require_path_base(home, platform)).join(".local/share")
}
_ => raise MissingHomeDirectory(platform~)
}
base.join(identifier).normalize().to_string()
}
///|
fn validate_app_identifier(identifier : String) -> Unit raise AppPathError {
let value = identifier.trim().to_owned()
if value == "" ||
value == "." ||
value == ".." ||
value.contains("/") ||
value.contains("\\") ||
value.contains("\u0000") {
raise InvalidIdentifier(identifier~)
}
}
///|
fn non_empty_path(value : String?) -> String? {
match value {
Some(path) => {
let path = path.trim().to_owned()
if path == "" {
None
} else {
Some(path)
}
}
None => None
}
}
///|
fn require_path_base(
value : String?,
platform : String,
) -> String raise AppPathError {
match non_empty_path(value) {
Some(path) => path
None => raise MissingHomeDirectory(platform~)
}
}