rust-query 0.9.1

A query builder using rust concepts.
Documentation
use rust_query::{Database, Select, TableRow, Transaction, aggregate, migration::schema, optional};

#[schema(Schema)]
#[version(0..=1)]
pub mod vN {

    pub struct Measurement {
        #[version(..1)]
        pub score: i64,
        #[version(1..)]
        pub value: f64,
        pub duration: i64,
        pub confidence: f64,
        pub timestamp: i64,
        pub location: rust_query::TableRow<Location>,
    }
    pub struct Location {
        pub name: String,
    }
}

mod using_v0 {
    use super::*;
    use rust_query::{FromExpr, Lazy};
    use v0::*;

    #[expect(unused)]
    #[derive(FromExpr, Select)]
    #[rust_query(From = Measurement)]
    struct Score {
        score: i64,
        timestamp: i64,
    }

    #[expect(unused)]
    fn read_scores(txn: &Transaction<Schema>) -> Vec<Score> {
        txn.query(|rows| {
            let m = rows.join(Measurement);
            rows.into_vec(ScoreSelect {
                score: &m.score,
                timestamp: &m.timestamp,
            })
        })
    }

    #[expect(unused)]
    fn read_scores2(txn: &Transaction<Schema>) -> Vec<Score> {
        txn.query(|rows| {
            let m = rows.join(Measurement);
            rows.into_vec(FromExpr::from_expr(m))
        })
    }

    #[expect(unused)]
    fn read_scores3(txn: &Transaction<Schema>) -> Vec<Lazy<'_, Measurement>> {
        txn.lazy_iter(Measurement).collect()
    }
}

fn main() {
    let db = using_v1::migrate();
    db.transaction_mut_ok(using_v1::do_stuff);
    delete_example::migrate();
}

#[test]
fn run() {
    main();
}

mod using_v1 {
    use super::*;
    use rust_query::{Lazy, Transaction, migration::Config};
    use v1::*;

    pub fn migrate() -> Database<Schema> {
        let m = Database::migrator(Config::open("db.sqlite"))
            .expect("database should not be older than supported versions");
        let m = m.migrate(|txn| v0::migrate::Schema {
            measurement: txn.migrate_ok(|old: Lazy<v0::Measurement>| v0::migrate::Measurement {
                value: old.score as f64,
            }),
        });
        m.finish()
            .expect("database should not be newer than supported versions")
    }

    pub fn do_stuff(txn: &'static mut Transaction<Schema>) {
        let loc: TableRow<Location> = txn.insert_ok(Location {
            name: "Amsterdam".to_owned(),
        });
        let _ = location_info(txn, loc);

        let txn = txn.downgrade();

        let is_deleted = txn
            .delete(loc)
            .expect("there should be no fk references to this row");
        assert!(is_deleted);

        let is_not_deleted_twice = !txn
            .delete(loc)
            .expect("there should be no fk references to this row");
        assert!(is_not_deleted_twice);
    }

    #[expect(unused)]
    #[derive(Select)]
    struct Info {
        average_value: f64,
        total_duration: i64,
    }

    fn location_info(txn: &Transaction<Schema>, loc: TableRow<Location>) -> Option<Info> {
        txn.query_one(aggregate(|rows| {
            let m = rows.join(Measurement);
            rows.filter(m.location.eq(loc));

            optional(|row| {
                let average_value = row.and(rows.avg(&m.value));
                row.then_select(InfoSelect {
                    average_value,
                    total_duration: rows.sum(&m.duration),
                })
            })
        }))
    }
}

mod delete_example {
    use rust_query::{Lazy, migration::Config};

    use super::*;
    #[schema(Schema)]
    #[version(0..=1)]
    pub mod vN {
        #[version(..1)]
        pub struct User {
            pub name: String,
        }
        #[version(1..)]
        #[from(User)]
        pub struct Author {
            pub name: String,
        }
        pub struct Book {
            pub author: rust_query::TableRow<Author>,
        }
    }

    pub fn migrate() {
        Database::migrator(Config::open_in_memory())
            .unwrap()
            .fixup(|txn| {
                // insert some data to migrate
                let author = txn.insert_ok(v0::User {
                    name: "foo".to_owned(),
                });
                txn.insert_ok(v0::Book { author });
            })
            .migrate(|txn| v0::migrate::Schema {
                author: txn.migrate_ok(|old: Lazy<v0::User>| v0::migrate::Author {
                    name: old.name.clone(),
                }),
            })
            .finish()
            .unwrap();
    }
}