We want trees to just be notes. We want to reference notes by Path instead of object ids. Stop thinking about the fs implementation and the fuse service with the same terms - they will be different backends.
168 lines
4.3 KiB
Python
168 lines
4.3 KiB
Python
from typing import Protocol, runtime_checkable, Optional, Dict, List, Self, NamedTuple, Iterable, TypedDict, Iterator
|
|
from uuid import UUID
|
|
import datetime as dt
|
|
import errno
|
|
|
|
type ObjectId = int | str | UUID
|
|
|
|
class TroveError(Exception):
|
|
"""Base class for all Trove errors."""
|
|
|
|
class ErrorWithErrno(TroveError):
|
|
"""Raised when an error occurs with an errno."""
|
|
|
|
def __init__(self, error: int, *args):
|
|
super().__init__(*args)
|
|
self.errno = error
|
|
|
|
class ErrorExists(ErrorWithErrno):
|
|
"""Raised when a note already exists."""
|
|
|
|
def __init__(self, *args):
|
|
super().__init__(errno.EEXIST, *args)
|
|
|
|
class ErrorNotFound(ErrorWithErrno):
|
|
"""Raised when a note is not found."""
|
|
|
|
def __init__(self, *args):
|
|
super().__init__(errno.ENOENT, *args)
|
|
|
|
class ErrorNotEmpty(ErrorWithErrno):
|
|
"""Raised when a directory is not empty."""
|
|
|
|
def __init__(self, *args):
|
|
super().__init__(errno.ENOTEMPTY, *args)
|
|
|
|
|
|
class BadNoteType(TypeError):
|
|
"""Raised when an invalid note type is encountered."""
|
|
|
|
class TreeExists(RuntimeError):
|
|
"""Raised when a label already exists."""
|
|
|
|
class NoteNotFound(KeyError):
|
|
"""Raised when a note is not found."""
|
|
|
|
class OpenArguments(TypedDict):
|
|
create: bool
|
|
|
|
class TreeEntry(NamedTuple):
|
|
name: str
|
|
object_id: ObjectId
|
|
|
|
DEFAULT_MIME = "application/octet-stream"
|
|
|
|
@runtime_checkable
|
|
class Note(Protocol):
|
|
"""
|
|
Protocol for a Note item.
|
|
Represents access to an individual note's content and metadata.
|
|
"""
|
|
|
|
@property
|
|
def object_id(self) -> ObjectId:
|
|
"""The unique identifier for this note."""
|
|
...
|
|
|
|
@property
|
|
def mime(self) -> str:
|
|
"""The MIME type of the note's content."""
|
|
...
|
|
|
|
@property
|
|
def readonly(self) -> bool:
|
|
"""Whether the note is read-only."""
|
|
...
|
|
|
|
@property
|
|
def mtime(self) -> dt.datetime:
|
|
"""The last modification time of the note."""
|
|
...
|
|
|
|
def get_raw_metadata(self, key: str) -> Optional[bytes]:
|
|
"""Retrieve metadata value for the given key."""
|
|
...
|
|
|
|
def set_raw_metadata(self, key: str, value: bytes) -> None:
|
|
"""Set metadata value for the given key."""
|
|
...
|
|
|
|
def read_content(self) -> bytes:
|
|
"""Read the raw content of the note."""
|
|
...
|
|
|
|
def write_content(self, data:bytes) -> None:
|
|
"""Write the raw content of the note."""
|
|
...
|
|
|
|
def children(self) -> Iterator[TreeEntry]:
|
|
"""Get all children of this note."""
|
|
...
|
|
|
|
def child(self, name: str) -> 'Note':
|
|
"""Retrieve a child note by name."""
|
|
...
|
|
|
|
def new_child(self, name: str, mime: str, content: bytes | None, executable: bool, hidden: bool) -> 'Note':
|
|
"""Create a new child note."""
|
|
...
|
|
|
|
def rm_child(self, name: str, recurse: bool):
|
|
"""Remove a child note."""
|
|
...
|
|
|
|
def has_children(self) -> bool:
|
|
"""Check if note has children."""
|
|
return next(self.children(), None) is not None
|
|
|
|
|
|
def new_child(note: Note, name: str, mime: str = DEFAULT_MIME, content: bytes | None = None, executable: bool = False, hidden: bool = False) -> Note:
|
|
return note.new_child(name=name, mime=mime, content=content, executable=executable, hidden=hidden)
|
|
|
|
|
|
@runtime_checkable
|
|
class Tree(Protocol):
|
|
def link(self, name: str, note: Note):
|
|
"""Link name to a given note."""
|
|
...
|
|
|
|
def unlink(self, name: str):
|
|
"""Remove name from the tree."""
|
|
...
|
|
|
|
def mkdir(self, name: str) -> Self:
|
|
"""Create a new Tree with the given name."""
|
|
...
|
|
|
|
def rmdir(self, name: str) -> None:
|
|
"""Remove a directory from the tree."""
|
|
...
|
|
|
|
def entries(self) -> Iterable[TreeEntry]:
|
|
"""Return all entries in the directory"""
|
|
...
|
|
|
|
|
|
@runtime_checkable
|
|
class TreeNote(Note, Tree, Protocol):
|
|
"""Tree Note"""
|
|
|
|
|
|
@runtime_checkable
|
|
class Trove(Protocol):
|
|
"""
|
|
Protocol for the Trove database API.
|
|
Provides high-level access to notes and trees.
|
|
"""
|
|
|
|
def get_raw_note(self, note: ObjectId) -> Note:
|
|
"""Retrieve a note by a object id"""
|
|
...
|
|
|
|
def create_blob(self, data: bytes | None = None) -> Note:
|
|
"""Create a new blob node at the given path with content"""
|
|
...
|
|
|
|
def get_root(self) -> TreeNote:
|
|
"""Get Tree Node at the given path"""
|
|
...
|