use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Filter {
BlobNone,
BlobLimit(u64),
TreeDepth(usize),
}
impl Filter {
pub fn parse(spec: &str) -> Result<Self> {
let unsupported = |reason| Error::UnsupportedFilter {
spec: spec.to_owned(),
reason,
};
if spec == "blob:none" {
return Ok(Filter::BlobNone);
}
if let Some(limit) = spec.strip_prefix("blob:limit=") {
return parse_size(limit).map(Filter::BlobLimit).ok_or_else(|| {
unsupported("the size is not a number with an optional k/m/g suffix")
});
}
if let Some(depth) = spec.strip_prefix("tree:") {
return depth
.parse::<usize>()
.map(Filter::TreeDepth)
.map_err(|_| unsupported("the depth is not a number"));
}
if spec.starts_with("combine:") {
return Err(unsupported("gunnar implements one filter at a time"));
}
Err(unsupported(
"gunnar implements blob:none, blob:limit=<n> and tree:<depth>",
))
}
pub(crate) fn keeps_tree(&self, depth: usize) -> bool {
match self {
Filter::BlobNone | Filter::BlobLimit(_) => true,
Filter::TreeDepth(max) => depth <= *max,
}
}
pub(crate) fn blob_limit(&self) -> Option<u64> {
match self {
Filter::BlobLimit(limit) => Some(*limit),
_ => None,
}
}
pub(crate) fn keeps_any_blob(&self) -> bool {
match self {
Filter::BlobNone => false,
Filter::BlobLimit(_) => true,
Filter::TreeDepth(max) => *max > 0,
}
}
}
fn parse_size(text: &str) -> Option<u64> {
let (digits, scale) = match text.as_bytes().last()? {
b'k' | b'K' => (&text[..text.len() - 1], 1024),
b'm' | b'M' => (&text[..text.len() - 1], 1024 * 1024),
b'g' | b'G' => (&text[..text.len() - 1], 1024 * 1024 * 1024),
_ => (text, 1),
};
digits.parse::<u64>().ok()?.checked_mul(scale)
}