Skip to content

Commit 1967ff9

Browse files
committed
temp
1 parent 8d3b18d commit 1967ff9

File tree

8 files changed

+61
-22
lines changed

8 files changed

+61
-22
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/tmc-langs-cli/src/output.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
44
use std::path::PathBuf;
55
use tmc_langs::{
66
CombinedCourseData, ConfigValue, DownloadOrUpdateCourseExercisesResult, ExerciseDesc,
7-
ExerciseDownload, ExercisePackagingConfiguration, LocalExercise, LocalMoocExercise,
8-
LocalTmcExercise, RunResult, StyleValidationResult, TmcConfig, UpdatedExercise, mooc,
7+
ExerciseDownload, ExercisePackagingConfiguration, LocalMoocExercise, LocalTmcExercise,
8+
RunResult, StyleValidationResult, TmcConfig, UpdatedExercise, mooc,
99
notification_reporter::Notification,
1010
tmc::{
1111
ClientUpdateData, Token, UpdateResult,

crates/tmc-langs-framework/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ rust-version.workspace = true
1010
tmc-langs-util.workspace = true
1111
ts-rs = { workspace = true, features = ["serde-compat"], optional = true }
1212

13+
blake3 = "1.4.0"
1314
fd-lock = "4.0.0"
1415
isolang = "2.1.0"
1516
log = "0.4.14"

crates/tmc-langs-framework/src/archive.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
//! Contains types that abstract over the various archive formats.
22
33
use crate::TmcError;
4+
use blake3::{Hash, Hasher};
45
use serde::Deserialize;
56
use std::{
67
fmt::Display,
78
io::{BufReader, Cursor, Read, Seek, Write},
89
ops::ControlFlow::{self, Break},
10+
os::unix::ffi::OsStrExt,
911
path::{Path, PathBuf},
1012
str::FromStr,
1113
};
@@ -92,9 +94,9 @@ impl<T: Read + Seek> Archive<T> {
9294
return Ok(Entry::Tar(entry));
9395
}
9496
}
95-
Err(TmcError::TarRead(std::io::Error::other(
96-
format!("Could not find {path} in tar"),
97-
)))
97+
Err(TmcError::TarRead(std::io::Error::other(format!(
98+
"Could not find {path} in tar"
99+
))))
98100
}
99101
Self(ArchiveInner::TarZstd(archive)) => {
100102
for entry in archive.entries().map_err(TmcError::TarRead)? {
@@ -103,9 +105,9 @@ impl<T: Read + Seek> Archive<T> {
103105
return Ok(Entry::TarZstd(entry));
104106
}
105107
}
106-
Err(TmcError::TarRead(std::io::Error::other(
107-
format!("Could not find {path} in tar"),
108-
)))
108+
Err(TmcError::TarRead(std::io::Error::other(format!(
109+
"Could not find {path} in tar"
110+
))))
109111
}
110112
Self(ArchiveInner::Zip(archive)) => {
111113
archive.by_name(path).map(Entry::Zip).map_err(Into::into)
@@ -292,17 +294,23 @@ pub enum Compression {
292294
}
293295

294296
impl Compression {
295-
pub fn compress(self, path: &Path) -> Result<Vec<u8>, TmcError> {
297+
pub fn compress(self, path: &Path, hash: bool) -> Result<(Vec<u8>, Option<Hash>), TmcError> {
298+
let mut hasher = if hash { Some(Hasher::new()) } else { None };
296299
let buf = match self {
297300
Self::Tar => {
298301
let buf = Cursor::new(Vec::new());
299302
let mut builder = tar::Builder::new(buf);
300303
walk_dir_for_compression(path, |entry, relative_path| {
301304
if entry.path().is_dir() {
305+
hasher.as_mut().map(|h| h.update(relative_path.as_bytes()));
302306
builder
303307
.append_dir(relative_path, entry.path())
304308
.map_err(TmcError::TarWrite)?;
305309
} else if entry.path().is_file() {
310+
if let Some(h) = &mut hasher {
311+
let file = file_util::read_file(entry.path())?;
312+
h.update(&file);
313+
}
306314
builder
307315
.append_path_with_name(entry.path(), relative_path)
308316
.map_err(TmcError::TarWrite)?;
@@ -319,10 +327,12 @@ impl Compression {
319327
let mut writer = zip::ZipWriter::new(buf);
320328
walk_dir_for_compression(path, |entry, relative_path| {
321329
if entry.path().is_dir() {
330+
hasher.as_mut().map(|h| h.update(relative_path.as_bytes()));
322331
writer.add_directory(relative_path, SimpleFileOptions::default())?;
323332
} else if entry.path().is_file() {
324-
writer.start_file(relative_path, SimpleFileOptions::default())?;
325333
let contents = file_util::read_file(entry.path())?;
334+
hasher.as_mut().map(|h| h.update(&contents));
335+
writer.start_file(relative_path, SimpleFileOptions::default())?;
326336
writer
327337
.write_all(&contents)
328338
.map_err(|err| TmcError::ZipWrite(path.to_path_buf(), err))?;
@@ -336,10 +346,15 @@ impl Compression {
336346
let mut builder = tar::Builder::new(tar_buf);
337347
walk_dir_for_compression(path, |entry, relative_path| {
338348
if entry.path().is_dir() {
349+
hasher.as_mut().map(|h| h.update(relative_path.as_bytes()));
339350
builder
340351
.append_dir(relative_path, entry.path())
341352
.map_err(TmcError::TarWrite)?;
342353
} else if entry.path().is_file() {
354+
if let Some(h) = &mut hasher {
355+
let file = file_util::read_file(entry.path())?;
356+
h.update(&file);
357+
}
343358
builder
344359
.append_path_with_name(entry.path(), relative_path)
345360
.map_err(TmcError::TarWrite)?;
@@ -350,7 +365,8 @@ impl Compression {
350365
zstd::stream::encode_all(tar_buf.as_slice(), 0).map_err(TmcError::ZstdWrite)?
351366
}
352367
};
353-
Ok(buf)
368+
let hash = hasher.map(|h| h.finalize());
369+
Ok((buf, hash))
354370
}
355371
}
356372

crates/tmc-langs-plugins/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ tmc-langs-notests.workspace = true
1616
tmc-langs-python3.workspace = true
1717
tmc-langs-r.workspace = true
1818

19+
blake3 = "1.4.0"
1920
log = "0.4.14"
2021
tar = "0.4.38"
2122
thiserror = "2.0.3"

crates/tmc-langs-plugins/src/compression.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Contains functions for compressing and uncompressing projects.
22
33
use crate::archive::ArchiveBuilder;
4+
use blake3::{Hash, Hasher};
45
use std::{
56
io::{Cursor, Read, Seek},
67
path::{Path, PathBuf},
@@ -17,7 +18,9 @@ pub fn compress_student_files(
1718
root_directory: &Path,
1819
compression: Compression,
1920
deterministic: bool,
20-
) -> Result<Vec<u8>, TmcError> {
21+
hash: bool,
22+
) -> Result<(Vec<u8>, Option<Hash>), TmcError> {
23+
let mut hasher = if hash { Some(Hasher::new()) } else { None };
2124
let mut writer = ArchiveBuilder::new(Cursor::new(vec![]), compression, deterministic);
2225

2326
for entry in WalkDir::new(root_directory)
@@ -42,14 +45,21 @@ pub fn compress_student_files(
4245
})
4346
.unwrap_or_else(|| entry.path());
4447
if entry.path().is_dir() {
45-
writer.add_directory(entry.path(), &path_to_zip_compatible_string(path))?;
48+
let path_in_archive = path_to_zip_compatible_string(path);
49+
hasher
50+
.as_mut()
51+
.map(|h| h.update(path_in_archive.as_bytes()));
52+
writer.add_directory(entry.path(), &path_in_archive)?;
4653
} else {
54+
let contents = file_util::read_file(entry.path())?;
55+
hasher.as_mut().map(|h| h.update(&contents));
4756
writer.add_file(entry.path(), &path_to_zip_compatible_string(path))?;
4857
}
4958
}
5059
}
60+
let hash = hasher.map(|h| h.finalize());
5161
let cursor = writer.finish()?;
52-
Ok(cursor.into_inner())
62+
Ok((cursor.into_inner(), hash))
5363
}
5464

5565
// ensures the / separator is used
@@ -218,11 +228,12 @@ mod test {
218228
File::create(missing_file_path).unwrap();
219229

220230
let path = temp.path().join("exercise-name");
221-
let zipped = compress_student_files(
231+
let (zipped, _hash) = compress_student_files(
222232
&EverythingIsStudentFilePolicy::new(&path).unwrap(),
223233
&path,
224234
Compression::Zip,
225235
true,
236+
false,
226237
)
227238
.unwrap();
228239
let mut archive = ZipArchive::new(Cursor::new(zipped)).unwrap();

crates/tmc-langs-plugins/src/lib.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod archive;
66
pub mod compression;
77
mod error;
88

9+
use blake3::Hash;
910
pub use error::PluginError;
1011
use std::{
1112
io::{Read, Seek},
@@ -52,15 +53,22 @@ pub fn compress_project(
5253
compression: Compression,
5354
deterministic: bool,
5455
naive: bool,
55-
) -> Result<Vec<u8>, PluginError> {
56-
let compressed = if naive {
57-
compression.compress(path)?
56+
hash: bool,
57+
) -> Result<(Vec<u8>, Option<Hash>), PluginError> {
58+
let (compressed, hash) = if naive {
59+
compression.compress(path, hash)?
5860
} else {
5961
let policy = get_student_file_policy(path)?;
60-
compression::compress_student_files(policy.as_ref(), path, compression, deterministic)?
62+
compression::compress_student_files(
63+
policy.as_ref(),
64+
path,
65+
compression,
66+
deterministic,
67+
hash,
68+
)?
6169
};
6270

63-
Ok(compressed)
71+
Ok((compressed, hash))
6472
}
6573

6674
/// Enum containing variants for each language plugin.

crates/tmc-langs/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,8 +820,8 @@ pub fn compress_project_to(
820820
compression
821821
);
822822

823-
let data = tmc_langs_plugins::compress_project(source, compression, deterministic, naive)?;
824-
let hash = blake3::hash(&data);
823+
let (data, hash) =
824+
tmc_langs_plugins::compress_project(source, compression, deterministic, naive)?;
825825
file_util::write_to_file(data, target)?;
826826
Ok(hash.to_string())
827827
}

0 commit comments

Comments
 (0)