use impl_prelude::*;
#[derive(Debug, Clone, Copy)]
pub struct CheckSize {
max_size: usize,
}
impl CheckSize {
pub fn new(max_size: usize) -> Self {
CheckSize {
max_size: max_size,
}
}
}
impl Check for CheckSize {
fn name(&self) -> &str {
"check-size"
}
fn check(&self, ctx: &CheckGitContext, commit: &Commit) -> Result<CheckResult> {
let mut result = CheckResult::new();
for diff in &commit.diffs {
if let StatusChange::Deleted = diff.status {
continue;
}
if diff.new_mode == "160000" {
continue;
}
let size_attr = ctx.check_attr("hooks-max-size", diff.name.as_path())?;
let max_size = match size_attr {
AttributeState::Unset => continue,
AttributeState::Value(ref v) => {
v.parse().unwrap_or_else(|_| {
result.add_error(format!("commit {} has an invalid value \
hooks-max-size={} for `{}`. The value must be \
an unsigned integer.",
commit.sha1,
v,
diff.name));
self.max_size
})
},
_ => self.max_size,
};
let cat_file = ctx.git()
.arg("cat-file")
.arg("-s")
.arg(diff.new_blob.as_str())
.output()
.chain_err(|| "failed to construct cat-file command")?;
if !cat_file.status.success() {
bail!(ErrorKind::Git(format!("failed to get the size of the {} blob: {}",
diff.new_blob,
String::from_utf8_lossy(&cat_file.stderr))));
}
let new_size: usize = String::from_utf8_lossy(&cat_file.stdout)
.trim()
.parse()
.unwrap_or_else(|msg| {
result.add_error(format!("commit {} has the file `{}` which has a size \
which did not parse: {}",
commit.sha1,
diff.name,
msg));
0
});
if new_size > max_size {
result.add_error(format!("commit {} creates blob {} at `{}` with size {} bytes \
({:.2} KiB) which is greater than the maximum size {} \
bytes ({:.2} KiB). If the file is intended to be \
committed, set the `hooks-max-size` attribute on its \
path.",
commit.sha1,
diff.new_blob,
diff.name,
new_size,
new_size as f64 / 1024.,
max_size,
max_size as f64 / 1024.));
}
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use checks::CheckSize;
use checks::test::*;
static CHECK_SIZE_COMMIT: &'static str = "1464c62cc09b01a8e86a8512dd400b705c760c42";
static ADD_SUBMODULE_TOPIC: &'static str = "fe90ee22ae3ce4b4dc41f8d0876e59355ff1e21c";
#[test]
fn test_check_size() {
let check = CheckSize::new(46);
let mut conf = GitCheckConfiguration::new();
conf.add_check(&check);
let result = test_check("test_check_size", CHECK_SIZE_COMMIT, &conf);
assert_eq!(result.warnings().len(), 0);
assert_eq!(result.alerts().len(), 0);
assert_eq!(result.errors().len(), 4);
assert_eq!(result.errors()[0],
"commit 1464c62cc09b01a8e86a8512dd400b705c760c42 creates blob \
921aae7a6949c74bc4bd53b4122fcd7ee3c819c6 at `no-value` with size 50 bytes \
(0.05 KiB) which is greater than the maximum size 46 bytes (0.04 KiB). If the \
file is intended to be committed, set the `hooks-max-size` attribute on its \
path.");
assert_eq!(result.errors()[1],
"commit 112e9b34401724bff57f68cf47c5065d4342b263 has an invalid value \
hooks-max-size=not-a-number for `bad-attr-value`. The value must be an \
unsigned integer.");
assert_eq!(result.errors()[2],
"commit a61fd3759b61a4a1f740f3fe656bc42151cefbdd creates blob \
293071f2f4dd15bb57904e08bf6529e748e4075a at `increased-limit` with size 273 \
bytes (0.27 KiB) which is greater than the maximum size 200 bytes (0.20 KiB). \
If the file is intended to be committed, set the `hooks-max-size` attribute \
on its path.");
assert_eq!(result.errors()[3],
"commit a61fd3759b61a4a1f740f3fe656bc42151cefbdd creates blob \
4fa03f0211ccd20b0285314d9469ccbee1edd81c at `large-file` with size 48 bytes \
(0.05 KiB) which is greater than the maximum size 46 bytes (0.04 KiB). If the \
file is intended to be committed, set the `hooks-max-size` attribute on its \
path.");
assert_eq!(result.temporary(), false);
assert_eq!(result.allowed(), false);
assert_eq!(result.pass(), false);
}
#[test]
fn test_check_size_submodule() {
let check = CheckSize::new(1024);
let mut conf = GitCheckConfiguration::new();
conf.add_check(&check);
let result = test_check("test_check_size_submodule", ADD_SUBMODULE_TOPIC, &conf);
assert_eq!(result.warnings().len(), 0);
assert_eq!(result.alerts().len(), 0);
assert_eq!(result.errors().len(), 0);
assert_eq!(result.temporary(), false);
assert_eq!(result.allowed(), false);
assert_eq!(result.pass(), true);
}
}