pub struct JsonStore<T>{ /* private fields */ }Expand description
Generic blob store keyed by a string id.
This is the SQLite-backed implementation of the adapter. The type name
is engine-neutral on purpose: the ZLayerZQL mirror overrides this entire
backend module with a ZQL-backed JsonStore exposing an identical API,
so the per-resource storage files that hold a JsonStore<T> regenerate
mechanically with no hand edits.
Use via a narrow wrapper type in each storage module (see
notifiers.rs::SqlxNotifierStore for the pattern).
Implementations§
Source§impl<T> JsonStore<T>
impl<T> JsonStore<T>
Sourcepub async fn open<P: AsRef<Path>>(
path: P,
table: JsonTable<T>,
) -> Result<Self, StorageError>
pub async fn open<P: AsRef<Path>>( path: P, table: JsonTable<T>, ) -> Result<Self, StorageError>
Open or create a SQLite database at the given path and ensure the
table + indexes exist.
§Errors
Returns StorageError if the database cannot be opened or if the
schema cannot be created.
Sourcepub async fn in_memory(table: JsonTable<T>) -> Result<Self, StorageError>
pub async fn in_memory(table: JsonTable<T>) -> Result<Self, StorageError>
Create an in-memory SQLite database (useful for tests) with the
table + indexes in place.
§Errors
Returns StorageError if the in-memory database cannot be created.
Sourcepub async fn put(&self, id: &str, value: &T) -> Result<(), StorageError>
pub async fn put(&self, id: &str, value: &T) -> Result<(), StorageError>
Upsert a record by its primary key id.
- If
idis new, a row is inserted withcreated_at = updated_at = now— both timestamps are taken fromnow()at write time, NOT from any field onT, because the adapter does not know which field (if any) ofTholds a timestamp. - If
idalready exists, every column is refreshed from the new blob exceptcreated_at, which is preserved.
§Errors
StorageError::AlreadyExistsif the write violates aUNIQUEconstraint on one of the peeled columns.StorageError::SerializationifTcannot be encoded to JSON.StorageError::Databasefor any othersqlxerror.
Sourcepub async fn get(&self, id: &str) -> Result<Option<T>, StorageError>
pub async fn get(&self, id: &str) -> Result<Option<T>, StorageError>
Fetch a record by primary key.
§Errors
StorageError::Databaseonsqlxfailure.StorageError::Serializationif the blob cannot be decoded.
Sourcepub async fn list(&self) -> Result<Vec<T>, StorageError>
pub async fn list(&self) -> Result<Vec<T>, StorageError>
List every record in the table, ordered by the primary-key column
(primary_key, default "id") ascending.
Most callers want a domain-specific ordering (e.g. notifier name),
which they can do by sorting in memory after list() returns. Doing
the sort here would require knowing which field of T to order by,
which the adapter cannot know generically.
§Errors
StorageError::Databaseonsqlxfailure.StorageError::Serializationif any blob cannot be decoded.
Sourcepub async fn list_lossy(&self) -> Result<Vec<T>, StorageError>
pub async fn list_lossy(&self) -> Result<Vec<T>, StorageError>
List every record in the table ordered by the primary-key column
ascending, tolerating rows whose data_json blob fails to
deserialize: each such row is logged at error level (with its
primary-key value) and skipped rather than failing the whole call.
This is the lossy counterpart to list. Use it for
stores where a single corrupt/legacy row must never take down the
entire listing — the deployments store relies on this so a poisoned
record can’t 500 every list_deployments call and survive restart.
Because the blob can’t be decoded into T for a skipped row, the
primary-key column is selected alongside data_json purely for the
log line. The PK is always a TEXT column, so it decodes as a string.
§Errors
Returns StorageError::Database on sqlx failure. Per-row
deserialize failures are skipped, not returned.
Sourcepub async fn get_by_unique(
&self,
column: &str,
value: &str,
) -> Result<Option<T>, StorageError>
pub async fn get_by_unique( &self, column: &str, value: &str, ) -> Result<Option<T>, StorageError>
Fetch a record by one of its peeled secondary columns.
The column must appear in the JsonTable::indexes list. Works for
any indexed column — unique or non-unique. When the column is
non-unique, the first matching row (by rowid) is returned.
§Errors
StorageError::Otherifcolumndoes not correspond to any declared index.StorageError::Databaseonsqlxfailure.StorageError::Serializationif the blob cannot be decoded.
Sourcepub async fn list_where(
&self,
column: &str,
value: &str,
) -> Result<Vec<T>, StorageError>
pub async fn list_where( &self, column: &str, value: &str, ) -> Result<Vec<T>, StorageError>
List every record where a peeled column matches a specific value.
Rows are ordered by id ascending, matching list.
Use list_where_opt if you need to filter
on NULL — passing "" to this function binds the empty string, not
SQL NULL.
§Errors
StorageError::Otherifcolumnis not declared in the table’s index list.StorageError::Databaseonsqlxfailure.StorageError::Serializationif any blob cannot be decoded.
Sourcepub async fn list_where_null(
&self,
column: &str,
) -> Result<Vec<T>, StorageError>
pub async fn list_where_null( &self, column: &str, ) -> Result<Vec<T>, StorageError>
List every record where a peeled column is SQL NULL. Rows are
ordered by id ascending.
§Errors
StorageError::Otherifcolumnis not declared in the table’s index list.StorageError::Databaseonsqlxfailure.StorageError::Serializationif any blob cannot be decoded.
Sourcepub async fn list_where_opt(
&self,
column: &str,
value: Option<&str>,
) -> Result<Vec<T>, StorageError>
pub async fn list_where_opt( &self, column: &str, value: Option<&str>, ) -> Result<Vec<T>, StorageError>
List every record filtered by a peeled column. Some(v) matches
column = ? with v bound; None matches column IS NULL. Rows are
ordered by id ascending.
This is the unified primitive used by both list_where
and list_where_null.
§Errors
StorageError::Otherifcolumnis not declared in the table’s index list.StorageError::Databaseonsqlxfailure.StorageError::Serializationif any blob cannot be decoded.
Sourcepub fn pool(&self) -> &SqlitePool
pub fn pool(&self) -> &SqlitePool
Return a reference to the underlying SqlitePool.
Exposed pub as a temporary migration bridge: sibling stores still
living in crates/zlayer-api/src/storage/ (tasks, workflows, groups)
run auxiliary queries — creating companion tables (e.g. task_runs,
group_members), doing cross-table transactions during cascade
deletes — against the same pool without opening a second connection.
It was pub(crate) while JsonStore lived inside zlayer-api;
the extraction into zlayer-store requires pub so those
cross-crate callers keep compiling. This is NOT a stable public API:
once the companion-table stores move into zlayer-store too, this
should return to a narrower visibility.
Sourcepub async fn count(&self) -> Result<u64, StorageError>
pub async fn count(&self) -> Result<u64, StorageError>
Return the total number of rows in the table.
§Errors
Returns StorageError::Database on sqlx failure.
Auto Trait Implementations§
impl<T> !RefUnwindSafe for JsonStore<T>
impl<T> !UnwindSafe for JsonStore<T>
impl<T> Freeze for JsonStore<T>
impl<T> Send for JsonStore<T>
impl<T> Sync for JsonStore<T>
impl<T> Unpin for JsonStore<T>
impl<T> UnsafeUnpin for JsonStore<T>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more