Skip to content

Commit

Permalink
Create tree of scanned project dir tracked files and subdirs. Measure…
Browse files Browse the repository at this point in the history
… time taken for initial scan of project directory.
  • Loading branch information
ctsrc committed Oct 6, 2024
1 parent 2db3592 commit f8d5ead
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/fs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod exclude;
pub mod scan_project_dir;
pub mod project_dir;
69 changes: 52 additions & 17 deletions src/fs/scan_project_dir.rs → src/fs/project_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::fs::exclude::EXCLUDE_FILES_BY_NAME;
use futures_util::future::join_all;
use smol::fs::read_dir;
use smol::fs::{read_dir, File};
use smol::stream::StreamExt;
use std::fmt::Debug;
use std::os::unix::ffi::OsStrExt;
Expand All @@ -28,7 +28,7 @@ static I_HAVE_ALREADY_BEEN_RUN: OnceLock<bool> = OnceLock::new();
///
/// Subsequent calls to this function should not be made. For staying up to date
/// with file system changes, file system event monitoring should be used.
pub async fn scan_project_dir(project_dir: PathBuf) -> Result<(), Error> {
pub async fn scan_project_dir(project_dir: PathBuf) -> Result<TrackedProjectDir, Error> {
let exclude = EXCLUDE_FILES_BY_NAME
.get()
.ok_or(Error::ExcludeRulesNotInitialized)?;
Expand All @@ -41,22 +41,46 @@ pub async fn scan_project_dir(project_dir: PathBuf) -> Result<(), Error> {
scan_dir(project_dir, exclude).await
}

async fn scan_dir(dir: PathBuf, exclude: &TrieHard<'static, &str>) -> Result<(), Error> {
info!(?dir, "Scanning directory");
/// A regular file that we are tracking updates and changes for,
/// from the project directory tree.
#[derive(Debug)]
pub struct TrackedProjectFile {
/// Absolute path to file.
pub fpath: PathBuf,
/// Open file handle.
pub file: File,
}

/// A directory that we are tracking updates and changes for,
/// from the project directory tree.
#[derive(Debug)]
pub struct TrackedProjectDir {
/// Regular files in this directory.
pub tracked_files: Vec<TrackedProjectFile>,
/// Subdirectories in this directory.
pub tracked_dirs: Vec<TrackedProjectDir>,
}

async fn scan_dir(
dpath: PathBuf,
exclude: &TrieHard<'static, &str>,
) -> Result<TrackedProjectDir, Error> {
info!(?dpath, "Scanning directory");

let mut read_dir = read_dir(&dir).await?;
let mut read_dir = read_dir(&dpath).await?;

//let mut files = vec![];
let mut tracked_files = vec![];
//let mut dirs = vec![];

let mut subdir_futs = vec![];

while let Some(dir_entry) = read_dir.try_next().await? {
let file_name = dir_entry.file_name();
debug!(?file_name, ?dir, "A dir entry was read from directory.");
debug!(?file_name, ?dpath, "A dir entry was read from directory.");
if let Some(matched) = exclude.get(dir_entry.file_name().as_bytes()) {
info!(
file_name = matched,
?dir,
?dpath,
"Skipping file based on exclusion rules."
);
continue;
Expand Down Expand Up @@ -89,19 +113,30 @@ async fn scan_dir(dir: PathBuf, exclude: &TrieHard<'static, &str>) -> Result<(),
// TODO: ^
let file_type = dir_entry.file_type().await?;
if file_type.is_symlink() {
info!(?file_name, ?dir, "Skipping file because it is a symlink.");
info!(?file_name, ?dpath, "Skipping file because it is a symlink.");
continue;
}

if file_type.is_dir() {
let mut child_dir = dir.clone();
child_dir.push(file_name);
subdir_futs.push(scan_dir(child_dir, exclude));
} else if file_type.is_dir() {
let mut child_dpath = dpath.clone();
child_dpath.push(file_name);
subdir_futs.push(scan_dir(child_dpath, exclude));
} else if file_type.is_file() {
let mut fpath = dpath.clone();
fpath.push(file_name);
let file = File::open(&fpath).await?;
let tracked_file = TrackedProjectFile { fpath, file };
tracked_files.push(tracked_file);
} else {
unreachable!("The only three kinds of file type we know of is directory, symlink and regular file.");
}
}

let res: Result<Vec<_>, _> = join_all(subdir_futs).await.into_iter().collect();
res?;
let tracked_dirs = res?;

let tracked_dir = TrackedProjectDir {
tracked_files,
tracked_dirs,
};

Ok(())
Ok(tracked_dir)
}
9 changes: 7 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use futures_util::{select, FutureExt, TryStreamExt};
use http_body_util::{combinators::BoxBody, BodyExt, Either, Full, StreamBody};
use http_horse::fs::{
exclude::{exclude, EXCLUDE_FILES_BY_NAME},
scan_project_dir::scan_project_dir,
project_dir::scan_project_dir,
};
use hyper::{
body::{Frame, Incoming},
Expand All @@ -23,6 +23,7 @@ use smol::stream::StreamExt;
use smol::{fs::File, io::AsyncReadExt, net::TcpListener, Timer};
use smol_hyper::rt::{FuturesIo, SmolExecutor, SmolTimer};
use smol_macros::{main, Executor};
use std::time::Instant;
use std::{
io::ErrorKind,
net::{IpAddr, SocketAddr},
Expand Down Expand Up @@ -163,7 +164,11 @@ async fn main(ex: &Executor<'_>) -> anyhow::Result<()> {
.inspect_err(|e| error!(existing_value = ?e, "Fatal: OnceLock has existing value."))
.map_err(|_| anyhow!("Failed to set value of OnceLock."))?;

let project_files = ex.spawn(scan_project_dir(project_dir.clone())).await?;
let instant_start_scan = Instant::now();
let project_dir_tree = ex.spawn(scan_project_dir(project_dir.clone())).await?;
let t_spent_scanning = Instant::now() - instant_start_scan;
info!(?t_spent_scanning, "Finished initial scan of project dir.");
debug!(?project_dir_tree, "Project dir tree.");

// FsEvent takes strings as arguments. We always want to use the canonical path,
// and because of that we have to convert back to String from PathBuf.
Expand Down

0 comments on commit f8d5ead

Please sign in to comment.