nu_protocol/
parser_path.rs

1use crate::{
2    FileId,
3    engine::{StateWorkingSet, VirtualPath},
4};
5use std::{
6    ffi::OsStr,
7    path::{Path, PathBuf},
8};
9
10/// An abstraction over a PathBuf that can have virtual paths (files and directories). Virtual
11/// paths always exist and represent a way to ship Nushell code inside the binary without requiring
12/// paths to be present in the file system.
13///
14/// Created from VirtualPath found in the engine state.
15#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
16pub enum ParserPath {
17    RealPath(PathBuf),
18    VirtualFile(PathBuf, usize),
19    VirtualDir(PathBuf, Vec<ParserPath>),
20}
21
22impl ParserPath {
23    pub fn is_dir(&self) -> bool {
24        match self {
25            ParserPath::RealPath(p) => p.is_dir(),
26            ParserPath::VirtualFile(..) => false,
27            ParserPath::VirtualDir(..) => true,
28        }
29    }
30
31    pub fn is_file(&self) -> bool {
32        match self {
33            ParserPath::RealPath(p) => p.is_file(),
34            ParserPath::VirtualFile(..) => true,
35            ParserPath::VirtualDir(..) => false,
36        }
37    }
38
39    pub fn exists(&self) -> bool {
40        match self {
41            ParserPath::RealPath(p) => p.exists(),
42            ParserPath::VirtualFile(..) => true,
43            ParserPath::VirtualDir(..) => true,
44        }
45    }
46
47    pub fn path(&self) -> &Path {
48        match self {
49            ParserPath::RealPath(p) => p,
50            ParserPath::VirtualFile(p, _) => p,
51            ParserPath::VirtualDir(p, _) => p,
52        }
53    }
54
55    pub fn path_buf(self) -> PathBuf {
56        match self {
57            ParserPath::RealPath(p) => p,
58            ParserPath::VirtualFile(p, _) => p,
59            ParserPath::VirtualDir(p, _) => p,
60        }
61    }
62
63    pub fn parent(&self) -> Option<&Path> {
64        match self {
65            ParserPath::RealPath(p) => p.parent(),
66            ParserPath::VirtualFile(p, _) => p.parent(),
67            ParserPath::VirtualDir(p, _) => p.parent(),
68        }
69    }
70
71    pub fn read_dir(&self) -> Option<Vec<ParserPath>> {
72        match self {
73            ParserPath::RealPath(p) => p.read_dir().ok().map(|read_dir| {
74                read_dir
75                    .flatten()
76                    .map(|dir_entry| ParserPath::RealPath(dir_entry.path()))
77                    .collect()
78            }),
79            ParserPath::VirtualFile(..) => None,
80            ParserPath::VirtualDir(_, files) => Some(files.clone()),
81        }
82    }
83
84    pub fn file_stem(&self) -> Option<&OsStr> {
85        self.path().file_stem()
86    }
87
88    pub fn extension(&self) -> Option<&OsStr> {
89        self.path().extension()
90    }
91
92    pub fn join(self, path: impl AsRef<Path>) -> ParserPath {
93        match self {
94            ParserPath::RealPath(p) => ParserPath::RealPath(p.join(path)),
95            ParserPath::VirtualFile(p, file_id) => ParserPath::VirtualFile(p.join(path), file_id),
96            ParserPath::VirtualDir(p, entries) => {
97                let new_p = p.join(path);
98                let mut pp = ParserPath::RealPath(new_p.clone());
99                for entry in entries {
100                    if new_p == entry.path() {
101                        pp = entry.clone();
102                    }
103                }
104                pp
105            }
106        }
107    }
108
109    pub fn open<'a>(
110        &'a self,
111        working_set: &'a StateWorkingSet,
112    ) -> std::io::Result<Box<dyn std::io::Read + 'a>> {
113        match self {
114            ParserPath::RealPath(p) => {
115                std::fs::File::open(p).map(|f| Box::new(f) as Box<dyn std::io::Read>)
116            }
117            ParserPath::VirtualFile(_, file_id) => working_set
118                .get_contents_of_file(FileId::new(*file_id))
119                .map(|bytes| Box::new(bytes) as Box<dyn std::io::Read>)
120                .ok_or(std::io::ErrorKind::NotFound.into()),
121
122            ParserPath::VirtualDir(..) => Err(std::io::ErrorKind::NotFound.into()),
123        }
124    }
125
126    pub fn read<'a>(&'a self, working_set: &'a StateWorkingSet) -> Option<Vec<u8>> {
127        self.open(working_set)
128            .and_then(|mut reader| {
129                let mut vec = vec![];
130                reader.read_to_end(&mut vec)?;
131                Ok(vec)
132            })
133            .ok()
134    }
135
136    pub fn from_virtual_path(
137        working_set: &StateWorkingSet,
138        name: &str,
139        virtual_path: &VirtualPath,
140    ) -> Self {
141        match virtual_path {
142            VirtualPath::File(file_id) => {
143                ParserPath::VirtualFile(PathBuf::from(name), file_id.get())
144            }
145            VirtualPath::Dir(entries) => ParserPath::VirtualDir(
146                PathBuf::from(name),
147                entries
148                    .iter()
149                    .map(|virtual_path_id| {
150                        let (virt_name, virt_path) = working_set.get_virtual_path(*virtual_path_id);
151                        ParserPath::from_virtual_path(working_set, virt_name, virt_path)
152                    })
153                    .collect(),
154            ),
155        }
156    }
157}