use super::super::*;
#[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;
}
let size_attr = try!(ctx.check_attr("hooks-max-size", diff.name.as_ref()));
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_short,
v,
diff.name));
self.max_size
})
},
_ => self.max_size,
};
let cat_file = try!(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_short,
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_short,
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 super::CheckSize;
use super::super::test::*;
static CHECK_SIZE_COMMIT: &'static str = "1464c62cc09b01a8e86a8512dd400b705c760c42";
#[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 1464c62 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 112e9b3 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 a61fd37 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 a61fd37 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.allowed(), false);
assert_eq!(result.pass(), false);
}
}