Skip to content

Commit 39b0e27

Browse files
committed
more docs, more tests
1 parent 1e515e9 commit 39b0e27

File tree

3 files changed

+147
-12
lines changed

3 files changed

+147
-12
lines changed

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# async-sqlx-session
2+
## [sqlx](https://github.com/launchbadge/sqlx)-backed session store for [async-session](https://github.com/http-rs/async-session)
3+
4+
[![crates.io version](https://img.shields.io/crates/v/async-sqlx-session.svg?style=flat-square)](https://lib.rs/async-sqlx-session)
5+
[![docs.rs docs](https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square)](https://docs.rs/async-sqlx-session)
6+
7+
* [API Docs](https://docs.rs/async-sqlx-session)
8+
* [Releases](https://github.com/jbr/async-sqlx-session/releases)
9+
* [Contributing](https://github.com/jbr/async-sqlx-session/blob/master/.github/CONTRIBUTING.md)
10+
11+
## Installation
12+
```sh
13+
$ cargo add async-sqlx-session
14+
```
15+
16+
## Safety
17+
This crate uses ``#![deny(unsafe_code)]`` to ensure everything is implemented in
18+
100% Safe Rust.
19+
20+
## License
21+
22+
<sup>
23+
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
24+
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
25+
</sup>
26+
27+
<br/>
28+
29+
<sub>
30+
Unless you explicitly state otherwise, any contribution intentionally submitted
31+
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
32+
be dual licensed as above, without any additional terms or conditions.
33+
</sub>

src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
//! # async-sqlx-session
2+
//!
3+
//! This crate currently only provides a sqlite session store, but
4+
//! will eventually support other databases as well, configurable
5+
//! through a feature flag.
6+
//!
7+
//! For now, see the documentation for
8+
//! [`SqliteStore`](crate::SqliteStore)
9+
#![forbid(unsafe_code, future_incompatible)]
10+
#![deny(
11+
missing_debug_implementations,
12+
nonstandard_style,
13+
missing_docs,
14+
unreachable_pub,
15+
missing_copy_implementations,
16+
unused_qualifications
17+
)]
18+
119
#[cfg(feature = "sqlite")]
220
mod sqlite;
321
#[cfg(feature = "sqlite")]

src/sqlite.rs

Lines changed: 96 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@ use std::time::Duration;
88
///
99
/// ```rust
1010
/// use async_sqlx_session::SqliteStore;
11-
/// use async_session::SessionStore;
11+
/// use async_session::{Session, SessionStore};
12+
/// use std::time::Duration;
13+
///
1214
/// # fn main() -> async_session::Result { async_std::task::block_on(async {
1315
/// let store = SqliteStore::new("sqlite:%3Amemory:").await?;
1416
/// store.migrate().await?;
15-
/// store.spawn_cleanup_task(std::time::Duration::from_secs(60 * 60));
17+
/// store.spawn_cleanup_task(Duration::from_secs(60 * 60));
1618
///
17-
/// let mut session = async_session::Session::new();
19+
/// let session = Session::new();
1820
/// session.insert("key".into(), "value".into());
1921
///
2022
/// let cookie_value = store.store_session(session).await.unwrap();
2123
/// let session = store.load_session(cookie_value).await.unwrap();
22-
/// assert_eq!(session.get("key"), Some("value".to_owned()));
24+
/// assert_eq!(session.get("key").unwrap(), "value");
2325
/// # Ok(()) }) }
2426
///
2527
#[derive(Clone, Debug)]
@@ -51,9 +53,12 @@ impl SqliteStore {
5153
}
5254
}
5355

54-
/// constructs a new SqliteStore from a sqlite: database url. the
55-
/// default table name for this session store will be
56-
/// "async_sessions". To override this, either chain with
56+
/// Constructs a new SqliteStore from a sqlite: database url. note
57+
/// that this documentation uses the special `:memory:` sqlite
58+
/// database for convenient testing, but a real application would
59+
/// use a path like `sqlite:///path/to/database.db`. The default
60+
/// table name for this session store will be "async_sessions". To
61+
/// override this, either chain with
5762
/// [`with_table_name`](crate::SqliteStore::with_table_name) or
5863
/// use
5964
/// [`new_with_table_name`](crate::SqliteStore::new_with_table_name)
@@ -62,8 +67,7 @@ impl SqliteStore {
6267
/// # use async_sqlx_session::SqliteStore;
6368
/// # use async_session::Result;
6469
/// # fn main() -> Result { async_std::task::block_on(async {
65-
/// let store = SqliteStore::new("sqlite:%3Amemory:").await?
66-
/// .with_table_name("custom_table_name");
70+
/// let store = SqliteStore::new("sqlite:%3Amemory:").await?;
6771
/// store.migrate().await;
6872
/// # Ok(()) }) }
6973
/// ```
@@ -92,6 +96,24 @@ impl SqliteStore {
9296

9397
/// Chainable method to add a custom table name. This will panic
9498
/// if the table name is not `[a-zA-Z0-9_-]+`.
99+
/// ```rust
100+
/// # use async_sqlx_session::SqliteStore;
101+
/// # use async_session::Result;
102+
/// # fn main() -> Result { async_std::task::block_on(async {
103+
/// let store = SqliteStore::new("sqlite:%3Amemory:").await?
104+
/// .with_table_name("custom_name");
105+
/// store.migrate().await;
106+
/// # Ok(()) }) }
107+
/// ```
108+
///
109+
/// ```should_panic
110+
/// # use async_sqlx_session::SqliteStore;
111+
/// # use async_session::Result;
112+
/// # fn main() -> Result { async_std::task::block_on(async {
113+
/// let store = SqliteStore::new("sqlite:%3Amemory:").await?
114+
/// .with_table_name("johnny (); drop users;");
115+
/// # Ok(()) }) }
116+
/// ```
95117
pub fn with_table_name(mut self, table_name: impl AsRef<str>) -> Self {
96118
let table_name = table_name.as_ref();
97119
if table_name.is_empty()
@@ -114,6 +136,18 @@ impl SqliteStore {
114136
/// store initialization. In the future, this may make
115137
/// exactly-once modifications to the schema of the session table
116138
/// on breaking releases.
139+
/// ```rust
140+
/// # use async_sqlx_session::SqliteStore;
141+
/// # use async_session::{Result, SessionStore, Session};
142+
/// # fn main() -> Result { async_std::task::block_on(async {
143+
/// let store = SqliteStore::new("sqlite:%3Amemory:").await?;
144+
/// assert!(store.count().await.is_err());
145+
/// store.migrate().await?;
146+
/// store.store_session(Session::new()).await;
147+
/// store.migrate().await?; // calling it a second time is safe
148+
/// assert_eq!(store.count().await?, 1);
149+
/// # Ok(()) }) }
150+
/// ```
117151
pub async fn migrate(&self) -> sqlx::Result<()> {
118152
log::info!("migrating sessions on `{}`", self.table_name);
119153

@@ -132,16 +166,35 @@ impl SqliteStore {
132166
Ok(())
133167
}
134168

169+
// private utility function because sqlite does not support
170+
// parametrized table names
135171
fn substitute_table_name(&self, query: &str) -> String {
136172
query.replace("%%TABLE_NAME%%", &self.table_name)
137173
}
138174

175+
/// retrieve a connection from the pool
139176
async fn connection(&self) -> sqlx::Result<PoolConnection<SqliteConnection>> {
140177
self.client.acquire().await
141178
}
142179

143180
/// Spawns an async_std::task that clears out stale (expired)
144181
/// sessions on a periodic basis.
182+
/// ```rust
183+
/// # use async_sqlx_session::SqliteStore;
184+
/// # use async_session::{Result, SessionStore, Session};
185+
/// # use std::time::Duration;
186+
/// # fn main() -> Result { async_std::task::block_on(async {
187+
/// let store = SqliteStore::new("sqlite:%3Amemory:").await?;
188+
/// store.migrate().await?;
189+
/// store.spawn_cleanup_task(Duration::from_secs(1));
190+
/// let mut session = Session::new();
191+
/// session.expire_in(Duration::from_secs(0));
192+
/// store.store_session(session).await;
193+
/// assert_eq!(store.count().await?, 1);
194+
/// async_std::task::sleep(Duration::from_secs(2)).await;
195+
/// assert_eq!(store.count().await?, 0);
196+
/// # Ok(()) }) }
197+
/// ```
145198
pub fn spawn_cleanup_task(&self, period: Duration) -> task::JoinHandle<()> {
146199
let store = self.clone();
147200
task::spawn(async move {
@@ -156,6 +209,20 @@ impl SqliteStore {
156209

157210
/// Performs a one-time cleanup task that clears out stale
158211
/// (expired) sessions. You may want to call this from cron.
212+
/// ```rust
213+
/// # use async_sqlx_session::SqliteStore;
214+
/// # use async_session::{chrono::{Utc,Duration}, Result, SessionStore, Session};
215+
/// # fn main() -> Result { async_std::task::block_on(async {
216+
/// let store = SqliteStore::new("sqlite:%3Amemory:").await?;
217+
/// store.migrate().await?;
218+
/// let mut session = Session::new();
219+
/// session.set_expiry(Utc::now() - Duration::seconds(5));
220+
/// store.store_session(session).await;
221+
/// assert_eq!(store.count().await?, 1);
222+
/// store.cleanup().await?;
223+
/// assert_eq!(store.count().await?, 0);
224+
/// # Ok(()) }) }
225+
/// ```
159226
pub async fn cleanup(&self) -> sqlx::Result<()> {
160227
let mut connection = self.connection().await?;
161228
sqlx::query(&self.substitute_table_name(
@@ -171,10 +238,27 @@ impl SqliteStore {
171238
Ok(())
172239
}
173240

241+
/// retrieves the number of sessions currently stored, including
242+
/// expired sessions
243+
///
244+
/// ```rust
245+
/// # use async_sqlx_session::SqliteStore;
246+
/// # use async_session::{Result, SessionStore, Session};
247+
/// # use std::time::Duration;
248+
/// # fn main() -> Result { async_std::task::block_on(async {
249+
/// let store = SqliteStore::new("sqlite:%3Amemory:").await?;
250+
/// store.migrate().await?;
251+
/// assert_eq!(store.count().await?, 0);
252+
/// store.store_session(Session::new()).await;
253+
/// assert_eq!(store.count().await?, 1);
254+
/// # Ok(()) }) }
255+
/// ```
256+
174257
pub async fn count(&self) -> sqlx::Result<i32> {
175-
let (count,) = sqlx::query_as("select count(*) from async_sessions")
176-
.fetch_one(&mut self.connection().await?)
177-
.await?;
258+
let (count,) =
259+
sqlx::query_as(&self.substitute_table_name("SELECT COUNT(*) FROM %%TABLE_NAME%%"))
260+
.fetch_one(&mut self.connection().await?)
261+
.await?;
178262

179263
Ok(count)
180264
}

0 commit comments

Comments
 (0)