Skip to main content

Repository

Struct Repository 

Source
pub struct Repository<M: Model, A: DatabaseAdapter> { /* private fields */ }
Expand description

Typed, high-level repository for a single model type M.

Repository is the main entry point for CRUD operations. It wraps an Arc-shared DatabaseAdapter so multiple repositories (or threads) can share one connection pool.

§Obtaining a repository

use rusticx::prelude::*;
use std::sync::Arc;

let adapter = PostgresAdapter::connect_url("postgres://localhost/mydb").await?;
let repo: Repository<User, _> = Repository::new(Arc::new(adapter));

§Thread safety

Repository is Clone and Send + Sync — share it freely across tasks and threads. The underlying connection pool handles concurrency.

Implementations§

Source§

impl<M: Model, A: DatabaseAdapter> Repository<M, A>

Source

pub fn new(adapter: Arc<A>) -> Self

Source

pub fn adapter(&self) -> &A

Source

pub async fn migrate(&self) -> Result<()>

Create the table / collection for M if it does not already exist.

Safe to call on every startup — it is idempotent (CREATE TABLE IF NOT EXISTS).

Source

pub async fn insert(&self, model: &M) -> Result<M>

Insert one model instance and return it with any generated fields filled in (e.g. database-generated timestamps, auto-increment IDs).

Source

pub async fn insert_many(&self, models: &[M]) -> Result<u64>

Insert a slice of models in a single transaction. Returns the count inserted.

Source

pub async fn find_all(&self) -> Result<Vec<M>>

Fetch every row in the table. Use find with a QueryBuilder for filtering.

Caution: can return very large result sets on big tables.

Source

pub async fn find_by_id(&self, id: impl Into<Value>) -> Result<Option<M>>

Find a single record by primary key. Returns None if not found.

Source

pub async fn find_one(&self, qb: QueryBuilder) -> Result<Option<M>>

Find the first record matching the given query. Returns None if no match.

Source

pub async fn find(&self, qb: QueryBuilder) -> Result<Vec<M>>

Find all records matching the given query.

Build the query with Repository::query for ergonomic chaining:

let results = repo.find(
    repo.query()
        .r#where("age", CondOp::Gte, 18)
        .order_by("name", Direction::Asc)
        .limit(50)
).await?;
Source

pub async fn paginate(&self, page: u64, per_page: u64) -> Result<Vec<M>>

Fetch one page of results. page is 1-indexed.

// Page 2, 20 records per page
let page = repo.paginate(2, 20).await?;
Source

pub async fn count(&self, qb: Option<QueryBuilder>) -> Result<u64>

Count records. Pass None to count all rows, or Some(qb) to count only rows matching the query.

Source

pub async fn update(&self, qb: QueryBuilder) -> Result<u64>

Update rows matching the query. Returns the number of rows affected.

Use .set(column, value) on the query builder to specify new values:

repo.update(
    repo.query()
        .r#where("email", CondOp::Eq, "alice@example.com")
        .set("age", 31)
        .set("active", false)
).await?;
Source

pub async fn save(&self, model: &M) -> Result<M>

Upsert: insert if the primary key is null/absent, update if it is set.

This is the idiomatic way to persist a model without knowing whether it already exists in the database.

Source

pub async fn delete_by_id(&self, id: impl Into<Value>) -> Result<u64>

Delete the record with the given primary key. Returns 1 if deleted, 0 if not found.

Source

pub async fn delete(&self, qb: QueryBuilder) -> Result<u64>

Delete all records matching the query. Returns the count deleted.

Source

pub fn query(&self) -> QueryBuilder

Start building a query pre-scoped to this model’s table.

This is the idiomatic starting point for all filtered operations:

repo.find(repo.query().r#where("active", CondOp::Eq, true)).await?

Auto Trait Implementations§

§

impl<M, A> Freeze for Repository<M, A>
where Arc<A>: Freeze, PhantomData<M>: Freeze,

§

impl<M, A> RefUnwindSafe for Repository<M, A>

§

impl<M, A> Send for Repository<M, A>
where Arc<A>: Send, PhantomData<M>: Send,

§

impl<M, A> Sync for Repository<M, A>
where Arc<A>: Sync, PhantomData<M>: Sync,

§

impl<M, A> Unpin for Repository<M, A>
where Arc<A>: Unpin, PhantomData<M>: Unpin,

§

impl<M, A> UnsafeUnpin for Repository<M, A>

§

impl<M, A> UnwindSafe for Repository<M, A>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.