aboutsummaryrefslogtreecommitdiff
path: root/arg_picker/src/builtin
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-07-28 11:42:27 +0800
committer魏曹先生 <1992414357@qq.com>2026-07-28 11:42:27 +0800
commit160460494f8c56fd42ff9ab96ace74cc4457f479 (patch)
treec0434dae5760a4ce0462c89cddf0ca7091fcc971 /arg_picker/src/builtin
parent5229379afe1306c8d6ebea4e84418ba5caa475ae (diff)
feat(picker): add filesystem-aware path value types
Add `FilePath`, `NoFilePath`, `DirPath`, `NoDirPath`, `SymlinkPath`, `NoSymlinkPath`, `NoPath`, and `RecursiveFiles` wrapper types with filesystem validation at parse time. Each type implements `SinglePickable` and provides standard conversions via `From`, `AsRef`, `Deref`, and `DerefMut`.
Diffstat (limited to 'arg_picker/src/builtin')
-rw-r--r--arg_picker/src/builtin/pick_paths.rs167
1 files changed, 167 insertions, 0 deletions
diff --git a/arg_picker/src/builtin/pick_paths.rs b/arg_picker/src/builtin/pick_paths.rs
new file mode 100644
index 0000000..8e6c0fd
--- /dev/null
+++ b/arg_picker/src/builtin/pick_paths.rs
@@ -0,0 +1,167 @@
+use std::{
+ fs,
+ path::{Path, PathBuf},
+};
+
+use crate::{
+ PickerArgResult::{self, NotFound, Parsed, Unparsed},
+ SinglePickable,
+ value::{
+ DirPath, FilePath, NoDirPath, NoFilePath, NoPath, NoSymlinkPath, RecursiveFiles,
+ SymlinkPath,
+ },
+};
+
+impl SinglePickable for FilePath {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ match <PathBuf as SinglePickable>::pick_single(str) {
+ Parsed(path) => {
+ if path.exists() && path.is_file() {
+ Parsed(FilePath::from(path))
+ } else {
+ NotFound
+ }
+ }
+ Unparsed => Unparsed,
+ NotFound => NotFound,
+ }
+ }
+}
+
+impl SinglePickable for NoFilePath {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ match <PathBuf as SinglePickable>::pick_single(str) {
+ Parsed(path) => {
+ if !path.exists() || !path.is_file() {
+ Parsed(NoFilePath::from(path))
+ } else {
+ NotFound
+ }
+ }
+ Unparsed => Unparsed,
+ NotFound => NotFound,
+ }
+ }
+}
+
+impl SinglePickable for DirPath {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ match <PathBuf as SinglePickable>::pick_single(str) {
+ Parsed(path) => {
+ if path.exists() && path.is_dir() {
+ Parsed(DirPath::from(path))
+ } else {
+ NotFound
+ }
+ }
+ Unparsed => Unparsed,
+ NotFound => NotFound,
+ }
+ }
+}
+
+impl SinglePickable for NoDirPath {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ match <PathBuf as SinglePickable>::pick_single(str) {
+ Parsed(path) => {
+ if !path.exists() || !path.is_dir() {
+ Parsed(NoDirPath::from(path))
+ } else {
+ NotFound
+ }
+ }
+ Unparsed => Unparsed,
+ NotFound => NotFound,
+ }
+ }
+}
+
+impl SinglePickable for SymlinkPath {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ match <PathBuf as SinglePickable>::pick_single(str) {
+ Parsed(path) => {
+ if path.exists() && path.is_symlink() {
+ Parsed(SymlinkPath::from(path))
+ } else {
+ NotFound
+ }
+ }
+ Unparsed => Unparsed,
+ NotFound => NotFound,
+ }
+ }
+}
+
+impl SinglePickable for NoSymlinkPath {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ match <PathBuf as SinglePickable>::pick_single(str) {
+ Parsed(path) => {
+ if !path.exists() || !path.is_symlink() {
+ Parsed(NoSymlinkPath::from(path))
+ } else {
+ NotFound
+ }
+ }
+ Unparsed => Unparsed,
+ NotFound => NotFound,
+ }
+ }
+}
+
+impl SinglePickable for NoPath {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ match <PathBuf as SinglePickable>::pick_single(str) {
+ Parsed(path) => {
+ if !path.exists() {
+ Parsed(NoPath::from(path))
+ } else {
+ NotFound
+ }
+ }
+ Unparsed => Unparsed,
+ NotFound => NotFound,
+ }
+ }
+}
+
+impl SinglePickable for RecursiveFiles {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ match <PathBuf as SinglePickable>::pick_single(str) {
+ Parsed(path) => {
+ if !path.exists() {
+ return NotFound;
+ }
+ if path.is_file() || path.is_symlink() {
+ return Parsed(RecursiveFiles::from(vec![path]));
+ }
+ let mut entries = Vec::new();
+ if let Ok(dir_entries) = fs::read_dir(&path) {
+ for entry in dir_entries.flatten() {
+ let entry_path = entry.path();
+ if entry_path.is_file() || entry_path.is_symlink() {
+ entries.push(entry_path);
+ } else if entry_path.is_dir() {
+ collect_files(&entry_path, &mut entries);
+ }
+ }
+ }
+ Parsed(RecursiveFiles::from(entries))
+ }
+ Unparsed => Unparsed,
+ NotFound => NotFound,
+ }
+ }
+}
+
+fn collect_files(dir: &Path, entries: &mut Vec<PathBuf>) {
+ if let Ok(dir_entries) = fs::read_dir(dir) {
+ for entry in dir_entries.flatten() {
+ let entry_path = entry.path();
+ if entry_path.is_file() || entry_path.is_symlink() {
+ entries.push(entry_path);
+ } else if entry_path.is_dir() {
+ collect_files(&entry_path, entries);
+ }
+ }
+ }
+}