From 3e52a0acdec93357806771eb04f0f479873a637b Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Fri, 17 Aug 2012 19:12:02 -0700 Subject: [PATCH] Initial sketch of path2 module, starting with WindowsPath. --- src/libcore/path2.rs | 311 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 src/libcore/path2.rs diff --git a/src/libcore/path2.rs b/src/libcore/path2.rs new file mode 100644 index 00000000000..c6b25476b96 --- /dev/null +++ b/src/libcore/path2.rs @@ -0,0 +1,311 @@ +extern mod std; + +struct WindowsPath { + host: option<~str>; + device: option<~str>; + is_absolute: bool; + components: ~[~str]; +} + +struct PosixPath { + is_absolute: bool; + components: ~[~str]; +} + +trait Path { + + static fn from_str((&str)) -> self; + fn to_str() -> ~str; + + fn dirname() -> ~str; + fn filename() -> option<~str>; + fn filestem() -> option<~str>; + fn filetype() -> option<~str>; + + fn with_dirname((&str)) -> self; + fn with_filename((&str)) -> self; + fn with_filestem((&str)) -> self; + fn with_filetype((&str)) -> self; + + fn push_components((&[~str])) -> self; + fn pop_component() -> self; +} + + +impl WindowsPath : Path { + + fn to_str() -> ~str { + match self.filename() { + none => self.dirname(), + some(ref f) => + if (self.components.len() == 1 && + !self.is_absolute && + self.host == none && + self.device == none) { + copy *f + } else { + self.dirname() + "\\" + *f + } + } + } + + static fn from_str(s: &str) -> WindowsPath { + let host; + let device; + let rest; + + match windows::extract_drive_prefix(s) { + some((ref d, ref r)) => { + host = none; + device = some(copy *d); + rest = copy *r; + } + none => { + match windows::extract_unc_prefix(s) { + some((ref h, ref r)) => { + host = some(copy *h); + device = none; + rest = copy *r; + } + none => { + host = none; + device = none; + rest = str::from_slice(s); + } + } + } + } + + let mut components = + str::split_nonempty(rest, |c| windows::is_sep(c as u8)); + let is_absolute = (rest.len() != 0 && windows::is_sep(rest[0])); + return WindowsPath { host: host, + device: device, + is_absolute: is_absolute, + components: components } + } + + fn dirname() -> ~str { + let mut s = ~""; + match self.host { + some(h) => { s += "\\\\"; s += h; } + none => { } + } + match self.device { + some(d) => { s += d; s += ":"; } + none => { } + } + if self.is_absolute { + s += "\\"; + } + let mut d = copy self.components; + if d.len() != 0 { + vec::pop(d); + } + s += str::connect(d, "\\"); + if s.len() == 0 { + s = ~"."; + } + return s; + } + + fn filename() -> option<~str> { + match self.components.len() { + 0 => none, + 1 => some(copy self.components[0]), + n => some(copy self.components[n - 1]) + } + } + + fn filestem() -> option<~str> { + match self.filename() { + none => none, + some(ref f) => { + match str::rfind_char(*f, '.') { + some(p) => some(f.slice(0, p)), + none => some(copy *f) + } + } + } + } + + fn filetype() -> option<~str> { + match self.filename() { + none => none, + some(ref f) => { + match str::rfind_char(*f, '.') { + some(p) if p+1 < f.len() => some(f.slice(p+1, f.len())), + _ => none + } + } + } + } + + fn with_dirname(d: &str) -> WindowsPath { + let dpath = from_str::(d); + match self.filename() { + some(ref f) => dpath.push_components(~[copy *f]), + none => dpath + } + } + + fn with_filename(f: &str) -> WindowsPath { + assert ! str::any(f, |c| windows::is_sep(c as u8)); + self.dir_path().push_components(~[str::from_slice(f)]) + } + + fn with_filestem(s: &str) -> WindowsPath { + match self.filetype() { + none => self.with_filename(s), + some(ref t) => + self.with_filename(str::from_slice(s) + "." + *t) + } + } + + fn with_filetype(t: &str) -> WindowsPath { + let t = ~"." + str::from_slice(t); + match self.filestem() { + none => self.with_filename(t), + some(ref s) => + self.with_filename(*s + t) + } + } + + fn dir_path() -> WindowsPath { + if self.components.len() != 0 { + self.pop_component() + } else { + copy self + } + } + + fn file_path() -> WindowsPath { + let cs = match self.filename() { + none => ~[], + some(ref f) => ~[copy *f] + }; + return WindowsPath { host: none, + device: none, + is_absolute: false, + components: cs } + } + + fn push_components(cs: &[~str]) -> WindowsPath { + return WindowsPath { components: self.components + cs, ..self } + } + + fn pop_component() -> WindowsPath { + let mut cs = copy self.components; + if cs.len() != 0 { + vec::pop(cs); + } + return WindowsPath { components: cs, ..self } + } +} + +// Various windows helpers, and tests for the impl. +mod windows { + + #[inline(always)] + fn is_sep(u: u8) -> bool { + u == '/' as u8 || u == '\\' as u8 + } + + fn extract_unc_prefix(s: &str) -> option<(~str,~str)> { + if (s.len() > 1 && + s[0] == '\\' as u8 && + s[1] == '\\' as u8) { + let mut i = 2; + while i < s.len() { + if s[i] == '\\' as u8 { + let pre = s.slice(2, i); + let rest = s.slice(i, s.len()); + return some((pre, rest)); + } + i += 1; + } + } + none + } + + fn extract_drive_prefix(s: &str) -> option<(~str,~str)> { + if (s.len() > 1 && + libc::isalpha(s[0] as libc::c_int) != 0 && + s[1] == ':' as u8) { + let rest = if s.len() == 2 { ~"" } else { s.slice(2, s.len()) }; + return some((s.slice(0,1), rest)); + } + none + } + + #[test] + fn test_extract_unc_prefixes() { + assert extract_unc_prefix("\\\\") == none; + assert extract_unc_prefix("\\\\hi") == none; + assert extract_unc_prefix("\\\\hi\\") == some((~"hi", ~"\\")); + assert extract_unc_prefix("\\\\hi\\there") == + some((~"hi", ~"\\there")); + assert extract_unc_prefix("\\\\hi\\there\\friends.txt") == + some((~"hi", ~"\\there\\friends.txt")); + } + + #[test] + fn test_extract_drive_prefixes() { + assert extract_drive_prefix("c") == none; + assert extract_drive_prefix("c:") == some((~"c", ~"")); + assert extract_drive_prefix("d:") == some((~"d", ~"")); + assert extract_drive_prefix("z:") == some((~"z", ~"")); + assert extract_drive_prefix("c:\\hi") == some((~"c", ~"\\hi")); + assert extract_drive_prefix("d:hi") == some((~"d", ~"hi")); + assert extract_drive_prefix("c:hi\\there.txt") == + some((~"c", ~"hi\\there.txt")); + assert extract_drive_prefix("c:\\hi\\there.txt") == + some((~"c", ~"\\hi\\there.txt")); + } + + #[test] + fn test_windows_paths() { + fn mk(s: &str) -> WindowsPath { from_str::(s) } + fn t(wp: &WindowsPath, s: &str) { + let ss = wp.to_str(); + let sss = str::from_slice(s); + if (ss != sss) { + debug!("got %s", ss); + debug!("expected %s", sss); + assert ss == sss; + } + } + + t(&(mk("hi")), "hi"); + t(&(mk("hi/there")), "hi\\there"); + t(&(mk("hi/there.txt")), "hi\\there.txt"); + + t(&(mk("there.txt") + .with_filetype("o")), "there.o"); + + t(&(mk("hi/there.txt") + .with_filetype("o")), "hi\\there.o"); + + t(&(mk("hi/there.txt") + .with_filetype("o") + .with_dirname("c:\\program files A")), + "c:\\program files A\\there.o"); + + t(&(mk("hi/there.txt") + .with_filetype("o") + .with_dirname("c:\\program files B\\")), + "c:\\program files B\\there.o"); + + t(&(mk("hi/there.txt") + .with_filetype("o") + .with_dirname("c:\\program files C\\/")), + "c:\\program files C\\there.o"); + + t(&(mk("c:\\program files (x86)\\rust") + .push_components([~"lib", ~"thingy.dll"]) + .with_filename("librustc.dll")), + "c:\\program files (x86)\\rust\\lib\\librustc.dll"); + + } + +}