Packages
livebook
0.5.1
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook/file_system/file.ex
defmodule Livebook.FileSystem.File do
@moduledoc false
# A file points to a specific location in the given
# file system.
#
# This module provides a number of high-level functions
# similar to the `File` and `Path` core module. Many
# functions simply delegate the work to the underlying
# file system.
defstruct [:file_system, :path]
alias Livebook.FileSystem
@type t :: %__MODULE__{
file_system: FileSystem.t(),
path: FileSystem.path()
}
@doc """
Builds a new file struct.
If no path is given, the default file system one is used.
"""
@spec new(FileSystem.t(), FileSystem.path() | nil) :: t()
def new(file_system, path \\ nil) do
default_path = FileSystem.default_path(file_system)
path =
if path do
resolved_path = FileSystem.resolve_path(file_system, default_path, path)
unless path == resolved_path do
raise ArgumentError, "expected an expanded absolute path, got: #{inspect(path)}"
end
path
else
default_path
end
%__MODULE__{file_system: file_system, path: path}
end
@doc """
Returns a new file within the `Livebook.FileSystem.Local`
file system.
"""
@spec local(FileSystem.path()) :: t()
def local(path) do
new(FileSystem.Local.new(), path)
end
@doc """
Returns a term uniquely identifying the file together
with its file system.
"""
@spec resource_identifier(t()) :: term()
def resource_identifier(file) do
{FileSystem.resource_identifier(file.file_system), file.path}
end
@doc """
Checks if the given file is within a file system local to its node.
"""
@spec local?(t()) :: term()
def local?(file) do
FileSystem.type(file.file_system) == :local
end
@doc """
Returns a new file resulting from resolving `subject`
against `file`.
An absolute path may be given, in which case it
replaces the file path altogether.
"""
@spec resolve(t(), String.t()) :: t()
def resolve(file, subject) do
dir = if dir?(file), do: file, else: containing_dir(file)
path = FileSystem.resolve_path(file.file_system, dir.path, subject)
new(file.file_system, path)
end
@doc """
Checks if the given file is a directory.
Note: this check relies solely on the file path.
"""
@spec dir?(t()) :: boolean()
def dir?(file) do
FileSystem.Utils.dir_path?(file.path)
end
@doc """
Checks if the given file is a regular file.
Note: this check relies solely on the file path.
"""
@spec regular?(t()) :: boolean()
def regular?(file) do
FileSystem.Utils.regular_path?(file.path)
end
@doc """
Returns file name.
"""
@spec name(t()) :: String.t()
def name(file) do
Path.basename(file.path)
end
@doc """
Returns a directory that contains the given file.
If a directory is given, the parent directory is returned.
Root directory is mapped to itself for consistency.
"""
@spec containing_dir(t()) :: t()
def containing_dir(file) do
parent_path =
if file.path == "/" do
"/"
else
file.path
|> String.trim_trailing("/")
|> Path.dirname()
|> FileSystem.Utils.ensure_dir_path()
end
new(file.file_system, parent_path)
end
@doc """
Returns a list of files located in the given directory.
## Options
* `:recursive` - whether to traverse all nested directories,
defaults to `false`
"""
@spec list(t(), keyword()) :: {:ok, list(t())} | {:error, FileSystem.error()}
def list(file, opts \\ []) do
recursive = Keyword.get(opts, :recursive, false)
with {:ok, paths} <- FileSystem.list(file.file_system, file.path, recursive) do
files = for path <- paths, do: new(file.file_system, path)
{:ok, files}
end
end
@doc """
Returns binary content of the given file.
"""
@spec read(t()) :: {:ok, binary()} | {:error, FileSystem.error()}
def read(file) do
FileSystem.read(file.file_system, file.path)
end
@doc """
Writes the given binary content to the given file.
"""
@spec write(t(), binary()) :: :ok | {:error, FileSystem.error()}
def write(file, content) do
FileSystem.write(file.file_system, file.path, content)
end
@doc """
Returns the current access level to the given file.
"""
@spec access(t()) :: {:ok, FileSystem.access()} | {:error, FileSystem.error()}
def access(file) do
FileSystem.access(file.file_system, file.path)
end
@doc """
Creates the given directory unless it already exists.
"""
@spec create_dir(t()) :: :ok | {:error, FileSystem.error()}
def create_dir(file) do
FileSystem.create_dir(file.file_system, file.path)
end
@doc """
Removes the given file.
"""
@spec remove(t()) :: :ok | {:error, FileSystem.error()}
def remove(file) do
FileSystem.remove(file.file_system, file.path)
end
@doc """
Copies the given file or directory contents.
Files from different file systems are supported,
however keep in mind that this involves reading
contents of individual files from one file system
and writing them to the other.
"""
@spec copy(t(), t()) :: :ok | {:error, FileSystem.error()}
def copy(source, destination)
def copy(%{file_system: file_system} = source, %{file_system: file_system} = destination) do
FileSystem.copy(file_system, source.path, destination.path)
end
def copy(source, destination) do
FileSystem.Utils.assert_same_type!(source.path, destination.path)
if dir?(source) do
with {:ok, child_files} <- list(source, recursive: true) do
child_files
|> Enum.filter(®ular?/1)
|> Enum.reduce(:ok, fn child_file, result ->
with :ok <- result do
path_sufix = String.replace_leading(child_file.path, source.path, "")
child_destination = resolve(destination, path_sufix)
copy_regular_file(child_file, child_destination)
end
end)
end
else
copy_regular_file(source, destination)
end
end
defp copy_regular_file(source, destination) do
with {:ok, content} <- read(source) do
write(destination, content)
end
end
@doc """
Renames the given file.
Files from different file systems are supported,
however keep in mind that this involves reading
contents of individual files from one file system
and writing them to the other.
"""
@spec rename(t(), t()) :: :ok | {:error, FileSystem.error()}
def rename(source, destination)
def rename(%{file_system: file_system} = source, %{file_system: file_system} = destination) do
FileSystem.rename(file_system, source.path, destination.path)
end
def rename(source, destination) do
FileSystem.Utils.assert_same_type!(source.path, destination.path)
with {:ok, destination_exist?} <- exists?(destination) do
if destination_exist? do
FileSystem.Utils.posix_error(:eexist)
else
with :ok <- copy(source, destination) do
remove(source)
end
end
end
end
@doc """
Returns a version identifier for the given file.
"""
@spec etag_for(t()) :: {:ok, String.t()} | {:error, FileSystem.error()}
def etag_for(file) do
FileSystem.etag_for(file.file_system, file.path)
end
@doc """
Checks if the given file exists.
"""
@spec exists?(t()) :: {:ok, boolean()} | {:error, FileSystem.error()}
def exists?(file) do
FileSystem.exists?(file.file_system, file.path)
end
end