mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-02 16:30:50 -06:00
Sonar: explicit imports, fn paths for closures, extract helpers for cognitive complexity
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
72c1d57d1e
commit
4e86fe7ad3
@@ -123,6 +123,60 @@ impl Criterion {
|
||||
Criterion { op, operand, bare_empty: false }
|
||||
}
|
||||
|
||||
fn matches_blank(&self, v: &Value) -> bool {
|
||||
let blank = matches!(v, Value::Empty);
|
||||
match self.op {
|
||||
Op::Eq => blank,
|
||||
Op::Ne => !blank,
|
||||
// "<" / ">" with nothing: Excel compares against "" for text cells, but an
|
||||
// empty string never matches ("<=" and ">=" count it in neither).
|
||||
_ => match v {
|
||||
Value::Text(t) if !t.is_empty() => cmp_op(self.op, compare_text(t, "")),
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn matches_number(&self, v: &Value, n: f64) -> bool {
|
||||
let cell = match v {
|
||||
Value::Number(x) => Some(*x),
|
||||
// Numeric text matches only for equality (COUNTIF("1") counts text "1").
|
||||
Value::Text(t) if matches!(self.op, Op::Eq | Op::Ne) => parse_number_text(t),
|
||||
_ => None,
|
||||
};
|
||||
match cell {
|
||||
Some(x) => cmp_op(self.op, compare_numbers(x, n)),
|
||||
None => self.op == Op::Ne,
|
||||
}
|
||||
}
|
||||
|
||||
fn matches_text(&self, v: &Value, pat: &str) -> bool {
|
||||
match self.op {
|
||||
Op::Eq | Op::Ne => {
|
||||
// Blank cells hold no text: `"*"` skips them and `"<>*"` counts them.
|
||||
let cell_text = match v {
|
||||
Value::Text(t) => Some(t.to_string()),
|
||||
_ => None,
|
||||
};
|
||||
let m = cell_text.is_some_and(|t| wildcard_match(pat, &t));
|
||||
if self.op == Op::Eq { m } else { !m }
|
||||
}
|
||||
_ => match v {
|
||||
Value::Text(t) => cmp_op(self.op, compare_text(t, pat)),
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn matches_cut_text(&self, v: &Value, kept: &str) -> bool {
|
||||
match self.op {
|
||||
Op::Eq => false,
|
||||
Op::Ne => true,
|
||||
Op::Lt | Op::Le => matches!(v, Value::Text(t) if compare_text(t, kept) != Ordering::Greater),
|
||||
Op::Gt | Op::Ge => matches!(v, Value::Text(t) if compare_text(t, kept) == Ordering::Greater),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a cell value satisfies the criterion.
|
||||
pub(crate) fn matches(&self, v: &Value) -> bool {
|
||||
let v = match v {
|
||||
@@ -133,31 +187,8 @@ impl Criterion {
|
||||
return matches!(&v, Value::Empty) || matches!(&v, Value::Text(t) if t.is_empty());
|
||||
}
|
||||
match &self.operand {
|
||||
Operand::Blank => {
|
||||
let blank = matches!(v, Value::Empty);
|
||||
match self.op {
|
||||
Op::Eq => blank,
|
||||
Op::Ne => !blank,
|
||||
// "<" / ">" with nothing: Excel compares against "" for text cells, but an
|
||||
// empty string never matches ("<=" and ">=" count it in neither).
|
||||
_ => match &v {
|
||||
Value::Text(t) if !t.is_empty() => cmp_op(self.op, compare_text(t, "")),
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
Operand::Number(n) => {
|
||||
let cell = match &v {
|
||||
Value::Number(x) => Some(*x),
|
||||
// Numeric text matches only for equality (COUNTIF("1") counts text "1").
|
||||
Value::Text(t) if matches!(self.op, Op::Eq | Op::Ne) => parse_number_text(t),
|
||||
_ => None,
|
||||
};
|
||||
match cell {
|
||||
Some(x) => cmp_op(self.op, compare_numbers(x, *n)),
|
||||
None => self.op == Op::Ne,
|
||||
}
|
||||
}
|
||||
Operand::Blank => self.matches_blank(&v),
|
||||
Operand::Number(n) => self.matches_number(&v, *n),
|
||||
Operand::Bool(b) => match &v {
|
||||
Value::Bool(x) => cmp_op(self.op, x.cmp(b)),
|
||||
_ => self.op == Op::Ne,
|
||||
@@ -166,27 +197,8 @@ impl Criterion {
|
||||
Value::Error(x) => cmp_op(self.op, x.code().cmp(&e.code())),
|
||||
_ => self.op == Op::Ne,
|
||||
},
|
||||
Operand::Text(pat) => match self.op {
|
||||
Op::Eq | Op::Ne => {
|
||||
// Blank cells hold no text: `"*"` skips them and `"<>*"` counts them.
|
||||
let cell_text = match &v {
|
||||
Value::Text(t) => Some(t.to_string()),
|
||||
_ => None,
|
||||
};
|
||||
let m = cell_text.is_some_and(|t| wildcard_match(pat, &t));
|
||||
if self.op == Op::Eq { m } else { !m }
|
||||
}
|
||||
_ => match &v {
|
||||
Value::Text(t) => cmp_op(self.op, compare_text(t, pat)),
|
||||
_ => false,
|
||||
},
|
||||
},
|
||||
Operand::CutText(kept) => match self.op {
|
||||
Op::Eq => false,
|
||||
Op::Ne => true,
|
||||
Op::Lt | Op::Le => matches!(&v, Value::Text(t) if compare_text(t, kept) != Ordering::Greater),
|
||||
Op::Gt | Op::Ge => matches!(&v, Value::Text(t) if compare_text(t, kept) == Ordering::Greater),
|
||||
},
|
||||
Operand::Text(pat) => self.matches_text(&v, pat),
|
||||
Operand::CutText(kept) => self.matches_cut_text(&v, kept),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,7 +742,7 @@ fn im_binary(a: &[Arg], f: fn(Cx, Cx) -> R<Cx>) -> R<Value> {
|
||||
fmt_complex(f(x, y)?, s.unwrap_or('i'))
|
||||
}
|
||||
fn imdiv(a: &[Arg], _c: &mut dyn Ctx) -> R<Value> {
|
||||
im_binary(a, |x, y| x.div(y))
|
||||
im_binary(a, Cx::div)
|
||||
}
|
||||
fn imsub(a: &[Arg], _c: &mut dyn Ctx) -> R<Value> {
|
||||
im_binary(a, |x, y| Ok(x.sub(y)))
|
||||
|
||||
@@ -278,7 +278,7 @@ fn aggregate(kind: Aggregate, xs: &mut [f64]) -> Option<f64> {
|
||||
Aggregate::Max => xs.iter().copied().reduce(f64::max),
|
||||
Aggregate::Min => xs.iter().copied().reduce(f64::min),
|
||||
Aggregate::Median => {
|
||||
xs.sort_by(|a, b| a.total_cmp(b));
|
||||
xs.sort_by(f64::total_cmp);
|
||||
let k = xs.len() / 2;
|
||||
let hi = xs.get(k).copied()?;
|
||||
if xs.len() % 2 == 1 { Some(hi) } else { Some((xs.get(k.checked_sub(1)?).copied()? + hi) / 2.0) }
|
||||
@@ -415,7 +415,7 @@ impl Timeline {
|
||||
return None;
|
||||
}
|
||||
let mut sorted = times.to_vec();
|
||||
sorted.sort_by(|a, b| a.total_cmp(b));
|
||||
sorted.sort_by(f64::total_cmp);
|
||||
sorted.dedup();
|
||||
let axis = detect_axis(&sorted, sys).ok()?;
|
||||
let last = axis.position(*sorted.last()?)?.round();
|
||||
|
||||
@@ -201,7 +201,7 @@ impl FnSpec {
|
||||
pub(crate) const VAR: usize = usize::MAX;
|
||||
|
||||
fn registry() -> &'static (Vec<FnSpec>, HashMap<&'static str, usize>) {
|
||||
static REG: OnceLock<(Vec<FnSpec>, HashMap<&'static str, usize>)> = OnceLock::new();
|
||||
static REG: OnceLock<(Vec<FnSpec>, HashMap<&str, usize>)> = OnceLock::new();
|
||||
REG.get_or_init(|| {
|
||||
let mut v: Vec<FnSpec> = Vec::new();
|
||||
for list in [
|
||||
|
||||
@@ -43,7 +43,7 @@ fn var_p(x: &[f64]) -> R<f64> {
|
||||
}
|
||||
|
||||
fn sorted(mut v: Vec<f64>) -> Vec<f64> {
|
||||
v.sort_by(|a, b| a.total_cmp(b));
|
||||
v.sort_by(f64::total_cmp);
|
||||
v
|
||||
}
|
||||
|
||||
|
||||
@@ -523,7 +523,7 @@ fn find_matches(text: &[char], delims: &[Vec<char>], insensitive: bool, from_end
|
||||
let mut j = t.len();
|
||||
while j > 0 {
|
||||
let best =
|
||||
ds.iter().filter(|d| j.checked_sub(d.len()).and_then(|st| t.get(st..j)).is_some_and(|w| w == d.as_slice())).map(|d| d.len()).max();
|
||||
ds.iter().filter(|d| j.checked_sub(d.len()).and_then(|st| t.get(st..j)).is_some_and(|w| w == d.as_slice())).map(Vec::len).max();
|
||||
match best {
|
||||
Some(l) => {
|
||||
out.push((j - l, j));
|
||||
@@ -537,7 +537,7 @@ fn find_matches(text: &[char], delims: &[Vec<char>], insensitive: bool, from_end
|
||||
}
|
||||
let mut i = 0;
|
||||
while i < t.len() {
|
||||
let best = ds.iter().filter(|d| t.get(i..i + d.len()).is_some_and(|w| w == d.as_slice())).map(|d| d.len()).max();
|
||||
let best = ds.iter().filter(|d| t.get(i..i + d.len()).is_some_and(|w| w == d.as_slice())).map(Vec::len).max();
|
||||
match best {
|
||||
Some(l) => {
|
||||
out.push((i, i + l));
|
||||
@@ -588,7 +588,7 @@ fn textafter(a: &[Arg], _c: &mut dyn Ctx) -> R<Value> {
|
||||
}
|
||||
|
||||
fn split_by(s: &[char], delims: &[Vec<char>], insensitive: bool) -> Vec<Vec<char>> {
|
||||
if delims.iter().all(|d| d.is_empty()) {
|
||||
if delims.iter().all(Vec::is_empty) {
|
||||
return vec![s.to_vec()];
|
||||
}
|
||||
let mut out = Vec::new();
|
||||
@@ -748,9 +748,9 @@ fn convert_replacement(r: &str) -> String {
|
||||
let mut i = 0;
|
||||
while i < c.len() {
|
||||
let ch = c[i];
|
||||
if ch == '$' && c.get(i + 1).is_some_and(|d| d.is_ascii_digit()) {
|
||||
if ch == '$' && c.get(i + 1).is_some_and(char::is_ascii_digit) {
|
||||
let mut j = i + 1;
|
||||
while c.get(j).is_some_and(|d| d.is_ascii_digit()) {
|
||||
while c.get(j).is_some_and(char::is_ascii_digit) {
|
||||
j += 1;
|
||||
}
|
||||
out.push_str("${");
|
||||
|
||||
Reference in New Issue
Block a user