sonar: method paths instead of closures (rust:S1612)

Poison recovery, OsStr::to_str and char predicates only; S4325 casts are
f64 to f32 and stay.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Dylan M. Taylor
2026-10-24 08:17:16 +00:00
co-authored by Claude Sonnet 5.5
parent eb5108786b
commit 4e9fa51f12
38 changed files with 98 additions and 96 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ struct State {
static STATE: LazyLock<Mutex<State>> = LazyLock::new(Mutex::default);
fn state() -> std::sync::MutexGuard<'static, State> {
STATE.lock().unwrap_or_else(|e| e.into_inner())
STATE.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
}
/// The web app's Media Browser.
+4 -4
View File
@@ -48,14 +48,14 @@ pub fn list() -> Vec<(String, usize)> {
/// The next write of `path` goes to browser storage only (no download).
pub fn save_quietly(path: &str) {
QUIET.lock().unwrap_or_else(|e| e.into_inner()).push(path.to_string());
QUIET.lock().unwrap_or_else(std::sync::PoisonError::into_inner).push(path.to_string());
}
/// A rendered file (from the page's exporter or a worker): kept in the table unpersisted and
/// downloaded at the next [`flush_downloads`].
pub fn add_output(path: &str, data: Arc<[u8]>) {
STORE.put_file(path, data, false);
OUTPUTS.lock().unwrap_or_else(|e| e.into_inner()).push(path.to_string());
OUTPUTS.lock().unwrap_or_else(std::sync::PoisonError::into_inner).push(path.to_string());
}
/// The table path for a picked/dropped file name.
@@ -120,7 +120,7 @@ impl Services for WebServices {
fn write_file(&self, path: &str, data: &[u8]) -> std::io::Result<()> {
put(path, data.into());
let quiet = {
let mut q = QUIET.lock().unwrap_or_else(|e| e.into_inner());
let mut q = QUIET.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let n = q.len();
q.retain(|p| p != path);
q.len() != n
@@ -184,7 +184,7 @@ pub fn export_sink() -> Arc<effectcraft_export::Sink> {
/// Download the render outputs written since the last call.
pub fn flush_downloads() {
let paths = std::mem::take(&mut *OUTPUTS.lock().unwrap_or_else(|e| e.into_inner()));
let paths = std::mem::take(&mut *OUTPUTS.lock().unwrap_or_else(std::sync::PoisonError::into_inner));
let mut out: Vec<(String, Arc<[u8]>)> = paths.into_iter().filter_map(|p| get(&p).map(|d| (file_name(&p).to_string(), d))).collect();
out.sort_by(|a, b| a.0.cmp(&b.0));
out.dedup_by(|a, b| a.0 == b.0);
+1 -1
View File
@@ -181,7 +181,7 @@ impl Default for Store {
impl Store {
pub fn lock(&self) -> std::sync::MutexGuard<'_, Mirror> {
self.mirror.lock().unwrap_or_else(|e| e.into_inner())
self.mirror.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
}
fn now(&self) -> f64 {
+1 -1
View File
@@ -50,7 +50,7 @@ fn app_icon() -> egui::IconData {
fn is_project_file(path: &str) -> bool {
std::path::Path::new(path)
.extension()
.and_then(|ext| ext.to_str())
.and_then(std::ffi::OsStr::to_str)
.is_some_and(|ext| ext.eq_ignore_ascii_case("ecproj") || ext.eq_ignore_ascii_case("ecprojx"))
}
+1 -1
View File
@@ -177,7 +177,7 @@ impl ColorSpace {
/// Parse an id or label (case-insensitive, loose).
pub fn parse(s: &str) -> Option<ColorSpace> {
let k: String = s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::<String>().to_ascii_lowercase();
let k: String = s.chars().filter(char::is_ascii_alphanumeric).collect::<String>().to_ascii_lowercase();
match k.as_str() {
"srgb" | "srgbiec6196621" | "srgbiec61966" | "iec6196621" => Some(ColorSpace::Srgb),
"rec709" | "bt709" | "hdtvrec709" | "hdtv" | "709" => Some(ColorSpace::Rec709),
+2 -2
View File
@@ -136,11 +136,11 @@ type Cache<T> = Mutex<HashMap<u64, Arc<T>>>;
fn cached<T>(c: &'static OnceLock<Cache<T>>, json: &str, parse: impl Fn(&str) -> Option<T>) -> Option<Arc<T>> {
let key = fnv(json.as_bytes());
let c = c.get_or_init(Default::default);
if let Some(a) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&key) {
if let Some(a) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key) {
return Some(a.clone());
}
let a = Arc::new(parse(json)?);
let mut m = c.lock().unwrap_or_else(|e| e.into_inner());
let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if m.len() > 8 {
m.clear();
}
+2 -2
View File
@@ -315,11 +315,11 @@ fn frozen_cache(j: &str) -> Option<Arc<FrozenCache>> {
let mut h = 0xcbf2_9ce4_8422_2325;
fnv(&mut h, j.as_bytes());
let c = C.get_or_init(Default::default);
if let Some(d) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&h) {
if let Some(d) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&h) {
return Some(d.clone());
}
let d = Arc::new(FrozenCache::from_json(j)?);
let mut m = c.lock().unwrap_or_else(|e| e.into_inner());
let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if m.len() > 8 {
m.clear();
}
+1 -1
View File
@@ -617,7 +617,7 @@ pub fn load_file(src: &str) -> Option<Arc<FileXform>> {
let parsed = if inline {
parse_file(src, "")
} else {
let ext = std::path::Path::new(src_t).extension().and_then(|e| e.to_str()).unwrap_or("");
let ext = std::path::Path::new(src_t).extension().and_then(std::ffi::OsStr::to_str).unwrap_or("");
std::fs::read_to_string(src_t).ok().and_then(|t| parse_file(&t, ext))
}
.map(Arc::new);
+8 -8
View File
@@ -132,11 +132,11 @@ pub fn data(params: &Params) -> Arc<RotoData> {
let mut h = 0xcbf2_9ce4_8422_2325;
fnv(&mut h, j.as_bytes());
let c = C.get_or_init(Default::default);
if let Some(d) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&h) {
if let Some(d) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&h) {
return d.clone();
}
let d = Arc::new(RotoData::from_json(j));
let mut m = c.lock().unwrap_or_else(|e| e.into_inner());
let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if m.len() > 64 {
m.clear();
}
@@ -247,11 +247,11 @@ fn cache() -> &'static Mutex<SegCache> {
/// A cached segmentation (chain key, scale).
pub fn cached(key: u64, scale: f64) -> Option<Arc<FrameSeg>> {
cache().lock().unwrap_or_else(|e| e.into_inner()).map.get(&(key, scale.to_bits())).cloned()
cache().lock().unwrap_or_else(std::sync::PoisonError::into_inner).map.get(&(key, scale.to_bits())).cloned()
}
pub fn store(key: u64, scale: f64, seg: Arc<FrameSeg>) {
let mut c = cache().lock().unwrap_or_else(|e| e.into_inner());
let mut c = cache().lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let k = (key, scale.to_bits());
if c.map.contains_key(&k) {
return;
@@ -301,7 +301,7 @@ fn from_store(key: u64, scale: f64) -> Option<Arc<FrameSeg>> {
/// Drop the cached segmentations of these chain keys (every scale).
pub fn forget(keys: impl IntoIterator<Item = u64>) {
let keys: std::collections::HashSet<u64> = keys.into_iter().collect();
let mut c = cache().lock().unwrap_or_else(|e| e.into_inner());
let mut c = cache().lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let gone: Vec<(u64, u64)> = c.map.keys().filter(|k| keys.contains(&k.0)).copied().collect();
for k in gone {
if let Some(s) = c.map.remove(&k) {
@@ -313,7 +313,7 @@ pub fn forget(keys: impl IntoIterator<Item = u64>) {
/// Drop every cached segmentation (Edit ▸ Purge).
pub fn purge() {
let mut c = cache().lock().unwrap_or_else(|e| e.into_inner());
let mut c = cache().lock().unwrap_or_else(std::sync::PoisonError::into_inner);
c.map.clear();
c.order.clear();
c.bytes = 0;
@@ -532,11 +532,11 @@ fn frozen(params: &Params) -> Option<Arc<FrozenCache>> {
let mut h = 0xcbf2_9ce4_8422_2325;
fnv(&mut h, j.as_bytes());
let c = C.get_or_init(Default::default);
if let Some(d) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&h) {
if let Some(d) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&h) {
return Some(d.clone());
}
let d = Arc::new(FrozenCache::from_json(j)?);
let mut m = c.lock().unwrap_or_else(|e| e.into_inner());
let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if m.len() > 8 {
m.clear();
}
+2 -2
View File
@@ -537,7 +537,7 @@ impl<S: Clone> SimCache<S> {
pub fn run(&self, key: u64, target: u64, init: impl FnOnce() -> S, mut step: impl FnMut(&mut S, u64)) -> std::sync::Arc<S> {
use std::sync::Arc;
let start = {
let mut g = self.inner.lock().unwrap_or_else(|e| e.into_inner());
let mut g = self.inner.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let tick = g.iter().map(|e| e.used).max().unwrap_or(0) + 1;
g.iter_mut().find(|e| e.key == key).and_then(|e| {
e.used = tick;
@@ -563,7 +563,7 @@ impl<S: Clone> SimCache<S> {
}
let out = Arc::new(st);
fresh.push((target, out.clone()));
let mut g = self.inner.lock().unwrap_or_else(|e| e.into_inner());
let mut g = self.inner.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let tick = g.iter().map(|e| e.used).max().unwrap_or(0) + 1;
let idx = match g.iter().position(|e| e.key == key) {
Some(ix) => ix,
+7 -7
View File
@@ -191,11 +191,11 @@ fn analysis_cached(json: &str) -> Option<(u64, Arc<WarpAnalysis>)> {
static C: OnceLock<AnalysisCache> = OnceLock::new();
let key = fnv(json.as_bytes());
let c = C.get_or_init(Default::default);
if let Some(a) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&key) {
if let Some(a) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key) {
return Some((key, a.clone()));
}
let a = Arc::new(WarpAnalysis::from_json(json)?);
let mut m = c.lock().unwrap_or_else(|e| e.into_inner());
let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if m.len() > 8 {
m.clear();
}
@@ -294,7 +294,7 @@ pub fn store_summary(key: [u64; 2], summary: PlanSummary) {
}
fn keep_summary(key: (u64, u64), summary: Arc<PlanSummary>) {
let mut m = summaries().lock().unwrap_or_else(|e| e.into_inner());
let mut m = summaries().lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if m.len() > 32 {
m.clear();
}
@@ -305,7 +305,7 @@ fn keep_summary(key: (u64, u64), summary: Arc<PlanSummary>) {
/// cached (seconds for a long Subspace Warp clip). `None` when not analysed.
pub fn summary(params: &Params) -> Option<([u64; 2], Arc<PlanSummary>)> {
let (key, a, st) = plan_key(params)?;
let cached = summaries().lock().unwrap_or_else(|e| e.into_inner()).get(&key).cloned();
let cached = summaries().lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key).cloned();
let sum = match cached {
Some(s) => s,
None => {
@@ -333,7 +333,7 @@ pub fn summary_at(params: &Params, time: f64) -> Option<(usize, Arc<PlanSummary>
pub fn cached_summary_at(params: &Params, time: f64) -> Option<(usize, Arc<PlanSummary>)> {
let (key, a, _) = plan_key(params)?;
let k = a.frame_at(time)?;
let sum = summaries().lock().unwrap_or_else(|e| e.into_inner()).get(&key).cloned();
let sum = summaries().lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key).cloned();
let sum = match sum {
Some(s) => s,
None => {
@@ -445,13 +445,13 @@ fn render(ctx: &EffectCtx, buf: Buf) -> Buf {
}
}
if !local.is_empty() {
let mut n = need_m.lock().unwrap_or_else(|e| e.into_inner());
let mut n = need_m.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
for (x, w) in local {
n[y * ow as usize + x] = w;
}
}
});
let need = need_m.into_inner().unwrap_or_else(|e| e.into_inner());
let need = need_m.into_inner().unwrap_or_else(std::sync::PoisonError::into_inner);
if synth && need.iter().any(|w| *w > 0.0) {
synthesize(ctx, &a, k, &src_of, &crop, sc, &mut out, &need);
}
+1 -1
View File
@@ -847,7 +847,7 @@ fn original_path(s: &Session) -> Option<String> {
/// importer takes. Anything else (a folder or app bundle, a script, a shortcut) is refused.
pub(crate) fn openable_original(path: &str) -> std::result::Result<(), String> {
let media = |p: &std::path::Path| {
let ext = p.extension().and_then(|e| e.to_str()).map(str::to_ascii_lowercase).unwrap_or_default();
let ext = p.extension().and_then(std::ffi::OsStr::to_str).map(str::to_ascii_lowercase).unwrap_or_default();
crate::media_browser::kind_of(&p.to_string_lossy()).is_some_and(|k| k != "project") || matches!(ext.as_str(), "pdf" | "ai" | "eps")
};
let name = std::path::Path::new(path).file_name().map(|n| n.to_string_lossy().into_owned()).unwrap_or_else(|| path.to_string());
+1 -1
View File
@@ -560,7 +560,7 @@ pub const DATA_EXTENSIONS: &[&str] = &["json", "csv", "tsv"];
/// A data footage item for `path` (JSON / CSV / TSV), `None` for other files.
pub(crate) fn data_footage(services: &dyn crate::Services, path: &str) -> Option<std::result::Result<effectcraft_project::Footage, String>> {
let ext = std::path::Path::new(path).extension().and_then(|e| e.to_str()).unwrap_or("").to_ascii_lowercase();
let ext = std::path::Path::new(path).extension().and_then(std::ffi::OsStr::to_str).unwrap_or("").to_ascii_lowercase();
if !DATA_EXTENSIONS.contains(&ext.as_str()) {
return None;
}
+1 -1
View File
@@ -708,7 +708,7 @@ const BUNDLE_EXTENSIONS: &[&str] = &[
/// Whether the system file manager would open folder `dir` as something other than a folder: a
/// bundle (`Tool.app`), or a Windows folder with a `{CLSID}` extension.
fn opens_as_bundle(dir: &std::path::Path) -> bool {
let ext = dir.extension().and_then(|e| e.to_str()).unwrap_or_default().to_ascii_lowercase();
let ext = dir.extension().and_then(std::ffi::OsStr::to_str).unwrap_or_default().to_ascii_lowercase();
ext.starts_with('{') || BUNDLE_EXTENSIONS.contains(&ext.as_str())
}
+1 -1
View File
@@ -107,7 +107,7 @@ impl Default for PaintOptions {
}
fn norm(s: &str) -> String {
s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::<String>().to_ascii_lowercase()
s.chars().filter(char::is_ascii_alphanumeric).collect::<String>().to_ascii_lowercase()
}
/// Index of `v` in `names` (by number or case/space-insensitive name).
+1 -1
View File
@@ -103,7 +103,7 @@ fn run(s: &mut Session, p: &Value) -> Result<Value> {
}
let supported = matches!(old.kind, FootageKind::Still | FootageKind::Sequence | FootageKind::Video | FootageKind::Audio)
&& old.layer.is_none() && old.page == 0;
let name = Path::new(&old.path).file_name().and_then(|n| n.to_str()).unwrap_or("");
let name = Path::new(&old.path).file_name().and_then(std::ffi::OsStr::to_str).unwrap_or("");
let paths = index.get(name).cloned().unwrap_or_default();
let mut candidates = vec![];
let mut accepted = vec![];
+2 -2
View File
@@ -80,10 +80,10 @@ impl MaskMethod {
}
}
pub fn from_name(s: &str) -> Option<MaskMethod> {
let n: String = s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::<String>().to_ascii_lowercase();
let n: String = s.chars().filter(char::is_ascii_alphanumeric).collect::<String>().to_ascii_lowercase();
MaskMethod::ALL
.into_iter()
.find(|m| m.id().to_ascii_lowercase() == n || m.label().chars().filter(|c| c.is_ascii_alphanumeric()).collect::<String>().to_ascii_lowercase() == n)
.find(|m| m.id().to_ascii_lowercase() == n || m.label().chars().filter(char::is_ascii_alphanumeric).collect::<String>().to_ascii_lowercase() == n)
}
pub fn model(self) -> Model {
match self {
+2 -2
View File
@@ -289,10 +289,10 @@ pub struct Inbox(Mutex<Vec<WorkerReply>>);
impl Inbox {
pub fn push(&self, r: WorkerReply) {
self.0.lock().unwrap_or_else(|e| e.into_inner()).push(r);
self.0.lock().unwrap_or_else(std::sync::PoisonError::into_inner).push(r);
}
pub fn take(&self) -> Vec<WorkerReply> {
std::mem::take(&mut *self.0.lock().unwrap_or_else(|e| e.into_inner()))
std::mem::take(&mut *self.0.lock().unwrap_or_else(std::sync::PoisonError::into_inner))
}
}
+1 -1
View File
@@ -110,7 +110,7 @@ pub struct JobShared {
}
pub(crate) fn lock<T>(m: &Mutex<T>) -> std::sync::MutexGuard<'_, T> {
m.lock().unwrap_or_else(|e| e.into_inner())
m.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
}
impl JobShared {
+2 -2
View File
@@ -20,7 +20,7 @@ use crate::render::{FootageSource, RenderOpts};
struct Synthetic;
fn file_px(path: &str, x: u32, y: u32) -> [f32; 4] {
let name = std::path::Path::new(path).file_name().and_then(|n| n.to_str()).unwrap_or("");
let name = std::path::Path::new(path).file_name().and_then(std::ffi::OsStr::to_str).unwrap_or("");
match name {
"red16.png" => [1.0, 0.0, 0.0, 1.0],
"blue8.png" => [0.0, 0.0, 1.0, 1.0],
@@ -43,7 +43,7 @@ fn file_px(path: &str, x: u32, y: u32) -> [f32; 4] {
}
fn size_of(path: &str) -> (u32, u32) {
match std::path::Path::new(path).file_name().and_then(|n| n.to_str()).unwrap_or("") {
match std::path::Path::new(path).file_name().and_then(std::ffi::OsStr::to_str).unwrap_or("") {
"red16.png" => (16, 16),
"blue8.png" => (8, 8),
"lines.mov" | "matte_black.png" | "matte_white.png" | "half.png" => (4, 4),
+1 -1
View File
@@ -157,7 +157,7 @@ pub struct TrackShared {
}
pub(crate) fn lock<T>(m: &Mutex<T>) -> std::sync::MutexGuard<'_, T> {
m.lock().unwrap_or_else(|e| e.into_inner())
m.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
}
/// A running (or finished, not yet polled) analysis.
+2 -2
View File
@@ -663,10 +663,10 @@ impl SimProfile {
}
pub fn parse(s: &str) -> Option<SimProfile> {
let k: String = s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::<String>().to_ascii_lowercase();
let k: String = s.chars().filter(char::is_ascii_alphanumeric).collect::<String>().to_ascii_lowercase();
SimProfile::ALL
.into_iter()
.find(|p| p.id().to_ascii_lowercase() == k || p.label().chars().filter(|c| c.is_ascii_alphanumeric()).collect::<String>().to_ascii_lowercase() == k)
.find(|p| p.id().to_ascii_lowercase() == k || p.label().chars().filter(char::is_ascii_alphanumeric).collect::<String>().to_ascii_lowercase() == k)
.or(match k.as_str() {
"off" => Some(SimProfile::None),
"hdtv" | "bt709" => Some(SimProfile::Rec709),
+4 -4
View File
@@ -247,7 +247,7 @@ impl<'a> Cx<'a> {
}
let img = self.crop_resize(img);
if let Some(ft) = &self.frame_times {
ft.lock().unwrap_or_else(|e| e.into_inner()).push((i, t0.elapsed().as_secs_f64()));
ft.lock().unwrap_or_else(std::sync::PoisonError::into_inner).push((i, t0.elapsed().as_secs_f64()));
}
img
}
@@ -327,7 +327,7 @@ impl<'a> Cx<'a> {
for dir in &self.job.options.overflow {
let p = std::path::Path::new(dir).join(&name).to_string_lossy().to_string();
if q.has_room(&p, bytes) {
self.overflowed.lock().unwrap_or_else(|e| e.into_inner()).push(p.clone());
self.overflowed.lock().unwrap_or_else(std::sync::PoisonError::into_inner).push(p.clone());
return p;
}
}
@@ -351,7 +351,7 @@ impl<'a> Cx<'a> {
Ok(r) => s.push_str(&format!("Result: Done ({} frames, {}×{}, {} bytes) in {seconds:.2} s\n", r.frames, r.width, r.height, r.bytes)),
Err(e) => s.push_str(&format!("Result: Failed\nError: {e}\n")),
}
let over = self.overflowed.lock().unwrap_or_else(|e| e.into_inner());
let over = self.overflowed.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if !over.is_empty() {
s.push_str(&format!("Storage overflow: {} file(s) written to overflow folders (first: {})\n", over.len(), over[0]));
}
@@ -366,7 +366,7 @@ impl<'a> Cx<'a> {
}
}
if let (RenderLog::PlusPerFrameInfo, Some(ft), Some(c)) = (level, &self.frame_times, comp) {
let mut v = ft.lock().unwrap_or_else(|e| e.into_inner()).clone();
let mut v = ft.lock().unwrap_or_else(std::sync::PoisonError::into_inner).clone();
v.sort_by_key(|(i, _)| *i);
s.push_str("\nFrames:\n");
let f0 = self.job.settings.first_frame(c);
+1 -1
View File
@@ -30,7 +30,7 @@ pub(crate) fn deferred_gpu() -> Option<(&'static Gpu, std::sync::MutexGuard<'sta
g
});
let g = crate::tests::require_gpu("deferred gpu tests", g.as_ref())?;
Some((g, LOCK.lock().unwrap_or_else(|e| e.into_inner())))
Some((g, LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner)))
}
/// Footage with GPU effects around a CPU-only one, a solid with Glow, and a precomp whose
+1 -1
View File
@@ -36,7 +36,7 @@ pub fn vector_doc(
}
fn is_pdf_like(path: &str, bytes: &[u8]) -> bool {
let ext = std::path::Path::new(path).extension().and_then(|e| e.to_str()).unwrap_or("").to_ascii_lowercase();
let ext = std::path::Path::new(path).extension().and_then(std::ffi::OsStr::to_str).unwrap_or("").to_ascii_lowercase();
effectcraft_pdf::sniff(bytes).is_some() && (effectcraft_pdf::EXTENSIONS.contains(&ext.as_str()) || bytes.starts_with(b"%PDF"))
}
+1 -1
View File
@@ -80,7 +80,7 @@ pub fn is_importable(path: &std::path::Path) -> bool {
}
pub(crate) fn ext_of(path: &std::path::Path) -> String {
path.extension().and_then(|e| e.to_str()).unwrap_or("").to_ascii_lowercase()
path.extension().and_then(std::ffi::OsStr::to_str).unwrap_or("").to_ascii_lowercase()
}
/// The `image` crate format of a still: from its signature, else from its extension (Targa has
+2 -2
View File
@@ -756,7 +756,7 @@ impl Inner {
drop(c);
return Ok(Arc::new(self.decode(&loc, footage)?));
}
c = self.done.wait(c).unwrap_or_else(|e| e.into_inner());
c = self.done.wait(c).unwrap_or_else(std::sync::PoisonError::into_inner);
}
self.misses.fetch_add(1, Ordering::Relaxed);
c.inflight.insert(loc.key.clone());
@@ -972,7 +972,7 @@ fn describe_problem(codec: &str, e: &MediaError) -> String {
}
fn lock<T>(m: &Mutex<T>) -> std::sync::MutexGuard<'_, T> {
m.lock().unwrap_or_else(|e| e.into_inner())
m.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
}
#[cfg(test)]
+2 -2
View File
@@ -201,7 +201,7 @@ impl TimeDisplayStyle {
}
}
pub fn parse(s: &str) -> Option<TimeDisplayStyle> {
let k: String = s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::<String>().to_ascii_lowercase();
let k: String = s.chars().filter(char::is_ascii_alphanumeric).collect::<String>().to_ascii_lowercase();
match k.as_str() {
"timecode" => Some(TimeDisplayStyle::Timecode),
"frames" => Some(TimeDisplayStyle::Frames),
@@ -1234,7 +1234,7 @@ impl Footage {
if self.kind != FootageKind::Data {
return None;
}
let ext = std::path::Path::new(&self.path).extension().and_then(|e| e.to_str()).unwrap_or("").to_ascii_lowercase();
let ext = std::path::Path::new(&self.path).extension().and_then(std::ffi::OsStr::to_str).unwrap_or("").to_ascii_lowercase();
Some(match ext.as_str() {
"csv" => "csv",
"tsv" | "tab" => "tsv",
+1 -1
View File
@@ -331,7 +331,7 @@ impl DiskCache {
let Ok(files) = std::fs::read_dir(shard.path()) else { continue };
for f in files.flatten() {
let p = f.path();
let name = p.file_name().and_then(|n| n.to_str()).unwrap_or("");
let name = p.file_name().and_then(std::ffi::OsStr::to_str).unwrap_or("");
if name.contains(".tmp") {
// A writer that died mid-write; old temporaries are removed.
if f.metadata().and_then(|m| m.modified()).is_ok_and(|t| t.elapsed().is_ok_and(|e| e.as_secs() > 600)) {
+1 -1
View File
@@ -408,7 +408,7 @@ impl View3D {
}
}
pub fn from_id(s: &str) -> Option<View3D> {
let k: String = s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::<String>().to_ascii_lowercase();
let k: String = s.chars().filter(char::is_ascii_alphanumeric).collect::<String>().to_ascii_lowercase();
View3D::ALL.into_iter().find(|v| v.id().eq_ignore_ascii_case(&k) || v.label().replace(' ', "").eq_ignore_ascii_case(&k))
}
pub fn is_ortho(self) -> bool {
+4 -4
View File
@@ -89,7 +89,7 @@ mod imp {
Sock::Listener(_) => None,
};
let entry = Arc::new(Entry { sock: Mutex::new(s), wake, closed: AtomicBool::new(false) });
let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
let mut t = table().lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let id = t.next;
t.next += 1;
t.socks.insert(id, entry);
@@ -98,12 +98,12 @@ mod imp {
/// The socket `id`, if open. The table is locked only for the lookup.
fn get(id: u32) -> Option<Arc<Entry>> {
table().lock().unwrap_or_else(|e| e.into_inner()).socks.get(&id).cloned()
table().lock().unwrap_or_else(std::sync::PoisonError::into_inner).socks.get(&id).cloned()
}
/// Forget socket `id`; a call blocked on it fails at once.
fn close(id: u32) {
let entry = table().lock().unwrap_or_else(|e| e.into_inner()).socks.remove(&id);
let entry = table().lock().unwrap_or_else(std::sync::PoisonError::into_inner).socks.remove(&id);
if let Some(entry) = entry {
entry.closed.store(true, Ordering::Release);
if let Some(w) = &entry.wake {
@@ -261,7 +261,7 @@ mod imp {
_ => err("the socket is not open"),
};
};
let mut sock = entry.sock.lock().unwrap_or_else(|e| e.into_inner());
let mut sock = entry.sock.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
// Recheck after locking: close or a failed mode transition may have retired it
// while this caller waited with an Arc obtained from the table.
if entry.closed.load(Ordering::Acquire) {
+3 -1
View File
@@ -150,7 +150,9 @@ impl Reader<'_> {
let mut files: Vec<PathBuf> = rd
.flatten()
.map(|e| e.path())
.filter(|p| p.file_name().and_then(|n| n.to_str()).is_some_and(|n| n.ends_with(".conf") && n.starts_with(|c: char| c.is_ascii_digit())))
.filter(|p| {
p.file_name().and_then(std::ffi::OsStr::to_str).is_some_and(|n| n.ends_with(".conf") && n.starts_with(|c: char| c.is_ascii_digit()))
})
.collect();
files.sort();
for f in files {
+20 -20
View File
@@ -116,7 +116,7 @@ fn walk(dir: &std::path::Path, depth: u32, out: &mut Vec<PathBuf>) {
if depth < 8 {
walk(&p, depth + 1, out);
}
} else if p.extension().and_then(|x| x.to_str()).is_some_and(|x| matches!(x.to_ascii_lowercase().as_str(), "ttf" | "otf" | "ttc" | "otc")) {
} else if p.extension().and_then(std::ffi::OsStr::to_str).is_some_and(|x| matches!(x.to_ascii_lowercase().as_str(), "ttf" | "otf" | "ttc" | "otc")) {
out.push(p);
}
}
@@ -319,7 +319,7 @@ fn push(db: &mut Db, info: FaceInfo) -> FaceId {
/// added.
pub fn add_source(src: &dyn FontSource) -> usize {
let faces = src.faces();
let mut d = db().write().unwrap_or_else(|e| e.into_inner());
let mut d = db().write().unwrap_or_else(std::sync::PoisonError::into_inner);
let mut n = 0;
for f in faces {
let dup = d.faces.iter().any(|g| g.info.family.eq_ignore_ascii_case(&f.family) && g.info.style.eq_ignore_ascii_case(&f.style));
@@ -329,7 +329,7 @@ pub fn add_source(src: &dyn FontSource) -> usize {
}
}
// A new face can belong to one of a script's usual families.
family_faces_cache().write().unwrap_or_else(|e| e.into_inner()).clear();
family_faces_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).clear();
n
}
@@ -337,9 +337,9 @@ pub fn add_source(src: &dyn FontSource) -> usize {
pub fn add_font_data(data: Vec<u8>) -> Vec<FaceId> {
let data = Arc::new(data);
let names = read_faces_bytes(&data);
let mut d = db().write().unwrap_or_else(|e| e.into_inner());
let mut d = db().write().unwrap_or_else(std::sync::PoisonError::into_inner);
let ids = names.into_iter().map(|n| push(&mut d, info_from(n, FaceData::Shared(data.clone()), "user"))).collect();
family_faces_cache().write().unwrap_or_else(|e| e.into_inner()).clear();
family_faces_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).clear();
ids
}
@@ -353,21 +353,21 @@ pub fn scan_system() -> usize {
if system_scanned() {
return 0;
}
let _scan = SCAN.lock().unwrap_or_else(|e| e.into_inner());
let _scan = SCAN.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if system_scanned() {
return 0;
}
let n = if cfg!(target_arch = "wasm32") { 0 } else { add_source(&DirectorySource::system()) };
db().write().unwrap_or_else(|e| e.into_inner()).scanned = true;
db().write().unwrap_or_else(std::sync::PoisonError::into_inner).scanned = true;
n
}
/// Scan the system font folders again, adding fonts installed since the last scan (faces already
/// known are kept, so font ids stay valid). Returns the number of faces added.
pub fn rescan_system() -> usize {
let _scan = SCAN.lock().unwrap_or_else(|e| e.into_inner());
let _scan = SCAN.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let n = if cfg!(target_arch = "wasm32") { 0 } else { add_source(&DirectorySource::system()) };
db().write().unwrap_or_else(|e| e.into_inner()).scanned = true;
db().write().unwrap_or_else(std::sync::PoisonError::into_inner).scanned = true;
n
}
@@ -388,12 +388,12 @@ pub fn scan_system_in_background() {
}
pub fn system_scanned() -> bool {
db().read().unwrap_or_else(|e| e.into_inner()).scanned
db().read().unwrap_or_else(std::sync::PoisonError::into_inner).scanned
}
/// A face by id (panics never: unknown ids give the default face).
pub fn face(id: FaceId) -> Arc<Face> {
let d = db().read().unwrap_or_else(|e| e.into_inner());
let d = db().read().unwrap_or_else(std::sync::PoisonError::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)]
@@ -402,7 +402,7 @@ pub fn face(id: FaceId) -> Arc<Face> {
/// All faces (bundled first).
pub fn all_faces() -> Vec<Arc<Face>> {
db().read().unwrap_or_else(|e| e.into_inner()).faces.clone()
db().read().unwrap_or_else(std::sync::PoisonError::into_inner).faces.clone()
}
/// Family names in the fonts' own languages: English family → native family (only families
@@ -743,15 +743,15 @@ static REQUESTED_CJK_LOCALE: RwLock<Option<String>> = RwLock::new(None);
pub fn set_cjk_locale(tag: &str) {
let tag = tag.trim().to_ascii_lowercase();
let requested = if tag.is_empty() || tag == "c" || tag == "posix" { None } else { Some(tag) };
*REQUESTED_CJK_LOCALE.write().unwrap_or_else(|e| e.into_inner()) = requested;
*REQUESTED_CJK_LOCALE.write().unwrap_or_else(std::sync::PoisonError::into_inner) = requested;
// The order of the CJK families follows the language, so a change of language drops the cached
// fallbacks.
fallback_cache().write().unwrap_or_else(|e| e.into_inner()).clear();
family_faces_cache().write().unwrap_or_else(|e| e.into_inner()).clear();
fallback_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).clear();
family_faces_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).clear();
}
fn requested_cjk_locale() -> Option<String> {
REQUESTED_CJK_LOCALE.read().unwrap_or_else(|e| e.into_inner()).clone()
REQUESTED_CJK_LOCALE.read().unwrap_or_else(std::sync::PoisonError::into_inner).clone()
}
/// The CJK locale to use: [`set_cjk_locale`] when the host asked for one, otherwise the locale
@@ -981,7 +981,7 @@ fn fallback_with_locale(c: char, prefer: FaceId, locale: impl FnOnce() -> CjkLoc
}
}
let key = (c as u32 >> 8, prefer);
if let Some(cached) = fallback_cache().read().unwrap_or_else(|e| e.into_inner()).get(&key).copied() {
if let Some(cached) = fallback_cache().read().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key).copied() {
match cached {
Some(id) if face(id).has_char(c) => return id,
Some(_) => {}
@@ -992,7 +992,7 @@ fn fallback_with_locale(c: char, prefer: FaceId, locale: impl FnOnce() -> CjkLoc
scan_system();
}
let found = system_fallback(c, &p.info.style);
fallback_cache().write().unwrap_or_else(|e| e.into_inner()).insert(key, found);
fallback_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).insert(key, found);
found.unwrap_or(prefer)
}
@@ -1000,7 +1000,7 @@ fn fallback_with_locale(c: char, prefer: FaceId, locale: impl FnOnce() -> CjkLoc
/// for `script`, in the order they are tried, each in the style of `prefer`. For CJK, the other CJK
/// languages' families follow in the locale's order.
fn family_faces(script: FallbackScript, prefer: &Face, locale: CjkLocale) -> Arc<Vec<FaceId>> {
if let Some(faces) = family_faces_cache().read().unwrap_or_else(|e| e.into_inner()).get(&(script, prefer.id, locale)) {
if let Some(faces) = family_faces_cache().read().unwrap_or_else(std::sync::PoisonError::into_inner).get(&(script, prefer.id, locale)) {
return faces.clone();
}
let order: Vec<FallbackScript> = match script {
@@ -1016,7 +1016,7 @@ fn family_faces(script: FallbackScript, prefer: &Face, locale: CjkLocale) -> Arc
let style = &prefer.info.style;
let faces: Arc<Vec<FaceId>> =
Arc::new(order.into_iter().flat_map(|s| families_for(s, locale)).filter_map(|fam| resolve_in(fam, style)).map(|r| r.face).collect());
family_faces_cache().write().unwrap_or_else(|e| e.into_inner()).insert((script, prefer.id, locale), faces.clone());
family_faces_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).insert((script, prefer.id, locale), faces.clone());
faces
}
+4 -4
View File
@@ -399,7 +399,7 @@ enum Form {
pub fn feature_substitutes(face: FaceId, tag: &[u8; 4], c: char) -> bool {
static C: OnceLock<Mutex<HashMap<(FaceId, [u8; 4], char), bool>>> = OnceLock::new();
let cache = C.get_or_init(Default::default);
if let Some(v) = cache.lock().unwrap_or_else(|e| e.into_inner()).get(&(face, *tag, c)) {
if let Some(v) = cache.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&(face, *tag, c)) {
return *v;
}
let f = fonts::face(face);
@@ -419,7 +419,7 @@ pub fn feature_substitutes(face: FaceId, tag: &[u8; 4], c: char) -> bool {
}
_ => false,
};
let mut m = cache.lock().unwrap_or_else(|e| e.into_inner());
let mut m = cache.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if m.len() > 100_000 {
m.clear();
}
@@ -1123,12 +1123,12 @@ pub fn layout_rich(text: &str, runs: &[(Range<usize>, TextStyle)], paras: &[Para
let key = h.finish();
static CLOCK: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let now = CLOCK.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if let Some(e) = cache().lock().unwrap_or_else(|e| e.into_inner()).get_mut(&key) {
if let Some(e) = cache().lock().unwrap_or_else(std::sync::PoisonError::into_inner).get_mut(&key) {
e.0 = now;
return e.1.clone();
}
let l = Arc::new(layout_rich_uncached(text, runs, paras));
let mut c = cache().lock().unwrap_or_else(|e| e.into_inner());
let mut c = cache().lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if c.len() >= 512 {
// evict the oldest quarter
let mut ages: Vec<u64> = c.values().map(|v| v.0).collect();
+2 -2
View File
@@ -54,7 +54,7 @@ impl OutlinePen for Pen {
pub(crate) fn outline_units(face: FaceId, gid: u32) -> Option<Arc<BezPath>> {
static C: OnceLock<Mutex<HashMap<(FaceId, u32), Option<Arc<BezPath>>>>> = OnceLock::new();
let c = C.get_or_init(Default::default);
if let Some(v) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&(face, gid)) {
if let Some(v) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&(face, gid)) {
return v.clone();
}
let f = fonts::face(face);
@@ -64,7 +64,7 @@ pub(crate) fn outline_units(face: FaceId, gid: u32) -> Option<Arc<BezPath>> {
g.draw(DrawSettings::unhinted(Size::unscaled(), LocationRef::default()), &mut pen).ok()?;
Some(Arc::new(pen.0))
});
let mut m = c.lock().unwrap_or_else(|e| e.into_inner());
let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if m.len() > 50_000 {
m.clear();
}
+4 -4
View File
@@ -36,7 +36,7 @@ pub fn intern(coords: &[(String, f32)]) -> u32 {
if key.windows(2).any(|w| w[0].0 == w[1].0) {
return 0;
}
let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
let mut t = table().lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(id) = t.by_coords.get(&key) {
return *id;
}
@@ -55,7 +55,7 @@ pub fn coords(id: u32) -> Arc<Vec<(String, f32)>> {
if id == 0 {
return Arc::new(Vec::new());
}
table().lock().unwrap_or_else(|e| e.into_inner()).values.get(id as usize - 1).cloned().unwrap_or_default()
table().lock().unwrap_or_else(std::sync::PoisonError::into_inner).values.get(id as usize - 1).cloned().unwrap_or_default()
}
fn table() -> &'static Mutex<Table> {
@@ -109,7 +109,7 @@ pub fn outline_units_at(face: FaceId, gid: u32, coords: &[(String, f32)]) -> Opt
let c = C.get_or_init(Default::default);
// Quantise to 1/100 of a unit so animation frames reuse outlines.
let key: Key = (face, gid, coords.iter().map(|(t, v)| (t.clone(), (v * 100.0).round() as i32)).collect());
if let Some(v) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&key) {
if let Some(v) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key) {
return v.clone();
}
let f = fonts::face(face);
@@ -120,7 +120,7 @@ pub fn outline_units_at(face: FaceId, gid: u32, coords: &[(String, f32)]) -> Opt
g.draw(DrawSettings::unhinted(Size::unscaled(), LocationRef::from(&loc)), &mut pen).ok()?;
Some(Arc::new(pen.0))
});
let mut m = c.lock().unwrap_or_else(|e| e.into_inner());
let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
if m.len() > 20_000 {
m.clear();
}
+1 -1
View File
@@ -127,7 +127,7 @@ pub fn format_size(bytes: u64) -> String {
/// The Kind column: what a recent file is, by its extension, in the interface language.
fn kind_of(path: &str) -> &'static str {
tr(match std::path::Path::new(path).extension().and_then(|e| e.to_str()).map(str::to_ascii_lowercase).as_deref() {
tr(match std::path::Path::new(path).extension().and_then(std::ffi::OsStr::to_str).map(str::to_ascii_lowercase).as_deref() {
Some("ecproj") => "EffectCraft project",
Some("aep" | "aepx") => "After Effects project",
Some("json" | "lottie") => "Lottie animation",
+2 -2
View File
@@ -176,7 +176,7 @@ fn repo_files() -> Result<Vec<String>, String> {
}
fn is_asset(path: &str) -> bool {
std::path::Path::new(path).extension().and_then(|e| e.to_str()).is_some_and(|e| ASSET_EXT.contains(&e.to_ascii_lowercase().as_str()))
std::path::Path::new(path).extension().and_then(std::ffi::OsStr::to_str).is_some_and(|e| ASSET_EXT.contains(&e.to_ascii_lowercase().as_str()))
}
fn assets() -> Result<(), String> {
@@ -384,7 +384,7 @@ fn serve_dir(dir: &std::path::Path, port: u16) -> Result<(), String> {
Ok(b) => ("200 OK", b),
Err(_) => ("404 Not Found", b"not found".to_vec()),
};
let mime = match file.extension().and_then(|e| e.to_str()).unwrap_or("") {
let mime = match file.extension().and_then(std::ffi::OsStr::to_str).unwrap_or("") {
"html" => "text/html; charset=utf-8",
"js" => "text/javascript",
"wasm" => "application/wasm",