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
### 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

View file

@ -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<RwLock<HashMap<String, Option<Vec<String>>>>> = 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<Findings, ScanError> {
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

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::path::{Path, PathBuf};

View file

@ -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<W: Write>(f: &mut W, writeables: &[Writable]) -> io::Result<()> {
// ehhhh

View file

@ -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<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 {
/// 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::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()
);

View file

@ -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<Mime>;
}

View file

@ -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,

View file

@ -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<Mime, i32> = 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;
}
}

View file

@ -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<String> = OnceCell::new();
static CLAP_LONG_VERSION: OnceCell<String> = OnceCell::new();
/// The version defined in Cargo.toml, prefixed with a v (e.g. "v0.3.1")
pub(crate) static CLAP_VERSION: Lazy<String> = 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<String> = 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)]