A cross-platform path manipulation library for MoonBit that provides robust handling of both Unix and Windows file system paths with automatic format detection and type-safe path operations.
///|
test {
let path = @path.Path::parse("./projects/")
let builder = @path.PathBuilder::from_path(path)
builder.set_extension("md")
builder.set_basename("README")
inspect(builder, content="projects/README.md")
builder.add_directory("proj_1")
// pop last file, and add new directory
inspect(builder, content="projects/proj_1/")
}///|
test {
let path = @path.Path::parse("C:\\projects\\")
let builder = @path.PathBuilder::from_path(path)
builder.set_file(base="README", extension="md")
inspect(builder, content="C:\\projects\\README.md")
builder.add_directory("proj_1")
// pop last file, and add new directory
inspect(builder, content="C:\\projects\\proj_1\\")
}///|
test "Simple String Operations" {
let path = @path.Path::parse("C:\\Users\\username\\Documents\\file.txt")
inspect(path.file(), content="Some(file.txt)")
inspect(path.directory()[:].join("\\"), content="Users\\username\\Documents")
inspect(path.prefix(), content="C:\\")
}///|
test "type safe directory components" {
let path = @path.Path::parse("/home/username/proj/src/")
guard path is UPath(path) else { panic() }
let parent_dir : ArrayView[@path.UnixPathComponent] = path.directory[0:path.directory.length() -
1]
@json.inspect(parent_dir, content=[
["UnixPathComponent", "home"],
["UnixPathComponent", "username"],
["UnixPathComponent", "proj"],
])
}///|
test "Unix relative path" {
let path = @path.Path::parse("home/username/proj/src/README.mbt.md")
inspect(path.directory(), content="home/username/proj/src/")
inspect(path.file().unwrap(), content="README.mbt.md")
inspect(path.is_absolute(), content="false")
inspect(path.is_relative(), content="true")
inspect(path.is_root_path(), content="false")
}///|
test "Unix absolute path" {
let path = @path.Path::parse("/home/username/proj/src/README.mbt.md")
inspect(path.directory(), content="home/username/proj/src/")
inspect(path.file().unwrap(), content="README.mbt.md")
inspect(path.is_absolute(), content="true")
inspect(path.is_relative(), content="false")
inspect(path.is_root_path(), content="true")
}///|
test "Windows relative path" {
let path = @path.Path::parse("Users\\username\\proj\\src\\README.mbt.md")
// This is used for Debug output
inspect(path.directory(), content="Users/username/proj/src/")
inspect(path.file().unwrap(), content="README.mbt.md")
inspect(path.is_absolute(), content="false")
inspect(path.is_relative(), content="true")
inspect(path.is_root_path(), content="false")
}///|
test "Windows absolute path" {
let path = @path.Path::parse("C:\\Users\\username\\proj\\src\\README.mbt.md")
// This is used for Debug output
inspect(path.directory(), content="Users/username/proj/src/")
inspect(path.file().unwrap(), content="README.mbt.md")
inspect(path.is_absolute(), content="true")
inspect(path.is_relative(), content="false")
inspect(path.is_root_path(), content="true")
}///|
test "Windows relative root path" {
let path = @path.Path::parse("\\Users\\username\\proj\\src\\README.mbt.md")
// This is used for Debug output
inspect(path.directory(), content="Users/username/proj/src/")
inspect(path.file().unwrap(), content="README.mbt.md")
inspect(path.is_absolute(), content="false")
inspect(path.is_relative(), content="true")
inspect(path.is_root_path(), content="true")
}///|
test {
let path = @path.Path::parse("./projects/")
let builder = @path.PathBuilder::from_path(path)
builder.set_extension("md")
builder.set_basename("README")
inspect(builder, content="projects/README.md")
builder.add_directory("proj_1")
// pop last file, and add new directory
inspect(builder, content="projects/proj_1/")
}///|
test {
let path = @path.Path::parse("C:\\projects\\")
let builder = @path.PathBuilder::from_path(path)
builder.set_file(base="README", extension="md")
inspect(builder, content="C:\\projects\\README.md")
builder.add_directory("proj_1")
// pop last file, and add new directory
inspect(builder, content="C:\\projects\\proj_1\\")
}///|
test "Simple String Operations" {
let path = @path.Path::parse("C:\\Users\\username\\Documents\\file.txt")
inspect(path.file(), content="Some(file.txt)")
inspect(path.directory()[:].join("\\"), content="Users\\username\\Documents")
inspect(path.prefix(), content="C:\\")
}///|
test "type safe directory components" {
let path = @path.Path::parse("/home/username/proj/src/")
guard path is UPath(path) else { panic() }
let parent_dir : ArrayView[@path.UnixPathComponent] = path.directory[0:path.directory.length() -
1]
@json.inspect(parent_dir, content=[
["UnixPathComponent", "home"],
["UnixPathComponent", "username"],
["UnixPathComponent", "proj"],
])
}///|
test "Unix relative path" {
let path = @path.Path::parse("home/username/proj/src/README.mbt.md")
inspect(path.directory(), content="home/username/proj/src/")
inspect(path.file().unwrap(), content="README.mbt.md")
inspect(path.is_absolute(), content="false")
inspect(path.is_relative(), content="true")
inspect(path.is_root_path(), content="false")
}///|
test "Unix absolute path" {
let path = @path.Path::parse("/home/username/proj/src/README.mbt.md")
inspect(path.directory(), content="home/username/proj/src/")
inspect(path.file().unwrap(), content="README.mbt.md")
inspect(path.is_absolute(), content="true")
inspect(path.is_relative(), content="false")
inspect(path.is_root_path(), content="true")
}///|
test "Windows relative path" {
let path = @path.Path::parse("Users\\username\\proj\\src\\README.mbt.md")
// This is used for Debug output
inspect(path.directory(), content="Users/username/proj/src/")
inspect(path.file().unwrap(), content="README.mbt.md")
inspect(path.is_absolute(), content="false")
inspect(path.is_relative(), content="true")
inspect(path.is_root_path(), content="false")
}///|
test "Windows absolute path" {
let path = @path.Path::parse("C:\\Users\\username\\proj\\src\\README.mbt.md")
// This is used for Debug output
inspect(path.directory(), content="Users/username/proj/src/")
inspect(path.file().unwrap(), content="README.mbt.md")
inspect(path.is_absolute(), content="true")
inspect(path.is_relative(), content="false")
inspect(path.is_root_path(), content="true")
}///|
test "Windows relative root path" {
let path = @path.Path::parse("\\Users\\username\\proj\\src\\README.mbt.md")
// This is used for Debug output
inspect(path.directory(), content="Users/username/proj/src/")
inspect(path.file().unwrap(), content="README.mbt.md")
inspect(path.is_absolute(), content="false")
inspect(path.is_relative(), content="true")
inspect(path.is_root_path(), content="true")
}trait IsStringpub(all) suberror PathError {
PathError(Int, String)
}type Directory[Component]fn[A : PlatformPath] Directory::new(components : ArrayView[StringView]) -> Directory[A] raise PathErrorpub struct File[Component] {
base : Component
extension : Component?
}fn[A : PlatformPath] File::new(base~ : StringView, extension? : StringView) -> File[A] raise PathErrortype PathBuilderimpl Show for PathBuilderfn PathBuilder::set_file(self : PathBuilder, base~ : StringView, extension~ : StringView) -> Unit raise PathErrorpub struct UnixPath {
is_absolute : Bool
directory : Directory[UnixPathComponent]
file : File[UnixPathComponent]?
}fn UnixPath::new(is_absolute? : Bool, directory~ : Directory[UnixPathComponent], file? : File[UnixPathComponent]) -> UnixPathtype UnixPathComponentimpl IsString for UnixPathComponentimpl PlatformPath for UnixPathComponentimpl Eq for UnixPathComponentimpl Hash for UnixPathComponentimpl Show for UnixPathComponentimpl ToJson for UnixPathComponentpub struct WinPath {
prefix : WinPrefix
directory : Directory[WinPathComponent]
file : File[WinPathComponent]?
}fn WinPath::new(prefix? : WinPrefix, directory~ : Directory[WinPathComponent], file? : File[WinPathComponent]) -> WinPathtype WinPathComponentimpl IsString for WinPathComponentimpl PlatformPath for WinPathComponentimpl Eq for WinPathComponentimpl Hash for WinPathComponentimpl Show for WinPathComponentimpl ToJson for WinPathComponentpub(all) enum WinPrefix {
Root
VerbatimSymlink(String)
VerbatimUNC(String, String)
VerbatimVolumeLetter(Char)
VerbatimVolumeGUID(String)
DeviceNS(String)
UNC(String, String)
VolumeLetter(Char)
None
}For file I/O, the "\?" prefix to a path string tells the Windows APIs to disable all string parsing and to send the string that follows it straight to the file system.
There is no need to perform any Unicode normalization on path and file name strings for use by the Windows file I/O API functions because the file system treats path and file names as an opaque sequence of WCHARs. Any normalization that your application requires should be performed with this in mind, external of any calls to related Windows file I/O API functions.
Because you cannot use the "\?" prefix with a relative path, relative paths are always limited to a total of MAX_PATH characters.
Install
Download zipA cross-platform path manipulation library for MoonBit that provides robust handling of both Unix and Windows file system paths with automatic format detection and type-safe path operations.