Add move operation, fix fuse server

This commit is contained in:
Andrew Mulbrook 2026-03-28 13:25:25 -05:00
parent 41480a39c9
commit 6470aee802
5 changed files with 90 additions and 45 deletions

View file

@ -1,12 +1,9 @@
import os
import sqlite3
import tempfile
import datetime as dt
from pathlib import Path, PurePosixPath
from typing import Optional, Dict, List, Self, Iterable, Iterator
from pathlib import Path
from typing import Optional, Iterable, Iterator, override
from .trove import Note, Trove, TreeNote, BadNoteType, TreeEntry, NoteNotFound, ObjectId
from . import fs_util as fsu
from . import trove as tr
class FSNote(Note):
@ -78,7 +75,7 @@ class FSNote(Note):
content = b""
if mime == 'inode/directory':
if content is not None:
if content:
raise NotImplementedError("FSNote does not support children")
return FSTreeNote(self._trove, self._new_child_subdir(name, False))
@ -104,27 +101,25 @@ class FSNote(Note):
target_path.unlink()
# TODO: remove meta directory!
def children(self) -> Iterator[TreeEntry]:
"""Get all children of this note."""
if not self._fs_path.is_dir():
def unlink_(self, name: str):
target_path = self._fs_path / name
if not target_path.exists():
return
for item in self._fs_path.iterdir():
if item.name == ".trove":
continue
yield TreeEntry(name=item.name, object_id=item.stat().st_ino)
if target_path.is_dir():
target_path.rmdir()
else:
target_path.unlink()
class FSTreeNote(FSNote, TreeNote):
def link(self, name: str, note: Note):
def link_(self, name: str, note: Note):
if not isinstance(note, FSNote):
raise BadNoteType("Only blob notes can be linked")
target_path = self._fs_path / name
if target_path.exists():
self.unlink(name)
self.unlink_(name)
note_path = note._fs_path
# If the note is in .working, move it to the new location.
if self._trove.working in note_path.parents:
os.rename(note_path, target_path)
@ -137,19 +132,20 @@ class FSTreeNote(FSNote, TreeNote):
# Fallback to rename if link fails (e.g. cross-device, though we assume single FS)
os.rename(note_path, target_path)
else:
# Directories cannot be hardlinked.
# Directories cannot be hardlinked.
# We move it to the new location.
os.rename(note_path, target_path)
def unlink(self, name: str):
target_path = self._fs_path / name
if not target_path.exists():
def children(self) -> Iterator[TreeEntry]:
"""Get all children of this note."""
if not self._fs_path.is_dir():
return
if target_path.is_dir():
target_path.rmdir()
else:
target_path.unlink()
for item in self._fs_path.iterdir():
if item.name == ".trove":
continue
yield TreeEntry(name=item.name, object_id=item.stat().st_ino)
class FSTreeNote(FSNote, TreeNote):
def mkdir(self, name: str) -> 'FSTreeNote':
target_path = self._fs_path / name
target_path.mkdir(exist_ok=True)
@ -164,6 +160,11 @@ class FSTreeNote(FSNote, TreeNote):
except OSError:
pass
def unlink(self, name: str):
return self.unlink_(name)
def link(self, name: str, note: Note):
return self.link_(name, note)
@ -198,6 +199,25 @@ class FSTrove(Trove):
raise NoteNotFound(note_id)
return self.get_raw_note_by_path(p)
@override
def move(self, src_parent: Note, src_name: str, dst_parent: Note, dst_name: str, overwrite: bool):
"""Move a child note to a new location."""
src_note = src_parent.child(src_name)
if not isinstance(src_note, FSNote):
raise tr.ErrorBadType("not a valid DB note")
if not isinstance(dst_parent, FSNote):
raise tr.ErrorBadType("not a valid DB note")
if not dst_parent._fs_path.is_dir():
raise NotImplementedError("FSTrove promoting during move")
# Remove existing target
if overwrite:
dst_parent.unlink_(dst_name)
# Link to new parent, unlink from old
dst_parent.link_(dst_name, src_note)
src_parent.rm_child(src_name, True)
def create_blob(self, data: bytes | None = None) -> Note:
raise NotImplementedError("FSTrove does not support blobs")