Skip to main content

max / quasi

3.0 KB · 89 lines History Blame Raw
1 //! The embedded store's migration runner.
2 //!
3 //! <!-- wiki: quasi-overview -->
4 //!
5 //! # Why this is a quasi crate
6 //!
7 //! The admission test in `CONTRIBUTING.md` asks which two implementations a
8 //! boundary has. When goingson and Balanced Breakfast moved to rusqlite they
9 //! each hand-wrote a runner, and a third would have been written by the first
10 //! app the scaffolder generated. Two copies existing is the evidence; the
11 //! crate is what stops the third.
12 //!
13 //! It is a quasi crate rather than a makeover one on the audience rule: a
14 //! migration runner tied to our ledger conventions is something you take once
15 //! you are on the stack, not something another developer picks up on its own.
16 //! There are good standalone migration crates and this is not competing with
17 //! them.
18 //!
19 //! # The two halves
20 //!
21 //! They share no dependency, and that is the point rather than tidiness.
22 //!
23 //! - [`runtime`](crate::migrate) reads the ledger and applies what is missing.
24 //! An app links it.
25 //! - [`build`](crate::embed) turns a directory of `.sql` files into a table
26 //! compiled into the binary. An app's `build.rs` links it, with
27 //! `default-features = false`, so a build script does not pull
28 //! `libsqlite3-sys` into a second compile for the host.
29 //!
30 //! ```toml
31 //! [dependencies]
32 //! quasi-store = { path = "../../../quasi/crates/quasi-store" }
33 //!
34 //! [build-dependencies]
35 //! quasi-store = { path = "../../../quasi/crates/quasi-store", default-features = false, features = ["build"] }
36 //! ```
37 //!
38 //! # Using it
39 //!
40 //! `build.rs`, once. Not compiled as a doctest: the two halves are separate
41 //! features and a doctest gets one set of them, so an example naming both would
42 //! have to turn on the driver this half exists to avoid.
43 //!
44 //! ```ignore
45 //! fn main() {
46 //! quasi_store::embed::from_dir("../../migrations").expect("migrations");
47 //! }
48 //! ```
49 //!
50 //! Then the crate, once:
51 //!
52 //! ```ignore
53 //! quasi_store::migrations!();
54 //!
55 //! let mut conn = rusqlite::Connection::open("app.db")?;
56 //! quasi_store::Migrator::new(MIGRATIONS).run(&mut conn)?;
57 //! ```
58 //!
59 //! # What it does not do
60 //!
61 //! Connections, pools, queries, or anything resembling a query layer. quasi
62 //! owns which driver is configured and how migrations run, and queries stay
63 //! written against `rusqlite` directly — the same line the README draws around
64 //! `sqlx`, for the same reason. There is no Postgres half here and there should
65 //! not be one: `sqlx` ships its own migrator and it is one of the reasons to
66 //! use `sqlx`.
67
68 #[cfg(feature = "build")]
69 pub mod embed;
70
71 #[cfg(feature = "runtime")]
72 pub mod migrate;
73
74 #[cfg(feature = "runtime")]
75 pub use crate::migrate::{MigrateError, Migration, Migrator};
76
77 /// Include the table [`embed::from_dir`] generated.
78 ///
79 /// A macro rather than a documented `include!` line, so the path the build
80 /// script writes to is named in one place. Expands to a
81 /// `static MIGRATIONS: &[Migration]`.
82 #[cfg(feature = "runtime")]
83 #[macro_export]
84 macro_rules! migrations {
85 () => {
86 include!(concat!(env!("OUT_DIR"), "/quasi_migrations.rs"));
87 };
88 }
89