1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
use std::{iter::FromIterator, sync::Arc};
use tokio_rusqlite::Connection as AsyncConnection;
use crate::errors::Result;
use crate::{Migrations, SchemaVersion, M};
#[cfg(feature = "from-directory")]
use include_dir::Dir;
/// Adapter to make `Migrations` available in an async context.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct AsyncMigrations {
migrations: Arc<Migrations<'static>>,
}
impl AsyncMigrations {
/// Create a proxy struct to a [Migrations](crate::Migrations) instance for use in an asynchronous context.
///
/// # Example
///
/// ```rust
/// use rusqlite_migration::{Migrations, AsyncMigrations, M};
///
/// let migrations = AsyncMigrations::new(vec![
/// M::up("CREATE TABLE animals (name TEXT);"),
/// M::up("CREATE TABLE food (name TEXT);"),
/// ]);
/// ```
#[must_use]
pub fn new(ms: Vec<M<'static>>) -> Self {
Self {
migrations: Arc::new(Migrations::new(ms)),
}
}
/// Proxy implementation of the same method in the [Migrations](crate::Migrations::from_directory) struct.
///
/// # Example
///
/// ```
/// use rusqlite_migration::AsyncMigrations;
/// use include_dir::{Dir, include_dir};
///
/// static MIGRATION_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/../examples/from-directory/migrations");
/// let migrations = AsyncMigrations::from_directory(&MIGRATION_DIR).unwrap();
/// ```
#[allow(clippy::missing_errors_doc)]
#[cfg(feature = "from-directory")]
pub fn from_directory(dir: &'static Dir<'static>) -> Result<Self> {
Ok(Self {
migrations: Arc::new(Migrations::from_directory(dir)?),
})
}
/// Asynchronous version of the same method in the [Migrations](crate::Migrations::current_version) struct.
///
/// # Example
///
/// ```rust
/// # tokio_test::block_on(async {
/// use rusqlite_migration::{Migrations, AsyncMigrations, M, SchemaVersion};
/// use std::num::NonZeroUsize;
///
/// let mut conn = tokio_rusqlite::Connection::open_in_memory().await.unwrap();
///
/// let migrations = AsyncMigrations::new(vec![
/// M::up("CREATE TABLE animals (name TEXT);"),
/// M::up("CREATE TABLE food (name TEXT);"),
/// ]);
///
/// assert_eq!(SchemaVersion::NoneSet, migrations.current_version(&conn).await.unwrap());
///
/// // Go to the latest version
/// migrations.to_latest(&mut conn).await.unwrap();
///
/// assert_eq!(SchemaVersion::Inside(NonZeroUsize::new(2).unwrap()), migrations.current_version(&conn).await.unwrap());
/// # })
/// ```
#[allow(clippy::missing_errors_doc)]
pub async fn current_version(&self, async_conn: &AsyncConnection) -> Result<SchemaVersion> {
let m = Arc::clone(&self.migrations);
async_conn
.call(move |conn| Ok(m.current_version(conn)))
.await?
}
/// Asynchronous version of the same method in the [Migrations](super::Migrations::to_latest) struct.
///
/// # Example
///
/// ```rust
/// # tokio_test::block_on(async {
/// use rusqlite_migration::{Migrations, AsyncMigrations, M};
/// let mut conn = tokio_rusqlite::Connection::open_in_memory().await.unwrap();
///
/// let migrations = AsyncMigrations::new(vec![
/// M::up("CREATE TABLE animals (name TEXT);"),
/// M::up("CREATE TABLE food (name TEXT);"),
/// ]);
///
/// // Go to the latest version
/// migrations.to_latest(&mut conn).await.unwrap();
///
/// // You can then insert values in the database
/// conn.call_unwrap(|conn| conn.execute("INSERT INTO animals (name) VALUES (?)", ["dog"])).await.unwrap();
/// conn.call_unwrap(|conn| conn.execute("INSERT INTO food (name) VALUES (?)", ["carrot"])).await.unwrap();
/// # });
/// ```
#[allow(clippy::missing_errors_doc)]
pub async fn to_latest(&self, async_conn: &mut AsyncConnection) -> Result<()> {
let m = Arc::clone(&self.migrations);
async_conn.call(move |conn| Ok(m.to_latest(conn))).await?
}
/// Asynchronous version of the same method in the [Migrations](crate::Migrations::to_version) struct.
///
/// # Example
///
/// ```rust
/// # tokio_test::block_on(async {
/// use rusqlite_migration::{Migrations, AsyncMigrations, M};
/// let mut conn = tokio_rusqlite::Connection::open_in_memory().await.unwrap();
/// let migrations = AsyncMigrations::new(vec![
/// // 0: version 0, before having run any migration
/// M::up("CREATE TABLE animals (name TEXT);").down("DROP TABLE animals;"),
/// // 1: version 1, after having created the “animals” table
/// M::up("CREATE TABLE food (name TEXT);").down("DROP TABLE food;"),
/// // 2: version 2, after having created the food table
/// ]);
///
/// migrations.to_latest(&mut conn).await.unwrap(); // Create all tables
///
/// // Go back to version 1, i.e. after running the first migration
/// migrations.to_version(&mut conn, 1).await;
/// conn.call(|conn| Ok(conn.execute("INSERT INTO animals (name) VALUES (?)", ["dog"]))).await.unwrap();
/// conn.call(|conn| Ok(conn.execute("INSERT INTO food (name) VALUES (?)", ["carrot"]).unwrap_err())).await;
///
/// // Go back to an empty database
/// migrations.to_version(&mut conn, 0).await;
/// conn.call(|conn| Ok(conn.execute("INSERT INTO animals (name) VALUES (?)", ["cat"]).unwrap_err())).await;
/// conn.call(|conn| Ok(conn.execute("INSERT INTO food (name) VALUES (?)", ["milk"]).unwrap_err())).await;
/// # })
/// ```
#[allow(clippy::missing_errors_doc)]
pub async fn to_version(&self, async_conn: &mut AsyncConnection, version: usize) -> Result<()> {
let m = Arc::clone(&self.migrations);
async_conn
.call(move |conn| Ok(m.to_version(conn, version)))
.await?
}
/// Asynchronous version of the same method in the [Migrations](crate::Migrations::validate) struct.
///
/// # Example
///
/// ```rust
/// #[cfg(test)]
/// mod tests {
///
/// // … Other tests …
///
/// #[tokio::test]
/// fn migrations_test() {
/// assert!(migrations.validate().await.is_ok());
/// }
/// }
/// ```
#[allow(clippy::missing_errors_doc)]
pub async fn validate(&self) -> Result<()> {
let mut async_conn = AsyncConnection::open_in_memory().await?;
self.to_latest(&mut async_conn).await
}
}
impl FromIterator<M<'static>> for AsyncMigrations {
fn from_iter<T: IntoIterator<Item = M<'static>>>(iter: T) -> Self {
Self {
migrations: Arc::new(Migrations::from_iter(iter)),
}
}
}