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:
parent
abbef64bbc
commit
41480a39c9
5 changed files with 245 additions and 234 deletions
|
|
@ -5,9 +5,10 @@ Implements BlobNote, TreeNote, and Trove protocols defined in trove.py.
|
|||
Depends on db.py (Sqlite3Trove) for storage.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from typing import Optional, Iterator
|
||||
from pathlib import Path
|
||||
import datetime as dt
|
||||
import uuid
|
||||
|
||||
from .db import Sqlite3Trove, NOTE_ROOT_ID
|
||||
|
||||
|
|
@ -20,9 +21,19 @@ class NoteImpl(Note):
|
|||
"""Concrete note implementation."""
|
||||
|
||||
def __init__(self, parent: 'TroveImpl', object_id: ObjectId):
|
||||
if not isinstance(object_id, uuid.UUID):
|
||||
object_id = uuid.UUID(str(object_id))
|
||||
assert isinstance(object_id, uuid.UUID)
|
||||
|
||||
self._parent = parent
|
||||
self._db = parent.db
|
||||
self._object_id = object_id
|
||||
self._object_id: uuid.UUID = object_id
|
||||
|
||||
@staticmethod
|
||||
def get_impl_id(note: Note) -> uuid.UUID:
|
||||
if not isinstance(note.object_id, uuid.UUID):
|
||||
raise TypeError("Note not compatible with NoteImpl")
|
||||
return note.object_id
|
||||
|
||||
# Note protocol
|
||||
@property
|
||||
|
|
@ -36,7 +47,8 @@ class NoteImpl(Note):
|
|||
@property
|
||||
def mtime(self) -> dt.datetime:
|
||||
"""Return modification time as UTC datetime."""
|
||||
return self._db.get_mtime(self._object_id)
|
||||
mtime = self._db.get_mtime(self._object_id)
|
||||
return mtime if mtime is not None else dt.datetime.now(tz=dt.timezone.utc)
|
||||
|
||||
@property
|
||||
def mime(self) -> str:
|
||||
|
|
@ -57,6 +69,39 @@ class NoteImpl(Note):
|
|||
def write_content(self, data: bytes) -> None:
|
||||
self._db.write_content(self._object_id, data)
|
||||
|
||||
def children(self) -> Iterator[TreeEntry]:
|
||||
"""Get all children of this note."""
|
||||
for name, object_id in self._db.list_tree(self._object_id).items():
|
||||
yield TreeEntry(name, object_id)
|
||||
|
||||
def new_child(self, name: str, mime: str, content: bytes | None, executable: bool, hidden: bool) -> Note:
|
||||
"""Create a new child note."""
|
||||
content = content if content is not None else b""
|
||||
object_id = self._db.write_blob(data=content, object_id=None, dtype=mime, executable=executable, hidden=hidden)
|
||||
# TODO fix this
|
||||
if mime == 'inode/directory':
|
||||
return TreeNoteImpl(self._parent, object_id)
|
||||
return NoteImpl(self._parent, object_id)
|
||||
|
||||
def child(self, name: str) -> Note:
|
||||
"""Retrieve a child note by name."""
|
||||
entries = self._db.list_tree(self._object_id)
|
||||
if name not in entries:
|
||||
raise tr.ErrorNotFound(name)
|
||||
child_id = entries[name]
|
||||
value = self._parent.get_raw_note(child_id)
|
||||
if value is None:
|
||||
raise tr.ErrorNotFound("dangling child link") # FIXME: better errors
|
||||
return value
|
||||
|
||||
def rm_child(self, name: str, recurse: bool) -> None:
|
||||
"""Remove a child note."""
|
||||
note = self.child(name)
|
||||
if note.has_children() and not recurse:
|
||||
raise tr.ErrorNotEmpty(name)
|
||||
self._db.unlink(self._object_id, name)
|
||||
|
||||
|
||||
|
||||
class TreeNoteImpl(NoteImpl, TreeNote):
|
||||
"""Concrete TreeNote: a tree object backed by the tree_entries table."""
|
||||
|
|
@ -64,7 +109,7 @@ class TreeNoteImpl(NoteImpl, TreeNote):
|
|||
# Tree protocol
|
||||
def link(self, name: str, note: Note) -> None:
|
||||
"""Link name to an existing note."""
|
||||
self._db.link(self._object_id, name, note.object_id)
|
||||
self._db.link(self._object_id, name, NoteImpl.get_impl_id(note))
|
||||
|
||||
def unlink(self, name: str) -> None:
|
||||
"""Remove an entry by name."""
|
||||
|
|
@ -81,25 +126,13 @@ class TreeNoteImpl(NoteImpl, TreeNote):
|
|||
"""Remove a directory from the tree."""
|
||||
self.unlink(name)
|
||||
|
||||
def child(self, name: str) -> Note:
|
||||
"""Retrieve a child note by name."""
|
||||
entries = self._db.list_tree(self._object_id)
|
||||
if name not in entries:
|
||||
raise KeyError(f"Entry '{name}' not found")
|
||||
child_id = entries[name]
|
||||
value = self._parent.get_raw_note(child_id)
|
||||
if value is None:
|
||||
raise KeyError(f"Entry '{name}' has no value")
|
||||
return value
|
||||
|
||||
|
||||
def entries(self):
|
||||
"""Return all entries as an iterable of TreeEntry."""
|
||||
for name, object_id in self._db.list_tree(self._object_id).items():
|
||||
yield TreeEntry(name, object_id)
|
||||
|
||||
def list(self) -> dict[str, ObjectId]:
|
||||
"""Return all entries as {name: object_id}."""
|
||||
return self._db.list_tree(self._object_id)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -137,6 +170,8 @@ class TroveImpl:
|
|||
# Trove protocol
|
||||
def get_raw_note(self, note_id: ObjectId) -> Note:
|
||||
"""Return a BlobNote or TreeNote for the given id, or None if not found."""
|
||||
if not isinstance(note_id, uuid.UUID):
|
||||
note_id = uuid.UUID(str(note_id))
|
||||
info = self._db.get_info(note_id)
|
||||
if info is None:
|
||||
raise NoteNotFound(note_id)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue