aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-06-27 18:52:49 +0800
committer魏曹先生 <1992414357@qq.com>2026-06-27 18:52:49 +0800
commit353fdc5b539aae0479c7404d0ed6404f82bf633a (patch)
tree0f0f07080aebf2ad8f0be537c7bc855618d89df6
parent5a23e6b3ad655b15b412720ab81b0508866bebce (diff)
feat(mingling): add directory environment resources and setup
Add four new resource types for common directory paths and a convenience setup struct that registers all of them at once.
-rw-r--r--CHANGELOG.md17
-rw-r--r--mingling/src/res.rs3
-rw-r--r--mingling/src/res/dirs.rs11
-rw-r--r--mingling/src/res/dirs/current_dir.rs82
-rw-r--r--mingling/src/res/dirs/current_exe.rs81
-rw-r--r--mingling/src/res/dirs/home_dir.rs96
-rw-r--r--mingling/src/res/dirs/temp_dir.rs77
-rw-r--r--mingling/src/setups.rs3
-rw-r--r--mingling/src/setups/dirs.rs42
9 files changed, 412 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7cc4e6f..066cc5a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -530,6 +530,23 @@ dispatcher! {
}
```
+10. **\[mingling\]** Added the `DirectoryEnvironmentSetup<C>` setup struct, which registers four common directory-based resources (`ResCurrentDir`, `ResCurrentExe`, `ResHomeDir`, `ResTempDir`) in a single call. These resources provide convenient access to the current working directory, the executable's path, the user's home directory, and the system temporary directory, respectively.
+
+```rust
+use mingling::setups::DirectoryEnvironmentSetup;
+
+program.with_setup(DirectoryEnvironmentSetup::<ThisProgram>::default());
+```
+
+11. **\[mingling\]** Added four new resource types for directory environments:
+
+ - `ResCurrentDir` — Wraps `std::env::current_dir()` as a global resource. Provides `new() -> Result`, `Default` (panics on failure), and conversions from/to `PathBuf`, `&Path`, and `&PathBuf`.
+ - `ResCurrentExe` — Wraps `std::env::current_exe()` as a global resource. Provides `new() -> Result`, `Default` (panics on failure), and conversions from/to `PathBuf`, `&Path`, and `&PathBuf`.
+ - `ResHomeDir` — Wraps the user's home directory (`$HOME` on Unix, `%USERPROFILE%` on Windows) as a global resource. Provides `new() -> Result`, `Default` (panics on failure), and conversions from/to `PathBuf`, `&Path`, and `&PathBuf`.
+ - `ResTempDir` — Wraps `std::env::temp_dir()` as a global resource. Provides `new()` (infallible), `Default`, and conversions from/to `PathBuf`, `&Path`, and `&PathBuf`.
+
+ All four types implement `Deref<Target = PathBuf>`, `DerefMut`, `AsRef<Path>`, `Clone`, `Debug`, and `PartialEq`.
+
#### **BREAKING CHANGES** (API CHANGES):
1. **\[core\]** Panic Unwind will not be supported when the `async` feature is enabled
diff --git a/mingling/src/res.rs b/mingling/src/res.rs
index 982fe91..1dd00aa 100644
--- a/mingling/src/res.rs
+++ b/mingling/src/res.rs
@@ -1,5 +1,8 @@
mod exit_code;
pub use exit_code::*;
+mod dirs;
+pub use dirs::*;
+
#[allow(unused_imports)]
pub use mingling_core::core_res::*;
diff --git a/mingling/src/res/dirs.rs b/mingling/src/res/dirs.rs
new file mode 100644
index 0000000..0a0a590
--- /dev/null
+++ b/mingling/src/res/dirs.rs
@@ -0,0 +1,11 @@
+mod current_dir;
+pub use current_dir::*;
+
+mod current_exe;
+pub use current_exe::*;
+
+mod temp_dir;
+pub use temp_dir::*;
+
+mod home_dir;
+pub use home_dir::*;
diff --git a/mingling/src/res/dirs/current_dir.rs b/mingling/src/res/dirs/current_dir.rs
new file mode 100644
index 0000000..1de84e0
--- /dev/null
+++ b/mingling/src/res/dirs/current_dir.rs
@@ -0,0 +1,82 @@
+use std::{
+ env::current_dir,
+ path::{Path, PathBuf},
+};
+
+/// A global resource for the Mingling library that provides the current working directory.
+///
+/// This struct wraps a `PathBuf` representing the current working directory at the time of
+/// creation. It is used as a shared resource in the Mingling framework so that multiple
+/// components can access the same base directory without repeatedly querying the OS.
+///
+/// # Default behavior
+///
+/// The `Default` implementation calls `std::env::current_dir().unwrap()` internally, which will
+/// **panic** if the current directory cannot be determined (e.g., if it has been deleted). If
+/// you want to handle this error gracefully, use [`ResCurrentDir::new`] instead, which returns
+/// a `Result`.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ResCurrentDir {
+ cwd: PathBuf
+}
+
+impl ResCurrentDir {
+ /// Creates a new `ResCurrentDir` by querying the OS for the current working directory.
+ ///
+ /// Returns `Err` if the current directory cannot be determined (e.g., the directory has been
+ /// deleted or permissions are insufficient). Unlike the `Default` implementation, this
+ /// method does not panic on failure.
+ pub fn new() -> Result<Self, std::io::Error> {
+ Ok(Self { cwd: current_dir()? })
+ }
+}
+
+impl Default for ResCurrentDir {
+ fn default() -> Self {
+ Self { cwd: current_dir().unwrap() }
+ }
+}
+
+impl From<PathBuf> for ResCurrentDir {
+ fn from(path: PathBuf) -> Self {
+ Self { cwd: path }
+ }
+}
+
+impl From<&Path> for ResCurrentDir {
+ fn from(path: &Path) -> Self {
+ Self { cwd: path.to_path_buf() }
+ }
+}
+
+impl From<&PathBuf> for ResCurrentDir {
+ fn from(path: &PathBuf) -> Self {
+ Self { cwd: path.clone() }
+ }
+}
+
+impl From<ResCurrentDir> for PathBuf {
+ fn from(res: ResCurrentDir) -> Self {
+ res.cwd
+ }
+}
+
+impl AsRef<Path> for ResCurrentDir {
+ fn as_ref(&self) -> &Path {
+ self.cwd.as_path()
+ }
+}
+
+impl std::ops::Deref for ResCurrentDir {
+ type Target = PathBuf;
+
+ fn deref(&self) -> &Self::Target {
+ &self.cwd
+ }
+}
+
+impl std::ops::DerefMut for ResCurrentDir {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.cwd
+ }
+}
diff --git a/mingling/src/res/dirs/current_exe.rs b/mingling/src/res/dirs/current_exe.rs
new file mode 100644
index 0000000..051fcee
--- /dev/null
+++ b/mingling/src/res/dirs/current_exe.rs
@@ -0,0 +1,81 @@
+use std::{
+ env::current_exe,
+ path::{Path, PathBuf},
+};
+
+/// A global resource that provides the path of the current executable.
+///
+/// This struct wraps a `PathBuf` representing the full filesystem path of the
+/// currently running executable at the time of creation.
+///
+/// # Default behavior
+///
+/// The `Default` implementation calls `std::env::current_exe().unwrap()` internally,
+/// which will **panic** if the executable path cannot be determined (e.g., if the
+/// `/proc` filesystem is not available). If you want to handle this error gracefully,
+/// use [`ResCurrentExe::new`] instead, which returns a `Result`.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ResCurrentExe {
+ exe: PathBuf,
+}
+
+impl ResCurrentExe {
+ /// Creates a new `ResCurrentExe` by querying the OS for the current executable path.
+ ///
+ /// Returns `Err` if the executable path cannot be determined (e.g., the `/proc`
+ /// filesystem is not available on Linux, or the process handle is invalid).
+ /// Unlike the `Default` implementation, this method does not panic on failure.
+ pub fn new() -> Result<Self, std::io::Error> {
+ Ok(Self { exe: current_exe()? })
+ }
+}
+
+impl Default for ResCurrentExe {
+ fn default() -> Self {
+ Self { exe: current_exe().unwrap() }
+ }
+}
+
+impl From<PathBuf> for ResCurrentExe {
+ fn from(path: PathBuf) -> Self {
+ Self { exe: path }
+ }
+}
+
+impl From<&Path> for ResCurrentExe {
+ fn from(path: &Path) -> Self {
+ Self { exe: path.to_path_buf() }
+ }
+}
+
+impl From<&PathBuf> for ResCurrentExe {
+ fn from(path: &PathBuf) -> Self {
+ Self { exe: path.clone() }
+ }
+}
+
+impl From<ResCurrentExe> for PathBuf {
+ fn from(res: ResCurrentExe) -> Self {
+ res.exe
+ }
+}
+
+impl AsRef<Path> for ResCurrentExe {
+ fn as_ref(&self) -> &Path {
+ self.exe.as_path()
+ }
+}
+
+impl std::ops::Deref for ResCurrentExe {
+ type Target = PathBuf;
+
+ fn deref(&self) -> &Self::Target {
+ &self.exe
+ }
+}
+
+impl std::ops::DerefMut for ResCurrentExe {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.exe
+ }
+}
diff --git a/mingling/src/res/dirs/home_dir.rs b/mingling/src/res/dirs/home_dir.rs
new file mode 100644
index 0000000..de2e5e7
--- /dev/null
+++ b/mingling/src/res/dirs/home_dir.rs
@@ -0,0 +1,96 @@
+use std::path::{Path, PathBuf};
+
+/// A global resource that provides the current user's home directory path.
+///
+/// This struct wraps a `PathBuf` representing the user's home directory.
+///
+/// **Platform notes:**
+/// - On **Unix**: reads the `HOME` environment variable.
+/// - On **Windows**: reads the `USERPROFILE` environment variable.
+///
+/// # Default behavior
+///
+/// The `Default` implementation calls [`ResHomeDir::new`] and **panics** if the home
+/// directory cannot be determined (e.g., the relevant environment variable is not set).
+/// Use [`ResHomeDir::new`] to handle this error gracefully.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ResHomeDir {
+ home: PathBuf,
+}
+
+impl ResHomeDir {
+ /// Creates a new `ResHomeDir` by querying the environment for the user's home directory.
+ ///
+ /// Returns `Err` if the home directory cannot be determined (e.g., the `HOME` or
+ /// `USERPROFILE` environment variable is not set).
+ pub fn new() -> Result<Self, std::io::Error> {
+ let home = home_dir_env()
+ .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "home directory not found"))?;
+ Ok(Self { home })
+ }
+}
+
+impl Default for ResHomeDir {
+ fn default() -> Self {
+ Self { home: home_dir_env().expect("home directory not found") }
+ }
+}
+
+impl From<PathBuf> for ResHomeDir {
+ fn from(path: PathBuf) -> Self {
+ Self { home: path }
+ }
+}
+
+impl From<&Path> for ResHomeDir {
+ fn from(path: &Path) -> Self {
+ Self { home: path.to_path_buf() }
+ }
+}
+
+impl From<&PathBuf> for ResHomeDir {
+ fn from(path: &PathBuf) -> Self {
+ Self { home: path.clone() }
+ }
+}
+
+impl From<ResHomeDir> for PathBuf {
+ fn from(res: ResHomeDir) -> Self {
+ res.home
+ }
+}
+
+impl AsRef<Path> for ResHomeDir {
+ fn as_ref(&self) -> &Path {
+ self.home.as_path()
+ }
+}
+
+impl std::ops::Deref for ResHomeDir {
+ type Target = PathBuf;
+
+ fn deref(&self) -> &Self::Target {
+ &self.home
+ }
+}
+
+impl std::ops::DerefMut for ResHomeDir {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.home
+ }
+}
+
+/// Returns the user's home directory by checking platform-specific environment variables.
+///
+/// - Unix: `$HOME`
+/// - Windows: `%USERPROFILE%`
+fn home_dir_env() -> Option<PathBuf> {
+ #[cfg(target_os = "windows")]
+ {
+ std::env::var_os("USERPROFILE").map(PathBuf::from)
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ std::env::var_os("HOME").map(PathBuf::from)
+ }
+}
diff --git a/mingling/src/res/dirs/temp_dir.rs b/mingling/src/res/dirs/temp_dir.rs
new file mode 100644
index 0000000..f4f0dca
--- /dev/null
+++ b/mingling/src/res/dirs/temp_dir.rs
@@ -0,0 +1,77 @@
+use std::{
+ env::temp_dir,
+ path::{Path, PathBuf},
+};
+
+/// A global resource that provides the system temporary directory path.
+///
+/// This struct wraps a `PathBuf` representing the platform's temporary directory
+/// (e.g., `/tmp` on Unix, `%TEMP%` on Windows).
+///
+/// Note that `std::env::temp_dir()` is infallible — it always returns a path,
+/// falling back to a platform-specific default (e.g., `/tmp` on Unix) if the
+/// environment variable is not set.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ResTempDir {
+ tmp: PathBuf,
+}
+
+impl ResTempDir {
+ /// Creates a new `ResTempDir` by querying the OS for the system temporary directory.
+ ///
+ /// This method is infallible since `std::env::temp_dir()` always succeeds,
+ /// returning a platform-specific default when environment variables are unset.
+ pub fn new() -> Self {
+ Self { tmp: temp_dir() }
+ }
+}
+
+impl Default for ResTempDir {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl From<PathBuf> for ResTempDir {
+ fn from(path: PathBuf) -> Self {
+ Self { tmp: path }
+ }
+}
+
+impl From<&Path> for ResTempDir {
+ fn from(path: &Path) -> Self {
+ Self { tmp: path.to_path_buf() }
+ }
+}
+
+impl From<&PathBuf> for ResTempDir {
+ fn from(path: &PathBuf) -> Self {
+ Self { tmp: path.clone() }
+ }
+}
+
+impl From<ResTempDir> for PathBuf {
+ fn from(res: ResTempDir) -> Self {
+ res.tmp
+ }
+}
+
+impl AsRef<Path> for ResTempDir {
+ fn as_ref(&self) -> &Path {
+ self.tmp.as_path()
+ }
+}
+
+impl std::ops::Deref for ResTempDir {
+ type Target = PathBuf;
+
+ fn deref(&self) -> &Self::Target {
+ &self.tmp
+ }
+}
+
+impl std::ops::DerefMut for ResTempDir {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.tmp
+ }
+}
diff --git a/mingling/src/setups.rs b/mingling/src/setups.rs
index 5546268..e572cf5 100644
--- a/mingling/src/setups.rs
+++ b/mingling/src/setups.rs
@@ -1,6 +1,9 @@
mod basic;
pub use basic::*;
+mod dirs;
+pub use dirs::*;
+
mod exit_code;
pub use exit_code::*;
diff --git a/mingling/src/setups/dirs.rs b/mingling/src/setups/dirs.rs
new file mode 100644
index 0000000..a7f81fa
--- /dev/null
+++ b/mingling/src/setups/dirs.rs
@@ -0,0 +1,42 @@
+use std::marker::PhantomData;
+
+use mingling_core::{
+ ProgramCollect,
+ setup::ProgramSetup,
+};
+
+use crate::res::{ResCurrentDir, ResCurrentExe, ResHomeDir, ResTempDir};
+
+/// Provides the ability to set up commonly used directory resources for the program.
+///
+/// This setup item registers the following directory resources in the program:
+/// - `ResCurrentDir`: Current working directory
+/// - `ResCurrentExe`: Directory containing the executable
+/// - `ResHomeDir`: User's home directory
+/// - `ResTempDir`: Temporary directory
+pub struct DirectoryEnvironmentSetup<C> {
+ _collect: PhantomData<C>,
+}
+
+impl<C> Default for DirectoryEnvironmentSetup<C>
+where
+ C: ProgramCollect<Enum = C> + 'static,
+{
+ fn default() -> Self {
+ Self {
+ _collect: PhantomData,
+ }
+ }
+}
+
+impl<C> ProgramSetup<C> for DirectoryEnvironmentSetup<C>
+where
+ C: ProgramCollect<Enum = C> + 'static,
+{
+ fn setup(self, program: &mut crate::Program<C>) {
+ program.with_resource(ResCurrentDir::default());
+ program.with_resource(ResCurrentExe::default());
+ program.with_resource(ResHomeDir::default());
+ program.with_resource(ResTempDir::default());
+ }
+}