Current section
Files
Jump to
Current section
Files
src/gleam/path.gleam
import gleam/string
import gleam/file
import gleam/list
import gleam/result
import gleam/bit_string
import gleam/io
pub type Kind {
Relative
Absolute
Volumerelative
}
/// Converts a relative path and returns an absolute path.
/// No attempt is made to create the shortest absolute path,
/// as this can give incorrect results on file systems that allow links.
pub external fn absolute(path: String) -> String =
"filename" "absname"
/// Returns the filename portion of a path
/// ## Examples
/// > path.basename("foo")
/// "foo"
/// > path.basename("/usr/foo")
/// "foo"
/// > path.basename("/")
/// ""
pub external fn basename(path: String) -> String =
"filename" "basename"
/// Returns the extension portion of a path, including the period.
/// ## Examples
/// > path.extension("foo.gleam")
/// ".gleam"
/// > path.extension("beam.src/kalle")
/// ""
pub external fn extension(path: String) -> String =
"filename" "extension"
/// Splits the path into a list at the path separator.
/// If an empty string is given, returns an empty list.
/// On Windows, path is split on both "\" and "/" separators
/// and the driver letter, if there is one, is always returned
/// in lowercase.
/// ## Examples
/// > path.split("")
/// []
/// > path.split("foo")
/// ["foo"]
/// > path.split("/foo/bar")
/// ["/", "foo", "bar"]
pub external fn split(path: String) -> List(String) =
"filename" "split"
/// Joins a list of path parts with directory separators.
/// If one of the path parts includes an absolute path, such as "/xxx",
/// the preceding elements, if any, are removed from the result.
///
/// The result is "normalized":
/// Redundant directory separators are removed.
/// In Windows, all directory separators are forward slashes and the drive letter is in lower case.
///
pub external fn join(path: List(String)) -> String =
"filename" "join"
/// Returns the path type.
/// ## Examples
/// ### Unix-like operating systems
/// path.kind("/") #=> Absolute
/// path.kind("/usr/local/bin") #=> Absolute
/// path.kind("usr/local/bin") #=> Relative
/// path.kind("../usr/local/bin") #=> Relative
/// path.kind("~/file") #=> Relative
/// ### Windows
/// path.kind("D:/usr/local/bin") #=> Absolute
/// path.kind("usr/local/bin") #=> Relative
/// path.kind("D:bar.ex") #=> Volumerelative
/// path.kind("/bar/foo.ex") #=> Volumerelative
pub fn kind(path: String) -> Kind {
let tuple(k, _) = case os_family() {
Win32 -> win32_path_kind(path)
Unix -> unix_path_kind(path)
}
k
}
fn unix_path_kind(path: String) -> tuple(Kind, String) {
case path {
"/" -> tuple(Absolute, ".")
<<"/":utf8, _:binary>> -> tuple(Absolute, string.drop_left(path, 1))
_ -> tuple(Relative, path)
}
}
fn win32_path_kind(path: String) -> tuple(Kind, String) {
case path {
<<"\\\\":utf8, _:binary>> | <<"//":utf8, _:binary>> -> tuple(
Absolute,
string.drop_left(path, 2),
)
<<"\\":utf8, _:binary>> | <<"/":utf8, _:binary>> -> tuple(
Volumerelative,
string.drop_left(path, 1),
)
<<_:utf8, ":":utf8, "\\":utf8, _:binary>> | <<
_:utf8,
":":utf8,
"/":utf8,
_:binary,
>> -> tuple(Absolute, string.drop_left(path, 3))
<<_:utf8, ":":utf8, _:binary>> -> tuple(
Volumerelative,
string.drop_left(path, 2),
)
_ -> tuple(Relative, path)
}
}
// TODO: https://github.com/elixir-lang/elixir/blob/75403032beb3f1d8e925b6c86590e21dbae70dab/lib/elixir/lib/path.ex#L189
// Expands the path relative to the path given as the second argument
// expanding any `.` and `..` characters.
// Note that this function treats a `path` with a leading `~` as
// an absolute one.
// The second argument is first expanded to an absolute path.
// ## Examples
// # Assuming that the absolute path to baz is /quux/baz
// Path.expand("foo/bar/../bar", "baz")
// #=> "/quux/baz/foo/bar"
// Path.expand("foo/bar/../bar", "/baz")
// #=> "/baz/foo/bar"
// Path.expand("/foo/bar/../bar", "/baz")
// #=> "/foo/bar"
// """
// pub fn expand_relative(path path: String, relative_to: String) -> String {
// "TODO"
// }
//---------------------
//-- I think these are done but commented out for warnings
// fn expand_home(path: String) -> Result(String, Nil) {
// case string.pop_grapheme(path) {
// Ok(tuple("~", rest)) -> {
// try home = file.user_home()
// case string.pop_grapheme(rest), os_family() {
// Error(Nil), _ -> Ok(home)
// Ok(tuple("\\", _)), Win32 -> Ok(string.concat([home, rest]))
// Ok(tuple("/", _)), Unix -> Ok(string.concat([home, rest]))
// _, _ -> Error(Nil)
// }
// }
// Error(Nil) -> Ok(path)
// }
// }
// fn expand_dot(path: String) -> String {
// case path {
// <<"/":utf8, rest:binary>> ->
// string.concat([
// "/",
// expand_dot_help(result.unwrap(bit_string.to_string(rest), "")),
// ])
// <<letter:8, ":/":utf8, rest:binary>> ->
// case letter >= 97 && letter <= 122 {
// True -> {
// assert Ok(tuple(drive, _)) = string.pop_grapheme(path)
// string.concat([
// drive,
// ":/",
// expand_dot_help(result.unwrap(bit_string.to_string(rest), "")),
// ])
// }
// False -> expand_dot_help(path)
// }
// _ -> expand_dot_help(path)
// }
// }
// fn expand_dot_help(path: String) -> String {
// path
// |> string.split("/")
// |> do_expand_dot([])
// }
// fn do_expand_dot(path: List(String), acc: List(String)) -> String {
// case path, acc {
// ["..", ..rest_p], [_, _, ..rest_acc] -> do_expand_dot(rest_p, rest_acc)
// ["..", ..rest_p], [] -> do_expand_dot(rest_p, [])
// [".", ..rest_p], _ -> do_expand_dot(rest_p, acc)
// [p, ..rest_p], _ -> do_expand_dot(rest_p, ["/", p, ..acc])
// [], [] -> ""
// [], ["/", ..rest_acc] ->
// rest_acc
// |> list.reverse()
// |> string.concat()
// }
// }
type OsFamily {
Win32
Unix
}
external fn os_family() -> OsFamily =
"gleam_file_bridge" "os_family"