Skip to content
heff

std.path

Paths, a type of their own: where a file is, read from a string in a style — Posix (/a/b) or Windows (C:\a\b, \\server\share\a, either slash read) — and written back in one. A path is its drive (on Windows, C: or //server/share), whether it starts at the root, and its segments; what is done with it is done with those, the same on every platform. Only this module makes one, so it is always in that shape: no empty segment, no ..

Reading one and writing one take the style as os, the system’s own (native) unless it is given: parse s reads s as the system the program runs on writes a path, parse (os := Windows) s as Windows does. So a string from the program and one from outside, an argument or a variable, are read the same way. native is not asked at run time: a C program is built for one system, whose style it has. A string is taken as a path where one is expected (readFile "src/main.hf", dir / "main.hf"), read so, by the coercion this module declares.

Style

data Style = Posix | Windows

How a system writes a path: Posix (/a/b) or Windows (C:\a\b, \\server\share\a, either slash read).

Path

data Path

A path: its drive (Windows: C: or //server/share, with /s; otherwise ""), whether it starts at the root, and its segments (no "", no .; .. only where normal leaves one). Made only by this module: by parse, from a string where a path is expected, and by what is done with paths.

native

native : Style

The style of the system the program runs on: Windows there, Posix elsewhere — in a C program, the system it is built for; in JavaScript, Node.js’s (Posix in a page).

styleOf

styleOf : String -> Style

The style of a platform, named as Node.js names it (win32, linux, …: process.platform).

>>> styleOf "darwin"
Posix

sep

sep : Style -> String

The separator the style writes.

lower

lower : String -> String

The ASCII letters of s, in lower case.

drive

drive : Path -> String

The drive ("" where there is none; on Windows, C:, or //server/share, with /s).

isAbsolute

isAbsolute : Path -> Bool

Whether the path starts at the root (of its drive).

segments

segments : Path -> List String

The segments, in order: no "", no ..

parse

parse : {os : Style := native} -> String -> Path

s read as a path in the style os (the system’s, native, unless it is given).

>>> segments (parse (os := Posix) "/a//b/./c/")
["a", "b", "c"]
>>> segments (parse (os := Posix) "C:\\a")
["C:\\a"]
>>> drive (parse (os := Windows) "c:\\x")
"c:"
>>> segments (parse (os := Windows) "\\\\srv\\share\\x/y")
["x", "y"]

render

render : {os : Style := native} -> Path -> String

p written in the style os (the system’s, native, unless it is given): . for nothing.

>>> render (os := Windows) (parse (os := Windows) "//srv/share/a")
"\\\\srv\\share\\a"
>>> render (os := Posix) (parse (os := Windows) "C:\\a\\b")
"C:/a/b"
>>> render (os := Posix) (parse (os := Posix) "")
"."

instanceCoeStringPath

instance instanceCoeStringPath : Coe String Path

A string where a path is expected: parse, in the system’s style.

>>> render (os := Posix) ("a//b.hf/" : Path)
"a/b.hf"
>>> segments ("a/../b" : Path)
["a", "..", "b"]
>>> isAbsolute ("/" : Path)
True
>>> isAbsolute (parse (os := Windows) "C:x")
False

instanceEqualityPath

instance instanceEqualityPath : Equality Path

The same drive, both absolute or neither, and the same segments, as they are: case counts, and .. is not taken away (normal both first, for that).

>>> ("a/b" : Path) == "a//b/"
True
>>> ("a/../b" : Path) == "b"
False

instanceDebugPath

instance instanceDebugPath : Debug Path

As a program writes it: the string, in the system’s style, where that reads back as the path; else as parse (os := Windows) reads it.

>>> debug ("/a/b" : Path)
"\"/a/b\""
>>> debug (parse (os := Windows) "C:/a")
"parse (os := Windows) \"C:\\\\a\""

instanceShowPath

instance instanceShowPath : Show Path

Written in the system’s style, render.

>>> show ("a/b" : Path)
"a/b"

current

current : Path

The path of nothing, relative: the directory a path is from.

>>> current
"."

normal

normal : Path -> Path

p without its ..s, where they can go.

>>> normal "/a/./b/../../../c/"
"/c"
>>> normal "a/../../b"
"../b"
>>> render (os := Windows) (normal (parse (os := Windows) "C:/a\\..\\b"))
"C:\\b"

components

components : Path -> List String

The segments of p, without the ..s that can go (not its drive, nor whether it is absolute).

>>> components (parse (os := Windows) "a\\b/./c\\..\\d")
["a", "b", "d"]

join

join : Path -> Path -> Path

b from a: b itself if it is absolute (on a’s drive if it has none), a and b one after the other otherwise; its ..s gone where they can go. a / b is the same.

>>> join "src" "heff/load.hf"
"src/heff/load.hf"
>>> join "/a/b" "../c"
"/a/c"
>>> join "/a" "/b"
"/b"
>>> render (os := Windows) (join (parse (os := Windows) "C:\\a") (parse (os := Windows) "\\b"))
"C:\\b"

instanceDivisionPath

instance instanceDivisionPath : Division Path

a / b is join a b: dir / "main.hf".

>>> ("src" : Path) / "heff" / "load.hf"
"src/heff/load.hf"
>>> render (os := Windows) (parse (os := Windows) "C:\\a" / "b/c.hf")
"C:\\a\\b\\c.hf"
>>> render (os := Windows) (parse (os := Windows) "C:/work" / "notes.txt")
"C:\\work\\notes.txt"

joinAll

joinAll : List Path -> Path

The paths, each from the one before.

>>> joinAll ["a", "b", "c"]
"a/b/c"

dirname

dirname : Path -> Path

The directory p is in (the root’s is the root).

>>> dirname "/a/b/c.hf"
"/a/b"
>>> dirname "c.hf"
"."
>>> dirname "/"
"/"
>>> render (os := Windows) (dirname (parse (os := Windows) "C:\\a"))
"C:\\"

basename

basename : Path -> String

The last segment of p ("" for a root).

>>> basename "/a/b/c.hf/"
"c.hf"
>>> basename (parse (os := Windows) "C:\\a\\b.hf")
"b.hf"

extension

extension : Path -> String

The extension of p’s last segment, with its . ("" if none: a name starting with . has none by that .).

>>> extension "src/main.hf"
".hf"
>>> extension "a.tar.gz"
".gz"
>>> extension ".heff"
""
>>> extension "Makefile"
""

stem

stem : Path -> String

p’s last segment, without its extension.

>>> stem "src/heff/load.hf"
"load"

relative

relative : {os : Style := native} -> Path -> Path -> Maybe Path

How to get to to from from, if there is a way: both are absolute on one drive, or both relative from one place. The style os (the system’s, unless it is given) says whether segments are the same in any case (on Windows) or not.

>>> relative "/a/b/c" "/a/d"
Just ("../../d")
>>> relative "/a" "/a/b/c.hf"
Just ("b/c.hf")
>>> relative "/a" "/a"
Just (".")
>>> relative "/a" "b"
Nothing
>>> relative (os := Windows) (parse (os := Windows) "C:\\Src\\heff") (parse (os := Windows) "c:\\src\\lib") == Just (parse (os := Windows) "..\\lib")
True
>>> relative (os := Windows) (parse (os := Windows) "C:\\a") (parse (os := Windows) "D:\\a")
Nothing

isUnder

isUnder : {os : Style := native} -> Path -> Path -> Bool

p is dir or under it (in any case on Windows, as relative).

>>> isUnder "/a/b" "/a/b/c"
True
>>> isUnder "/a/b" "/a/bc"
False
>>> isUnder "/a/b" "/a/b"
True
>>> isUnder "src" "src/../lib/x.hf"
False