Compare commits

...

10 commits

Author SHA1 Message Date
02f377cbb2
release v0.3.2 🥳
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2021-06-14 18:27:50 +10:00
d0a6e918cc
block compilation if both backends are enabled 2021-06-14 18:23:49 +10:00
ec4ad77136
donut repeat yourself 2021-06-14 17:54:11 +10:00
8b2b26d44f
The Great Grammar Heist 2021-06-14 17:47:21 +10:00
bd1a2a3e3a
print some more handy info in trace logs 2021-06-14 17:35:47 +10:00
0278fe252e
kode kleanup 2021-06-14 17:08:52 +10:00
6934dd0b5e
move some commonly used constants into a single module 2021-06-14 17:07:38 +10:00
1ecc6e6c6e
semicolonic cleanse 2021-06-14 16:53:41 +10:00
f8dcdf8a3c
nicer output for fif -V and fif --version 2021-06-14 16:29:41 +10:00
95956f8e30
fixed powershell output format 0u0; 2021-06-13 19:24:21 +10:00
13 changed files with 149 additions and 61 deletions

1
.gitignore vendored
View file

@ -9,3 +9,4 @@ fif_*
cargo-timing*.html cargo-timing*.html
todo.txt todo.txt
/pkg/* /pkg/*
/out

View file

@ -10,6 +10,7 @@
<excludeFolder url="file://$MODULE_DIR$/awful" /> <excludeFolder url="file://$MODULE_DIR$/awful" />
<excludeFolder url="file://$MODULE_DIR$/.mypy_cache" /> <excludeFolder url="file://$MODULE_DIR$/.mypy_cache" />
<excludeFolder url="file://$MODULE_DIR$/pkg" /> <excludeFolder url="file://$MODULE_DIR$/pkg" />
<excludeFolder url="file://$MODULE_DIR$/out" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

View file

@ -2,7 +2,17 @@
Dates are given in YYYY-MM-DD format. Dates are given in YYYY-MM-DD format.
## v0.3 ## v0.3
### v0.3.1 (2021-07-06) ### v0.3.2 (2021-06-14)
#### Bugfixes
- Fixed PowerShell output regression introduced in v0.2.13, whoops
#### Other
- Nicer version output: `fif -V` reports "fif v0.3.2" (instead of just "fif 0.3.2" without the "v"), and `fif --version`
reports `fif v0.3.2 (XDG-Mime backend)`, or whatever backend you're using
- fif's trace output now includes its version, backend, operating system, and architecture
- Block compilation if both the `xdg-mime-backend` and `infer-backend`
[features](https://gitlab.com/Lynnesbian/fif/-/wikis/Cargo-Features) are enabled
### v0.3.1 (2021-06-07)
#### Features #### Features
- Added JSON output support via `-o json` - Added JSON output support via `-o json`
- Added plaintext output support via `-o text` - Added plaintext output support via `-o text`
@ -50,7 +60,7 @@ Dates are given in YYYY-MM-DD format.
- Added .rpa (Ren'Py archive) support to infer backend - Added .rpa (Ren'Py archive) support to infer backend
- [`xdg-mime`] no longer uses git version - [`xdg-mime`] no longer uses git version
- Output `\r\n` on Windows - Output `\r\n` on Windows
- Use a macro to generate `Writable` arrays, making the code a little bit cleaner and nicer to write - Use a macro to generate `Writable` arrays, making the code a little cleaner and nicer to write
### v0.2.12 (2021-04-14) ### v0.2.12 (2021-04-14)
#### Features #### Features
@ -73,13 +83,13 @@ Dates are given in YYYY-MM-DD format.
(files without extensions are still skipped unless the -S flag is used) (files without extensions are still skipped unless the -S flag is used)
#### Bugfixes #### Bugfixes
- Fixed compilation on big endian 32-bit architectures (see - Fixed compilation on big endian 32-bit architectures (see
[here](https://github.com/bodil/smartstring/blob/v0.2.6/src/config.rs#L101-L103) for why that was a problem in the first [here](https://github.com/bodil/smartstring/blob/v0.2.6/src/config.rs#L101-L103) for why that was a problem in the
place) first place)
- Fixed broken tests for the [`infer`] backend - Fixed broken tests for the [`infer`] backend
#### Other #### Other
- Better mime type detection: - Better mime type detection:
- Consider "some/x-thing" and "some/thing" to be identical - Consider "some/x-thing" and "some/thing" to be identical
- Use a patched version of mime_guess (which took a while to make 0u0;) with many more extension<->type mappings - Use a patched version of mime_guess (which took a while to make 0u0;) with many more extension/type mappings
### v0.2.10 (2021-03-26) ### v0.2.10 (2021-03-26)
- PowerShell support! - PowerShell support!
@ -109,7 +119,7 @@ Dates are given in YYYY-MM-DD format.
### v0.2.6 (2021-02-28) ### v0.2.6 (2021-02-28)
- Added tests! - Added tests!
- Default to [`xdg-mime`] on all Unixy platforms, not just Linux - this also includes the various *BSDs (I've tested - Default to [`xdg-mime`] on all Unixy platforms, not just Linux - this also includes the various *BSDs (I've tested
FreeBSD), macOS (haven't tested, but I have a very old MacBook running Leopard that has file preinstalled, so it FreeBSD), macOS (haven't tested, but I have a very old MacBook running Leopard that has `file` preinstalled, so it
*should* work fine), Redox OS (haven't tested), etc. *should* work fine), Redox OS (haven't tested), etc.
### v0.2.5 (2021-02-27) ### v0.2.5 (2021-02-27)
@ -143,7 +153,7 @@ Dates are given in YYYY-MM-DD format.
#### Features #### Features
- Added extension sets -- you can now use, for example, `-E images` to check files with known image extensions - Added extension sets -- you can now use, for example, `-E images` to check files with known image extensions
- Shell script output now uses `printf` instead of `echo` - Shell script output now uses `printf` instead of `echo`
- Added [`infer`] backend - Added [`infer`] backend, configurable with [Cargo features](https://gitlab.com/Lynnesbian/fif/-/wikis/Cargo-Features)
#### Bugfixes #### Bugfixes
- Fixed broken singlethreaded support - Fixed broken singlethreaded support
#### Other #### Other
@ -166,7 +176,7 @@ Dates are given in YYYY-MM-DD format.
### v0.1.0 (2021-02-04) ### v0.1.0 (2021-02-04)
Initial commit! Initial commit!
- Only one backend - [`xdg-mime`] - Only one backend - [`xdg-mime`]
- Prints files directly rather than outputting a script - No output formats - just prints a list of files to be renamed
- Only supported flags are `-e` (specify extensions) and `-s` (scan hidden files) - Only supported flags are `-e` (specify extensions) and `-s` (scan hidden files)
<!-- links --> <!-- links -->

6
Cargo.lock generated
View file

@ -183,7 +183,7 @@ checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
[[package]] [[package]]
name = "fif" name = "fif"
version = "0.3.1" version = "0.3.2"
dependencies = [ dependencies = [
"cached", "cached",
"cfg-if", "cfg-if",
@ -369,9 +369,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.7.2" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"

View file

@ -1,7 +1,7 @@
[package] [package]
name = "fif" name = "fif"
description = "A command-line tool for detecting and optionally correcting files with incorrect extensions." description = "A command-line tool for detecting and optionally correcting files with incorrect extensions."
version = "0.3.1" version = "0.3.2"
authors = ["Lynnesbian <lynne@bune.city>"] authors = ["Lynnesbian <lynne@bune.city>"]
edition = "2018" edition = "2018"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
@ -29,7 +29,7 @@ walkdir = "2.3.2"
log = "0.4.14" log = "0.4.14"
mime_guess = { package = "new_mime_guess", version = "2.1.0" } mime_guess = { package = "new_mime_guess", version = "2.1.0" }
snailquote = "0.3.0" snailquote = "0.3.0"
once_cell = "1.7.2" once_cell = "1.8.0"
rayon = { version = "1.5.0", optional = true } rayon = { version = "1.5.0", optional = true }
exitcode = "1.1.2" exitcode = "1.1.2"
cfg-if = "1.0.0" cfg-if = "1.0.0"

10
build.rs Normal file
View file

@ -0,0 +1,10 @@
#[allow(unreachable_code, clippy::pedantic)]
fn main() -> Result<(), String> {
#[cfg(all(feature = "infer-backend", feature = "xdg-mime-backend"))]
// fail build if the user has set both the infer and xdg-mime backends
return Err(String::from(
"fif cannot be compiled with multiple backends set - please enable only one, or use the default.",
));
Ok(())
}

View file

@ -2,30 +2,36 @@
set -e set -e
_extra="" _extra=""
_ver="+stable"
if [ "$1" == "ci" ]; then if [ "$1" == "ci" ]; then
# deny on warnings when running in CI # deny on warnings when running in CI
_extra="-Dwarnings" _extra="-Dwarnings"
elif [ "$1" == "nightly" ]; then
_ver="+nightly"
fi fi
# allow find to fail # allow find to fail
find . -name '*.rs' -exec touch "{}" \; || true find . -name '*.rs' -exec touch "{}" \; || true
cargo clippy --all-features --tests -- \ _backends=( "xdg-mime-backend" "infer-backend" )
-W clippy::nursery \
-W clippy::perf \ for backend in "${_backends[@]}"; do
-W clippy::pedantic \ cargo $_ver clippy --tests --features="$backend" -- \
-W clippy::complexity \ -W clippy::nursery \
-W clippy::cargo \ -W clippy::perf \
-W clippy::float_cmp_const \ -W clippy::pedantic \
-W clippy::lossy_float_literal \ -W clippy::complexity \
-W clippy::multiple_inherent_impl \ -W clippy::cargo \
-W clippy::string_to_string \ -W clippy::float_cmp_const \
-W clippy::wrong_pub_self_convention \ -W clippy::lossy_float_literal \
-A clippy::unused_io_amount \ -W clippy::multiple_inherent_impl \
-A clippy::redundant_closure_for_method_calls \ -W clippy::string_to_string \
-A clippy::shadow_unrelated \ -A clippy::unused_io_amount \
-A clippy::option_if_let_else \ -A clippy::redundant_closure_for_method_calls \
"$_extra" -A clippy::shadow_unrelated \
-A clippy::option_if_let_else \
"$_extra"
done
# ALLOWS: # ALLOWS:
# unused_io_amount: there are two places where i want to read up to X bytes and i'm fine with getting less than that # unused_io_amount: there are two places where i want to read up to X bytes and i'm fine with getting less than that

2
clippy.toml Normal file
View file

@ -0,0 +1,2 @@
# avoid-breaking-exported-api = false # only available on nightly for now
cognitive-complexity-threshold = 15

View file

@ -10,7 +10,8 @@ use cfg_if::cfg_if;
use snailquote::escape; use snailquote::escape;
use crate::findings::ScanError; use crate::findings::ScanError;
use crate::{Findings, BACKEND}; use crate::utils::clap_long_version;
use crate::Findings;
use itertools::Itertools; use itertools::Itertools;
/// A macro for creating an array of `Writable`s without needing to pepper your code with `into()`s. /// A macro for creating an array of `Writable`s without needing to pepper your code with `into()`s.
@ -49,9 +50,6 @@ macro_rules! writablesln {
}; };
} }
/// The current version of fif, as defined in Cargo.toml.
const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
#[doc(hidden)] #[doc(hidden)]
type Entries<'a> = [Result<Findings<'a>, ScanError<'a>>]; type Entries<'a> = [Result<Findings<'a>, ScanError<'a>>];
@ -76,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 {} ({} backend)", VERSION.unwrap_or("???"), BACKEND) } fn generated_by() -> String { format!("Generated by fif {}", clap_long_version()) }
fn smart_write<W: Write>(f: &mut W, writeables: &[Writable]) -> io::Result<()> { fn smart_write<W: Write>(f: &mut W, writeables: &[Writable]) -> io::Result<()> {
// ehhhh // ehhhh
@ -86,9 +84,9 @@ fn smart_write<W: Write>(f: &mut W, writeables: &[Writable]) -> io::Result<()> {
Writable::Newline => { Writable::Newline => {
cfg_if! { cfg_if! {
if #[cfg(windows)] { if #[cfg(windows)] {
write!(f, "\r\n")? write!(f, "\r\n")?;
} else { } else {
writeln!(f,)? writeln!(f,)?;
} }
} }
} }
@ -100,9 +98,9 @@ fn smart_write<W: Write>(f: &mut W, writeables: &[Writable]) -> io::Result<()> {
// the escaped string is the same as the input - this will occur for inputs like "file.txt" which don't // the escaped string is the same as the input - this will occur for inputs like "file.txt" which don't
// need to be escaped. however, it's Best Practice™ to escape such strings anyway, so we prefix/suffix the // need to be escaped. however, it's Best Practice™ to escape such strings anyway, so we prefix/suffix the
// escaped string with single quotes. // escaped string with single quotes.
write!(f, "'{}'", escaped)? write!(f, "'{}'", escaped)?;
} else { } else {
write!(f, "{}", escaped)? write!(f, "{}", escaped)?;
} }
} else { } else {
write!(f, "'")?; write!(f, "'")?;
@ -116,7 +114,7 @@ fn smart_write<W: Write>(f: &mut W, writeables: &[Writable]) -> io::Result<()> {
f.write_all(&*path.as_os_str().as_bytes())?; f.write_all(&*path.as_os_str().as_bytes())?;
} }
} }
write!(f, "'")? write!(f, "'")?;
} }
} }
} }
@ -171,9 +169,9 @@ pub trait Format {
for finding in findings { for finding in findings {
if let Some(ext) = finding.recommended_extension() { if let Some(ext) = finding.recommended_extension() {
self.rename(f, finding.file, &finding.file.with_extension(ext.as_str()))? self.rename(f, finding.file, &finding.file.with_extension(ext.as_str()))?;
} else { } else {
self.no_known_extension(f, finding.file)? self.no_known_extension(f, finding.file)?;
} }
} }
@ -242,7 +240,7 @@ impl Format for PowerShell {
fn no_known_extension<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> { fn no_known_extension<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> {
smart_write( smart_write(
f, f,
writables![ writablesln![
"Write-Output @'", "Write-Output @'",
Newline, Newline,
"No known extension for ", "No known extension for ",
@ -256,7 +254,7 @@ impl Format for PowerShell {
fn unreadable<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> { fn unreadable<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> {
smart_write( smart_write(
f, f,
writables!["Write-Output @'", Newline, "Failed to read ", path, Newline, "'@"], writablesln!["Write-Output @'", Newline, "Failed to read ", path, Newline, "'@"],
) )
} }

View file

@ -34,6 +34,7 @@ use crate::findings::ScanError;
use crate::formats::{Format, PowerShell, Shell}; use crate::formats::{Format, PowerShell, Shell};
use crate::mime_db::MimeDb; use crate::mime_db::MimeDb;
use crate::parameters::{OutputFormat, ScanOpts}; use crate::parameters::{OutputFormat, ScanOpts};
use crate::utils::{clap_long_version, os_name};
use std::collections::BTreeSet; use std::collections::BTreeSet;
mod findings; mod findings;
@ -45,22 +46,20 @@ pub(crate) mod string_type;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
mod utils;
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 [OnceCell] holding an instance of [mime_db::MimeDb]. /// A [OnceCell] holding an instance of [mime_db::MimeDb].
static MIMEDB: OnceCell<mime_db::InferDb> = OnceCell::new(); static MIMEDB: OnceCell<mime_db::InferDb> = OnceCell::new();
/// The backend being used; either "Infer" or "XDG-Mime".
const BACKEND: &str = "Infer";
} else { } else {
/// A [OnceCell] holding an instance of [mime_db::MimeDb]. /// A [OnceCell] holding an instance of [mime_db::MimeDb].
static MIMEDB: OnceCell<mime_db::XdgDb> = OnceCell::new(); static MIMEDB: OnceCell<mime_db::XdgDb> = OnceCell::new();
/// The backend being used; either "Infer" or "XDG-Mime".
const BACKEND: &str = "XDG-Mime";
} }
} }
#[doc(hidden)] #[doc(hidden)]
#[allow(clippy::cognitive_complexity)]
fn main() { fn main() {
let args: parameters::Parameters = parameters::Parameters::parse(); let args: parameters::Parameters = parameters::Parameters::parse();
@ -73,6 +72,12 @@ fn main() {
// .target(env_logger::Target::Stdout) // log to stdout rather than stderr // .target(env_logger::Target::Stdout) // log to stdout rather than stderr
.init(); .init();
trace!(
"fif {}, running on {} {}",
clap_long_version(),
std::env::consts::ARCH,
os_name()
);
trace!("Initialise mimetype database"); trace!("Initialise mimetype database");
init_db(); init_db();
@ -126,7 +131,7 @@ fn main() {
r.file, r.file,
r.mime, r.mime,
r.recommended_extension().unwrap_or_else(|| "???".into()) r.recommended_extension().unwrap_or_else(|| "???".into())
) );
} }
Err(f) => warn!("{}", f), Err(f) => warn!("{}", f),
} }

View file

@ -1,6 +1,7 @@
//! [Clap] struct used to parse command line arguments. //! [Clap] struct used to parse command line arguments.
use crate::string_type::String as StringType; use crate::string_type::String as StringType;
use crate::utils::{clap_long_version, clap_version};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use clap::{AppSettings, Clap}; use clap::{AppSettings, Clap};
use std::collections::BTreeSet; use std::collections::BTreeSet;
@ -33,7 +34,8 @@ pub enum OutputFormat {
#[derive(Clap, Debug)] #[derive(Clap, Debug)]
#[clap( #[clap(
version = option_env!("CARGO_PKG_VERSION").unwrap_or("???"), version = clap_version(),
long_version = clap_long_version(),
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.",

View file

@ -49,7 +49,7 @@ fn get_ext() {
ext_checks.insert(Path::new(".hidden"), None); ext_checks.insert(Path::new(".hidden"), None);
for (path, ext) in ext_checks { for (path, ext) in ext_checks {
assert_eq!(extension_from_path(path), ext) assert_eq!(extension_from_path(path), ext);
} }
} }
@ -204,7 +204,7 @@ fn argument_parsing() {
follow_symlinks: true, follow_symlinks: true,
}, },
"ScanOpts are incorrect" "ScanOpts are incorrect"
) );
} }
#[test] #[test]
@ -214,7 +214,7 @@ fn positional_args() {
assert_eq!( assert_eq!(
Parameters::parse_from(vec!["fif", flag, "images", "directory"]).dir, Parameters::parse_from(vec!["fif", flag, "images", "directory"]).dir,
PathBuf::from("directory") PathBuf::from("directory")
) );
} }
} }
@ -272,12 +272,12 @@ fn exclude_set_overrides_include_set() {
.iter() .iter()
.chain(ExtensionSet::Video.extensions().iter()) .chain(ExtensionSet::Video.extensions().iter())
{ {
assert!(extensions.contains(&ext), "Extensions should contain {}!", ext) assert!(extensions.contains(&ext), "Extensions should contain {}!", ext);
} }
// ensure all of images' extensions are excluded // ensure all of images' extensions are excluded
for ext in ExtensionSet::Images.extensions() { for ext in ExtensionSet::Images.extensions() {
assert!(!extensions.contains(&ext), "Extensions should not contain {}!", ext) assert!(!extensions.contains(&ext), "Extensions should not contain {}!", ext);
} }
} }
@ -322,7 +322,7 @@ fn identify_random_bytes() {
for (mime, count) in &results { for (mime, count) in &results {
println!("{}:\t{} counts", mime, count); println!("{}:\t{} counts", mime, count);
} }
println!("No type found:\t{} counts", 500 - results.values().sum::<i32>()) println!("No type found:\t{} counts", 500 - results.values().sum::<i32>());
} }
#[test] #[test]
@ -360,7 +360,7 @@ fn outputs_move_commands() {
"{} output doesn't contain move command!\n===\n{}", "{} output doesn't contain move command!\n===\n{}",
format, format,
contents contents
) );
} }
} }
@ -394,7 +394,7 @@ fn test_json() {
contents.contains(IMAGE_JPEG.essence_str()), contents.contains(IMAGE_JPEG.essence_str()),
"JSON output doesn't contain move command!\n===\n{}", "JSON output doesn't contain move command!\n===\n{}",
contents contents
) );
} }
#[test] #[test]
@ -412,7 +412,7 @@ fn media_contains_audio_video_images() {
assert_eq!( assert_eq!(
Parameters::parse_from(&["fif", "-E", "media"]).extensions(), Parameters::parse_from(&["fif", "-E", "media"]).extensions(),
Parameters::parse_from(&["fif", "-E", "audio,video,images"]).extensions() Parameters::parse_from(&["fif", "-E", "audio,video,images"]).extensions()
) );
} }
#[test] #[test]
@ -429,7 +429,7 @@ fn writables_is_correct() {
Writable::Space Writable::Space
], ],
writables!["henlo", (Path::new("henlo")), Newline, Space] writables!["henlo", (Path::new("henlo")), Newline, Space]
) );
} }
#[test] #[test]
@ -452,6 +452,6 @@ fn verbosity() {
expected_results.insert("-vvvvvvvv", "trace"); expected_results.insert("-vvvvvvvv", "trace");
for (flags, level) in expected_results { for (flags, level) in expected_results {
assert_eq!(Parameters::parse_from(&["fif", flags]).default_verbosity(), level) assert_eq!(Parameters::parse_from(&["fif", flags]).default_verbosity(), level);
} }
} }

53
src/utils.rs Normal file
View file

@ -0,0 +1,53 @@
use cfg_if::cfg_if;
use once_cell::sync::OnceCell;
/// The current version of fif, as defined in Cargo.toml.
pub const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
cfg_if! {
if #[cfg(any(all(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] {
/// The backend being used; either "Infer" or "XDG-Mime".
pub const BACKEND: &str = "Infer";
} else {
/// The backend being used; either "Infer" or "XDG-Mime".
pub const BACKEND: &str = "XDG-Mime";
}
}
// 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();
/// Sets [`CLAP_VERSION`] to be the version defined in Cargo.toml, prefixed with a v (e.g. "v0.3.1"), then returns it as
/// a String.
pub fn clap_version() -> &'static str { CLAP_VERSION.get_or_init(|| format!("v{}", VERSION.unwrap_or("???"))) }
/// Sets [`CLAP_LONG_VERSION`] to be similar to [`CLAP_VERSION`], followed by the chosen backend in parentheses (e.g.
/// "v0.3.1 (XDG-Mime backend)"), then returns it as a String.
pub fn clap_long_version() -> &'static str {
CLAP_LONG_VERSION.get_or_init(|| format!("v{} ({} backend)", VERSION.unwrap_or("???"), BACKEND))
}
/// Returns the name of the target operating system, like "Windows" or "macOS". Won't account for things like Wine or
/// Linuxulator.
#[allow(clippy::option_map_unit_fn)]
pub fn os_name() -> String {
use std::env::consts::OS;
match OS {
"ios" => "iOS".into(),
"macos" => "macOS".into(),
"freebsd" => "FreeBSD".into(),
"openbsd" => "OpenBSD".into(),
"netbsd" => "NetBSD".into(),
"vxworks" => "VxWorks".into(),
_ => {
// generic case: return consts::OS with the first letter in uppercase ("linux" -> "Linux")
let mut os_upper = String::from(OS);
os_upper.get_mut(0..1).map(|first| first.make_ascii_uppercase());
os_upper
}
}
}