1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Parsed {
pub dir: String,
pub root: String,
pub base: String,
pub name: String,
pub ext: String,
}
impl Default for Parsed {
fn default() -> Self {
Self {
dir: "".to_owned(),
root: "".to_owned(),
base: "".to_owned(),
name: "".to_owned(),
ext: "".to_owned(),
}
}
}
pub(crate) fn normalize_string(
path: &str,
allow_above_root: bool,
separator: &char,
is_path_separator: &dyn Fn(&char) -> bool,
) -> String {
let path = path.chars().collect::<Vec<char>>();
let mut res: Vec<char> = Vec::new();
let mut last_segment_length = 0;
let mut last_slash = -1;
let mut dots = 0;
let mut code = ' ';
{
let mut i = 0;
let path_len = path.len();
while i <= path_len {
if i < path_len {
code = *path.get(i).unwrap();
} else if is_path_separator(&code) {
break;
} else {
code = CHAR_FORWARD_SLASH
}
if is_path_separator(&code) {
if last_slash == i as i32 - 1 || dots == 1 {
// noop
} else if dots == 2 {
if res.len() < 2
|| last_segment_length != 2
|| res.get(res.len() - 1).unwrap() != &CHAR_DOT
|| res.get(res.len() - 2).unwrap() != &CHAR_DOT
{
if res.len() > 2 {
let last_slash_index =
last_index_of(&res, separator).map_or(-1, |s| s as i32);
if last_slash_index == -1 {
res = vec![];
last_segment_length = 0
} else {
res = res[0..last_slash_index as usize].to_vec();
last_segment_length = res.len() as i32
- 1
- last_index_of(&res, separator).map_or(-1, |s| s as i32);
}
last_slash = i as i32;
dots = 0;
i += 1;
continue;
} else if res.len() != 0 {
res = vec![];
last_segment_length = 0;
last_slash = i as i32;
dots = 0;
i += 1;
continue;
}
}
if allow_above_root {
if res.len() > 0 {
res.push(*separator);
}
res.push('.');
res.push('.');
last_segment_length = 2;
}
} else {
if res.len() > 0 {
res.push(*separator)
}
path[(last_slash + 1) as usize..i as usize]
.iter()
.for_each(|c| res.push(*c));
last_segment_length = i as i32 - last_slash - 1;
}
last_slash = i as i32;
dots = 0;
} else if code == CHAR_DOT && dots != -1 {
dots += 1;
} else {
dots = -1;
}
i += 1;
}
}
res.into_iter().collect()
}
fn last_index_of(vec: &Vec<char>, tar: &char) -> Option<usize> {
vec.iter()
.enumerate()
.rev()
.find_map(|(idx, c)| if c == tar { Some(idx) } else { None })
}
#[inline]
pub(crate) fn is_empty(s: &str) -> bool {
s.len() == 0
}
pub(crate) fn format_inner(sep: &str, path_object: Parsed) -> String {
let root = path_object.root.clone();
let dir = if !is_empty(&path_object.dir) {
path_object.dir
} else {
path_object.root
};
let base = if !is_empty(&path_object.base) {
path_object.base
} else {
format!("{}{}", &path_object.name, &path_object.ext)
};
if is_empty(&dir) {
return base;
}
if dir == root {
format!("{}{}", dir, base)
} else {
format!("{}{}{}", dir, sep, base)
}
}
#[inline]
pub(crate) fn is_posix_path_separator(code: &char) -> bool {
code == &CHAR_FORWARD_SLASH
}
// Alphabet chars.
// const CHAR_UPPERCASE_A: char = 65; /* A */
// const CHAR_LOWERCASE_A: char = 97; /* a */
// const CHAR_UPPERCASE_Z: char = 90; /* Z */
// const CHAR_LOWERCASE_Z: char = 122; /* z */
// const CHAR_UPPERCASE_C: char = 67; /* C */
// const CHAR_LOWERCASE_B: char = 98; /* b */
// const CHAR_LOWERCASE_E: char = 101; /* e */
// const CHAR_LOWERCASE_N: char = 110; /* n */
// // Non-alphabetic chars.
pub(crate) const CHAR_DOT: char = '.'; /* . */
pub(crate) const CHAR_FORWARD_SLASH: char = '/'; /* / */
// const CHAR_BACKWARD_SLASH: char = 92; /* \ */
// const CHAR_VERTICAL_LINE: char = 124; /* | */
// const CHAR_COLON: char = 58; /* : */
// const CHAR_QUESTION_MARK: char = 63; /* ? */
// const CHAR_UNDERSCORE: char = 95; /* _ */
// const CHAR_LINE_FEED: char = 10; /* \n */
// const CHAR_CARRIAGE_RETURN: char = 13; /* \r */
// const CHAR_TAB: char = 9; /* \t */
// const CHAR_FORM_FEED: char = 12; /* \f */
// const CHAR_EXCLAMATION_MARK: char = 33; /* ! */
// const CHAR_HASH: char = 35; /* # */
// const CHAR_SPACE: char = 32; /* */
// const CHAR_NO_BREAK_SPACE: char = 160; /* \u00A0 */
// const CHAR_ZERO_WIDTH_NOBREAK_SPACE: char = 65279; /* \uFEFF */
// const CHAR_LEFT_SQUARE_BRACKET: char = 91; /* [ */
// const CHAR_RIGHT_SQUARE_BRACKET: char = 93; /* ] */
// const CHAR_LEFT_ANGLE_BRACKET: char = 60; /* < */
// const CHAR_RIGHT_ANGLE_BRACKET: char = 62; /* > */
// const CHAR_LEFT_CURLY_BRACKET: char = 123; /* { */
// const CHAR_RIGHT_CURLY_BRACKET: char = 125; /* } */
// const CHAR_HYPHEN_MINUS: char = 45; /* - */
// const CHAR_PLUS: char = 43; /* + */
// const CHAR_DOUBLE_QUOTE: char = 34; /* " */
// const CHAR_SINGLE_QUOTE: char = 39; /* ' */
// const CHAR_PERCENT: char = 37; /* % */
// const CHAR_SEMICOLON: char = 59; /* ; */
// const CHAR_CIRCUMFLEX_ACCENT: char = 94; /* ^ */
// const CHAR_GRAVE_ACCENT: char = 96; /* ` */
// const CHAR_AT: char = 64; /* @ */
// const CHAR_AMPERSAND: char = 38; /* & */
// const CHAR_EQUAL: char = 61; /* = */
// // Digits
// const CHAR_0: char = 48; /* 0 */
// const CHAR_9: char = 57; /* 9 */
// const EOL: isWindows ? '\r\n' : '\n'
