2017-08-20 22:59:16 +03:00
|
|
|
use std;
|
|
|
|
|
2017-09-05 18:07:57 +03:00
|
|
|
use diesel;
|
2017-08-20 22:59:16 +03:00
|
|
|
use diesel::sqlite::SqliteConnection;
|
2017-08-21 00:44:52 +03:00
|
|
|
use diesel::prelude::*;
|
2017-09-08 16:37:58 +03:00
|
|
|
use futures::{self, Future, IntoFuture};
|
2017-09-05 15:55:10 +03:00
|
|
|
use r2d2::Pool;
|
|
|
|
use r2d2_diesel::ConnectionManager;
|
2017-08-20 22:59:16 +03:00
|
|
|
|
2017-08-20 23:17:16 +03:00
|
|
|
use models;
|
|
|
|
|
2017-09-05 15:55:10 +03:00
|
|
|
#[derive(Clone)]
|
2017-08-20 22:59:16 +03:00
|
|
|
pub struct State {
|
2017-09-05 15:55:10 +03:00
|
|
|
connection_pool: Pool<ConnectionManager<SqliteConnection>>
|
2017-08-20 22:59:16 +03:00
|
|
|
}
|
|
|
|
|
2017-09-05 15:55:10 +03:00
|
|
|
pub type Error = Box<std::error::Error + Send + Sync>;
|
|
|
|
|
2017-08-20 22:59:16 +03:00
|
|
|
impl State {
|
2017-09-05 15:55:10 +03:00
|
|
|
pub fn new(connection_pool: Pool<ConnectionManager<SqliteConnection>>) -> State {
|
|
|
|
State { connection_pool }
|
2017-08-20 22:59:16 +03:00
|
|
|
}
|
|
|
|
|
2017-09-05 15:55:10 +03:00
|
|
|
pub fn get_article_revision_by_id(&self, article_id: i32) -> Result<Option<models::ArticleRevision>, Error> {
|
2017-08-21 00:44:52 +03:00
|
|
|
use schema::article_revisions;
|
|
|
|
|
|
|
|
Ok(article_revisions::table
|
|
|
|
.filter(article_revisions::article_id.eq(article_id))
|
|
|
|
.order(article_revisions::revision.desc())
|
|
|
|
.limit(1)
|
2017-09-05 15:55:10 +03:00
|
|
|
.load::<models::ArticleRevision>(&*self.connection_pool.get()?)?
|
2017-08-21 00:44:52 +03:00
|
|
|
.pop())
|
|
|
|
}
|
2017-09-05 18:07:57 +03:00
|
|
|
|
2017-09-08 16:37:58 +03:00
|
|
|
pub fn update_article(&self, article_id: i32, base_revision: i32, body: String) -> futures::BoxFuture<models::ArticleRevision, Error> {
|
|
|
|
self.connection_pool.get().into_future()
|
|
|
|
.map_err(Into::into)
|
|
|
|
.and_then(move |conn| {
|
|
|
|
conn.transaction(|| {
|
|
|
|
use schema::article_revisions;
|
2017-09-05 18:07:57 +03:00
|
|
|
|
2017-09-08 16:37:58 +03:00
|
|
|
let (latest_revision, title) = article_revisions::table
|
|
|
|
.filter(article_revisions::article_id.eq(article_id))
|
|
|
|
.order(article_revisions::revision.desc())
|
|
|
|
.limit(1)
|
|
|
|
.select((article_revisions::revision, article_revisions::title))
|
|
|
|
.load::<(i32, String)>(&*conn)?
|
|
|
|
.pop()
|
|
|
|
.unwrap_or_else(|| unimplemented!("TODO Missing an error type"));
|
2017-09-05 18:07:57 +03:00
|
|
|
|
2017-09-08 16:37:58 +03:00
|
|
|
if latest_revision != base_revision {
|
|
|
|
// TODO: If it is the same edit repeated, just respond OK
|
|
|
|
// TODO: If there is a conflict, transform the edit to work seamlessly
|
|
|
|
unimplemented!("TODO Missing handling of revision conflicts");
|
|
|
|
}
|
|
|
|
let new_revision = base_revision + 1;
|
2017-09-05 18:07:57 +03:00
|
|
|
|
2017-09-08 16:37:58 +03:00
|
|
|
#[derive(Insertable)]
|
|
|
|
#[table_name="article_revisions"]
|
|
|
|
struct NewRevision<'a> {
|
|
|
|
article_id: i32,
|
|
|
|
revision: i32,
|
|
|
|
title: &'a str,
|
|
|
|
body: &'a str,
|
|
|
|
}
|
2017-09-05 18:07:57 +03:00
|
|
|
|
2017-09-08 16:37:58 +03:00
|
|
|
diesel::insert(&NewRevision {
|
|
|
|
article_id,
|
|
|
|
revision: new_revision,
|
|
|
|
title: &title,
|
|
|
|
body: &body,
|
|
|
|
})
|
|
|
|
.into(article_revisions::table)
|
|
|
|
.execute(&*conn)?;
|
2017-09-05 18:07:57 +03:00
|
|
|
|
2017-09-08 16:37:58 +03:00
|
|
|
Ok(article_revisions::table
|
|
|
|
.filter(article_revisions::article_id.eq(article_id))
|
|
|
|
.filter(article_revisions::revision.eq(new_revision))
|
|
|
|
.load::<models::ArticleRevision>(&*conn)?
|
|
|
|
.pop()
|
|
|
|
.expect("We just inserted this row!")
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.boxed()
|
2017-09-05 18:07:57 +03:00
|
|
|
}
|
2017-08-20 22:59:16 +03:00
|
|
|
}
|