Skip to content

Commit

Permalink
Merge pull request #11 from jkneer/master
Browse files Browse the repository at this point in the history
replaced lazy_static with OnceLock
  • Loading branch information
kardeiz authored Nov 7, 2024
2 parents ee35c34 + 2e95304 commit 9428a94
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 112 deletions.
34 changes: 9 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
[package]
authors = ["Jacob Brown <[email protected]>"]
name = "sanitize-filename"
version = "0.5.0"
version = "0.6.0"
keywords = ["filename", "sanitizer"]
license = "MIT"
readme = "README.md"
edition = "2021"
rust-version = "1.70.0"
repository = "https://github.com/kardeiz/sanitize-filename"
description = "A simple filename sanitizer, based on Node's sanitize-filename"

[dependencies]
lazy_static = "1"
regex = { version = "1.9", default-features = false, features = ["std", "unicode-case"] }
regex = { version = "1.11", default-features = false, features = [
"std",
"unicode-case",
] }
157 changes: 73 additions & 84 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,52 @@
use std::sync::OnceLock;

extern crate regex;
use regex::{Regex, RegexBuilder};

#[macro_use]
extern crate lazy_static;
static ILLEGAL_RE: OnceLock<Regex> = OnceLock::new();
static CONTROL_RE: OnceLock<Regex> = OnceLock::new();
static RESERVED_RE: OnceLock<Regex> = OnceLock::new();
static WINDOWS_RESERVED_RE: OnceLock<Regex> = OnceLock::new();
static WINDOWS_TRAILING_RE: OnceLock<Regex> = OnceLock::new();

use regex::{Regex, RegexBuilder};
fn illegal_re() -> &'static Regex {
ILLEGAL_RE.get_or_init(|| Regex::new(r#"[/\?<>\\:\*\|":]"#).unwrap())
}

fn control_re() -> &'static Regex {
CONTROL_RE.get_or_init(|| Regex::new(r#"[\x00-\x1f\x80-\x9f]"#).unwrap())
}

fn reserved_re() -> &'static Regex {
RESERVED_RE.get_or_init(|| Regex::new(r#"^\.+$"#).unwrap())
}

fn windows_reserved_re() -> &'static Regex {
WINDOWS_RESERVED_RE.get_or_init(|| {
RegexBuilder::new(r#"(?i)^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$"#)
.case_insensitive(true)
.build()
.unwrap()
})
}

lazy_static! {
static ref ILLEGAL_RE: Regex = Regex::new(r#"[/\?<>\\:\*\|":]"#).unwrap();
static ref CONTROL_RE: Regex = Regex::new(r#"[\x00-\x1f\x80-\x9f]"#).unwrap();
static ref RESERVED_RE: Regex = Regex::new(r#"^\.+$"#).unwrap();
static ref WINDOWS_RESERVED_RE: Regex = RegexBuilder::new(r#"(?i)^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$"#)
.case_insensitive(true)
.build()
.unwrap();
static ref WINDOWS_TRAILING_RE: Regex = Regex::new(r#"[\. ]+$"#).unwrap();
fn windows_trailing_re() -> &'static Regex {
WINDOWS_TRAILING_RE.get_or_init(|| Regex::new(r#"[\. ]+$"#).unwrap())
}

#[derive(Clone)]
pub struct Options<'a> {
pub windows: bool,
pub truncate: bool,
pub replacement: &'a str
pub replacement: &'a str,
}

impl<'a> Default for Options<'a> {
fn default() -> Self {
Options {
windows: cfg!(windows),
truncate: true,
replacement: ""
replacement: "",
}
}
}
Expand All @@ -38,19 +56,24 @@ pub fn sanitize<S: AsRef<str>>(name: S) -> String {
}

pub fn sanitize_with_options<S: AsRef<str>>(name: S, options: Options) -> String {

let Options { windows, truncate, replacement } = options;
let Options {
windows,
truncate,
replacement,
} = options;
let name = name.as_ref();
let name = ILLEGAL_RE.replace_all(&name, replacement);
let name = CONTROL_RE.replace_all(&name, replacement);
let name = RESERVED_RE.replace(&name, replacement);

let name = illegal_re().replace_all(&name, replacement);
let name = control_re().replace_all(&name, replacement);
let name = reserved_re().replace(&name, replacement);

let collect = |name: ::std::borrow::Cow<str>| {
if truncate && name.len() > 255 {
let mut end = 255;
loop {
if name.is_char_boundary(end) { break; }
if name.is_char_boundary(end) {
break;
}
end -= 1;
}
String::from(&name[..end])
Expand All @@ -60,13 +83,12 @@ pub fn sanitize_with_options<S: AsRef<str>>(name: S, options: Options) -> String
};

if windows {
let name = WINDOWS_RESERVED_RE.replace(&name, replacement);
let name = WINDOWS_TRAILING_RE.replace(&name, replacement);
let name = windows_reserved_re().replace(&name, replacement);
let name = windows_trailing_re().replace(&name, replacement);
collect(name)
} else {
collect(name)
}

}

#[derive(Clone)]
Expand All @@ -89,39 +111,36 @@ pub fn is_sanitized<S: AsRef<str>>(name: S) -> bool {
}

pub fn is_sanitized_with_options<S: AsRef<str>>(name: S, options: OptionsForCheck) -> bool {

let OptionsForCheck { windows, truncate } = options;
let name = name.as_ref();
if ILLEGAL_RE.is_match(&name) {

if illegal_re().is_match(&name) {
return false;
}
if CONTROL_RE.is_match(&name) {
if control_re().is_match(&name) {
return false;
}
if RESERVED_RE.is_match(&name) {
if reserved_re().is_match(&name) {
return false;
}
if truncate && name.len() > 255 {
return false;
}
if windows {
if WINDOWS_RESERVED_RE.is_match(&name) {
if windows_reserved_re().is_match(&name) {
return false;
}
if WINDOWS_TRAILING_RE.is_match(&name) {
if windows_trailing_re().is_match(&name) {
return false;
}
}

return true;

}

#[cfg(test)]
mod tests {


// From https://github.com/parshap/node-sanitize-filename/blob/master/test.js
static NAMES: &'static [&'static str] = &[
"the quick brown fox jumped over the lazy dog",
Expand Down Expand Up @@ -166,7 +185,7 @@ mod tests {
"./././foobar",
"|*.what",
"LPT9.asdf",
"foobar..."
"foobar...",
];

static NAMES_CLEANED: &'static [&'static str] = &[
Expand Down Expand Up @@ -212,53 +231,14 @@ mod tests {
"...foobar",
".what",
"",
"foobar"
"foobar",
];

static NAMES_IS_SANITIZED: &'static [bool] = &[
true,
true,
false,
false,
true,
true,
false,
false,
false,
false,
false,
false,
true,
false,
false,
true,
false,
true,
false,
true,
false,
false,
false,
false,
true,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false
true, true, false, false, true, true, false, false, false, false, false, false, true,
false, false, true, false, true, false, true, false, false, false, false, true, false,
false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false,
];

#[test]
Expand All @@ -267,11 +247,14 @@ mod tests {
let options = super::Options {
windows: true,
truncate: true,
replacement: ""
replacement: "",
};

for (idx, name) in NAMES.iter().enumerate() {
assert_eq!(super::sanitize_with_options(name, options.clone()), NAMES_CLEANED[idx]);
assert_eq!(
super::sanitize_with_options(name, options.clone()),
NAMES_CLEANED[idx]
);
}

let long = ::std::iter::repeat('a').take(300).collect::<String>();
Expand All @@ -285,10 +268,16 @@ mod tests {
};

for (idx, name) in NAMES.iter().enumerate() {
assert_eq!(super::is_sanitized_with_options(name, options.clone()), NAMES_IS_SANITIZED[idx]);
assert_eq!(
super::is_sanitized_with_options(name, options.clone()),
NAMES_IS_SANITIZED[idx]
);
}

let long = ::std::iter::repeat('a').take(300).collect::<String>();
assert_eq!(super::is_sanitized_with_options(long, options.clone()), false);
assert_eq!(
super::is_sanitized_with_options(long, options.clone()),
false
);
}
}
}

0 comments on commit 9428a94

Please sign in to comment.