-module(fio@path). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/fio/path.gleam"). -export([join/2, split/1, base_name/1, directory_name/1, extension/1, strip_extension/1, is_absolute/1, expand/1, stem/1, with_extension/2, join_all/1, safe_relative/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. -file("src/fio/path.gleam", 6). ?DOC(" Join two path segments.\n"). -spec join(binary(), binary()) -> binary(). join(Left, Right) -> filepath:join(Left, Right). -file("src/fio/path.gleam", 11). ?DOC(" Split a path into its segments.\n"). -spec split(binary()) -> list(binary()). split(Path) -> filepath:split(Path). -file("src/fio/path.gleam", 16). ?DOC(" Get the base name (filename) of a path.\n"). -spec base_name(binary()) -> binary(). base_name(Path) -> filepath:base_name(Path). -file("src/fio/path.gleam", 21). ?DOC(" Get the directory portion of a path.\n"). -spec directory_name(binary()) -> binary(). directory_name(Path) -> filepath:directory_name(Path). -file("src/fio/path.gleam", 26). ?DOC(" Get the file extension (without dot).\n"). -spec extension(binary()) -> {ok, binary()} | {error, nil}. extension(Path) -> filepath:extension(Path). -file("src/fio/path.gleam", 31). ?DOC(" Remove the extension from a path.\n"). -spec strip_extension(binary()) -> binary(). strip_extension(Path) -> filepath:strip_extension(Path). -file("src/fio/path.gleam", 36). ?DOC(" Check if a path is absolute.\n"). -spec is_absolute(binary()) -> boolean(). is_absolute(Path) -> filepath:is_absolute(Path). -file("src/fio/path.gleam", 42). ?DOC( " Expand/normalize a path, resolving `.` and `..` segments.\n" " Returns Error(Nil) if `..` would go above the root.\n" ). -spec expand(binary()) -> {ok, binary()} | {error, nil}. expand(Path) -> filepath:expand(Path). -file("src/fio/path.gleam", 49). ?DOC(" Get the stem (filename without extension).\n"). -spec stem(binary()) -> binary(). stem(Path) -> Name = base_name(Path), case extension(Path) of {ok, _} -> strip_extension(Name); {error, _} -> Name end. -file("src/fio/path.gleam", 58). ?DOC(" Change the extension of a path.\n"). -spec with_extension(binary(), binary()) -> binary(). with_extension(Path, Ext) -> <<<<(strip_extension(Path))/binary, "."/utf8>>/binary, Ext/binary>>. -file("src/fio/path.gleam", 73). -spec join_all_loop(binary(), list(binary())) -> binary(). join_all_loop(Acc, Segments) -> case Segments of [] -> Acc; [Next | Rest] -> join_all_loop(join(Acc, Next), Rest) end. -file("src/fio/path.gleam", 63). ?DOC(" Join a list of path segments.\n"). -spec join_all(list(binary())) -> binary(). join_all(Segments) -> case Segments of [] -> <<"."/utf8>>; [First | Rest] -> join_all_loop(First, Rest) end. -file("src/fio/path.gleam", 80). -spec is_windows_drive(binary()) -> boolean(). is_windows_drive(Path) -> case string:length(Path) >= 2 of true -> First = string:lowercase(gleam@string:slice(Path, 0, 1)), Second = gleam@string:slice(Path, 1, 1), (Second =:= <<":"/utf8>>) andalso gleam_stdlib:contains_string( <<"abcdefghijklmnopqrstuvwxyz"/utf8>>, First ); false -> false end. -file("src/fio/path.gleam", 114). -spec normalize_separators(binary()) -> binary(). normalize_separators(Path) -> gleam@string:join(gleam@string:split(Path, <<"\\"/utf8>>), <<"/"/utf8>>). -file("src/fio/path.gleam", 97). ?DOC( " Validate that a path is a safe relative path (does not escape root).\n" "\n" " This is primarily intended for sanitizing user-provided paths. On Windows,\n" " backslashes are treated as separators and are normalized to forward slashes.\n" ). -spec safe_relative(binary()) -> {ok, binary()} | {error, nil}. safe_relative(Path) -> Normalized = normalize_separators(Path), case is_absolute(Normalized) orelse is_windows_drive(Normalized) of true -> {error, nil}; false -> case expand(Normalized) of {ok, Expanded} -> case is_absolute(Expanded) of true -> {error, nil}; false -> {ok, Expanded} end; {error, _} -> {error, nil} end end.