///|
pub impl Show for Path with output(self, logger) {
match self {
UPath(path) => path.output(logger)
WPath(path) => path.output(logger)
}
}
///|
pub fn Path::parse(s : StringView) -> Path raise {
if s.is_empty() {
raise Failure("Empty path string")
}
// Check if it's a Windows path (has drive letter pattern like "C:")
if is_windows_path(s) {
WPath(WinPath::parse(s))
} else {
UPath(UnixPath::parse(s))
}
}
///|
/// Check if a path string is a Windows path
/// Windows paths start with a drive letter followed by a colon (e.g., "C:", "D:\\")
fn is_windows_path(s : StringView) -> Bool {
if s.length() < 2 {
return false
}
let first_char = s.get_char(0)
let second_char = s.get_char(1)
match (first_char, second_char) {
(Some(c), Some(':')) => (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
_ => false
}
}
///|
test "Path::parse Windows paths" {
// Test basic Windows path with drive letter
let path1 = Path::parse("C:")
match path1 {
WPath(win_path) => {
inspect(win_path.disk, content="C")
inspect(win_path.directory.length(), content="0")
inspect(win_path.file, content="None")
}
_ => fail("Expected Windows path")
}
// Test Windows path with directory
let path2 = Path::parse("D:\\Users\\John")
match path2 {
WPath(win_path) => {
inspect(win_path.disk, content="D")
inspect(win_path.directory.length(), content="2")
inspect(win_path.directory[0].0, content="Users")
inspect(win_path.directory[1].0, content="John")
inspect(win_path.file, content="None")
}
_ => fail("Expected Windows path")
}
// Test Windows path with file
let path3 = Path::parse("E:\\Documents\\readme.txt")
match path3 {
WPath(win_path) => {
inspect(win_path.disk, content="E")
inspect(win_path.directory.length(), content="1")
inspect(win_path.directory[0].0, content="Documents")
match win_path.file {
Some(file) => {
inspect(file.base.0, content="readme")
inspect(file.extension, content="txt")
}
None => fail("Expected file")
}
}
_ => fail("Expected Windows path")
}
// Test Windows path with file at root
let path4 = Path::parse("F:\\config.json")
match path4 {
WPath(win_path) => {
inspect(win_path.disk, content="F")
inspect(win_path.directory.length(), content="0")
match win_path.file {
Some(file) => {
inspect(file.base.0, content="config")
inspect(file.extension, content="json")
}
None => fail("Expected file")
}
}
_ => fail("Expected Windows path")
}
}
///|
test "Path::parse Unix paths" {
// Test root path
let path1 = Path::parse("/")
match path1 {
UPath(unix_path) => {
inspect(unix_path.directory.length(), content="0")
inspect(unix_path.file, content="None")
}
_ => fail("Expected Unix path")
}
// Test Unix path with directory
let path2 = Path::parse("/home/user/documents")
match path2 {
UPath(unix_path) => {
inspect(unix_path.directory.length(), content="3")
inspect(unix_path.directory[0].0, content="home")
inspect(unix_path.directory[1].0, content="user")
inspect(unix_path.directory[2].0, content="documents")
inspect(unix_path.file, content="None")
}
_ => fail("Expected Unix path")
}
// Test Unix path with file
let path3 = Path::parse("/etc/config/settings.conf")
match path3 {
UPath(unix_path) => {
inspect(unix_path.directory.length(), content="2")
inspect(unix_path.directory[0].0, content="etc")
inspect(unix_path.directory[1].0, content="config")
match unix_path.file {
Some(file) => {
inspect(file.base.0, content="settings")
inspect(file.extension, content="conf")
}
None => fail("Expected file")
}
}
_ => fail("Expected Unix path")
}
// Test Unix path with file at root
let path4 = Path::parse("/README.md")
match path4 {
UPath(unix_path) => {
inspect(unix_path.directory.length(), content="0")
match unix_path.file {
Some(file) => {
inspect(file.base.0, content="README")
inspect(file.extension, content="md")
}
None => fail("Expected file")
}
}
_ => fail("Expected Unix path")
}
// Test relative Unix path
let path5 = Path::parse("relative/path/file.txt")
match path5 {
UPath(unix_path) => {
inspect(unix_path.directory.length(), content="2")
inspect(unix_path.directory[0].0, content="relative")
inspect(unix_path.directory[1].0, content="path")
match unix_path.file {
Some(file) => {
inspect(file.base.0, content="file")
inspect(file.extension, content="txt")
}
None => fail("Expected file")
}
}
_ => fail("Expected Unix path")
}
}
///|
test "Path::parse edge cases" {
// Test empty path - should fail
let result1 = try? Path::parse("")
match result1 {
Err(_) => () // Expected
Ok(_) => fail("Empty path should fail")
}
// Test path without file extension - "node" is treated as directory
let path2 = Path::parse("/usr/bin/node")
match path2 {
UPath(unix_path) => {
inspect(unix_path.directory.length(), content="3")
inspect(unix_path.directory[2].0, content="node")
inspect(unix_path.file, content="None")
}
_ => fail("Expected Unix path")
}
// Test Windows path with spaces
let path3 = Path::parse("C:\\Program Files\\My App\\app.exe")
match path3 {
WPath(win_path) => {
inspect(win_path.directory.length(), content="2")
inspect(win_path.directory[0].0, content="Program Files")
inspect(win_path.directory[1].0, content="My App")
match win_path.file {
Some(file) => {
inspect(file.base.0, content="app")
inspect(file.extension, content="exe")
}
None => fail("Expected file")
}
}
_ => fail("Expected Windows path")
}
}
///|
test "Path::parse detection logic" {
// Should detect Windows paths correctly
inspect(is_windows_path("C:"), content="true")
inspect(is_windows_path("D:\\"), content="true")
inspect(is_windows_path("z:\\file.txt"), content="true")
// Should detect Unix paths correctly
inspect(is_windows_path("/"), content="false")
inspect(is_windows_path("/home"), content="false")
inspect(is_windows_path("relative/path"), content="false")
inspect(is_windows_path("file.txt"), content="false")
}
///|
test "Path::parse Show implementation" {
// Test Windows path string representation
let win_path = Path::parse("C:\\Users\\John\\document.pdf")
inspect(win_path.to_string(), content="C:\\Users\\John\\document.pdf")
// Test Unix path string representation
let unix_path = Path::parse("/home/user/script.sh")
inspect(unix_path.to_string(), content="/home/user/script.sh")
// Test root paths
let win_root = Path::parse("D:")
inspect(win_root.to_string(), content="D:\\")
let unix_root = Path::parse("/")
inspect(unix_root.to_string(), content="/")
}