cargo_packager_resource_resolver/lib.rs
1// Copyright 2023-2023 CrabNebula Ltd.
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! # cargo-packager-resource-resolver
6//!
7//! Resource resolver for apps that were packaged by [`cargo-packager`](https://docs.rs/cargo-packager).
8//!
9//! It resolves the root path which contains resources, which was set using the `resources`
10//! field of [cargo packager configuration](https://docs.rs/cargo-packager/latest/cargo_packager/config/struct.Config.html).
11//!
12//! ## Get the resource path
13//!
14//! ```
15//! use cargo_packager_resource_resolver::{resources_dir, PackageFormat};
16//!
17//! let resource_path = resources_dir(PackageFormat::Nsis).unwrap();
18//! ```
19//! ## Automatically detect formats
20//!
21//! <div class="warning">
22//!
23//! This feature is only available for apps that were built with cargo packager. So the node js binding will not work.
24//!
25//! </div>
26//!
27//! 1. Make sure to use the `before_each_package_command` field of [cargo packager configuration](https://docs.rs/cargo-packager/latest/cargo_packager/config/struct.Config.html) to build your app (this will not work with the `before_packaging_command` field).
28//! 2. Active the feature `auto-detect-format`.
29//!
30//! ```rs
31//! use cargo_packager_resource_resolver::{resources_dir, current_format};
32//!
33//! let resource_path = resources_dir(current_format()).unwrap();
34//! ```
35//!
36use std::path::PathBuf;
37
38use cargo_packager_utils::current_exe::current_exe;
39pub use cargo_packager_utils::PackageFormat;
40use error::Result;
41
42mod error;
43
44pub use error::Error;
45
46/// Get the current package format.
47/// Can only be used if the app was build with cargo-packager
48/// and when the `before-each-package-command` Cargo feature is enabled.
49#[cfg(feature = "auto-detect-format")]
50pub fn current_format() -> crate::Result<PackageFormat> {
51 // sync with PackageFormat::short_name function of packager crate
52 // maybe having a special crate for the Config struct,
53 // that both packager and resource-resolver could be a
54 // better alternative
55 if cfg!(CARGO_PACKAGER_FORMAT = "app") {
56 Ok(PackageFormat::App)
57 } else if cfg!(CARGO_PACKAGER_FORMAT = "dmg") {
58 Ok(PackageFormat::Dmg)
59 } else if cfg!(CARGO_PACKAGER_FORMAT = "wix") {
60 Ok(PackageFormat::Wix)
61 } else if cfg!(CARGO_PACKAGER_FORMAT = "nsis") {
62 Ok(PackageFormat::Nsis)
63 } else if cfg!(CARGO_PACKAGER_FORMAT = "deb") {
64 Ok(PackageFormat::Deb)
65 } else if cfg!(CARGO_PACKAGER_FORMAT = "appimage") {
66 Ok(PackageFormat::AppImage)
67 } else if cfg!(CARGO_PACKAGER_FORMAT = "pacman") {
68 Ok(PackageFormat::Pacman)
69 } else {
70 Err(Error::UnkownPackageFormat)
71 }
72}
73
74/// Retrieve the resource path of your app, packaged with cargo packager.
75///
76/// ## Example
77///
78/// ```
79/// use cargo_packager_resource_resolver::{resources_dir, PackageFormat};
80///
81/// let resource_path = resources_dir(PackageFormat::Nsis).unwrap();
82/// ```
83pub fn resources_dir(package_format: PackageFormat) -> Result<PathBuf> {
84 match package_format {
85 PackageFormat::App | PackageFormat::Dmg => {
86 let exe = current_exe()?;
87 let exe_dir = exe
88 .parent()
89 .ok_or_else(|| Error::ParentNotFound(exe.clone()))?;
90 Ok(exe_dir.join("../Resources"))
91 }
92 PackageFormat::Wix | PackageFormat::Nsis => {
93 let exe = current_exe()?;
94 let exe_dir = exe
95 .parent()
96 .ok_or_else(|| Error::ParentNotFound(exe.clone()))?;
97 Ok(exe_dir.to_path_buf())
98 }
99 PackageFormat::Deb | PackageFormat::Pacman => {
100 let exe = current_exe()?;
101 let exe_name = exe.file_name().unwrap().to_string_lossy();
102
103 let path = format!("/usr/lib/{}/", exe_name);
104 Ok(PathBuf::from(path))
105 }
106
107 PackageFormat::AppImage => {
108 let appdir = std::env::var_os("APPDIR").ok_or(Error::AppDirNotFound)?;
109
110 // validate that we're actually running on an AppImage
111 // an AppImage is mounted to `/$TEMPDIR/.mount_${appPrefix}${hash}`
112 // see https://github.com/AppImage/AppImageKit/blob/1681fd84dbe09c7d9b22e13cdb16ea601aa0ec47/src/runtime.c#L501
113 // note that it is safe to use `std::env::current_exe` here since we just loaded an AppImage.
114 let is_temp = std::env::current_exe()
115 .map(|p| {
116 p.display()
117 .to_string()
118 .starts_with(&format!("{}/.mount_", std::env::temp_dir().display()))
119 })
120 .unwrap_or(true);
121
122 if !is_temp {
123 return Err(Error::InvalidAppImage);
124 }
125
126 let appdir: &std::path::Path = appdir.as_ref();
127
128 let exe = current_exe()?;
129 let exe_name = exe.file_name().unwrap().to_string_lossy();
130
131 Ok(PathBuf::from(format!(
132 "{}/usr/lib/{}",
133 appdir.display(),
134 exe_name
135 )))
136 }
137 _ => Err(Error::UnsupportedPackageFormat),
138 }
139}