1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
// TODO: make recommendations for the above
use anyhow::{
Context,
Result,
};
use futures::{
future::try_join_all,
stream::FuturesUnordered,
};
use tokio::{
fs::OpenOptions,
task::JoinError,
};
use walkdir::WalkDir;
use crate::{
constants::{
BUILD_DIR,
LIB_DIR,
ROOT_DIR,
RUNTIME,
SOURCE_DIR,
TEST_DIR,
},
java::{
FileType,
Project,
},
};
impl Project {
/// Checks the project for common CodingRooms errors
pub fn check_health(&self) -> Result<()> {
tracing::info!("Checking Project Health...");
let project = Project::new()?;
let rt = RUNTIME.handle().clone();
let _guard = rt.enter();
let handle1 = rt.spawn(async {
let files = WalkDir::new(ROOT_DIR.as_path())
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.map(|e| e.path().to_path_buf())
.map(|path| {
tokio::spawn(async move {
match tokio::fs::metadata(path.clone()).await {
Ok(m) => {
if m.len() == 0 {
tracing::warn!("File {}\n\tis empty", &path.display())
}
if let Err(e) =
OpenOptions::new().read(true).write(true).open(&path).await
{
tracing::warn!(
"File {}\n\tcould not be opened (read + write): {}",
&path.display(),
e
)
}
}
Err(e) => {
tracing::warn!("Could not read file {}: {}", path.display(), e)
}
};
if path.extension().unwrap_or_default() == "jar" {
let output = tokio::process::Command::new("zip")
.arg("-T")
.arg(&path)
.output()
.await
.unwrap_or_else(|_| {
panic!("Could not run zip -T on {}", &path.display())
});
if !output.status.success() {
tracing::warn!(
"File {}\n\tis not a valid zip file: {}",
&path.display(),
String::from_utf8_lossy(&output.stderr)
)
}
}
})
})
.collect::<FuturesUnordered<_>>();
try_join_all(files).await
});
let handle2 = rt.spawn(async move {
let files = project
.files()
.iter()
.map(|file| {
let file = file.clone();
tokio::spawn(async move {
if file.package_name().is_none() {
tracing::warn!(
"File {}\n\tdoesn't belong to any package",
file.path().display()
);
} else {
let expected_path = if let FileType::Test = file.kind() {
TEST_DIR.join(file.package_name().unwrap())
} else {
SOURCE_DIR.join(file.package_name().unwrap())
};
if file.path().parent().unwrap_or(&ROOT_DIR) != expected_path.as_path()
{
tracing::warn!(
"File {}\n\tis in the wrong directory.\n\t\tExpected: \
{}\n\t\tFound: {}",
file.path().display(),
expected_path.display(),
file.path().parent().unwrap_or(&ROOT_DIR).to_string_lossy()
);
}
}
})
})
.collect::<FuturesUnordered<_>>();
try_join_all(files).await
});
rt.block_on(async {
if BUILD_DIR.join(".vscode").exists() {
tokio::fs::remove_dir_all(BUILD_DIR.join(".vscode").as_path())
.await
.with_context(|| {
format!("Could not delete {}", BUILD_DIR.join(".vscode").display())
})
.unwrap();
}
if BUILD_DIR.join(LIB_DIR.display().to_string()).exists() {
tokio::fs::remove_dir_all(BUILD_DIR.join(LIB_DIR.display().to_string()).as_path())
.await
.with_context(|| {
format!(
"Could not delete {}",
BUILD_DIR.join(LIB_DIR.display().to_string()).display()
)
})
.unwrap();
}
let handles = FuturesUnordered::from_iter(vec![handle1, handle2]);
try_join_all(handles).await
})?
.into_iter()
.collect::<Result<Vec<Vec<()>>, JoinError>>()?;
tracing::info!(
"This is information an instructor can use to help you, please don't try to interpret \
it yourself or make any changes to your submission based on it."
);
Ok(())
}
}