diff --git a/crates/text/src/fonts.rs b/crates/text/src/fonts.rs index c70fedc4..0c22e4e6 100644 --- a/crates/text/src/fonts.rs +++ b/crates/text/src/fonts.rs @@ -309,6 +309,9 @@ pub fn system_scanned() -> bool { /// A face by id (panics never: unknown ids give the default face). pub fn face(id: FaceId) -> Arc { let d = db().read().unwrap_or_else(|e| e.into_inner()); + // The bundled fonts are compiled in and always parse (`tests::bundled_families_and_resolution` checks them), so + // the database is never empty. + #[allow(clippy::expect_used)] d.faces.get(id).or_else(|| d.faces.first()).cloned().expect("bundled fonts present") } @@ -388,7 +391,8 @@ pub fn resolve(family: &str, style: &str) -> Resolved { return r; } } - let mut r = resolve_in(DEFAULT_FAMILY, style).expect("Inter is bundled"); + // Inter is bundled; face 0 (the first bundled face) is the last resort. + let mut r = resolve_in(DEFAULT_FAMILY, style).unwrap_or(Resolved { face: 0, synth_bold: false, synth_italic: false, missing: true }); r.missing = !family.is_empty() && !family.eq_ignore_ascii_case(DEFAULT_FAMILY); r } @@ -403,13 +407,10 @@ fn resolve_in(family: &str, style: &str) -> Option { return Some(Resolved { face: f.id, synth_bold: false, synth_italic: false, missing: false }); } let (w, it) = style_wants(style); - let best = fam - .iter() - .min_by_key(|f| { - let italic_pen = if f.info.italic == it { 0 } else { 1000 }; - italic_pen + (f.info.weight as i32 - w as i32).unsigned_abs() - }) - .expect("non-empty"); + let best = fam.iter().min_by_key(|f| { + let italic_pen = if f.info.italic == it { 0 } else { 1000 }; + italic_pen + (f.info.weight as i32 - w as i32).unsigned_abs() + })?; Some(Resolved { face: best.id, synth_bold: w >= 600 && best.info.weight <= 500, synth_italic: it && !best.info.italic, missing: false }) } diff --git a/crates/text/src/layout.rs b/crates/text/src/layout.rs index f90aa7f3..629013b7 100644 --- a/crates/text/src/layout.rs +++ b/crates/text/src/layout.rs @@ -776,7 +776,7 @@ fn paragraph(text: &str, base: usize, runs: &[(Range, TextStyle)], primar last_ok = Some(p); break; } - let b = last_ok.take().expect("checked"); + let Some(b) = last_ok.take() else { break }; ranges.push(start..b); start = b; } @@ -1015,9 +1015,9 @@ pub fn layout_rich_uncached(text: &str, runs: &[(Range, TextStyle)], para x0 = x0.min(0.0); x1 = x1.max(w); } - let first = &lay.lines[0]; - let last = lay.lines.last().expect("at least one line"); - lay.bounds = [x0, first.baseline - first.ascent, x1.max(x0), last.baseline + last.descent]; + if let (Some(first), Some(last)) = (lay.lines.first(), lay.lines.last()) { + lay.bounds = [x0, first.baseline - first.ascent, x1.max(x0), last.baseline + last.descent]; + } lay } diff --git a/crates/text/src/lib.rs b/crates/text/src/lib.rs index 65232f02..02c99052 100644 --- a/crates/text/src/lib.rs +++ b/crates/text/src/lib.rs @@ -5,6 +5,8 @@ //! - This module turns a layer's [`TextDoc`] into per-character glyph outlines (Bezier paths) with //! character / word / line indices, which is what text animators and selectors work on. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] + pub mod fonts; pub mod layout; pub mod path_text;