summaryrefslogtreecommitdiff
path: root/ext/fs/in_memory_fs.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-03-07 20:16:32 -0500
committerGitHub <noreply@github.com>2024-03-07 20:16:32 -0500
commit2dfc0aca7c6a04d54fe6f9a73be70fc4c591d552 (patch)
tree58fb01c46364e4888097e7135b2f829f38ce990c /ext/fs/in_memory_fs.rs
parent2ed984ba3aa638c3f088ac1edc5c779c7d9195d1 (diff)
fix(publish): make include and exclude work (#22720)
1. Stops `deno publish` using some custom include/exclude behaviour from other sub commands 2. Takes ancestor directories into account when resolving gitignore 3. Backards compatible change that adds ability to unexclude an exclude by using a negated glob at a more specific level for all sub commands (see https://github.com/denoland/deno_config/pull/44).
Diffstat (limited to 'ext/fs/in_memory_fs.rs')
-rw-r--r--ext/fs/in_memory_fs.rs425
1 files changed, 425 insertions, 0 deletions
diff --git a/ext/fs/in_memory_fs.rs b/ext/fs/in_memory_fs.rs
new file mode 100644
index 000000000..fdd0ad7e7
--- /dev/null
+++ b/ext/fs/in_memory_fs.rs
@@ -0,0 +1,425 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+// Allow using Arc for this module.
+#![allow(clippy::disallowed_types)]
+
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
+use std::io::Error;
+use std::io::ErrorKind;
+use std::path::Path;
+use std::path::PathBuf;
+use std::rc::Rc;
+use std::sync::Arc;
+
+use deno_core::normalize_path;
+use deno_core::parking_lot::Mutex;
+use deno_io::fs::File;
+use deno_io::fs::FsError;
+use deno_io::fs::FsResult;
+use deno_io::fs::FsStat;
+
+use crate::interface::FsDirEntry;
+use crate::interface::FsFileType;
+use crate::FileSystem;
+use crate::OpenOptions;
+
+#[derive(Debug)]
+enum PathEntry {
+ Dir,
+ File(Vec<u8>),
+}
+
+/// A very basic in-memory file system useful for swapping out in
+/// the place of a RealFs for testing purposes.
+///
+/// Please develop this out as you need functionality.
+#[derive(Debug, Default)]
+pub struct InMemoryFs {
+ entries: Mutex<HashMap<PathBuf, Arc<PathEntry>>>,
+}
+
+impl InMemoryFs {
+ pub fn setup_text_files(&self, files: Vec<(String, String)>) {
+ for (path, text) in files {
+ let path = PathBuf::from(path);
+ self.mkdir_sync(path.parent().unwrap(), true, 0).unwrap();
+ self
+ .write_file_sync(
+ &path,
+ OpenOptions::write(true, false, false, None),
+ &text.into_bytes(),
+ )
+ .unwrap();
+ }
+ }
+
+ fn get_entry(&self, path: &Path) -> Option<Arc<PathEntry>> {
+ let path = normalize_path(path);
+ self.entries.lock().get(&path).cloned()
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FileSystem for InMemoryFs {
+ fn cwd(&self) -> FsResult<PathBuf> {
+ Err(FsError::NotSupported)
+ }
+
+ fn tmp_dir(&self) -> FsResult<PathBuf> {
+ Err(FsError::NotSupported)
+ }
+
+ fn chdir(&self, _path: &Path) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+
+ fn umask(&self, _mask: Option<u32>) -> FsResult<u32> {
+ Err(FsError::NotSupported)
+ }
+
+ fn open_sync(
+ &self,
+ _path: &Path,
+ _options: OpenOptions,
+ ) -> FsResult<Rc<dyn File>> {
+ Err(FsError::NotSupported)
+ }
+ async fn open_async(
+ &self,
+ path: PathBuf,
+ options: OpenOptions,
+ ) -> FsResult<Rc<dyn File>> {
+ self.open_sync(&path, options)
+ }
+
+ fn mkdir_sync(
+ &self,
+ path: &Path,
+ recursive: bool,
+ _mode: u32,
+ ) -> FsResult<()> {
+ let path = normalize_path(path);
+
+ if let Some(parent) = path.parent() {
+ let entry = self.entries.lock().get(parent).cloned();
+ match entry {
+ Some(entry) => match &*entry {
+ PathEntry::File(_) => {
+ return Err(FsError::Io(Error::new(
+ ErrorKind::InvalidInput,
+ "Parent is a file",
+ )))
+ }
+ PathEntry::Dir => {}
+ },
+ None => {
+ if recursive {
+ self.mkdir_sync(parent, true, 0)?;
+ } else {
+ return Err(FsError::Io(Error::new(
+ ErrorKind::NotFound,
+ "Not found",
+ )));
+ }
+ }
+ }
+ }
+
+ let entry = self.entries.lock().get(&path).cloned();
+ match entry {
+ Some(entry) => match &*entry {
+ PathEntry::File(_) => Err(FsError::Io(Error::new(
+ ErrorKind::InvalidInput,
+ "Is a file",
+ ))),
+ PathEntry::Dir => Ok(()),
+ },
+ None => {
+ self.entries.lock().insert(path, Arc::new(PathEntry::Dir));
+ Ok(())
+ }
+ }
+ }
+ async fn mkdir_async(
+ &self,
+ path: PathBuf,
+ recursive: bool,
+ mode: u32,
+ ) -> FsResult<()> {
+ self.mkdir_sync(&path, recursive, mode)
+ }
+
+ fn chmod_sync(&self, _path: &Path, _mode: u32) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+ async fn chmod_async(&self, path: PathBuf, mode: u32) -> FsResult<()> {
+ self.chmod_sync(&path, mode)
+ }
+
+ fn chown_sync(
+ &self,
+ _path: &Path,
+ _uid: Option<u32>,
+ _gid: Option<u32>,
+ ) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+ async fn chown_async(
+ &self,
+ path: PathBuf,
+ uid: Option<u32>,
+ gid: Option<u32>,
+ ) -> FsResult<()> {
+ self.chown_sync(&path, uid, gid)
+ }
+
+ fn remove_sync(&self, _path: &Path, _recursive: bool) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+ async fn remove_async(&self, path: PathBuf, recursive: bool) -> FsResult<()> {
+ self.remove_sync(&path, recursive)
+ }
+
+ fn copy_file_sync(&self, _from: &Path, _to: &Path) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+ async fn copy_file_async(&self, from: PathBuf, to: PathBuf) -> FsResult<()> {
+ self.copy_file_sync(&from, &to)
+ }
+
+ fn cp_sync(&self, _from: &Path, _to: &Path) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+ async fn cp_async(&self, from: PathBuf, to: PathBuf) -> FsResult<()> {
+ self.cp_sync(&from, &to)
+ }
+
+ fn stat_sync(&self, path: &Path) -> FsResult<FsStat> {
+ let entry = self.get_entry(path);
+ match entry {
+ Some(entry) => match &*entry {
+ PathEntry::Dir => Ok(FsStat {
+ is_file: false,
+ is_directory: true,
+ is_symlink: false,
+ size: 0,
+ mtime: None,
+ atime: None,
+ birthtime: None,
+ dev: 0,
+ ino: 0,
+ mode: 0,
+ nlink: 0,
+ uid: 0,
+ gid: 0,
+ rdev: 0,
+ blksize: 0,
+ blocks: 0,
+ is_block_device: false,
+ is_char_device: false,
+ is_fifo: false,
+ is_socket: false,
+ }),
+ PathEntry::File(data) => Ok(FsStat {
+ is_file: true,
+ is_directory: false,
+ is_symlink: false,
+ size: data.len() as u64,
+ mtime: None,
+ atime: None,
+ birthtime: None,
+ dev: 0,
+ ino: 0,
+ mode: 0,
+ nlink: 0,
+ uid: 0,
+ gid: 0,
+ rdev: 0,
+ blksize: 0,
+ blocks: 0,
+ is_block_device: false,
+ is_char_device: false,
+ is_fifo: false,
+ is_socket: false,
+ }),
+ },
+ None => Err(FsError::Io(Error::new(ErrorKind::NotFound, "Not found"))),
+ }
+ }
+ async fn stat_async(&self, path: PathBuf) -> FsResult<FsStat> {
+ self.stat_sync(&path)
+ }
+
+ fn lstat_sync(&self, _path: &Path) -> FsResult<FsStat> {
+ Err(FsError::NotSupported)
+ }
+ async fn lstat_async(&self, path: PathBuf) -> FsResult<FsStat> {
+ self.lstat_sync(&path)
+ }
+
+ fn realpath_sync(&self, _path: &Path) -> FsResult<PathBuf> {
+ Err(FsError::NotSupported)
+ }
+ async fn realpath_async(&self, path: PathBuf) -> FsResult<PathBuf> {
+ self.realpath_sync(&path)
+ }
+
+ fn read_dir_sync(&self, _path: &Path) -> FsResult<Vec<FsDirEntry>> {
+ Err(FsError::NotSupported)
+ }
+ async fn read_dir_async(&self, path: PathBuf) -> FsResult<Vec<FsDirEntry>> {
+ self.read_dir_sync(&path)
+ }
+
+ fn rename_sync(&self, _oldpath: &Path, _newpath: &Path) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+ async fn rename_async(
+ &self,
+ oldpath: PathBuf,
+ newpath: PathBuf,
+ ) -> FsResult<()> {
+ self.rename_sync(&oldpath, &newpath)
+ }
+
+ fn link_sync(&self, _oldpath: &Path, _newpath: &Path) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+ async fn link_async(
+ &self,
+ oldpath: PathBuf,
+ newpath: PathBuf,
+ ) -> FsResult<()> {
+ self.link_sync(&oldpath, &newpath)
+ }
+
+ fn symlink_sync(
+ &self,
+ _oldpath: &Path,
+ _newpath: &Path,
+ _file_type: Option<FsFileType>,
+ ) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+ async fn symlink_async(
+ &self,
+ oldpath: PathBuf,
+ newpath: PathBuf,
+ file_type: Option<FsFileType>,
+ ) -> FsResult<()> {
+ self.symlink_sync(&oldpath, &newpath, file_type)
+ }
+
+ fn read_link_sync(&self, _path: &Path) -> FsResult<PathBuf> {
+ Err(FsError::NotSupported)
+ }
+ async fn read_link_async(&self, path: PathBuf) -> FsResult<PathBuf> {
+ self.read_link_sync(&path)
+ }
+
+ fn truncate_sync(&self, _path: &Path, _len: u64) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+ async fn truncate_async(&self, path: PathBuf, len: u64) -> FsResult<()> {
+ self.truncate_sync(&path, len)
+ }
+
+ fn utime_sync(
+ &self,
+ _path: &Path,
+ _atime_secs: i64,
+ _atime_nanos: u32,
+ _mtime_secs: i64,
+ _mtime_nanos: u32,
+ ) -> FsResult<()> {
+ Err(FsError::NotSupported)
+ }
+ async fn utime_async(
+ &self,
+ path: PathBuf,
+ atime_secs: i64,
+ atime_nanos: u32,
+ mtime_secs: i64,
+ mtime_nanos: u32,
+ ) -> FsResult<()> {
+ self.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
+ }
+
+ fn write_file_sync(
+ &self,
+ path: &Path,
+ options: OpenOptions,
+ data: &[u8],
+ ) -> FsResult<()> {
+ let path = normalize_path(path);
+ let has_parent_dir = path
+ .parent()
+ .and_then(|parent| self.get_entry(parent))
+ .map(|e| matches!(*e, PathEntry::Dir))
+ .unwrap_or(false);
+ if !has_parent_dir {
+ return Err(FsError::Io(Error::new(
+ ErrorKind::NotFound,
+ "Parent directory does not exist",
+ )));
+ }
+ let mut entries = self.entries.lock();
+ let entry = entries.entry(path.clone());
+ match entry {
+ Entry::Occupied(mut entry) => {
+ if let PathEntry::File(existing_data) = &**entry.get() {
+ if options.create_new {
+ return Err(FsError::Io(Error::new(
+ ErrorKind::AlreadyExists,
+ "File already exists",
+ )));
+ }
+ if options.append {
+ let mut new_data = existing_data.clone();
+ new_data.extend_from_slice(data);
+ entry.insert(Arc::new(PathEntry::File(new_data)));
+ } else {
+ entry.insert(Arc::new(PathEntry::File(data.to_vec())));
+ }
+ Ok(())
+ } else {
+ Err(FsError::Io(Error::new(
+ ErrorKind::InvalidInput,
+ "Not a file",
+ )))
+ }
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(Arc::new(PathEntry::File(data.to_vec())));
+ Ok(())
+ }
+ }
+ }
+
+ async fn write_file_async(
+ &self,
+ path: PathBuf,
+ options: OpenOptions,
+ data: Vec<u8>,
+ ) -> FsResult<()> {
+ self.write_file_sync(&path, options, &data)
+ }
+
+ fn read_file_sync(&self, path: &Path) -> FsResult<Vec<u8>> {
+ let entry = self.get_entry(path);
+ match entry {
+ Some(entry) => match &*entry {
+ PathEntry::File(data) => Ok(data.clone()),
+ PathEntry::Dir => Err(FsError::Io(Error::new(
+ ErrorKind::InvalidInput,
+ "Is a directory",
+ ))),
+ },
+ None => Err(FsError::Io(Error::new(ErrorKind::NotFound, "Not found"))),
+ }
+ }
+ async fn read_file_async(&self, path: PathBuf) -> FsResult<Vec<u8>> {
+ self.read_file_sync(&path)
+ }
+}