#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum CaseNormalization {
#[default]
Preserve,
AsciiLowercase,
UnicodeLowercase,
PlatformDefault,
}
impl CaseNormalization {
pub fn apply(self, value: &str) -> String {
match self {
Self::Preserve => value.to_owned(),
Self::AsciiLowercase => value.to_ascii_lowercase(),
Self::UnicodeLowercase => value.to_lowercase(),
Self::PlatformDefault => {
if cfg!(windows) {
value.to_ascii_lowercase()
} else {
value.to_owned()
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TextNormalizationOptions {
pub trim_whitespace: bool,
pub trim_trailing_separators: bool,
pub normalize_separators: bool,
pub case: CaseNormalization,
}
impl Default for TextNormalizationOptions {
fn default() -> Self {
Self {
trim_whitespace: true,
trim_trailing_separators: true,
normalize_separators: true,
case: CaseNormalization::Preserve,
}
}
}
impl TextNormalizationOptions {
pub fn new() -> Self {
Self::default()
}
}
pub fn normalize_path_token(value: &str, options: &TextNormalizationOptions) -> String {
let mut s = if options.trim_whitespace {
value.trim()
} else {
value
}
.to_owned();
if options.trim_trailing_separators {
while s.ends_with('/') || s.ends_with('\\') {
s.pop();
}
}
if options.normalize_separators {
s = s.replace('\\', "/");
while s.contains("//") {
s = s.replace("//", "/");
}
}
options.case.apply(&s)
}