From 741048839cf8688d2a8694fda40876118b7a3647 Mon Sep 17 00:00:00 2001 From: Lynnesbian Date: Sat, 25 Sep 2021 00:53:35 +1000 Subject: [PATCH] replace remaining OnceCells w/ Lazys, simplifying i prefer Lazy because it simplifies initialisation by a lot, allowing for the removal of stuff like `init_db()` and the `clap_version()` functions --- CHANGELOG.md | 4 ++-- src/files.rs | 9 +++++---- src/findings.rs | 3 +++ src/formats.rs | 4 ++-- src/lib.rs | 7 +++++-- src/main.rs | 4 ++-- src/mime_db.rs | 2 ++ src/parameters.rs | 8 ++++---- src/tests/mod.rs | 29 +++++++---------------------- src/utils.rs | 40 ++++++++++++++++------------------------ 10 files changed, 48 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73e1711..41c6bec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Unreleased ### Added -- AIFF (Audio Interchange File Format, a PCM audio format like WAV) detection to [`infer`] +- AIFF (Audio Interchange File Format, a PCM audio format like WAV) detection to [`infer`] backend - `--version` output now includes the (short) hash of the git commit fif was built from ### Other - Refactoring - split fif into `main.rs` and `lib.rs`, moved file-related functionality (directory scanning, etc.) into @@ -17,7 +17,7 @@ files module, removed string module, etc. headings that (pointlessly?) previously divided the changelog into v0.3, v0.2, and v0.1 - A few minor grammar tweaks and reorganisations - Replaced [`cached`] dependency with a simple HashMap-backed store -- Use [`once_cell`]'s `Lazy` instead of its `OnceCell` for the `MIMEDB` const +- Replace all occurrences of [`once_cell`]'s `OnceCell` with equivalent `Lazy`-based implementations ## v0.3.6 - 2021-08-16 ### Other diff --git a/src/files.rs b/src/files.rs index 2c6cdde..d1072e4 100644 --- a/src/files.rs +++ b/src/files.rs @@ -1,8 +1,8 @@ +//! File handling - scanning, detecting MIME types, and so on. + use std::collections::{BTreeSet, HashMap}; use std::fs::File; -use std::io; -use std::io::{Read, Seek, SeekFrom}; -use std::ops::Deref; +use std::io::{self, Read, Seek, SeekFrom}; use std::path::Path; use std::str::FromStr; use std::sync::RwLock; @@ -19,6 +19,7 @@ use crate::mime_db::MimeDb; use crate::parameters::ScanOpts; use crate::{String, MIMEDB}; +/// Cache of mimetypes and their associated extensions, used by [`mime_extension_lookup()`] static MIMEXT: Lazy>>>> = Lazy::new(|| RwLock::new(HashMap::new())); cfg_if! { @@ -97,7 +98,7 @@ pub fn wanted_file( pub fn scan_file(entry: &DirEntry, canonical_paths: bool) -> Result { let path = entry.path(); // try to determine mimetype for this entry - let result = match mime_type(MIMEDB.deref(), path) { + let result = match mime_type(&*MIMEDB, path) { // an error occurred while trying to read the file Err(_) => return Err(ScanError::File(path)), // the file was read successfully, but we were unable to determine its mimetype diff --git a/src/findings.rs b/src/findings.rs index 50a5495..131a0b2 100644 --- a/src/findings.rs +++ b/src/findings.rs @@ -1,3 +1,6 @@ +//! The [`Findings`] and [`ScanError`] structs, used for conveying whether a given file was able to be scanned and +//! whether its MIME type could be inferred. + use std::fmt::{Display, Formatter}; use std::path::{Path, PathBuf}; diff --git a/src/formats.rs b/src/formats.rs index df9415f..9ac226e 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -11,7 +11,7 @@ use itertools::{Either, Itertools}; use snailquote::escape; use crate::findings::ScanError; -use crate::utils::clap_long_version; +use crate::utils::CLAP_LONG_VERSION; use crate::Findings; use crate::String; @@ -74,7 +74,7 @@ impl<'a> From<&'a OsStr> for Writable<'a> { fn from(p: &'a OsStr) -> Writable<'a> { Writable::Path(p.as_ref()) } } -fn generated_by() -> String { format!("Generated by fif {}", clap_long_version()).into() } +fn generated_by() -> String { format!("Generated by fif {}", CLAP_LONG_VERSION.as_str()).into() } pub fn smart_write(f: &mut W, writeables: &[Writable]) -> io::Result<()> { // ehhhh diff --git a/src/lib.rs b/src/lib.rs index 2e27eb8..19212ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ #![forbid(unsafe_code)] #![warn(trivial_casts, unused_lifetimes, unused_qualifications)] +//! This library consists of all of the things fif needs to run. It only exists as a library to separate code, and to +//! make testing a bit easier. I don't recommend using this as a library for your crate, as it may have breaking +//! changes without incrementing the major version, as it's really only meant to be a place for fif's internals to live. pub mod files; pub mod findings; @@ -27,9 +30,9 @@ cfg_if! { cfg_if! { if #[cfg(any(all(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] { /// A [Lazy] holding an instance of [mime_db::MimeDb]. - pub static MIMEDB: Lazy = Lazy::new(|| crate::mime_db::InferDb::init()); + pub static MIMEDB: Lazy = Lazy::new(crate::mime_db::InferDb::init); } else { /// A [Lazy] holding an instance of [mime_db::MimeDb]. - pub static MIMEDB: Lazy = Lazy::new(|| crate::mime_db::XdgDb::init()); + pub static MIMEDB: Lazy = Lazy::new(crate::mime_db::XdgDb::init); } } diff --git a/src/main.rs b/src/main.rs index a044c62..ac2a9a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,7 @@ use clap::Clap; use fif::files::{scan_directory, scan_from_walkdir}; use fif::formats::Format; use fif::parameters::OutputFormat; -use fif::utils::{clap_long_version, os_name}; +use fif::utils::{os_name, CLAP_LONG_VERSION}; use fif::{formats, parameters}; use log::{debug, error, info, trace, warn, Level}; @@ -54,7 +54,7 @@ fn main() { trace!( "fif {}, running on {} {}", - clap_long_version(), + CLAP_LONG_VERSION.as_str(), std::env::consts::ARCH, os_name() ); diff --git a/src/mime_db.rs b/src/mime_db.rs index 8497a70..420c4dd 100644 --- a/src/mime_db.rs +++ b/src/mime_db.rs @@ -4,7 +4,9 @@ use cfg_if::cfg_if; use mime::Mime; pub trait MimeDb { + /// Initialise the database. fn init() -> Self; + /// Given a slice of bytes, returns the inferred mimetype, if any. fn get_type(&self, data: &[u8]) -> Option; } diff --git a/src/parameters.rs b/src/parameters.rs index 318018a..a58793f 100644 --- a/src/parameters.rs +++ b/src/parameters.rs @@ -6,7 +6,7 @@ use std::path::PathBuf; use cfg_if::cfg_if; use clap::{AppSettings, Clap}; -use crate::utils::{clap_long_version, clap_version}; +use crate::utils::{CLAP_LONG_VERSION, CLAP_VERSION}; use crate::String as StringType; cfg_if! { @@ -35,8 +35,8 @@ pub enum OutputFormat { #[derive(Clap, Debug)] #[allow(clippy::struct_excessive_bools)] #[clap( - version = clap_version(), - long_version = clap_long_version(), + version = CLAP_VERSION.as_str(), + long_version = CLAP_LONG_VERSION.as_str(), author = option_env!("CARGO_PKG_AUTHORS").unwrap_or("Lynnesbian"), about = option_env!("CARGO_PKG_DESCRIPTION").unwrap_or("File Info Fixer"), before_help = "Copyright © 2021 Lynnesbian under the GPL3 (or later) License.", @@ -258,7 +258,7 @@ pub enum ExtensionSet { #[clap(alias = "videos")] Video, /// Extensions used for media file formats. This acts as a combination of the [Images](ExtensionSet::Images), - /// [Audio](ExtensionSet::Audio) and [Videos](ExtensionSet::Videos) variants. + /// [Audio](ExtensionSet::Audio) and [Video](ExtensionSet::Video) variants. Media, /// Extensions used for document file formats, such as `pdf`, `odt`, `docx`, etc. Documents, diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 5d23395..cd500d8 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -3,12 +3,11 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; use clap::Clap; -use fif::files::{mime_extension_lookup, BUF_SIZE}; -use fif::files::{scan_directory, scan_from_walkdir}; +use fif::files::{mime_extension_lookup, scan_directory, scan_from_walkdir, BUF_SIZE}; use fif::findings::Findings; use fif::formats::{Format, PowerShell, Shell}; use fif::mime_db::MimeDb; -use fif::String; +use fif::{String, MIMEDB}; use mime::{Mime, APPLICATION_OCTET_STREAM, APPLICATION_PDF, IMAGE_JPEG, IMAGE_PNG}; use crate::parameters::ExtensionSet; @@ -19,18 +18,6 @@ const PNG_BYTES: &[u8] = b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"; const PDF_BYTES: &[u8] = b"%PDF-"; const ZIP_BYTES: &[u8] = b"PK\x03\x04"; -cfg_if::cfg_if! { - if #[cfg(any(all(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] { - fn get_mime_db() -> fif::mime_db::InferDb { - fif::mime_db::InferDb::init() - } - } else { - fn get_mime_db() -> fif::mime_db::XdgDb { - fif::mime_db::XdgDb::init() - } - } -} - fn application_zip() -> Mime { use std::str::FromStr; Mime::from_str("application/zip").unwrap() @@ -55,11 +42,10 @@ fn get_ext() { #[test] /// Ensure that the mime types for JPEG, PNG, PDF, and ZIP are detected from their magic numbers. fn detect_type() { - let db = get_mime_db(); - assert_eq!(db.get_type(JPEG_BYTES), Some(IMAGE_JPEG)); - assert_eq!(db.get_type(PNG_BYTES), Some(IMAGE_PNG)); - assert_eq!(db.get_type(PDF_BYTES), Some(APPLICATION_PDF)); - assert_eq!(db.get_type(ZIP_BYTES), Some(application_zip())); + assert_eq!(MIMEDB.get_type(JPEG_BYTES), Some(IMAGE_JPEG)); + assert_eq!(MIMEDB.get_type(PNG_BYTES), Some(IMAGE_PNG)); + assert_eq!(MIMEDB.get_type(PDF_BYTES), Some(APPLICATION_PDF)); + assert_eq!(MIMEDB.get_type(ZIP_BYTES), Some(application_zip())); } #[test] @@ -317,14 +303,13 @@ fn rejects_bad_args() { /// mime database somehow panics or hangs. fn identify_random_bytes() { use rand::RngCore; - let db = get_mime_db(); let mut rng = rand::thread_rng(); let mut bytes: [u8; BUF_SIZE * 2] = [0; BUF_SIZE * 2]; let mut results: HashMap = HashMap::new(); for _ in 1..1000 { rng.fill_bytes(&mut bytes); - if let Some(detected_type) = db.get_type(&bytes) { + if let Some(detected_type) = MIMEDB.get_type(&bytes) { *results.entry(detected_type).or_insert(0) += 1; } } diff --git a/src/utils.rs b/src/utils.rs index 621e49c..6e84e49 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,7 @@ +//! Various minor utilities. + use cfg_if::cfg_if; -use once_cell::sync::OnceCell; +use once_cell::sync::Lazy; use crate::String; @@ -16,30 +18,20 @@ cfg_if! { } } -// the version and long_version given to clap need to be a &str, but we want to use format!, which returns a String. -// we can't just do something like `version = format!(...).as_str()`, because clap needs to know that the version will -// live for a given lifetime, which we need to satisfy by making our String static. of course, you can't use format! -// statically, so we need to use a OnceCell or similar to get around this. -static CLAP_VERSION: OnceCell = OnceCell::new(); -static CLAP_LONG_VERSION: OnceCell = OnceCell::new(); +/// The version defined in Cargo.toml, prefixed with a v (e.g. "v0.3.1") +pub(crate) static CLAP_VERSION: Lazy = Lazy::new(|| String::from("v") + VERSION.unwrap_or("???")); -/// Sets [`CLAP_VERSION`] to be the version defined in Cargo.toml, prefixed with a v (e.g. "v0.3.1"), then returns it as -/// an str. -pub fn clap_version() -> &'static str { CLAP_VERSION.get_or_init(|| String::from("v") + VERSION.unwrap_or("???")) } - -/// Sets [`CLAP_LONG_VERSION`] to be similar to [`CLAP_VERSION`], followed by the chosen backend and abbreviated git -/// commit hash in parentheses (e.g. "v0.3.6 (XDG-Mime backend, commit #043e097)"), then returns it as an str. -pub fn clap_long_version() -> &'static str { - CLAP_LONG_VERSION.get_or_init(|| { - format!( - "v{} ({} backend, commit #{})", - VERSION.unwrap_or("???"), - BACKEND, - option_env!("GIT_SHA").unwrap_or("???") - ) - .into() - }) -} +/// Similar to [`CLAP_VERSION`], followed by the chosen backend and abbreviated git commit hash in parentheses - For +/// example, "v0.3.6 (XDG-Mime backend, commit #043e097)" +pub static CLAP_LONG_VERSION: Lazy = Lazy::new(|| { + format!( + "v{} ({} backend, commit #{})", + VERSION.unwrap_or("???"), + BACKEND, + option_env!("GIT_SHA").unwrap_or("???") + ) + .into() +}); /// Returns the name of the target operating system with proper casing, like "Windows" or "macOS". #[allow(clippy::option_map_unit_fn)]