100 lines
2.3 KiB
Rust
100 lines
2.3 KiB
Rust
use std::sync::{Condvar, Mutex};
|
|
|
|
pub fn normalize_title(input: &str) -> String {
|
|
let mut out = String::with_capacity(input.len());
|
|
for ch in input.chars() {
|
|
if ch.is_ascii_alphanumeric() {
|
|
out.push(ch.to_ascii_lowercase());
|
|
} else if ch.is_whitespace() {
|
|
out.push(' ');
|
|
} else {
|
|
out.push(' ');
|
|
}
|
|
}
|
|
collapse_whitespace(&out)
|
|
}
|
|
|
|
pub fn sanitize_filename(input: &str) -> String {
|
|
let mut out = String::with_capacity(input.len());
|
|
for ch in input.chars() {
|
|
if matches!(ch, '<' | '>' | ':' | '"' | '/' | '\\' | '|' | '?' | '*') {
|
|
out.push(' ');
|
|
} else {
|
|
out.push(ch);
|
|
}
|
|
}
|
|
collapse_whitespace(&out)
|
|
}
|
|
|
|
pub fn collapse_whitespace(input: &str) -> String {
|
|
let mut out = String::with_capacity(input.len());
|
|
let mut last_space = false;
|
|
for ch in input.chars() {
|
|
if ch.is_whitespace() {
|
|
if !last_space {
|
|
out.push(' ');
|
|
last_space = true;
|
|
}
|
|
} else {
|
|
last_space = false;
|
|
out.push(ch);
|
|
}
|
|
}
|
|
out.trim().to_string()
|
|
}
|
|
|
|
pub struct Semaphore {
|
|
state: Mutex<usize>,
|
|
cvar: Condvar,
|
|
}
|
|
|
|
impl Semaphore {
|
|
pub fn new(count: usize) -> Self {
|
|
Self {
|
|
state: Mutex::new(count),
|
|
cvar: Condvar::new(),
|
|
}
|
|
}
|
|
|
|
pub fn acquire(&self) -> SemaphoreGuard<'_> {
|
|
let mut count = self.state.lock().unwrap();
|
|
while *count == 0 {
|
|
count = self.cvar.wait(count).unwrap();
|
|
}
|
|
*count -= 1;
|
|
SemaphoreGuard { sem: self }
|
|
}
|
|
}
|
|
|
|
pub struct SemaphoreGuard<'a> {
|
|
sem: &'a Semaphore,
|
|
}
|
|
|
|
impl Drop for SemaphoreGuard<'_> {
|
|
fn drop(&mut self) {
|
|
let mut count = self.sem.state.lock().unwrap();
|
|
*count += 1;
|
|
self.sem.cvar.notify_one();
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{collapse_whitespace, normalize_title, sanitize_filename};
|
|
|
|
#[test]
|
|
fn normalizes_title() {
|
|
assert_eq!(normalize_title("The.Matrix!!"), "the matrix");
|
|
}
|
|
|
|
#[test]
|
|
fn sanitizes_filename() {
|
|
assert_eq!(sanitize_filename("Bad:Name/Here"), "Bad Name Here");
|
|
}
|
|
|
|
#[test]
|
|
fn collapses_whitespace() {
|
|
assert_eq!(collapse_whitespace("a b c"), "a b c");
|
|
}
|
|
}
|