Automatically rebase edits to historic revisions, for #23
This commit is contained in:
parent
9be9575639
commit
e92eab2c8f
2 changed files with 40 additions and 8 deletions
|
@ -8,7 +8,7 @@ use diff;
|
|||
|
||||
use self::chunk_iterator::ChunkIterator;
|
||||
use self::output::*;
|
||||
use self::output::Output::*;
|
||||
use self::output::Output::Resolved;
|
||||
|
||||
pub use self::output::Output;
|
||||
|
||||
|
|
46
src/state.rs
46
src/state.rs
|
@ -7,6 +7,7 @@ use futures_cpupool::{self, CpuFuture};
|
|||
use r2d2::Pool;
|
||||
use r2d2_diesel::ConnectionManager;
|
||||
|
||||
use merge;
|
||||
use models;
|
||||
use schema::*;
|
||||
|
||||
|
@ -173,6 +174,34 @@ impl<'a> SyncState<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
fn rebase_update(&self, article_id: i32, target_base_revision: i32, existing_base_revision: i32, _title: &str, body: String)
|
||||
-> Result<String, Error>
|
||||
{
|
||||
// TODO Also rebase title
|
||||
|
||||
let mut a = body;
|
||||
|
||||
for revision in existing_base_revision..target_base_revision {
|
||||
let mut stored = article_revisions::table
|
||||
.filter(article_revisions::article_id.eq(article_id))
|
||||
.filter(article_revisions::revision.ge(revision))
|
||||
.filter(article_revisions::revision.le(revision+1))
|
||||
.order(article_revisions::revision.asc())
|
||||
.select((article_revisions::body))
|
||||
.load::<(String)>(self.db_connection)?;
|
||||
|
||||
let b = stored.pop().expect("Application layer guarantee");
|
||||
let o = stored.pop().expect("Application layer guarantee");
|
||||
|
||||
a = match merge::merge_lines(&a, &o, &b) {
|
||||
merge::MergeResult::Clean(merged) => merged,
|
||||
_ => unimplemented!("Missing handling of merge conflicts"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
pub fn update_article(&self, article_id: i32, base_revision: i32, title: String, body: String, author: Option<String>)
|
||||
-> Result<models::ArticleRevision, Error>
|
||||
{
|
||||
|
@ -193,19 +222,23 @@ impl<'a> SyncState<'a> {
|
|||
))
|
||||
.first::<(i32, String, String)>(self.db_connection)?;
|
||||
|
||||
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");
|
||||
// TODO: If this is an historic edit repeated, just respond OK
|
||||
// This scheme would make POST idempotent.
|
||||
|
||||
if base_revision > latest_revision {
|
||||
Err("This edit is based on a future version of the article")?;
|
||||
}
|
||||
let new_revision = base_revision + 1;
|
||||
|
||||
let body = self.rebase_update(article_id, latest_revision, base_revision, &title, body)?;
|
||||
|
||||
let new_revision = latest_revision + 1;
|
||||
|
||||
let slug = decide_slug(self.db_connection, article_id, &prev_title, &title, Some(&prev_slug))?;
|
||||
|
||||
diesel::update(
|
||||
article_revisions::table
|
||||
.filter(article_revisions::article_id.eq(article_id))
|
||||
.filter(article_revisions::revision.eq(base_revision))
|
||||
.filter(article_revisions::revision.eq(latest_revision))
|
||||
)
|
||||
.set(article_revisions::latest.eq(false))
|
||||
.execute(self.db_connection)?;
|
||||
|
@ -453,7 +486,6 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // Support is unimplemented
|
||||
fn update_article_when_edit_conflict_then_merge() {
|
||||
init!(state);
|
||||
|
||||
|
|
Loading…
Reference in a new issue