diff options
author | Tom Smeding <tom@tomsmeding.com> | 2025-01-20 23:08:11 +0100 |
---|---|---|
committer | Tom Smeding <tom@tomsmeding.com> | 2025-01-20 23:08:11 +0100 |
commit | c04ced6609a90ddebf15c6337c7761a0697a3497 (patch) | |
tree | bc8310665c30ef2c59c56bda3eea5d0b475f28db /server/src/path.rs | |
parent | 6dfa979b23c486005ef1185f0f0ca411fd235fd5 (diff) |
Diffstat (limited to 'server/src/path.rs')
-rw-r--r-- | server/src/path.rs | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/server/src/path.rs b/server/src/path.rs new file mode 100644 index 0000000..17aa263 --- /dev/null +++ b/server/src/path.rs @@ -0,0 +1,61 @@ +// Invariants: +// - 'orig' is a valid path, and in particular, the '/'-split components are non-empty; +// - 'comps' describes a non-empty prefix of 'orig'. +#[derive(Debug)] +pub struct Path<'a> { + orig: &'a str, + comps: Vec<(usize, &'a str)>, // (offset, component) +} + +impl<'a> Path<'a> { + /// Returns the split path if the path is valid. A valid path has the following requirements: + /// * It does not contain unicode control characters; + /// * When split on '/', the resulting components are all non-empty and neither start nor end + /// with unicode whitespace characters. + pub fn split(s: &str) -> Option<Path> { + let mut comps = Vec::new(); + let mut start = 0; // of current component + for (i, c) in s.char_indices() { + match c { + '/' if i == start => return None, // empty component + '/' => { + let comp = &s[start..i]; + if comp.starts_with(|c2: char| c2.is_whitespace()) || + comp.ends_with(|c2: char| c2.is_whitespace()) { + return None; + } + comps.push((start, comp)); + start = i + 1; + }, + _ if c.is_control() => return None, + _ => {}, // include in current component + } + } + + // check and add the last component + if start == s.len() { + return None; // slash at end of input + } + comps.push((start, &s[start..s.len()])); + + Some(Path { orig: s, comps }) + } + + pub fn without_last(&self) -> Option<(Path, &str)> { + let n = self.comps.len(); + if n > 1 { + Some((Path { orig: self.orig, comps: self.comps[0 .. n - 1].into() }, self.comps[n - 1].1)) + } else { + None + } + } + + pub fn join(&self) -> &str { + if self.comps.len() == 0 { + "" + } else { + let (lastoff, lastcomp) = self.comps[self.comps.len() - 1]; + &self.orig[0 .. lastoff + lastcomp.len()] + } + } +} |