Make State::update_article return a Future.
In preparation for threading
This commit is contained in:
parent
db4f18807d
commit
50b9ebf59e
2 changed files with 57 additions and 57 deletions
26
src/site.rs
26
src/site.rs
|
@ -178,21 +178,15 @@ impl Resource for ArticleResource {
|
||||||
|
|
||||||
body
|
body
|
||||||
.concat2()
|
.concat2()
|
||||||
.map_err(|x| Box::new(x) as Box<::std::error::Error + Send + Sync>)
|
.map_err(Into::into)
|
||||||
.and_then(move |body| {
|
.and_then(|body| {
|
||||||
let update: UpdateArticle = match serde_urlencoded::from_bytes(&body) {
|
serde_urlencoded::from_bytes(&body)
|
||||||
Ok(x) => x,
|
.map_err(Into::into)
|
||||||
Err(err) => return futures::finished(Response::new()
|
})
|
||||||
.with_status(hyper::StatusCode::BadRequest)
|
.and_then(move |update: UpdateArticle| {
|
||||||
.with_body(format!("{:#?}", err))
|
self.state.update_article(self.data.article_id, update.base_revision, update.body)
|
||||||
).boxed()
|
})
|
||||||
};
|
.and_then(|updated| {
|
||||||
|
|
||||||
let updated = match self.state.update_article(self.data.article_id, update.base_revision, &update.body) {
|
|
||||||
Ok(x) => x,
|
|
||||||
Err(x) => return futures::failed(x).boxed(),
|
|
||||||
};
|
|
||||||
|
|
||||||
futures::finished(Response::new()
|
futures::finished(Response::new()
|
||||||
.with_status(hyper::StatusCode::Ok)
|
.with_status(hyper::StatusCode::Ok)
|
||||||
.with_header(ContentType(APPLICATION_JSON.clone()))
|
.with_header(ContentType(APPLICATION_JSON.clone()))
|
||||||
|
@ -201,7 +195,7 @@ impl Resource for ArticleResource {
|
||||||
rendered: &render_markdown(&updated.body),
|
rendered: &render_markdown(&updated.body),
|
||||||
created: &Local.from_utc_datetime(&updated.created).to_string(),
|
created: &Local.from_utc_datetime(&updated.created).to_string(),
|
||||||
}).expect("Should never fail"))
|
}).expect("Should never fail"))
|
||||||
).boxed()
|
)
|
||||||
})
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
88
src/state.rs
88
src/state.rs
|
@ -3,6 +3,7 @@ use std;
|
||||||
use diesel;
|
use diesel;
|
||||||
use diesel::sqlite::SqliteConnection;
|
use diesel::sqlite::SqliteConnection;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
use futures::{self, Future, IntoFuture};
|
||||||
use r2d2::Pool;
|
use r2d2::Pool;
|
||||||
use r2d2_diesel::ConnectionManager;
|
use r2d2_diesel::ConnectionManager;
|
||||||
|
|
||||||
|
@ -31,51 +32,56 @@ impl State {
|
||||||
.pop())
|
.pop())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_article(&self, article_id: i32, base_revision: i32, body: &str) -> Result<models::ArticleRevision, Error> {
|
pub fn update_article(&self, article_id: i32, base_revision: i32, body: String) -> futures::BoxFuture<models::ArticleRevision, Error> {
|
||||||
let conn = self.connection_pool.get()?;
|
self.connection_pool.get().into_future()
|
||||||
conn.transaction(|| {
|
.map_err(Into::into)
|
||||||
use schema::article_revisions;
|
.and_then(move |conn| {
|
||||||
|
conn.transaction(|| {
|
||||||
|
use schema::article_revisions;
|
||||||
|
|
||||||
let (latest_revision, title) = article_revisions::table
|
let (latest_revision, title) = article_revisions::table
|
||||||
.filter(article_revisions::article_id.eq(article_id))
|
.filter(article_revisions::article_id.eq(article_id))
|
||||||
.order(article_revisions::revision.desc())
|
.order(article_revisions::revision.desc())
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.select((article_revisions::revision, article_revisions::title))
|
.select((article_revisions::revision, article_revisions::title))
|
||||||
.load::<(i32, String)>(&*conn)?
|
.load::<(i32, String)>(&*conn)?
|
||||||
.pop()
|
.pop()
|
||||||
.unwrap_or_else(|| unimplemented!("TODO Missing an error type"));
|
.unwrap_or_else(|| unimplemented!("TODO Missing an error type"));
|
||||||
|
|
||||||
if latest_revision != base_revision {
|
if latest_revision != base_revision {
|
||||||
// TODO: If it is the same edit repeated, just respond OK
|
// TODO: If it is the same edit repeated, just respond OK
|
||||||
// TODO: If there is a conflict, transform the edit to work seamlessly
|
// TODO: If there is a conflict, transform the edit to work seamlessly
|
||||||
unimplemented!("TODO Missing handling of revision conflicts");
|
unimplemented!("TODO Missing handling of revision conflicts");
|
||||||
}
|
}
|
||||||
let new_revision = base_revision + 1;
|
let new_revision = base_revision + 1;
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[table_name="article_revisions"]
|
#[table_name="article_revisions"]
|
||||||
struct NewRevision<'a> {
|
struct NewRevision<'a> {
|
||||||
article_id: i32,
|
article_id: i32,
|
||||||
revision: i32,
|
revision: i32,
|
||||||
title: &'a str,
|
title: &'a str,
|
||||||
body: &'a str,
|
body: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::insert(&NewRevision {
|
diesel::insert(&NewRevision {
|
||||||
article_id,
|
article_id,
|
||||||
revision: new_revision,
|
revision: new_revision,
|
||||||
title: &title,
|
title: &title,
|
||||||
body
|
body: &body,
|
||||||
|
})
|
||||||
|
.into(article_revisions::table)
|
||||||
|
.execute(&*conn)?;
|
||||||
|
|
||||||
|
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!")
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.into(article_revisions::table)
|
})
|
||||||
.execute(&*conn)?;
|
.boxed()
|
||||||
|
|
||||||
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!"))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue