mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-09 21:16:26 +08:00
249 lines
7.4 KiB
Elixir
249 lines
7.4 KiB
Elixir
defprotocol Livebook.FileSystem do
|
|
# This protocol defines an interface for a virtual file system that
|
|
# can be plugged into Livebook.
|
|
|
|
@typedoc """
|
|
An identifier uniquely identifying the given file system.
|
|
|
|
Every file system struct is expected have an `:id` field.
|
|
|
|
The identifier should be computed deterministically based on the
|
|
specific resource used as the file system. This ensures that
|
|
identifiers persisted in a notebook work for multiple users, as
|
|
long as they have a file system using the same resource.
|
|
|
|
Ths identifier should also include file system type and hub id
|
|
(if applicable) in order to avoid conflicts.
|
|
"""
|
|
@type id :: String.t()
|
|
|
|
@typedoc """
|
|
A path uniquely identifies file in the file system.
|
|
|
|
Path has most of the semantics of regular file paths, with the
|
|
following exceptions:
|
|
|
|
* path must be be absolute for consistency
|
|
|
|
* directory path must have a trailing slash, whereas regular file
|
|
path must not have a trailing slash. Rationale: certain file
|
|
systems allow a directory and a file with the same name to
|
|
co-exist, while path needs to distinguish between them
|
|
|
|
"""
|
|
@type path :: String.t()
|
|
|
|
@typedoc """
|
|
A human-readable error message clarifying the operation failure
|
|
reason.
|
|
"""
|
|
@type error :: String.t()
|
|
|
|
@type access :: :read | :write | :read_write | :none
|
|
|
|
@doc """
|
|
Returns the file system type.
|
|
|
|
Based on the underlying resource, the type can be either:
|
|
|
|
* `:local` - if the resource is local to its node
|
|
|
|
* `:global` - if the resource is external and accessible from any
|
|
node
|
|
|
|
"""
|
|
@spec type(t()) :: :local | :global
|
|
def type(file_system)
|
|
|
|
@doc """
|
|
Returns the default directory path.
|
|
|
|
To some extent this is similar to current working directory in a
|
|
regular file system. For most file systems this will just be the
|
|
root path.
|
|
"""
|
|
@spec default_path(t()) :: path()
|
|
def default_path(file_system)
|
|
|
|
@doc """
|
|
Returns a list of files located in the given directory.
|
|
|
|
When `recursive` is set to `true`, nested directories are traversed
|
|
and the final list includes all the paths.
|
|
"""
|
|
@spec list(t(), path(), boolean()) :: {:ok, list(path())} | {:error, error()}
|
|
def list(file_system, path, recursive)
|
|
|
|
@doc """
|
|
Returns binary content of the given file.
|
|
"""
|
|
@spec read(t(), path()) :: {:ok, binary()} | {:error, error()}
|
|
def read(file_system, path)
|
|
|
|
@doc """
|
|
Writes the given binary content to the given file.
|
|
|
|
If the file exists, it gets overridden.
|
|
|
|
If the file doesn't exist, it gets created along with all the
|
|
necessary directories.
|
|
"""
|
|
@spec write(t(), path(), binary()) :: :ok | {:error, error()}
|
|
def write(file_system, path, content)
|
|
|
|
@doc """
|
|
Returns the current access level to the given file.
|
|
|
|
If determining the access is costly, then this function may always
|
|
return the most liberal access, since all access functions return
|
|
error on an invalid attempt.
|
|
"""
|
|
@spec access(t(), path()) :: {:ok, access()} | {:error, error()}
|
|
def access(file_system, path)
|
|
|
|
@doc """
|
|
Creates the given directory unless it already exists.
|
|
|
|
All necessary parent directories are created as well.
|
|
"""
|
|
@spec create_dir(t(), path()) :: :ok | {:error, error()}
|
|
def create_dir(file_system, path)
|
|
|
|
@doc """
|
|
Removes the given file.
|
|
|
|
If a directory is given, all of its contents are removed recursively.
|
|
|
|
If the file doesn't exist, no error is returned.
|
|
"""
|
|
@spec remove(t(), path()) :: :ok | {:error, error()}
|
|
def remove(file_system, path)
|
|
|
|
@doc """
|
|
Copies the given file.
|
|
|
|
The given files must be of the same type.
|
|
|
|
If regular files are given, the contents are copied, potentially
|
|
overriding the destination if it already exists.
|
|
|
|
If directories are given, the directory contents are copied
|
|
recursively.
|
|
"""
|
|
@spec copy(t(), path(), path()) :: :ok | {:error, error()}
|
|
def copy(file_system, source_path, destination_path)
|
|
|
|
@doc """
|
|
Renames the given file.
|
|
|
|
If a directory is given, it gets renamed as expected and consequently
|
|
all of the child paths change.
|
|
|
|
If the destination exists, an error is returned.
|
|
"""
|
|
@spec rename(t(), path(), path()) :: :ok | {:error, error()}
|
|
def rename(file_system, source_path, destination_path)
|
|
|
|
@doc """
|
|
Returns a version identifier for the given file.
|
|
|
|
The resulting value must be a string of ASCII characters placed
|
|
between double quotes, suitable for use as the value of the ETag
|
|
HTTP header.
|
|
"""
|
|
@spec etag_for(t(), path()) :: {:ok, String.t()} | {:error, error()}
|
|
def etag_for(file_system, path)
|
|
|
|
@doc """
|
|
Checks if the given path exists in the file system.
|
|
"""
|
|
@spec exists?(t(), path()) :: {:ok, boolean()} | {:error, error()}
|
|
def exists?(file_system, path)
|
|
|
|
@doc """
|
|
Resolves `subject` against a valid directory path.
|
|
|
|
The `subject` may be either relative or absolute, contain special
|
|
sequences such as ".." and ".", but the interpretation is left up
|
|
to the file system.
|
|
|
|
In other words, this has the semantics of path join followed by
|
|
expand.
|
|
"""
|
|
@spec resolve_path(t(), path(), String.t()) :: path()
|
|
def resolve_path(file_system, dir_path, subject)
|
|
|
|
@doc """
|
|
Initializes chunked write to the given file.
|
|
|
|
Should return the initial state, which is then reduced over in
|
|
`write_stream_chunk/3`.
|
|
"""
|
|
@spec write_stream_init(t(), path(), keyword()) :: {:ok, state} | {:error, error()}
|
|
when state: term()
|
|
def write_stream_init(file_system, path, opts)
|
|
|
|
@doc """
|
|
Writes a file chunk.
|
|
|
|
There is no assumption on the chunk size, you can accumulate chunks
|
|
in `state` and perform the write operation once the desired chunk
|
|
size is achieved.
|
|
"""
|
|
@spec write_stream_chunk(t(), state, binary()) :: {:ok, state} | {:error, error()}
|
|
when state: term()
|
|
def write_stream_chunk(file_system, state, chunk)
|
|
|
|
@doc """
|
|
Finalizes chunked write operation.
|
|
|
|
This function is called when all chunks have been successfully
|
|
written.
|
|
|
|
Note that if the finish operation fails, `write_stream_halt/2`
|
|
is **not** expected to be called, so you should do the necessary
|
|
cleanup here in case of failure as well.
|
|
"""
|
|
@spec write_stream_finish(t(), state) :: :ok | {:error, error()} when state: term()
|
|
def write_stream_finish(file_system, state)
|
|
|
|
@doc """
|
|
Halts chunked write operation.
|
|
|
|
This function is called when writing any of the chunks fails or the
|
|
writing is aborted by the caller.
|
|
"""
|
|
@spec write_stream_halt(t(), state) :: :ok | {:error, error()} when state: term()
|
|
def write_stream_halt(file_system, state)
|
|
|
|
@doc """
|
|
Similar to `read/2`, but streams file contents into `collectable`
|
|
chunk by chunk.
|
|
|
|
The `Collectable` protocol does not make room for gracefully
|
|
signalling an error, so implementations generally raise an
|
|
exception. `read_stream_into/3` is not expected to raise, so make
|
|
sure to convert collectable exceptions into an error tuple.
|
|
"""
|
|
@spec read_stream_into(t(), path(), Collectable.t()) ::
|
|
{:ok, Collectable.t()} | {:error, error()}
|
|
def read_stream_into(file_system, path, collectable)
|
|
|
|
@doc """
|
|
Loads fields into given file system.
|
|
"""
|
|
@spec load(t(), map()) :: struct()
|
|
def load(file_system, fields)
|
|
|
|
@doc """
|
|
Transforms file system to the attributes map.
|
|
"""
|
|
@spec dump(t()) :: map()
|
|
def dump(file_system)
|
|
|
|
@doc """
|
|
Returns file system metadata for external storages.
|
|
"""
|
|
@spec external_metadata(t()) :: %{name: String.t(), error_field: String.t()}
|
|
def external_metadata(file_system)
|
|
end
|