Add more meta information to trove entries
This commit is contained in:
parent
4ada169bbe
commit
bd4d571c95
5 changed files with 80 additions and 7 deletions
|
|
@ -71,7 +71,7 @@ class Sqlite3Trove:
|
|||
con.commit()
|
||||
obj = cls(con)
|
||||
if initialize:
|
||||
obj.write_blob(b"", NODE_ROOT_ID)
|
||||
obj.write_blob(b"", NOTE_ROOT_ID)
|
||||
return obj
|
||||
|
||||
def close(self):
|
||||
|
|
@ -103,6 +103,15 @@ class Sqlite3Trove:
|
|||
return None
|
||||
return bytes(row["data"]) if row["data"] is not None else b""
|
||||
|
||||
def get_mtime(self, object_id: int) -> datetime | None:
|
||||
"""Return the modified timestamp for an object, or None if not found."""
|
||||
row = self._con.execute(
|
||||
"SELECT modified FROM objects WHERE id = ?", (object_id,)
|
||||
).fetchone()
|
||||
if row is None:
|
||||
return None
|
||||
return datetime.fromisoformat(row["modified"])
|
||||
|
||||
def read_metadata(self, object_id: int, key: str) -> bytes | None:
|
||||
"""Return raw metadata value for (uuid, key), or None if not found."""
|
||||
row = self._con.execute(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
import sqlite3
|
||||
import tempfile
|
||||
import datetime as dt
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, List, Self, Iterable
|
||||
from .trove import Note, Trove, TreeNote, BlobNote, Blob, Tree, BadNoteType, TreeEntry, NoteNotFound
|
||||
|
|
@ -24,6 +25,24 @@ class FSNote(Note):
|
|||
raise ValueError("Note not yet saved to disk")
|
||||
return self._inode
|
||||
|
||||
@property
|
||||
def mtime(self):
|
||||
"""Return modification time as datetime."""
|
||||
stat = self._path.stat()
|
||||
return dt.datetime.fromtimestamp(stat.st_mtime, tz=dt.timezone.utc)
|
||||
|
||||
@property
|
||||
def readonly(self) -> bool:
|
||||
"""Check if the note is readonly based on file permissions."""
|
||||
if self._inode is None:
|
||||
return False
|
||||
return not os.access(self._path, os.W_OK)
|
||||
|
||||
@property
|
||||
def mime(self) -> str:
|
||||
"""Return MIME type, defaulting to generic binary stream."""
|
||||
return "application/octet-stream"
|
||||
|
||||
@property
|
||||
def _path(self) -> Path:
|
||||
if self._fs_path is not None:
|
||||
|
|
@ -60,6 +79,11 @@ class FSBlobNote(FSNote, BlobNote):
|
|||
pass
|
||||
|
||||
class FSTreeNote(FSNote, TreeNote):
|
||||
@property
|
||||
def mime(self) -> str:
|
||||
"""Return MIME type for directory/tree nodes."""
|
||||
return "inode/directory"
|
||||
|
||||
def link(self, name: str, note: Note):
|
||||
if not isinstance(note, FSBlobNote):
|
||||
raise BadNoteType("Only blob notes can be linked")
|
||||
|
|
|
|||
|
|
@ -194,20 +194,26 @@ class TroveFuseOps(pyfuse3.Operations):
|
|||
attr.st_nlink = 1
|
||||
attr.st_uid = os.getuid()
|
||||
attr.st_gid = os.getgid()
|
||||
|
||||
mtime_ns = int(note.mtime.timestamp() * 1e9)
|
||||
now_ns = int(time.time() * 1e9)
|
||||
attr.st_atime_ns = now_ns
|
||||
attr.st_mtime_ns = now_ns
|
||||
attr.st_ctime_ns = now_ns
|
||||
attr.st_mtime_ns = mtime_ns
|
||||
attr.st_ctime_ns = mtime_ns
|
||||
attr.generation = 0
|
||||
attr.entry_timeout = 5.0
|
||||
attr.attr_timeout = 5.0
|
||||
|
||||
# Determine permissions based on readonly property
|
||||
if is_tree:
|
||||
attr.st_mode = stat.S_IFDIR | 0o755
|
||||
mode = 0o755 if not note.readonly else 0o555
|
||||
attr.st_mode = stat.S_IFDIR | mode
|
||||
attr.st_size = 0
|
||||
attr.st_blksize = 512
|
||||
attr.st_blocks = 0
|
||||
else:
|
||||
attr.st_mode = stat.S_IFREG | 0o644
|
||||
mode = 0o644 if not note.readonly else 0o444
|
||||
attr.st_mode = stat.S_IFREG | mode
|
||||
attr.st_size = size
|
||||
attr.st_blksize = 512
|
||||
attr.st_blocks = (size + 511) // 512
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Protocol, runtime_checkable, Optional, Dict, List, Self, NamedTuple, Iterable, TypedDict
|
||||
from uuid import UUID
|
||||
from pathlib import PurePosixPath
|
||||
import datetime as dt
|
||||
|
||||
|
||||
type ObjectId = int | str | UUID
|
||||
|
|
@ -26,11 +27,27 @@ 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."""
|
||||
...
|
||||
|
|
@ -84,10 +101,12 @@ class Tree(Protocol):
|
|||
"""Return all entries as {name: object_id}."""
|
||||
...
|
||||
|
||||
class BlobNote(Note, Blob):
|
||||
@runtime_checkable
|
||||
class BlobNote(Note, Blob, Protocol):
|
||||
"""Blob Note"""
|
||||
|
||||
class TreeNote(Note, Tree):
|
||||
@runtime_checkable
|
||||
class TreeNote(Note, Tree, Protocol):
|
||||
"""Tree Note"""
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ tree serialization respectively.
|
|||
|
||||
from typing import Optional, Self
|
||||
from pathlib import Path
|
||||
import datetime as dt
|
||||
|
||||
from .db import Sqlite3Trove, NOTE_ROOT_ID
|
||||
from .tree import Tree as TreeData
|
||||
|
|
@ -30,6 +31,20 @@ class NoteImpl(Note):
|
|||
def object_id(self) -> int:
|
||||
return self._object_id
|
||||
|
||||
@property
|
||||
def readonly(self) -> bool:
|
||||
return False
|
||||
|
||||
@property
|
||||
def mtime(self) -> dt.datetime:
|
||||
"""Return modification time as Unix timestamp, or None if not set."""
|
||||
return self._db.get_mtime(self._object_id)
|
||||
|
||||
@property
|
||||
def mime(self) -> str:
|
||||
"""Return MIME type, defaulting to generic binary stream."""
|
||||
return "application/octet-stream"
|
||||
|
||||
def get_raw_metadata(self, key: str) -> Optional[bytes]:
|
||||
return self._db.read_metadata(self._object_id, key)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue