Crate miniorm

Source
Expand description

§miniorm

Build Test Clippy Doc

Crates.io Docs.rs Crates.io Crates.io

§Introduction

The miniorm crate provides a very simple ORM on top of sqlx.

sqlx already provides a FromRow trait that can be derived automatically in order to convert a row from the database into an object. However, there is no corresponding macro that would allow convert an object back into a row to be inserted into the database.

This is where miniorm comes in. It provides multiple traits that can also be automatically derived. Using these traits, miniorm provides a Store type that builds on top and all the standard “CRUD” operations:

  • (C)reate
  • (R)ead
  • (U)pdate
  • (D)elete

At the moment, miniorm supports the three most common database backend:

  • Sqlite
  • MySql
  • Postgres

Each backend should be enabled using the corresponding feature flag.

§Example

use sqlx::FromRow;
use miniorm::prelude::*;

#[derive(Debug, Clone, Eq, PartialEq, FromRow, Entity)]
struct Todo {
    #[column(TEXT NOT NULL)]
    description: String,

    #[column(BOOLEAN NOT NULL DEFAULT false)]
    done: bool,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = sqlx::SqlitePool::connect(":memory:").await?;
    let store = miniorm::Store::new(db);

    let todo = Todo {
        description: "checkout miniorm".into(),
        done: false,
    };

    store.recreate_table().await?;

    println!("Inserting...");
    let todo = store.create(todo).await?;

    println!("Retrieveing by id...");
    let mut fetched = store.read(todo.id()).await?;
    assert_eq!(todo, fetched);

    println!("Updating by id...");
    fetched.done = true;
    let after_update = store.update(fetched).await?;
    assert_eq!(after_update.id(), todo.id());

    println!("Listing all...");
    let all = store.list().await?;
    assert_eq!(all.len(), 1);
    assert_eq!(&after_update, &all[0]);

    println!("Deleting by id...");
    store.delete(todo.id()).await?;

    println!("Checking delete successful");
    assert!(matches!(
        store.read(todo.id()).await,
        Err(sqlx::Error::RowNotFound)
    ));

    Ok(())
}
This example requires the sqlite and

§But wait, there’s more!

One can turn a Store into an Router that can be installed to serve these CRUD operations over a REST api. For this your entity type should implement Serialize and Deserialize from serde

This requires the axum feature flag.

use axum::Router;
use miniorm::prelude::*;
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use std::net::SocketAddr;
use tokio::net::TcpListener;

#[derive(Debug, Clone, Eq, PartialEq, FromRow, Entity, Serialize, Deserialize)]
struct Todo {
    #[column(TEXT NOT NULL)]
    description: String,

    #[column(BOOLEAN NOT NULL DEFAULT false)]
    done: bool,
}

impl Todo {
    pub fn new(description: impl AsRef<str>) -> Self {
        let description = description.as_ref().to_string();
        let done = false;
        Todo { description, done }
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // connect to db 
    let db = sqlx::SqlitePool::connect(":memory:").await?;

    // initialize todo store
    let todos = Store::new(db);
    todos.recreate_table().await?;
    todos.create(Todo::new("do the laundry")).await?;
    todos.create(Todo::new("wash the dishes")).await?;
    todos.create(Todo::new("go walk the dog")).await?;
    todos.create(Todo::new("groceries")).await?;

    // create the app
    let app = Router::new().nest("/todos", todos.into_axum_router());
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("listening on http://{}", addr);

    // serve the app
    let listener = TcpListener::bind(&addr).await.unwrap();
    axum::serve(listener, app).await?;

    Ok(())
}
This example requires the sqlite and axum feature flags.

Modules§

prelude
Prelude including all the necessary traits for convenience

Structs§

Store
A Store is a wrapper around a Pool that allows to perform basic, so-called “CRUD” operations.
WithId
WithId is a wrapper of struct that provides an additional id field which is used in the database to identify the entity.

Derive Macros§

Entity
Derive macro to automatically derive the Schema and Bind traits.