-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c0af533
Showing
6 changed files
with
396 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/target | ||
**/*.rs.bk |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
authors = ["Jacob Brown <[email protected]>"] | ||
name = "sanitize-filename" | ||
version = "0.1.0" | ||
keywords = ["filename", "sanitizer"] | ||
license = "MIT" | ||
readme = "README.md" | ||
repository = "https://github.com/kardeiz/sanitize-filename" | ||
description = "A simple filename sanitizer, based on Node's sanitize-filename" | ||
|
||
[dependencies] | ||
lazy_static = "1.1.0" | ||
regex = "1.0.5" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# sanitize-filename | ||
|
||
A basic filename sanitizer, based on Node's [sanitize-filename](https://www.npmjs.com/package/sanitize-filename). | ||
|
||
Use like: | ||
|
||
```rust | ||
extern crate sanitize_filename; | ||
|
||
fn main() { | ||
println!("{}", sanitize_filename::sanitize("some-user-defined/../../../string")); | ||
// prints some-user-defined......string | ||
} | ||
``` | ||
|
||
You can also configure a few basic options: | ||
|
||
```rust | ||
let options = sanitize_filename::Options { | ||
truncate: true, // true by default, truncates to 255 characters | ||
windows: true, // default value depends on the OS, removes reserved names like `con` from start of strings on Windows | ||
replacement: "" // str to replace sanitized chars/strings | ||
}; | ||
|
||
let sanitized = sanitize_filename::sanitize_with_options("some-user-defined/../../../string", options); | ||
``` | ||
|
||
Also provides a basic command line binary. Use like: | ||
|
||
```bash | ||
cargo install sanitize-filename | ||
sanitize-filename my_filename.txt | ||
``` | ||
|
||
``` | ||
Pass a file name to clean to the program (also reads STDIN) | ||
FLAGS: | ||
-r, --replace <r> Replacement characters | ||
--windows, --no-windows Whether to handle filenames for Windows | ||
--truncate, --no-truncate Whether to truncate file names to 255 characters | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
extern crate regex; | ||
|
||
#[macro_use] | ||
extern crate lazy_static; | ||
|
||
use regex::{Regex, RegexBuilder}; | ||
|
||
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(); | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct Options<'a> { | ||
pub windows: bool, | ||
pub truncate: bool, | ||
pub replacement: &'a str | ||
} | ||
|
||
impl<'a> Default for Options<'a> { | ||
fn default() -> Self { | ||
Options { | ||
windows: cfg!(windows), | ||
truncate: true, | ||
replacement: "" | ||
} | ||
} | ||
} | ||
|
||
pub fn sanitize<S: AsRef<str>>(name: S) -> String { | ||
sanitize_with_options(name, Options::default()) | ||
} | ||
|
||
pub fn sanitize_with_options<S: AsRef<str>>(name: S, options: Options) -> String { | ||
|
||
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 collect = |name: ::std::borrow::Cow<str>| { | ||
if truncate { | ||
name.chars().take(255).collect() | ||
} else { | ||
String::from(name) | ||
} | ||
}; | ||
|
||
if windows { | ||
let name = WINDOWS_RESERVED_RE.replace(&name, replacement); | ||
let name = WINDOWS_TRAILING_RE.replace(&name, replacement); | ||
collect(name) | ||
} else { | ||
collect(name) | ||
} | ||
|
||
} | ||
|
||
#[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", | ||
"résumé", | ||
"hello\u{0000}world", | ||
"hello\nworld", | ||
"semi;colon.js", | ||
";leading-semi.js", | ||
"slash\\.js", | ||
"slash/.js", | ||
"col:on.js", | ||
"star*.js", | ||
"question?.js", | ||
"quote\".js", | ||
"singlequote'.js", | ||
"brack<e>ts.js", | ||
"p|pes.js", | ||
"plus+.js", | ||
"'five and six<seven'.js", | ||
" space at front", | ||
"space at end ", | ||
".period", | ||
"period.", | ||
"relative/path/to/some/dir", | ||
"/abs/path/to/some/dir", | ||
"~/.\u{0000}notssh/authorized_keys", | ||
"", | ||
"h?w", | ||
"h/w", | ||
"h*w", | ||
".", | ||
"..", | ||
"./", | ||
"../", | ||
"/..", | ||
"/../", | ||
"*.|.", | ||
"./", | ||
"./foobar", | ||
"../foobar", | ||
"../../foobar", | ||
"./././foobar", | ||
"|*.what", | ||
"LPT9.asdf" | ||
]; | ||
|
||
static NAMES_CLEANED: &'static [&'static str] = &[ | ||
"the quick brown fox jumped over the lazy dog", | ||
"résumé", | ||
"helloworld", | ||
"helloworld", | ||
"semi;colon.js", | ||
";leading-semi.js", | ||
"slash.js", | ||
"slash.js", | ||
"colon.js", | ||
"star.js", | ||
"question.js", | ||
"quote.js", | ||
"singlequote'.js", | ||
"brackets.js", | ||
"ppes.js", | ||
"plus+.js", | ||
"'five and sixseven'.js", | ||
" space at front", | ||
"space at end ", | ||
".period", | ||
"period.", | ||
"relativepathtosomedir", | ||
"abspathtosomedir", | ||
"~.notsshauthorized_keys", | ||
"", | ||
"hw", | ||
"hw", | ||
"hw", | ||
"", | ||
"", | ||
"", | ||
"", | ||
"", | ||
"", | ||
"", | ||
"", | ||
".foobar", | ||
"..foobar", | ||
"....foobar", | ||
"...foobar", | ||
".what", | ||
"" | ||
]; | ||
|
||
#[test] | ||
fn it_works() { | ||
|
||
let options = super::Options { | ||
windows: true, | ||
truncate: true, | ||
replacement: "" | ||
}; | ||
|
||
for (idx, name) in NAMES.iter().enumerate() { | ||
assert_eq!(super::sanitize_with_options(name, options.clone()), NAMES_CLEANED[idx]); | ||
} | ||
|
||
let long = ::std::iter::repeat('a').take(300).collect::<String>(); | ||
let shorter = ::std::iter::repeat('a').take(255).collect::<String>(); | ||
assert_eq!(super::sanitize_with_options(long, options.clone()), shorter); | ||
|
||
} | ||
} |
Oops, something went wrong.