summaryrefslogtreecommitdiff
path: root/src/utils/globber.rs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-01-27 06:02:59 +0800
committer魏曹先生 <1992414357@qq.com>2026-01-27 06:02:59 +0800
commit4eef9ce364bb660421a96052a3fb126a33b22c63 (patch)
treea36947411d83205dc743881cd2a30d8c907d4b57 /src/utils/globber.rs
parent243d521fd19af169910506529e737a797e9bc583 (diff)
Extract CLI utilities into a separate crate
Diffstat (limited to 'src/utils/globber.rs')
-rw-r--r--src/utils/globber.rs276
1 files changed, 0 insertions, 276 deletions
diff --git a/src/utils/globber.rs b/src/utils/globber.rs
deleted file mode 100644
index dbb77ea..0000000
--- a/src/utils/globber.rs
+++ /dev/null
@@ -1,276 +0,0 @@
-use std::{io::Error, path::PathBuf, str::FromStr};
-
-use just_enough_vcs::utils::string_proc::format_path::format_path_str;
-
-use crate::utils::globber::constants::{SPLIT_STR, get_base_dir_current};
-
-pub struct Globber {
- pattern: String,
- base: PathBuf,
- names: Vec<String>,
-}
-
-#[allow(dead_code)]
-impl Globber {
- pub fn new(pattern: String, base: PathBuf) -> Self {
- Self {
- pattern,
- base,
- names: Vec::new(),
- }
- }
-
- pub fn names(&self) -> Vec<&String> {
- self.names.iter().collect()
- }
-
- pub fn base(&self) -> &PathBuf {
- &self.base
- }
-
- pub fn into_names(self) -> Vec<String> {
- self.names
- }
-
- pub fn paths(&self) -> Vec<PathBuf> {
- self.names.iter().map(|n| self.base.join(n)).collect()
- }
-
- pub fn glob<F>(mut self, get_names: F) -> Result<Self, std::io::Error>
- where
- F: Fn(PathBuf) -> Vec<GlobItem>,
- {
- let full_path = format!("{}{}{}", self.base.display(), SPLIT_STR, self.pattern);
-
- let (path, pattern) = if let Some(last_split) = full_path.rfind(SPLIT_STR) {
- let (path_part, pattern_part) = full_path.split_at(last_split);
- let mut path = path_part.to_string();
- if !path.ends_with(SPLIT_STR) {
- path.push_str(SPLIT_STR);
- }
- (
- format_path_str(path)?,
- pattern_part[SPLIT_STR.len()..].to_string(),
- )
- } else {
- (String::default(), full_path)
- };
-
- self.base = match PathBuf::from_str(&path) {
- Ok(r) => r,
- Err(_) => {
- return Err(Error::new(
- std::io::ErrorKind::InvalidInput,
- format!("Invalid path: \"{}\"", &path),
- ));
- }
- };
-
- let pattern = if pattern.is_empty() {
- "*".to_string()
- } else if pattern == "." {
- "*".to_string()
- } else if pattern.ends_with(SPLIT_STR) {
- format!("{}*", pattern)
- } else {
- pattern
- };
-
- if !pattern.contains('*') && !pattern.contains('?') {
- self.names = vec![pattern];
- return Ok(self);
- }
-
- let mut collected = Vec::new();
-
- collect_files(&path.into(), "./".to_string(), &mut collected, &get_names);
- fn collect_files<F>(
- base: &PathBuf,
- current: String,
- file_names: &mut Vec<String>,
- get_names: &F,
- ) where
- F: Fn(PathBuf) -> Vec<GlobItem>,
- {
- let current_path = if current.is_empty() {
- base.clone()
- } else {
- base.join(&current)
- };
-
- let items = get_names(current_path);
- for item in items {
- match item {
- GlobItem::File(file_name) => {
- let relative_path = {
- format_path_str(format!("{}{}{}", current, SPLIT_STR, file_name))
- .unwrap_or_default()
- };
- file_names.push(relative_path)
- }
- GlobItem::Directory(dir_name) => {
- let new_current = {
- format_path_str(format!("{}{}{}", current, SPLIT_STR, dir_name))
- .unwrap_or_default()
- };
- collect_files(base, new_current, file_names, get_names);
- }
- }
- }
- }
-
- self.names = collected
- .iter()
- .filter_map(|name| match_pattern(name, &pattern))
- .collect();
-
- Ok(self)
- }
-}
-
-fn match_pattern(name: &str, pattern: &str) -> Option<String> {
- if pattern.is_empty() {
- return None;
- }
-
- let name_chars: Vec<char> = name.chars().collect();
- let pattern_chars: Vec<char> = pattern.chars().collect();
-
- let mut name_idx = 0;
- let mut pattern_idx = 0;
- let mut star_idx = -1;
- let mut match_idx = -1;
-
- while name_idx < name_chars.len() {
- if pattern_idx < pattern_chars.len()
- && (pattern_chars[pattern_idx] == '?'
- || pattern_chars[pattern_idx] == name_chars[name_idx])
- {
- name_idx += 1;
- pattern_idx += 1;
- } else if pattern_idx < pattern_chars.len() && pattern_chars[pattern_idx] == '*' {
- star_idx = pattern_idx as i32;
- match_idx = name_idx as i32;
- pattern_idx += 1;
- } else if star_idx != -1 {
- pattern_idx = (star_idx + 1) as usize;
- match_idx += 1;
- name_idx = match_idx as usize;
- } else {
- return None;
- }
- }
-
- while pattern_idx < pattern_chars.len() && pattern_chars[pattern_idx] == '*' {
- pattern_idx += 1;
- }
-
- if pattern_idx == pattern_chars.len() {
- Some(name.to_string())
- } else {
- None
- }
-}
-
-impl<T: AsRef<str>> From<T> for Globber {
- fn from(pattern: T) -> Self {
- let (base_dir, pattern) = get_base_dir_current(pattern.as_ref().to_string());
- Self::new(pattern, base_dir)
- }
-}
-
-#[derive(Debug, Clone, Hash)]
-pub enum GlobItem {
- File(String),
- Directory(String),
-}
-
-impl PartialEq for GlobItem {
- fn eq(&self, other: &Self) -> bool {
- match (self, other) {
- (GlobItem::File(a), GlobItem::File(b)) => a == b,
- (GlobItem::Directory(a), GlobItem::Directory(b)) => a == b,
- _ => false,
- }
- }
-}
-
-impl std::fmt::Display for GlobItem {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- GlobItem::File(name) => write!(f, "{}", name),
- GlobItem::Directory(name) => write!(f, "{}", name),
- }
- }
-}
-
-impl Eq for GlobItem {}
-
-pub mod constants {
- use std::{env::current_dir, path::PathBuf};
-
- #[cfg(unix)]
- pub(crate) const CURRENT_DIR_PREFIX: &str = "./";
- #[cfg(windows)]
- pub(crate) const CURRENT_DIR_PREFIX: &str = ".\\";
-
- #[cfg(unix)]
- pub(crate) const USER_DIR_PREFIX: &str = "~";
- #[cfg(windows)]
- pub(crate) const USER_DIR_PREFIX: &str = "~\\";
-
- #[cfg(unix)]
- pub(crate) const ROOT_DIR_PREFIX: &str = "/";
- #[cfg(windows)]
- pub(crate) const ROOT_DIR_PREFIX: &str = "\\";
-
- #[cfg(unix)]
- pub(crate) const SPLIT_STR: &str = "/";
- #[cfg(windows)]
- pub(crate) const SPLIT_STR: &str = "\\";
-
- pub fn get_base_dir_current(input: String) -> (PathBuf, String) {
- get_base_dir(input, current_dir().unwrap_or_default())
- }
-
- pub fn get_base_dir(input: String, current_dir: PathBuf) -> (PathBuf, String) {
- if let Some(remaining) = input.strip_prefix(CURRENT_DIR_PREFIX) {
- (current_dir, remaining.to_string())
- } else if let Some(remaining) = input.strip_prefix(USER_DIR_PREFIX) {
- (dirs::home_dir().unwrap_or_default(), remaining.to_string())
- } else if let Some(remaining) = input.strip_prefix(ROOT_DIR_PREFIX) {
- {
- #[cfg(unix)]
- {
- (PathBuf::from(ROOT_DIR_PREFIX), remaining.to_string())
- }
- #[cfg(windows)]
- {
- let current_drive = current_dir
- .components()
- .find_map(|comp| {
- if let std::path::Component::Prefix(prefix_component) = comp {
- Some(prefix_component)
- } else {
- None
- }
- })
- .and_then(|prefix_component| match prefix_component.kind() {
- std::path::Prefix::Disk(drive_letter)
- | std::path::Prefix::VerbatimDisk(drive_letter) => {
- Some((drive_letter as char).to_string())
- }
- _ => None,
- })
- .unwrap_or_else(|| "C".to_string());
- (
- PathBuf::from(format!("{}:{}", current_drive, ROOT_DIR_PREFIX)),
- remaining.to_string(),
- )
- }
- }
- } else {
- (current_dir, input)
- }
- }
-}