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
This commit is contained in:
Lynne Megido 2021-09-25 00:53:35 +10:00
parent 0c31277191
commit 741048839c
Signed by: lynnesbian
GPG Key ID: F0A184B5213D9F90
10 changed files with 48 additions and 62 deletions

View File

@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## Unreleased ## Unreleased
### Added ### 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 - `--version` output now includes the (short) hash of the git commit fif was built from
### Other ### Other
- Refactoring - split fif into `main.rs` and `lib.rs`, moved file-related functionality (directory scanning, etc.) into - 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 headings that (pointlessly?) previously divided the changelog into v0.3, v0.2, and v0.1
- A few minor grammar tweaks and reorganisations - A few minor grammar tweaks and reorganisations
- Replaced [`cached`] dependency with a simple HashMap-backed store - 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 ## v0.3.6 - 2021-08-16
### Other ### Other

View File

@ -1,8 +1,8 @@
//! File handling - scanning, detecting MIME types, and so on.
use std::collections::{BTreeSet, HashMap}; use std::collections::{BTreeSet, HashMap};
use std::fs::File; use std::fs::File;
use std::io; use std::io::{self, Read, Seek, SeekFrom};
use std::io::{Read, Seek, SeekFrom};
use std::ops::Deref;
use std::path::Path; use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use std::sync::RwLock; use std::sync::RwLock;
@ -19,6 +19,7 @@ use crate::mime_db::MimeDb;
use crate::parameters::ScanOpts; use crate::parameters::ScanOpts;
use crate::{String, MIMEDB}; use crate::{String, MIMEDB};
/// Cache of mimetypes and their associated extensions, used by [`mime_extension_lookup()`]
static MIMEXT: Lazy<RwLock<HashMap<String, Option<Vec<String>>>>> = Lazy::new(|| RwLock::new(HashMap::new())); static MIMEXT: Lazy<RwLock<HashMap<String, Option<Vec<String>>>>> = Lazy::new(|| RwLock::new(HashMap::new()));
cfg_if! { cfg_if! {
@ -97,7 +98,7 @@ pub fn wanted_file(
pub fn scan_file(entry: &DirEntry, canonical_paths: bool) -> Result<Findings, ScanError> { pub fn scan_file(entry: &DirEntry, canonical_paths: bool) -> Result<Findings, ScanError> {
let path = entry.path(); let path = entry.path();
// try to determine mimetype for this entry // 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 // an error occurred while trying to read the file
Err(_) => return Err(ScanError::File(path)), Err(_) => return Err(ScanError::File(path)),
// the file was read successfully, but we were unable to determine its mimetype // the file was read successfully, but we were unable to determine its mimetype

View File

@ -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::fmt::{Display, Formatter};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};

View File

@ -11,7 +11,7 @@ use itertools::{Either, Itertools};
use snailquote::escape; use snailquote::escape;
use crate::findings::ScanError; use crate::findings::ScanError;
use crate::utils::clap_long_version; use crate::utils::CLAP_LONG_VERSION;
use crate::Findings; use crate::Findings;
use crate::String; 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 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<W: Write>(f: &mut W, writeables: &[Writable]) -> io::Result<()> { pub fn smart_write<W: Write>(f: &mut W, writeables: &[Writable]) -> io::Result<()> {
// ehhhh // ehhhh

View File

@ -1,5 +1,8 @@
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![warn(trivial_casts, unused_lifetimes, unused_qualifications)] #![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 files;
pub mod findings; pub mod findings;
@ -27,9 +30,9 @@ cfg_if! {
cfg_if! { cfg_if! {
if #[cfg(any(all(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] { 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]. /// A [Lazy] holding an instance of [mime_db::MimeDb].
pub static MIMEDB: Lazy<mime_db::InferDb> = Lazy::new(|| crate::mime_db::InferDb::init()); pub static MIMEDB: Lazy<mime_db::InferDb> = Lazy::new(crate::mime_db::InferDb::init);
} else { } else {
/// A [Lazy] holding an instance of [mime_db::MimeDb]. /// A [Lazy] holding an instance of [mime_db::MimeDb].
pub static MIMEDB: Lazy<mime_db::XdgDb> = Lazy::new(|| crate::mime_db::XdgDb::init()); pub static MIMEDB: Lazy<mime_db::XdgDb> = Lazy::new(crate::mime_db::XdgDb::init);
} }
} }

View File

@ -24,7 +24,7 @@ use clap::Clap;
use fif::files::{scan_directory, scan_from_walkdir}; use fif::files::{scan_directory, scan_from_walkdir};
use fif::formats::Format; use fif::formats::Format;
use fif::parameters::OutputFormat; 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 fif::{formats, parameters};
use log::{debug, error, info, trace, warn, Level}; use log::{debug, error, info, trace, warn, Level};
@ -54,7 +54,7 @@ fn main() {
trace!( trace!(
"fif {}, running on {} {}", "fif {}, running on {} {}",
clap_long_version(), CLAP_LONG_VERSION.as_str(),
std::env::consts::ARCH, std::env::consts::ARCH,
os_name() os_name()
); );

View File

@ -4,7 +4,9 @@ use cfg_if::cfg_if;
use mime::Mime; use mime::Mime;
pub trait MimeDb { pub trait MimeDb {
/// Initialise the database.
fn init() -> Self; fn init() -> Self;
/// Given a slice of bytes, returns the inferred mimetype, if any.
fn get_type(&self, data: &[u8]) -> Option<Mime>; fn get_type(&self, data: &[u8]) -> Option<Mime>;
} }

View File

@ -6,7 +6,7 @@ use std::path::PathBuf;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use clap::{AppSettings, Clap}; 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; use crate::String as StringType;
cfg_if! { cfg_if! {
@ -35,8 +35,8 @@ pub enum OutputFormat {
#[derive(Clap, Debug)] #[derive(Clap, Debug)]
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
#[clap( #[clap(
version = clap_version(), version = CLAP_VERSION.as_str(),
long_version = clap_long_version(), long_version = CLAP_LONG_VERSION.as_str(),
author = option_env!("CARGO_PKG_AUTHORS").unwrap_or("Lynnesbian"), author = option_env!("CARGO_PKG_AUTHORS").unwrap_or("Lynnesbian"),
about = option_env!("CARGO_PKG_DESCRIPTION").unwrap_or("File Info Fixer"), about = option_env!("CARGO_PKG_DESCRIPTION").unwrap_or("File Info Fixer"),
before_help = "Copyright © 2021 Lynnesbian under the GPL3 (or later) License.", before_help = "Copyright © 2021 Lynnesbian under the GPL3 (or later) License.",
@ -258,7 +258,7 @@ pub enum ExtensionSet {
#[clap(alias = "videos")] #[clap(alias = "videos")]
Video, Video,
/// Extensions used for media file formats. This acts as a combination of the [Images](ExtensionSet::Images), /// 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, Media,
/// Extensions used for document file formats, such as `pdf`, `odt`, `docx`, etc. /// Extensions used for document file formats, such as `pdf`, `odt`, `docx`, etc.
Documents, Documents,

View File

@ -3,12 +3,11 @@ use std::ffi::OsStr;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use clap::Clap; use clap::Clap;
use fif::files::{mime_extension_lookup, BUF_SIZE}; use fif::files::{mime_extension_lookup, scan_directory, scan_from_walkdir, BUF_SIZE};
use fif::files::{scan_directory, scan_from_walkdir};
use fif::findings::Findings; use fif::findings::Findings;
use fif::formats::{Format, PowerShell, Shell}; use fif::formats::{Format, PowerShell, Shell};
use fif::mime_db::MimeDb; 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 mime::{Mime, APPLICATION_OCTET_STREAM, APPLICATION_PDF, IMAGE_JPEG, IMAGE_PNG};
use crate::parameters::ExtensionSet; 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 PDF_BYTES: &[u8] = b"%PDF-";
const ZIP_BYTES: &[u8] = b"PK\x03\x04"; 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 { fn application_zip() -> Mime {
use std::str::FromStr; use std::str::FromStr;
Mime::from_str("application/zip").unwrap() Mime::from_str("application/zip").unwrap()
@ -55,11 +42,10 @@ fn get_ext() {
#[test] #[test]
/// Ensure that the mime types for JPEG, PNG, PDF, and ZIP are detected from their magic numbers. /// Ensure that the mime types for JPEG, PNG, PDF, and ZIP are detected from their magic numbers.
fn detect_type() { fn detect_type() {
let db = get_mime_db(); assert_eq!(MIMEDB.get_type(JPEG_BYTES), Some(IMAGE_JPEG));
assert_eq!(db.get_type(JPEG_BYTES), Some(IMAGE_JPEG)); assert_eq!(MIMEDB.get_type(PNG_BYTES), Some(IMAGE_PNG));
assert_eq!(db.get_type(PNG_BYTES), Some(IMAGE_PNG)); assert_eq!(MIMEDB.get_type(PDF_BYTES), Some(APPLICATION_PDF));
assert_eq!(db.get_type(PDF_BYTES), Some(APPLICATION_PDF)); assert_eq!(MIMEDB.get_type(ZIP_BYTES), Some(application_zip()));
assert_eq!(db.get_type(ZIP_BYTES), Some(application_zip()));
} }
#[test] #[test]
@ -317,14 +303,13 @@ fn rejects_bad_args() {
/// mime database somehow panics or hangs. /// mime database somehow panics or hangs.
fn identify_random_bytes() { fn identify_random_bytes() {
use rand::RngCore; use rand::RngCore;
let db = get_mime_db();
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let mut bytes: [u8; BUF_SIZE * 2] = [0; BUF_SIZE * 2]; let mut bytes: [u8; BUF_SIZE * 2] = [0; BUF_SIZE * 2];
let mut results: HashMap<Mime, i32> = HashMap::new(); let mut results: HashMap<Mime, i32> = HashMap::new();
for _ in 1..1000 { for _ in 1..1000 {
rng.fill_bytes(&mut bytes); 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; *results.entry(detected_type).or_insert(0) += 1;
} }
} }

View File

@ -1,5 +1,7 @@
//! Various minor utilities.
use cfg_if::cfg_if; use cfg_if::cfg_if;
use once_cell::sync::OnceCell; use once_cell::sync::Lazy;
use crate::String; 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. /// The version defined in Cargo.toml, prefixed with a v (e.g. "v0.3.1")
// we can't just do something like `version = format!(...).as_str()`, because clap needs to know that the version will pub(crate) static CLAP_VERSION: Lazy<String> = Lazy::new(|| String::from("v") + VERSION.unwrap_or("???"));
// 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<String> = OnceCell::new();
static CLAP_LONG_VERSION: OnceCell<String> = OnceCell::new();
/// Sets [`CLAP_VERSION`] to be the version defined in Cargo.toml, prefixed with a v (e.g. "v0.3.1"), then returns it as /// Similar to [`CLAP_VERSION`], followed by the chosen backend and abbreviated git commit hash in parentheses - For
/// an str. /// example, "v0.3.6 (XDG-Mime backend, commit #043e097)"
pub fn clap_version() -> &'static str { CLAP_VERSION.get_or_init(|| String::from("v") + VERSION.unwrap_or("???")) } pub static CLAP_LONG_VERSION: Lazy<String> = Lazy::new(|| {
format!(
/// Sets [`CLAP_LONG_VERSION`] to be similar to [`CLAP_VERSION`], followed by the chosen backend and abbreviated git "v{} ({} backend, commit #{})",
/// commit hash in parentheses (e.g. "v0.3.6 (XDG-Mime backend, commit #043e097)"), then returns it as an str. VERSION.unwrap_or("???"),
pub fn clap_long_version() -> &'static str { BACKEND,
CLAP_LONG_VERSION.get_or_init(|| { option_env!("GIT_SHA").unwrap_or("???")
format!( )
"v{} ({} backend, commit #{})", .into()
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". /// Returns the name of the target operating system with proper casing, like "Windows" or "macOS".
#[allow(clippy::option_map_unit_fn)] #[allow(clippy::option_map_unit_fn)]