pub enum Extension {
Ext2(u16),
Ext3(u32),
Ext4(u32),
}
Expand description
§Extension.
This enum can be used to efficiently check a file path’s extension case- insensitively against a hard-coded reference extension. It is likely overkill in most situations, but if you’re looking to optimize the filtering of large path lists, this can turn those painful nanosecond operations into pleasant picosecond ones!
The magic is largely down to storing values as u16
or u32
integers and
comparing those (rather than byte slices or OsStr
), and not messing
around with the path Components
iterator. (Note, this is done using the
safe u*::from_le_bytes()
methods rather than casting chicanery.)
At the moment, only extensions sized between 2-4 bytes are supported as those sizes are the most common and also translate perfectly to primitives, but larger values may be added in the future.
§Reference Constructors.
A “reference” extension is one known to you ahead of time, i.e. what you’re
looking for. These can be constructed using the constant Extension::new2
,
Extension::new3
, and Extension::new4
methods.
Because these are “known” values, no logical validation is performed. If you do something silly like mix case or type them incorrectly, equality tests will fail. You’d only be hurting yourself!
use dowser::Extension;
const EXT2: Extension = Extension::new2(*b"gz");
const EXT3: Extension = Extension::new3(*b"png");
const EXT4: Extension = Extension::new4(*b"html");
The main idea is you’ll pre-compute these values and compare unknown runtime values against them later.
§Runtime Constructors.
A “runtime” extension, for lack of a better adjective, is a value you
don’t know ahead of time, e.g. from a user-supplied path. These can be
constructed using the Extension::try_from2
, Extension::try_from3
,
and Extension::try_from4
methods, which accept any AsRef<Path>
argument.
The method you choose should match the length you’re looking for. For
example, if you’re hoping for a PNG, use Extension::try_from3
.
use dowser::Extension;
const EXT3: Extension = Extension::new3(*b"png");
assert_eq!(Extension::try_from3("/path/to/IMAGE.PNG"), Some(EXT3));
assert_eq!(Extension::try_from3("/path/to/doc.html"), None);
§Examples
To filter a list of image paths with the standard library — say, matching PNGs — you would do something like:
use std::ffi::OsStr;
use std::path::PathBuf;
// Imagine this is much longer…
let paths = vec![PathBuf::from("/path/to/image.png")];
paths.iter()
.filter(|p| p.extension()
.map_or(false, |e| e.eq_ignore_ascii_case(OsStr::new("png")))
)
.for_each(|p| todo!());
Using Extension
instead, the same operation looks like:
use dowser::Extension;
use std::path::PathBuf;
// Imagine this is much longer…
let paths = vec![PathBuf::from("/path/to/image.png")];
// The reference extension.
const EXT: Extension = Extension::new3(*b"png");
paths.iter()
.filter(|p| Extension::try_from3(p).map_or(false, |e| e == EXT))
.for_each(|p| todo!());
Variants§
Ext2(u16)
§2-char Extension.
Like .gz
.
Ext3(u32)
§3-char Extension.
Like .png
.
Ext4(u32)
§4-char Extension.
Like .html
.
Implementations§
source§impl Extension
impl Extension
§Unchecked Instantiation.
sourcepub const fn new2(src: [u8; 2]) -> Self
pub const fn new2(src: [u8; 2]) -> Self
§New Unchecked (2).
Create a new Extension
, unchecked, from two bytes, e.g. *b"gz"
.
This should be lowercase and not include a period.
This method is intended for known values that you want to check unknown values against. Sanity-checking is traded for performance, but you’re only hurting yourself if you misuse it.
For compile-time generation, see Extension::codegen
.
§Examples
use dowser::Extension;
const MY_EXT: Extension = Extension::new2(*b"gz");
sourcepub const fn new3(src: [u8; 3]) -> Self
pub const fn new3(src: [u8; 3]) -> Self
§New Unchecked (3).
Create a new Extension
, unchecked, from three bytes, e.g. *b"gif"
.
This should be lowercase and not include a period.
This method is intended for known values that you want to check unknown values against. Sanity-checking is traded for performance, but you’re only hurting yourself if you misuse it.
For compile-time generation, see Extension::codegen
.
§Examples
use dowser::Extension;
const MY_EXT: Extension = Extension::new3(*b"gif");
sourcepub const fn new4(src: [u8; 4]) -> Self
pub const fn new4(src: [u8; 4]) -> Self
§New Unchecked (4).
Create a new Extension
, unchecked, from four bytes, e.g. *b"html"
.
This should be lowercase and not include a period.
This method is intended for known values that you want to check unknown values against. Sanity-checking is traded for performance, but you’re only hurting yourself if you misuse it.
For compile-time generation, see Extension::codegen
.
§Examples
use dowser::Extension;
const MY_EXT: Extension = Extension::new4(*b"html");
source§impl Extension
impl Extension
§From Paths.
sourcepub fn try_from2<P>(path: P) -> Option<Self>
pub fn try_from2<P>(path: P) -> Option<Self>
§Try From Path (2).
This method is used to (try to) pull a 2-byte extension from a file
path. This requires that the path be at least 4 bytes, with anything
but a forward/backward slash at [len - 4]
and a dot at [len - 3]
.
If successful, it will return an Extension::Ext2
that can be
compared against your reference Extension
. Casing will be fixed
automatically.
§Examples
use dowser::Extension;
const MY_EXT: Extension = Extension::new2(*b"gz");
assert_eq!(Extension::try_from2("/path/to/file.gz"), Some(MY_EXT));
assert_eq!(Extension::try_from2("/path/to/file.GZ"), Some(MY_EXT));
assert_eq!(Extension::try_from2("/path/to/file.png"), None);
assert_ne!(Extension::try_from2("/path/to/file.br"), Some(MY_EXT));
sourcepub fn try_from3<P>(path: P) -> Option<Self>
pub fn try_from3<P>(path: P) -> Option<Self>
§Try From Path (3).
This method is used to (try to) pull a 3-byte extension from a file
path. This requires that the path be at least 5 bytes, with anything
but a forward/backward slash at [len - 5]
and a dot at [len - 4]
.
If successful, it will return an Extension::Ext3
that can be
compared against your reference Extension
. Casing will be fixed
automatically.
§Examples
use dowser::Extension;
const MY_EXT: Extension = Extension::new3(*b"png");
assert_eq!(Extension::try_from3("/path/to/file.png"), Some(MY_EXT));
assert_eq!(Extension::try_from3("/path/to/FILE.PNG"), Some(MY_EXT));
assert_eq!(Extension::try_from3("/path/to/file.html"), None);
assert_ne!(Extension::try_from3("/path/to/file.jpg"), Some(MY_EXT));
sourcepub fn try_from4<P>(path: P) -> Option<Self>
pub fn try_from4<P>(path: P) -> Option<Self>
§Try From Path (4).
This method is used to (try to) pull a 4-byte extension from a file
path. This requires that the path be at least 6 bytes, with anything
but a forward/backward slash at [len - 6]
and a dot at [len - 5]
.
If successful, it will return an Extension::Ext4
that can be
compared against your reference Extension
. Casing will be fixed
automatically.
§Examples
use dowser::Extension;
const MY_EXT: Extension = Extension::new4(*b"html");
assert_eq!(Extension::try_from4("/path/to/file.html"), Some(MY_EXT));
assert_eq!(Extension::try_from4("/path/to/FILE.HTML"), Some(MY_EXT));
assert_eq!(Extension::try_from4("/path/to/file.png"), None);
assert_ne!(Extension::try_from4("/path/to/file.xhtm"), Some(MY_EXT));
source§impl Extension
impl Extension
§From Slices.
sourcepub const fn slice_ext2(path: &[u8]) -> Option<Self>
pub const fn slice_ext2(path: &[u8]) -> Option<Self>
§Extension Slice (2).
This method is used to (try to) pull a 2-byte extension from a file
path in slice form. This requires that the path be at least 4 bytes,
with anything but a forward/backward slash at [len - 4]
and a dot at
[len - 3]
.
If successful, it will return an Extension::Ext2
that can be
compared against your reference Extension
. Casing will be fixed
automatically.
§Examples
use dowser::Extension;
const MY_EXT: Extension = Extension::new2(*b"gz");
assert_eq!(Extension::slice_ext2(b"/path/to/file.gz"), Some(MY_EXT));
assert_eq!(Extension::slice_ext2(b"/path/to/file.GZ"), Some(MY_EXT));
// Non-matches.
assert_eq!(Extension::slice_ext2(b"/path/to/.gz"), None);
assert_eq!(Extension::slice_ext2(b"/path/to\\.gz"), None);
assert_eq!(Extension::slice_ext2(b"/path/to/file.png"), None);
assert_ne!(Extension::slice_ext2(b"/path/to/file.br"), Some(MY_EXT));
sourcepub const fn slice_ext3(path: &[u8]) -> Option<Self>
pub const fn slice_ext3(path: &[u8]) -> Option<Self>
§Extension Slice (3).
This method is used to (try to) pull a 3-byte extension from a file
path in slice form. This requires that the path be at least 5 bytes,
with anything but a forward/backward slash at [len - 5]
and a dot at
[len - 4]
.
If successful, it will return an Extension::Ext3
that can be
compared against your reference Extension
. Casing will be fixed
automatically.
§Examples
use dowser::Extension;
const MY_EXT: Extension = Extension::new3(*b"png");
assert_eq!(Extension::slice_ext3(b"/path/to/file.png"), Some(MY_EXT));
assert_eq!(Extension::slice_ext3(b"/path/to/FILE.PNG"), Some(MY_EXT));
// Non-matches.
assert_eq!(Extension::slice_ext3(b"/path/to/.png"), None);
assert_eq!(Extension::slice_ext3(b"/path/to\\.png"), None);
assert_eq!(Extension::slice_ext3(b"/path/to/file.html"), None);
assert_ne!(Extension::slice_ext3(b"/path/to/file.jpg"), Some(MY_EXT));
sourcepub const fn slice_ext4(path: &[u8]) -> Option<Self>
pub const fn slice_ext4(path: &[u8]) -> Option<Self>
§Extension Slice (4).
This method is used to (try to) pull a 4-byte extension from a file
path in slice form. This requires that the path be at least 6 bytes,
with anything but a forward/backward slash at [len - 6]
and a dot at
[len - 5]
.
If successful, it will return an Extension::Ext4
that can be
compared against your reference Extension
. Casing will be fixed
automatically.
§Examples
use dowser::Extension;
const MY_EXT: Extension = Extension::new4(*b"html");
assert_eq!(Extension::slice_ext4(b"/path/to/file.html"), Some(MY_EXT));
assert_eq!(Extension::slice_ext4(b"/path/to/FILE.HTML"), Some(MY_EXT));
// Non-matches.
assert_eq!(Extension::slice_ext2(b"/path/to/.html"), None);
assert_eq!(Extension::slice_ext2(b"/path/to\\.html"), None);
assert_eq!(Extension::slice_ext4(b"/path/to/file.png"), None);
assert_ne!(Extension::slice_ext4(b"/path/to/file.xhtm"), Some(MY_EXT));
sourcepub fn slice_ext(src: &[u8]) -> Option<&[u8]>
pub fn slice_ext(src: &[u8]) -> Option<&[u8]>
§Slice Extension.
This returns the file extension portion of a path as a byte slice,
similar to std::path::Path::extension
, but faster since it is dealing with
straight bytes.
The extension is found by jumping to the last period, ensuring the byte before that period is not a path separator, and that there are one or more bytes after that period (none of which are path separators).
If the above are all good, a slice containing everything after that last period is returned.
§Examples
use dowser::Extension;
// Uppercase in, uppercase out.
assert_eq!(
Extension::slice_ext(b"/path/to/IMAGE.JPEG"),
Some(&b"JPEG"[..])
);
// Lowercase in, lowercase out.
assert_eq!(
Extension::slice_ext(b"/path/to/file.docx"),
Some(&b"docx"[..])
);
// These are all bad, though:
assert_eq!(
Extension::slice_ext(b"/path/to/.hide"),
None
);
assert_eq!(
Extension::slice_ext(b"/path/to/"),
None
);
assert_eq!(
Extension::slice_ext(b"/path/to/file."),
None
);
source§impl Extension
impl Extension
§Codegen Helpers.
sourcepub fn codegen(src: &[u8]) -> String
pub fn codegen(src: &[u8]) -> String
§Codegen Helper.
This compile-time method can be used in a build.rs
script to
generate a pre-computed Extension
value of any supported length
(2-4 bytes).
Unlike the runtime methods, this will automatically fix case and period inconsistencies, but ideally you should still pass it just the letters, in lowercase, because you have the power to do so. Haha.
§Examples
use dowser::Extension;
// This is what it looks like.
assert_eq!(
Extension::codegen(b"js"),
"Extension::Ext2(29_546_u16)"
);
assert_eq!(
Extension::codegen(b"jpg"),
"Extension::Ext3(1_735_420_462_u32)"
);
assert_eq!(
Extension::codegen(b"html"),
"Extension::Ext4(1_819_112_552_u32)"
);
In a typical build.rs
workflow, you’d be building up a string of
other code around it, and saving it all to a file, like:
use dowser::Extension;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
let out = format!(
"const MY_EXT: Extension = {};",
Extension::codegen(b"jpg")
);
let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap())
.join("compile-time-vars.rs");
let mut f = File::create(out_path).unwrap();
f.write_all(out.as_bytes()).unwrap();
f.flush().unwrap();
}
Then in your main program, say lib.rs
, you’d toss an include!()
to
that file to import the code as code, like:
use dowser::Extension;
include!(concat!(env!("OUT_DIR"), "/compile-time-vars.rs"));
Et voilà, you’ve saved yourself a nanosecond of runtime effort! Haha.
§Panics
This will panic if the extension (minus punctuation) is not 2-4 bytes or contains whitespace or path separators.