mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-13 23:16:03 +08:00
120 lines
3.5 KiB
Elixir
120 lines
3.5 KiB
Elixir
defmodule Livebook.FileSystem.Utils do
|
|
alias Livebook.FileSystem
|
|
|
|
@doc """
|
|
Asserts that the given path is a directory.
|
|
"""
|
|
@spec assert_dir_path!(FileSystem.path()) :: :ok
|
|
def assert_dir_path!(path) do
|
|
unless dir_path?(path) do
|
|
raise ArgumentError, "expected a directory path, got: #{inspect(path)}"
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
@doc """
|
|
Asserts that the given path is a regular file path.
|
|
"""
|
|
@spec assert_regular_path!(FileSystem.path()) :: :ok
|
|
def assert_regular_path!(path) do
|
|
unless regular_path?(path) do
|
|
raise ArgumentError, "expected a regular file path, got: #{inspect(path)}"
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
@doc """
|
|
Checks if the given path describes a directory.
|
|
"""
|
|
@spec dir_path?(FileSystem.path()) :: boolean()
|
|
def dir_path?(path) do
|
|
String.ends_with?(path, "/")
|
|
end
|
|
|
|
@doc """
|
|
Checks if the given path describes a regular file.
|
|
"""
|
|
@spec regular_path?(FileSystem.path()) :: boolean()
|
|
def regular_path?(path) do
|
|
not String.ends_with?(path, "/")
|
|
end
|
|
|
|
@doc """
|
|
Asserts that the given paths are of the same type.
|
|
"""
|
|
@spec assert_same_type!(FileSystem.path(), FileSystem.path()) :: :ok
|
|
def assert_same_type!(path1, path2) do
|
|
if dir_path?(path1) != dir_path?(path2) do
|
|
raise ArgumentError,
|
|
"expected paths of the same type, got: #{inspect(path1)} and #{inspect(path2)}"
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
@doc """
|
|
Converts the given path into dir path by appending a trailing
|
|
slash if necessary.
|
|
"""
|
|
@spec ensure_dir_path(String.t()) :: FileSystem.path()
|
|
def ensure_dir_path(path) do
|
|
if String.ends_with?(path, "/") do
|
|
path
|
|
else
|
|
path <> "/"
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Converts the given posix error atom into readable error tuple.
|
|
"""
|
|
@spec posix_error(atom()) :: {:error, FileSystem.error()}
|
|
def posix_error(error) do
|
|
message = error |> :file.format_error() |> List.to_string()
|
|
{:error, message}
|
|
end
|
|
|
|
@doc """
|
|
Implements `Livebook.FileSystem.resolve_path` assuming Unix-like
|
|
path conventions.
|
|
|
|
This function assumes absolute paths to have a leading "/"
|
|
and handles sequences such as "." and "..".
|
|
"""
|
|
@spec resolve_unix_like_path(FileSystem.path(), String.t()) :: FileSystem.t()
|
|
def resolve_unix_like_path(relative_to, subject) do
|
|
dir_path = relative_to |> Path.dirname() |> ensure_dir_path()
|
|
|
|
subject =
|
|
if Path.basename(subject) in [".", ".."] do
|
|
ensure_dir_path(subject)
|
|
else
|
|
subject
|
|
end
|
|
|
|
absolute_path? = String.starts_with?(subject, "/")
|
|
path = if absolute_path?, do: subject, else: dir_path <> subject
|
|
|
|
path
|
|
|> String.split("/")
|
|
|> remove_in_middle("")
|
|
|> expand_parts([])
|
|
|> Enum.join("/")
|
|
end
|
|
|
|
defp remove_in_middle([], _elem), do: []
|
|
defp remove_in_middle([head], _elem), do: [head]
|
|
defp remove_in_middle([head | tail], elem), do: remove_in_middle(tail, elem, [head])
|
|
|
|
defp remove_in_middle([head], _elem, acc), do: Enum.reverse([head | acc])
|
|
defp remove_in_middle([elem | tail], elem, acc), do: remove_in_middle(tail, elem, acc)
|
|
defp remove_in_middle([head | tail], elem, acc), do: remove_in_middle(tail, elem, [head | acc])
|
|
|
|
defp expand_parts([], acc), do: Enum.reverse(acc)
|
|
defp expand_parts(["." | parts], acc), do: expand_parts(parts, acc)
|
|
defp expand_parts([".." | parts], [_parent] = acc), do: expand_parts(parts, acc)
|
|
defp expand_parts([".." | parts], [_parent | acc]), do: expand_parts(parts, acc)
|
|
defp expand_parts([part | parts], acc), do: expand_parts(parts, [part | acc])
|
|
end
|