Start API refactor to remove separate Tree

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.
This commit is contained in:
Andrew Mulbrook 2026-03-26 21:00:35 -05:00
parent abbef64bbc
commit 41480a39c9
5 changed files with 245 additions and 234 deletions

View file

@ -1,14 +1,39 @@
from typing import Protocol, runtime_checkable, Optional, Dict, List, Self, NamedTuple, Iterable, TypedDict
from typing import Protocol, runtime_checkable, Optional, Dict, List, Self, NamedTuple, Iterable, TypedDict, Iterator
from uuid import UUID
from pathlib import PurePosixPath
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."""
@ -21,6 +46,12 @@ class NoteNotFound(KeyError):
class OpenArguments(TypedDict):
create: bool
class TreeEntry(NamedTuple):
name: str
object_id: ObjectId
DEFAULT_MIME = "application/octet-stream"
@runtime_checkable
class Note(Protocol):
"""
@ -64,10 +95,30 @@ class Note(Protocol):
"""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)
class TreeEntry(NamedTuple):
name: str
object_id: ObjectId
@runtime_checkable
class Tree(Protocol):
@ -87,17 +138,10 @@ class Tree(Protocol):
"""Remove a directory from the tree."""
...
def child(self, name: str) -> Note:
"""Retrieve a child note by name."""
...
def entries(self) -> Iterable[TreeEntry]:
"""Return all entries in the directory"""
...
def list(self) -> dict[str, int]:
"""Return all entries as {name: object_id}."""
...
@runtime_checkable
class TreeNote(Note, Tree, Protocol):