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:
parent
0c31277191
commit
741048839c
10 changed files with 48 additions and 62 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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};
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
);
|
);
|
||||||
|
|
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
40
src/utils.rs
40
src/utils.rs
|
@ -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)]
|
||||||
|
|
Loading…
Reference in a new issue