init
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,425 @@
|
||||
-module(filepath).
|
||||
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
|
||||
|
||||
-export([join/2, split_unix/1, directory_name/1, is_absolute/1, split_windows/1, split/1, base_name/1, extension/1, strip_extension/1, expand/1]).
|
||||
|
||||
-if(?OTP_RELEASE >= 27).
|
||||
-define(MODULEDOC(Str), -moduledoc(Str)).
|
||||
-define(DOC(Str), -doc(Str)).
|
||||
-else.
|
||||
-define(MODULEDOC(Str), -compile([])).
|
||||
-define(DOC(Str), -compile([])).
|
||||
-endif.
|
||||
|
||||
?MODULEDOC(
|
||||
" Work with file paths in Gleam!\n"
|
||||
"\n"
|
||||
" This library expects paths to be valid unicode. If you need to work with\n"
|
||||
" non-unicode paths you will need to convert them to unicode before using\n"
|
||||
" this library.\n"
|
||||
).
|
||||
|
||||
-file("src/filepath.gleam", 49).
|
||||
-spec relative(binary()) -> binary().
|
||||
relative(Path) ->
|
||||
case Path of
|
||||
<<"/"/utf8, Path@1/binary>> ->
|
||||
relative(Path@1);
|
||||
|
||||
_ ->
|
||||
Path
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 56).
|
||||
-spec remove_trailing_slash(binary()) -> binary().
|
||||
remove_trailing_slash(Path) ->
|
||||
case gleam@string:ends_with(Path, <<"/"/utf8>>) of
|
||||
true ->
|
||||
gleam@string:drop_end(Path, 1);
|
||||
|
||||
false ->
|
||||
Path
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 35).
|
||||
?DOC(
|
||||
" Join two paths together.\n"
|
||||
"\n"
|
||||
" This function does not expand `..` or `.` segments, use the `expand`\n"
|
||||
" function to do this.\n"
|
||||
"\n"
|
||||
" ## Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" join(\"/usr/local\", \"bin\")\n"
|
||||
" // -> \"/usr/local/bin\"\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec join(binary(), binary()) -> binary().
|
||||
join(Left, Right) ->
|
||||
_pipe@2 = case {Left, Right} of
|
||||
{_, <<"/"/utf8>>} ->
|
||||
Left;
|
||||
|
||||
{<<""/utf8>>, _} ->
|
||||
relative(Right);
|
||||
|
||||
{<<"/"/utf8>>, <<"/"/utf8, _/binary>>} ->
|
||||
Right;
|
||||
|
||||
{<<"/"/utf8>>, _} ->
|
||||
<<Left/binary, Right/binary>>;
|
||||
|
||||
{_, _} ->
|
||||
_pipe = remove_trailing_slash(Left),
|
||||
_pipe@1 = gleam@string:append(_pipe, <<"/"/utf8>>),
|
||||
gleam@string:append(_pipe@1, relative(Right))
|
||||
end,
|
||||
remove_trailing_slash(_pipe@2).
|
||||
|
||||
-file("src/filepath.gleam", 97).
|
||||
?DOC(
|
||||
" Split a path into its segments, using `/` as the path separator.\n"
|
||||
"\n"
|
||||
" Typically you would want to use `split` instead of this function, but if you\n"
|
||||
" want non-Windows path behaviour on a Windows system then you can use this\n"
|
||||
" function.\n"
|
||||
"\n"
|
||||
" ## Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" split(\"/usr/local/bin\", \"bin\")\n"
|
||||
" // -> [\"/\", \"usr\", \"local\", \"bin\"]\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec split_unix(binary()) -> list(binary()).
|
||||
split_unix(Path) ->
|
||||
_pipe = case gleam@string:split(Path, <<"/"/utf8>>) of
|
||||
[<<""/utf8>>] ->
|
||||
[];
|
||||
|
||||
[<<""/utf8>> | Rest] ->
|
||||
[<<"/"/utf8>> | Rest];
|
||||
|
||||
Rest@1 ->
|
||||
Rest@1
|
||||
end,
|
||||
gleam@list:filter(_pipe, fun(X) -> X /= <<""/utf8>> end).
|
||||
|
||||
-file("src/filepath.gleam", 267).
|
||||
-spec get_directory_name(list(binary()), binary(), binary()) -> binary().
|
||||
get_directory_name(Path, Acc, Segment) ->
|
||||
case Path of
|
||||
[<<"/"/utf8>> | Rest] ->
|
||||
get_directory_name(
|
||||
Rest,
|
||||
<<Acc/binary, Segment/binary>>,
|
||||
<<"/"/utf8>>
|
||||
);
|
||||
|
||||
[First | Rest@1] ->
|
||||
get_directory_name(Rest@1, Acc, <<Segment/binary, First/binary>>);
|
||||
|
||||
[] ->
|
||||
Acc
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 259).
|
||||
?DOC(
|
||||
" Get the directory name of a path, that is the path without the file name.\n"
|
||||
"\n"
|
||||
" ## Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" directory_name(\"/usr/local/bin\")\n"
|
||||
" // -> \"/usr/local\"\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec directory_name(binary()) -> binary().
|
||||
directory_name(Path) ->
|
||||
Path@1 = remove_trailing_slash(Path),
|
||||
case Path@1 of
|
||||
<<"/"/utf8, Rest/binary>> ->
|
||||
get_directory_name(
|
||||
gleam@string:to_graphemes(Rest),
|
||||
<<"/"/utf8>>,
|
||||
<<""/utf8>>
|
||||
);
|
||||
|
||||
_ ->
|
||||
get_directory_name(
|
||||
gleam@string:to_graphemes(Path@1),
|
||||
<<""/utf8>>,
|
||||
<<""/utf8>>
|
||||
)
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 294).
|
||||
?DOC(
|
||||
" Check if a path is absolute.\n"
|
||||
"\n"
|
||||
" ## Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" is_absolute(\"/usr/local/bin\")\n"
|
||||
" // -> True\n"
|
||||
" ```\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" is_absolute(\"usr/local/bin\")\n"
|
||||
" // -> False\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec is_absolute(binary()) -> boolean().
|
||||
is_absolute(Path) ->
|
||||
gleam@string:starts_with(Path, <<"/"/utf8>>).
|
||||
|
||||
-file("src/filepath.gleam", 339).
|
||||
-spec expand_segments(list(binary()), list(binary())) -> {ok, binary()} |
|
||||
{error, nil}.
|
||||
expand_segments(Path, Base) ->
|
||||
case {Base, Path} of
|
||||
{[<<""/utf8>>], [<<".."/utf8>> | _]} ->
|
||||
{error, nil};
|
||||
|
||||
{[], [<<".."/utf8>> | _]} ->
|
||||
{error, nil};
|
||||
|
||||
{[_ | Base@1], [<<".."/utf8>> | Path@1]} ->
|
||||
expand_segments(Path@1, Base@1);
|
||||
|
||||
{_, [<<"."/utf8>> | Path@2]} ->
|
||||
expand_segments(Path@2, Base);
|
||||
|
||||
{_, [S | Path@3]} ->
|
||||
expand_segments(Path@3, [S | Base]);
|
||||
|
||||
{_, []} ->
|
||||
{ok, gleam@string:join(lists:reverse(Base), <<"/"/utf8>>)}
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 364).
|
||||
-spec root_slash_to_empty(list(binary())) -> list(binary()).
|
||||
root_slash_to_empty(Segments) ->
|
||||
case Segments of
|
||||
[<<"/"/utf8>> | Rest] ->
|
||||
[<<""/utf8>> | Rest];
|
||||
|
||||
_ ->
|
||||
Segments
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 153).
|
||||
-spec pop_windows_drive_specifier(binary()) -> {gleam@option:option(binary()),
|
||||
binary()}.
|
||||
pop_windows_drive_specifier(Path) ->
|
||||
Start = gleam@string:slice(Path, 0, 3),
|
||||
Codepoints = gleam@string:to_utf_codepoints(Start),
|
||||
case gleam@list:map(Codepoints, fun gleam@string:utf_codepoint_to_int/1) of
|
||||
[Drive, Colon, Slash] when (((Slash =:= 47) orelse (Slash =:= 92)) andalso (Colon =:= 58)) andalso (((Drive >= 65) andalso (Drive =< 90)) orelse ((Drive >= 97) andalso (Drive =< 122))) ->
|
||||
Drive_letter = gleam@string:slice(Path, 0, 1),
|
||||
Drive@1 = <<(gleam@string:lowercase(Drive_letter))/binary,
|
||||
":/"/utf8>>,
|
||||
Path@1 = gleam@string:drop_start(Path, 3),
|
||||
{{some, Drive@1}, Path@1};
|
||||
|
||||
_ ->
|
||||
{none, Path}
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 120).
|
||||
?DOC(
|
||||
" Split a path into its segments, using `/` and `\\` as the path separators. If\n"
|
||||
" there is a drive letter at the start of the path then it is lowercased.\n"
|
||||
"\n"
|
||||
" Typically you would want to use `split` instead of this function, but if you\n"
|
||||
" want Windows path behaviour on a non-Windows system then you can use this\n"
|
||||
" function.\n"
|
||||
"\n"
|
||||
" ## Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" split(\"/usr/local/bin\", \"bin\")\n"
|
||||
" // -> [\"/\", \"usr\", \"local\", \"bin\"]\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec split_windows(binary()) -> list(binary()).
|
||||
split_windows(Path) ->
|
||||
{Drive, Path@1} = pop_windows_drive_specifier(Path),
|
||||
Segments = begin
|
||||
_pipe = gleam@string:split(Path@1, <<"/"/utf8>>),
|
||||
gleam@list:flat_map(
|
||||
_pipe,
|
||||
fun(_capture) -> gleam@string:split(_capture, <<"\\"/utf8>>) end
|
||||
)
|
||||
end,
|
||||
Segments@1 = case Drive of
|
||||
{some, Drive@1} ->
|
||||
[Drive@1 | Segments];
|
||||
|
||||
none ->
|
||||
Segments
|
||||
end,
|
||||
case Segments@1 of
|
||||
[<<""/utf8>>] ->
|
||||
[];
|
||||
|
||||
[<<""/utf8>> | Rest] ->
|
||||
[<<"/"/utf8>> | Rest];
|
||||
|
||||
Rest@1 ->
|
||||
Rest@1
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 77).
|
||||
?DOC(
|
||||
" Split a path into its segments.\n"
|
||||
"\n"
|
||||
" When running on Windows both `/` and `\\` are treated as path separators, and \n"
|
||||
" if the path starts with a drive letter then the drive letter then it is\n"
|
||||
" lowercased.\n"
|
||||
"\n"
|
||||
" ## Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" split(\"/usr/local/bin\", \"bin\")\n"
|
||||
" // -> [\"/\", \"usr\", \"local\", \"bin\"]\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec split(binary()) -> list(binary()).
|
||||
split(Path) ->
|
||||
case filepath_ffi:is_windows() of
|
||||
true ->
|
||||
split_windows(Path);
|
||||
|
||||
false ->
|
||||
split_unix(Path)
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 240).
|
||||
?DOC(
|
||||
" Get the base name of a path, that is the name of the file without the\n"
|
||||
" containing directory.\n"
|
||||
"\n"
|
||||
" ## Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" base_name(\"/usr/local/bin\")\n"
|
||||
" // -> \"bin\"\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec base_name(binary()) -> binary().
|
||||
base_name(Path) ->
|
||||
gleam@bool:guard(Path =:= <<"/"/utf8>>, <<""/utf8>>, fun() -> _pipe = Path,
|
||||
_pipe@1 = split(_pipe),
|
||||
_pipe@2 = gleam@list:last(_pipe@1),
|
||||
gleam@result:unwrap(_pipe@2, <<""/utf8>>) end).
|
||||
|
||||
-file("src/filepath.gleam", 190).
|
||||
?DOC(
|
||||
" Get the file extension of a path.\n"
|
||||
"\n"
|
||||
" ## Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" extension(\"src/main.gleam\")\n"
|
||||
" // -> Ok(\"gleam\")\n"
|
||||
" ```\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" extension(\"package.tar.gz\")\n"
|
||||
" // -> Ok(\"gz\")\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec extension(binary()) -> {ok, binary()} | {error, nil}.
|
||||
extension(Path) ->
|
||||
File = base_name(Path),
|
||||
case gleam@string:split(File, <<"."/utf8>>) of
|
||||
[<<""/utf8>>, _] ->
|
||||
{error, nil};
|
||||
|
||||
[_, Extension] ->
|
||||
{ok, Extension};
|
||||
|
||||
[_ | Rest] ->
|
||||
gleam@list:last(Rest);
|
||||
|
||||
_ ->
|
||||
{error, nil}
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 219).
|
||||
?DOC(
|
||||
" Remove the extension from a file, if it has any.\n"
|
||||
" \n"
|
||||
" ## Examples\n"
|
||||
" \n"
|
||||
" ```gleam\n"
|
||||
" strip_extension(\"src/main.gleam\")\n"
|
||||
" // -> \"src/main\"\n"
|
||||
" ```\n"
|
||||
" \n"
|
||||
" ```gleam\n"
|
||||
" strip_extension(\"package.tar.gz\")\n"
|
||||
" // -> \"package.tar\"\n"
|
||||
" ```\n"
|
||||
" \n"
|
||||
" ```gleam\n"
|
||||
" strip_extension(\"src/gleam\")\n"
|
||||
" // -> \"src/gleam\"\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec strip_extension(binary()) -> binary().
|
||||
strip_extension(Path) ->
|
||||
case extension(Path) of
|
||||
{ok, Extension} ->
|
||||
gleam@string:drop_end(Path, gleam@string:length(Extension) + 1);
|
||||
|
||||
{error, nil} ->
|
||||
Path
|
||||
end.
|
||||
|
||||
-file("src/filepath.gleam", 324).
|
||||
?DOC(
|
||||
" Expand `..` and `.` segments in a path.\n"
|
||||
"\n"
|
||||
" If the path has a `..` segment that would go up past the root of the path\n"
|
||||
" then an error is returned. This may be useful to example to ensure that a\n"
|
||||
" path specified by a user does not go outside of a directory.\n"
|
||||
"\n"
|
||||
" If the path is absolute then the result will always be absolute.\n"
|
||||
"\n"
|
||||
" ## Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" expand(\"/usr/local/../bin\")\n"
|
||||
" // -> Ok(\"/usr/bin\")\n"
|
||||
" ```\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" expand(\"/tmp/../..\")\n"
|
||||
" // -> Error(Nil)\n"
|
||||
" ```\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" expand(\"src/../..\")\n"
|
||||
" // -> Error(\"..\")\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec expand(binary()) -> {ok, binary()} | {error, nil}.
|
||||
expand(Path) ->
|
||||
Is_absolute = is_absolute(Path),
|
||||
Result = begin
|
||||
_pipe = Path,
|
||||
_pipe@1 = split(_pipe),
|
||||
_pipe@2 = root_slash_to_empty(_pipe@1),
|
||||
_pipe@3 = expand_segments(_pipe@2, []),
|
||||
gleam@result:map(_pipe@3, fun remove_trailing_slash/1)
|
||||
end,
|
||||
case Is_absolute andalso (Result =:= {ok, <<""/utf8>>}) of
|
||||
true ->
|
||||
{ok, <<"/"/utf8>>};
|
||||
|
||||
false ->
|
||||
Result
|
||||
end.
|
||||
@@ -0,0 +1,527 @@
|
||||
import * as $bool from "../gleam_stdlib/gleam/bool.mjs";
|
||||
import * as $list from "../gleam_stdlib/gleam/list.mjs";
|
||||
import * as $option from "../gleam_stdlib/gleam/option.mjs";
|
||||
import { None, Some } from "../gleam_stdlib/gleam/option.mjs";
|
||||
import * as $result from "../gleam_stdlib/gleam/result.mjs";
|
||||
import * as $string from "../gleam_stdlib/gleam/string.mjs";
|
||||
import { is_windows } from "./filepath_ffi.mjs";
|
||||
import { Ok, Error, toList, Empty as $Empty, prepend as listPrepend, isEqual } from "./gleam.mjs";
|
||||
|
||||
const codepoint_z_up = 122;
|
||||
|
||||
const codepoint_a_up = 97;
|
||||
|
||||
const codepoint_z = 90;
|
||||
|
||||
const codepoint_a = 65;
|
||||
|
||||
const codepoint_colon = 58;
|
||||
|
||||
const codepoint_backslash = 92;
|
||||
|
||||
const codepoint_slash = 47;
|
||||
|
||||
function remove_trailing_slash(path) {
|
||||
let $ = $string.ends_with(path, "/");
|
||||
if ($) {
|
||||
return $string.drop_end(path, 1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
function relative(loop$path) {
|
||||
while (true) {
|
||||
let path = loop$path;
|
||||
if (path.charCodeAt(0) === 47) {
|
||||
let path$1 = path.slice(1);
|
||||
loop$path = path$1;
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join two paths together.
|
||||
*
|
||||
* This function does not expand `..` or `.` segments, use the `expand`
|
||||
* function to do this.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ```gleam
|
||||
* join("/usr/local", "bin")
|
||||
* // -> "/usr/local/bin"
|
||||
* ```
|
||||
*/
|
||||
export function join(left, right) {
|
||||
let _block;
|
||||
if (right === "/") {
|
||||
_block = left;
|
||||
} else if (right.charCodeAt(0) === 47) {
|
||||
if (left === "") {
|
||||
_block = relative(right);
|
||||
} else if (left === "/") {
|
||||
_block = right;
|
||||
} else {
|
||||
let _pipe = remove_trailing_slash(left);
|
||||
let _pipe$1 = $string.append(_pipe, "/");
|
||||
_block = $string.append(_pipe$1, relative(right));
|
||||
}
|
||||
} else if (left === "") {
|
||||
_block = relative(right);
|
||||
} else if (left === "/") {
|
||||
_block = left + right;
|
||||
} else {
|
||||
let _pipe = remove_trailing_slash(left);
|
||||
let _pipe$1 = $string.append(_pipe, "/");
|
||||
_block = $string.append(_pipe$1, relative(right));
|
||||
}
|
||||
let _pipe = _block;
|
||||
return remove_trailing_slash(_pipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a path into its segments, using `/` as the path separator.
|
||||
*
|
||||
* Typically you would want to use `split` instead of this function, but if you
|
||||
* want non-Windows path behaviour on a Windows system then you can use this
|
||||
* function.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ```gleam
|
||||
* split("/usr/local/bin", "bin")
|
||||
* // -> ["/", "usr", "local", "bin"]
|
||||
* ```
|
||||
*/
|
||||
export function split_unix(path) {
|
||||
let _block;
|
||||
let $ = $string.split(path, "/");
|
||||
if ($ instanceof $Empty) {
|
||||
_block = $;
|
||||
} else {
|
||||
let $1 = $.tail;
|
||||
if ($1 instanceof $Empty) {
|
||||
let $2 = $.head;
|
||||
if ($2 === "") {
|
||||
_block = toList([]);
|
||||
} else {
|
||||
_block = $;
|
||||
}
|
||||
} else {
|
||||
let $2 = $.head;
|
||||
if ($2 === "") {
|
||||
let rest = $1;
|
||||
_block = listPrepend("/", rest);
|
||||
} else {
|
||||
_block = $;
|
||||
}
|
||||
}
|
||||
}
|
||||
let _pipe = _block;
|
||||
return $list.filter(_pipe, (x) => { return x !== ""; });
|
||||
}
|
||||
|
||||
function pop_windows_drive_specifier(path) {
|
||||
let start = $string.slice(path, 0, 3);
|
||||
let codepoints = $string.to_utf_codepoints(start);
|
||||
let $ = $list.map(codepoints, $string.utf_codepoint_to_int);
|
||||
if ($ instanceof $Empty) {
|
||||
return [new None(), path];
|
||||
} else {
|
||||
let $1 = $.tail;
|
||||
if ($1 instanceof $Empty) {
|
||||
return [new None(), path];
|
||||
} else {
|
||||
let $2 = $1.tail;
|
||||
if ($2 instanceof $Empty) {
|
||||
return [new None(), path];
|
||||
} else {
|
||||
let $3 = $2.tail;
|
||||
if ($3 instanceof $Empty) {
|
||||
let drive = $.head;
|
||||
let colon = $1.head;
|
||||
let slash = $2.head;
|
||||
if (
|
||||
(((slash === 47) || (slash === 92)) && (colon === 58)) && (((drive >= 65) && (drive <= 90)) || ((drive >= 97) && (drive <= 122)))
|
||||
) {
|
||||
let drive_letter = $string.slice(path, 0, 1);
|
||||
let drive$1 = $string.lowercase(drive_letter) + ":/";
|
||||
let path$1 = $string.drop_start(path, 3);
|
||||
return [new Some(drive$1), path$1];
|
||||
} else {
|
||||
return [new None(), path];
|
||||
}
|
||||
} else {
|
||||
return [new None(), path];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a path into its segments, using `/` and `\` as the path separators. If
|
||||
* there is a drive letter at the start of the path then it is lowercased.
|
||||
*
|
||||
* Typically you would want to use `split` instead of this function, but if you
|
||||
* want Windows path behaviour on a non-Windows system then you can use this
|
||||
* function.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ```gleam
|
||||
* split("/usr/local/bin", "bin")
|
||||
* // -> ["/", "usr", "local", "bin"]
|
||||
* ```
|
||||
*/
|
||||
export function split_windows(path) {
|
||||
let $ = pop_windows_drive_specifier(path);
|
||||
let drive = $[0];
|
||||
let path$1 = $[1];
|
||||
let _block;
|
||||
let _pipe = $string.split(path$1, "/");
|
||||
_block = $list.flat_map(
|
||||
_pipe,
|
||||
(_capture) => { return $string.split(_capture, "\\"); },
|
||||
);
|
||||
let segments = _block;
|
||||
let _block$1;
|
||||
if (drive instanceof Some) {
|
||||
let drive$1 = drive[0];
|
||||
_block$1 = listPrepend(drive$1, segments);
|
||||
} else {
|
||||
_block$1 = segments;
|
||||
}
|
||||
let segments$1 = _block$1;
|
||||
if (segments$1 instanceof $Empty) {
|
||||
return segments$1;
|
||||
} else {
|
||||
let $1 = segments$1.tail;
|
||||
if ($1 instanceof $Empty) {
|
||||
let $2 = segments$1.head;
|
||||
if ($2 === "") {
|
||||
return toList([]);
|
||||
} else {
|
||||
return segments$1;
|
||||
}
|
||||
} else {
|
||||
let $2 = segments$1.head;
|
||||
if ($2 === "") {
|
||||
let rest = $1;
|
||||
return listPrepend("/", rest);
|
||||
} else {
|
||||
return segments$1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a path into its segments.
|
||||
*
|
||||
* When running on Windows both `/` and `\` are treated as path separators, and
|
||||
* if the path starts with a drive letter then the drive letter then it is
|
||||
* lowercased.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ```gleam
|
||||
* split("/usr/local/bin", "bin")
|
||||
* // -> ["/", "usr", "local", "bin"]
|
||||
* ```
|
||||
*/
|
||||
export function split(path) {
|
||||
let $ = is_windows();
|
||||
if ($) {
|
||||
return split_windows(path);
|
||||
} else {
|
||||
return split_unix(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base name of a path, that is the name of the file without the
|
||||
* containing directory.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ```gleam
|
||||
* base_name("/usr/local/bin")
|
||||
* // -> "bin"
|
||||
* ```
|
||||
*/
|
||||
export function base_name(path) {
|
||||
return $bool.guard(
|
||||
path === "/",
|
||||
"",
|
||||
() => {
|
||||
let _pipe = path;
|
||||
let _pipe$1 = split(_pipe);
|
||||
let _pipe$2 = $list.last(_pipe$1);
|
||||
return $result.unwrap(_pipe$2, "");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file extension of a path.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ```gleam
|
||||
* extension("src/main.gleam")
|
||||
* // -> Ok("gleam")
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* extension("package.tar.gz")
|
||||
* // -> Ok("gz")
|
||||
* ```
|
||||
*/
|
||||
export function extension(path) {
|
||||
let file = base_name(path);
|
||||
let $ = $string.split(file, ".");
|
||||
if ($ instanceof $Empty) {
|
||||
return new Error(undefined);
|
||||
} else {
|
||||
let $1 = $.tail;
|
||||
if ($1 instanceof $Empty) {
|
||||
let rest = $1;
|
||||
return $list.last(rest);
|
||||
} else {
|
||||
let $2 = $1.tail;
|
||||
if ($2 instanceof $Empty) {
|
||||
let $3 = $.head;
|
||||
if ($3 === "") {
|
||||
return new Error(undefined);
|
||||
} else {
|
||||
let extension$1 = $1.head;
|
||||
return new Ok(extension$1);
|
||||
}
|
||||
} else {
|
||||
let rest = $1;
|
||||
return $list.last(rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the extension from a file, if it has any.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ```gleam
|
||||
* strip_extension("src/main.gleam")
|
||||
* // -> "src/main"
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* strip_extension("package.tar.gz")
|
||||
* // -> "package.tar"
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* strip_extension("src/gleam")
|
||||
* // -> "src/gleam"
|
||||
* ```
|
||||
*/
|
||||
export function strip_extension(path) {
|
||||
let $ = extension(path);
|
||||
if ($ instanceof Ok) {
|
||||
let extension$1 = $[0];
|
||||
return $string.drop_end(path, $string.length(extension$1) + 1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
function get_directory_name(loop$path, loop$acc, loop$segment) {
|
||||
while (true) {
|
||||
let path = loop$path;
|
||||
let acc = loop$acc;
|
||||
let segment = loop$segment;
|
||||
if (path instanceof $Empty) {
|
||||
return acc;
|
||||
} else {
|
||||
let $ = path.head;
|
||||
if ($ === "/") {
|
||||
let rest = path.tail;
|
||||
loop$path = rest;
|
||||
loop$acc = acc + segment;
|
||||
loop$segment = "/";
|
||||
} else {
|
||||
let first = $;
|
||||
let rest = path.tail;
|
||||
loop$path = rest;
|
||||
loop$acc = acc;
|
||||
loop$segment = segment + first;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the directory name of a path, that is the path without the file name.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ```gleam
|
||||
* directory_name("/usr/local/bin")
|
||||
* // -> "/usr/local"
|
||||
* ```
|
||||
*/
|
||||
export function directory_name(path) {
|
||||
let path$1 = remove_trailing_slash(path);
|
||||
if (path$1.charCodeAt(0) === 47) {
|
||||
let rest = path$1.slice(1);
|
||||
return get_directory_name($string.to_graphemes(rest), "/", "");
|
||||
} else {
|
||||
return get_directory_name($string.to_graphemes(path$1), "", "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a path is absolute.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ```gleam
|
||||
* is_absolute("/usr/local/bin")
|
||||
* // -> True
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* is_absolute("usr/local/bin")
|
||||
* // -> False
|
||||
* ```
|
||||
*/
|
||||
export function is_absolute(path) {
|
||||
return $string.starts_with(path, "/");
|
||||
}
|
||||
|
||||
function expand_segments(loop$path, loop$base) {
|
||||
while (true) {
|
||||
let path = loop$path;
|
||||
let base = loop$base;
|
||||
if (path instanceof $Empty) {
|
||||
return new Ok($string.join($list.reverse(base), "/"));
|
||||
} else if (base instanceof $Empty) {
|
||||
let $ = path.head;
|
||||
if ($ === "..") {
|
||||
return new Error(undefined);
|
||||
} else if ($ === ".") {
|
||||
let path$1 = path.tail;
|
||||
loop$path = path$1;
|
||||
loop$base = base;
|
||||
} else {
|
||||
let s = $;
|
||||
let path$1 = path.tail;
|
||||
loop$path = path$1;
|
||||
loop$base = listPrepend(s, base);
|
||||
}
|
||||
} else {
|
||||
let $ = base.tail;
|
||||
if ($ instanceof $Empty) {
|
||||
let $1 = path.head;
|
||||
if ($1 === "..") {
|
||||
let $2 = base.head;
|
||||
if ($2 === "") {
|
||||
return new Error(undefined);
|
||||
} else {
|
||||
let path$1 = path.tail;
|
||||
let base$1 = $;
|
||||
loop$path = path$1;
|
||||
loop$base = base$1;
|
||||
}
|
||||
} else if ($1 === ".") {
|
||||
let path$1 = path.tail;
|
||||
loop$path = path$1;
|
||||
loop$base = base;
|
||||
} else {
|
||||
let s = $1;
|
||||
let path$1 = path.tail;
|
||||
loop$path = path$1;
|
||||
loop$base = listPrepend(s, base);
|
||||
}
|
||||
} else {
|
||||
let $1 = path.head;
|
||||
if ($1 === "..") {
|
||||
let path$1 = path.tail;
|
||||
let base$1 = $;
|
||||
loop$path = path$1;
|
||||
loop$base = base$1;
|
||||
} else if ($1 === ".") {
|
||||
let path$1 = path.tail;
|
||||
loop$path = path$1;
|
||||
loop$base = base;
|
||||
} else {
|
||||
let s = $1;
|
||||
let path$1 = path.tail;
|
||||
loop$path = path$1;
|
||||
loop$base = listPrepend(s, base);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function root_slash_to_empty(segments) {
|
||||
if (segments instanceof $Empty) {
|
||||
return segments;
|
||||
} else {
|
||||
let $ = segments.head;
|
||||
if ($ === "/") {
|
||||
let rest = segments.tail;
|
||||
return listPrepend("", rest);
|
||||
} else {
|
||||
return segments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand `..` and `.` segments in a path.
|
||||
*
|
||||
* If the path has a `..` segment that would go up past the root of the path
|
||||
* then an error is returned. This may be useful to example to ensure that a
|
||||
* path specified by a user does not go outside of a directory.
|
||||
*
|
||||
* If the path is absolute then the result will always be absolute.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ```gleam
|
||||
* expand("/usr/local/../bin")
|
||||
* // -> Ok("/usr/bin")
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* expand("/tmp/../..")
|
||||
* // -> Error(Nil)
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* expand("src/../..")
|
||||
* // -> Error("..")
|
||||
* ```
|
||||
*/
|
||||
export function expand(path) {
|
||||
let is_absolute$1 = is_absolute(path);
|
||||
let _block;
|
||||
let _pipe = path;
|
||||
let _pipe$1 = split(_pipe);
|
||||
let _pipe$2 = root_slash_to_empty(_pipe$1);
|
||||
let _pipe$3 = expand_segments(_pipe$2, toList([]));
|
||||
_block = $result.map(_pipe$3, remove_trailing_slash);
|
||||
let result = _block;
|
||||
let $ = is_absolute$1 && (isEqual(result, new Ok("")));
|
||||
if ($) {
|
||||
return new Ok("/");
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
-module(filepath_ffi).
|
||||
|
||||
-export([is_windows/0]).
|
||||
|
||||
is_windows() ->
|
||||
case os:type() of
|
||||
{win32, _} -> true;
|
||||
_ -> false
|
||||
end.
|
||||
@@ -0,0 +1,6 @@
|
||||
export function is_windows() {
|
||||
return (
|
||||
globalThis?.process?.platform === "win32" ||
|
||||
globalThis?.Deno?.build?.os === "windows"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "../prelude.mjs";
|
||||
Reference in New Issue
Block a user