#fpath - File Path Utilities

    Level: 1 Package: bobzhang/zip/types/fpath Dependencies: None

    #Overview

    The fpath package provides utilities for manipulating and normalizing file paths in ZIP archives. It ensures paths are in the proper Unix format and handles directory path conventions.

    #Features

    • Unix Path Normalization: Convert Windows \ to Unix /
    • Directory Path Handling: Ensure directories end with /
    • Path Sanitization: Remove redundant separators and . components
    • Type Safety: Fpath type alias for better API clarity

    #API

    #Types

    pub typealias String as Fpath

    File path type. Internally just a string, but provides semantic clarity.

    #Functions

    #fpath_ensure_unix(path : Fpath) -> Fpath

    Convert any path to Unix-style (forward slashes).

    Transformation:
    • dir\subdir\file.txtdir/subdir/file.txt
    • dir/subdir/file.txtdir/subdir/file.txt (unchanged)

    Example:
    ///|
    test {
    let windows_path : @fpath.Fpath = "C:\\Users\\name\\file.txt"
    let unix_path = @fpath.ensure_unix(windows_path)
    @json.inspect(unix_path, content="C:/Users/name/file.txt")
    }

    #fpath_ensure_directoryness(path : Fpath) -> Fpath

    Ensure directory paths end with /.

    Transformation:
    • mydirmydir/
    • mydir/mydir/ (unchanged)
    • mydir/subdirmydir/subdir/

    Example:
    let dir = "src/main" let dir_path = fpath_ensure_directoryness(dir) // Result: "src/main/"

    #fpath_sanitize(path : Fpath) -> Fpath

    Remove redundant path separators and normalize . components.

    Transformations:
    • dir//subdir///file.txtdir/subdir/file.txt
    • dir/./subdir/./file.txtdir/subdir/file.txt
    • ./dir/file.txtdir/file.txt

    Example:
    let messy_path = "src//./main///utils/./file.txt" let clean_path = fpath_sanitize(messy_path) // Result: "src/main/utils/file.txt"

    #Usage Examples

    #Creating ZIP-Compatible Paths

    // Windows input let user_path = "Documents\\Projects\\src\\main.mbt" // Normalize for ZIP let zip_path = fpath_ensure_unix(user_path) let clean_path = fpath_sanitize(zip_path) // Result: "Documents/Projects/src/main.mbt"

    #Directory Handling

    // Create directory entry let dir_name = "src/utils" let dir_path = fpath_ensure_directoryness( fpath_ensure_unix( fpath_sanitize(dir_name) ) ) // Result: "src/utils/"

    #Path Pipeline

    fn normalize_for_zip(path : String) -> Fpath { path |> fpath_sanitize |> fpath_ensure_unix } fn normalize_directory(path : String) -> Fpath { path |> normalize_for_zip |> fpath_ensure_directoryness } let file_path = normalize_for_zip("dir\\./subdir//file.txt") // Result: "dir/subdir/file.txt" let dir_path = normalize_directory("my\\project\\src") // Result: "my/project/src/"

    #ZIP Path Requirements

    #Path Format

    • Separator: Must use forward slash /
    • No Backslash: Windows \ not allowed
    • Directories: Must end with /
    • Case: Preserved (ZIP is case-preserving)
    • Encoding: UTF-8 (when GP flag bit 11 set)

    #Invalid Paths

    ZIP archives should avoid:
    • Absolute paths (/home/user/file.txt)
    • Drive letters (C:/file.txt)
    • Parent references (../file.txt)
    • Null bytes or control characters

    Note: This package doesn't validate security concerns. The main ZIP package should handle security validation.

    #Path Normalization Rules

    #Separator Normalization

    Input: "dir\\subdir\\file.txt" Step 1: Replace '\' with '/' Output: "dir/subdir/file.txt"

    #Redundant Separator Removal

    Input: "dir///subdir//file.txt" Step 1: Replace multiple '/' with single '/' Output: "dir/subdir/file.txt"

    #Dot Component Removal

    Input: "dir/./subdir/./file.txt" Step 1: Remove '/.' components Output: "dir/subdir/file.txt"

    #Directory Slash Addition

    Input: "dir/subdir" Step 1: Check if ends with '/' Step 2: If not, append '/' Output: "dir/subdir/"

    #Common Patterns

    #File Entry Path

    fn make_file_path(raw_path : String) -> Fpath { raw_path |> fpath_sanitize // Clean up |> fpath_ensure_unix // Unix separators }

    #Directory Entry Path

    fn make_dir_path(raw_path : String) -> Fpath { raw_path |> fpath_sanitize // Clean up |> fpath_ensure_unix // Unix separators |> fpath_ensure_directoryness // Add trailing slash }

    #Full Normalization

    fn normalize_zip_path(raw_path : String, is_dir : Bool) -> Fpath { let path = raw_path |> fpath_sanitize |> fpath_ensure_unix if is_dir { fpath_ensure_directoryness(path) } else { path } }

    #Implementation Notes

    #String Operations

    • ensure_unix: Simple character replacement (\/)
    • ensure_directoryness: String length check + append
    • sanitize: Regex-like pattern replacement (simplified)

    #Performance

    • Time: O(n) where n is path length
    • Space: O(n) for result string
    • No Allocation: If path already normalized (depends on implementation)

    #Edge Cases

    • Empty string: Returns empty string (all functions)
    • Root /: Preserved
    • Single slash /: Preserved
    • Only dots ./././: Results in empty string (sanitize)

    #Testing

    Run tests with:
    moon test types/fpath

    Tests cover:
    • Unix path conversion
    • Directory path handling
    • Path sanitization
    • Edge cases (empty, root, special chars)
    • Multiple transformations
    • Idempotency (applying twice has same result)

    #Dependencies

    None - This is a Level 1 package with no external dependencies.

    #Used By

    • types (Level 2) - Re-exports fpath functions
    • Main zip package - Via types re-exports
    • Any package handling ZIP file paths

    #Future Enhancements

    Potential additions:
    • Path validation (security checks)
    • Absolute path detection
    • Parent reference (..) handling
    • Path component extraction
    • Path joining/splitting
    • Unicode normalization
    • Max path length validation (ZIP limit: 65535)

    #References

    #Notes

    • No Validation: Functions don't validate path security or legality
    • Preserves Content: Doesn't modify filename or extension
    • Case Sensitive: Treats paths as case-sensitive (ZIP standard)
    • UTF-8: Assumes UTF-8 encoding (set GP flag bit 11 in ZIP)

    Fpath

    pub(all) struct Fpath(String) derive(Compare, Eq, Hash,
    FromJson
    )

    File path in ZIP archive
    impl Add for Fpath
    impl Show for Fpath
    impl ToJson for Fpath

    Fpath::add

    fn Fpath::add(x : Fpath, other : Fpath) -> Fpath

    Fpath::at

    #alias("_[_]")
    fn Fpath::at(self : Fpath, index : Int) -> Int

    Get character at index

    Fpath::compare

    fn Fpath::compare(Fpath, Fpath) -> Int

    Fpath::equal

    fn Fpath::equal(Fpath, Fpath) -> Bool

    Fpath::hash

    fn Fpath::hash(self : Fpath) -> Int

    Fpath::hash_combine

    fn Fpath::hash_combine(Fpath, Hasher) -> Unit

    Fpath::is_empty

    fn Fpath::is_empty(self : Fpath) -> Bool

    Check if path is empty

    Fpath::length

    fn Fpath::length(self : Fpath) -> Int

    Get the length of the path string

    Fpath::not_equal

    fn Fpath::not_equal(x : Fpath, y : Fpath) -> Bool

    Fpath::op_ge

    fn Fpath::op_ge(x : Fpath, y : Fpath) -> Bool

    Fpath::op_gt

    fn Fpath::op_gt(x : Fpath, y : Fpath) -> Bool

    Fpath::op_le

    fn Fpath::op_le(x : Fpath, y : Fpath) -> Bool

    Fpath::op_lt

    fn Fpath::op_lt(x : Fpath, y : Fpath) -> Bool

    Fpath::output

    fn Fpath::output(x : Fpath, logger : &Logger) -> Unit

    Fpath::to_json

    fn Fpath::to_json(x : Fpath) -> Json

    Fpath::to_string

    fn Fpath::to_string(x : Fpath) -> String

    ensure_directoryness

    fn ensure_directoryness(path : Fpath) -> Fpath

    Ensure path ends with '/' (for directories) Ensure trailing '/' for directory semantics (adds if missing; empty -> "./").

    ensure_unix

    fn ensure_unix(path : Fpath) -> Fpath

    Convert backslashes to forward slashes (for Windows paths) Normalize path separators to '/' (Windows->Unix style), preserving relative structure.

    sanitize

    fn sanitize(path : Fpath) -> Fpath

    Sanitize a file path by removing dangerous segments Removes: empty segments, ".", "..", and absolute path markers Remove dangerous segments ("", ".", "..") and collapse separators (“/” and “\”).

    Source Files