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