1use nu_glob::MatchOptions;
2use nu_path::{canonicalize_with, expand_path_with};
3use nu_protocol::{NuGlob, ShellError, Signals, Span, Spanned, shell_error::io::IoError};
4use std::{
5 fs,
6 path::{Component, Path, PathBuf},
7};
8
9#[allow(clippy::type_complexity)]
17pub fn glob_from(
18 pattern: &Spanned<NuGlob>,
19 cwd: &Path,
20 span: Span,
21 options: Option<MatchOptions>,
22 signals: Signals,
23) -> Result<
24 (
25 Option<PathBuf>,
26 Box<dyn Iterator<Item = Result<PathBuf, ShellError>> + Send>,
27 ),
28 ShellError,
29> {
30 let no_glob_for_pattern = matches!(pattern.item, NuGlob::DoNotExpand(_));
31 let pattern_span = pattern.span;
32 let (prefix, pattern) = if nu_glob::is_glob(pattern.item.as_ref()) {
33 let mut p = PathBuf::new();
35 let path = PathBuf::from(&pattern.item.as_ref());
36 let components = path.components();
37 let mut counter = 0;
38
39 for c in components {
40 if let Component::Normal(os) = c
41 && nu_glob::is_glob(os.to_string_lossy().as_ref())
42 {
43 break;
44 }
45 p.push(c);
46 counter += 1;
47 }
48
49 let mut just_pattern = PathBuf::new();
50 for c in counter..path.components().count() {
51 if let Some(comp) = path.components().nth(c) {
52 just_pattern.push(comp);
53 }
54 }
55 if no_glob_for_pattern {
56 just_pattern = PathBuf::from(nu_glob::Pattern::escape(&just_pattern.to_string_lossy()));
57 }
58
59 let path = expand_path_with(p, cwd, pattern.item.is_expand());
61 let escaped_prefix = PathBuf::from(nu_glob::Pattern::escape(&path.to_string_lossy()));
62
63 (Some(path), escaped_prefix.join(just_pattern))
64 } else {
65 let path = PathBuf::from(&pattern.item.as_ref());
66 let path = expand_path_with(path, cwd, pattern.item.is_expand());
67 let is_symlink = match fs::symlink_metadata(&path) {
68 Ok(attr) => attr.file_type().is_symlink(),
69 Err(_) => false,
70 };
71
72 if is_symlink {
73 (path.parent().map(|parent| parent.to_path_buf()), path)
74 } else {
75 let path = match canonicalize_with(path.clone(), cwd) {
76 Ok(p) if nu_glob::is_glob(p.to_string_lossy().as_ref()) => {
77 PathBuf::from(nu_glob::Pattern::escape(&p.to_string_lossy()))
81 }
82 Ok(p) => p,
83 Err(err) => {
84 return Err(IoError::new(err, pattern_span, path).into());
85 }
86 };
87 (path.parent().map(|parent| parent.to_path_buf()), path)
88 }
89 };
90
91 let pattern = pattern.to_string_lossy().to_string();
92 let glob_options = options.unwrap_or_default();
93
94 let glob = nu_glob::glob_with(&pattern, glob_options, signals).map_err(|e| {
95 nu_protocol::ShellError::GenericError {
96 error: "Error extracting glob pattern".into(),
97 msg: e.to_string(),
98 span: Some(span),
99 help: None,
100 inner: vec![],
101 }
102 })?;
103
104 Ok((
105 prefix,
106 Box::new(glob.map(move |x| match x {
107 Ok(v) => Ok(v),
108 Err(e) => Err(nu_protocol::ShellError::GenericError {
109 error: "Error extracting glob pattern".into(),
110 msg: e.to_string(),
111 span: Some(span),
112 help: None,
113 inner: vec![],
114 }),
115 })),
116 ))
117}