use std::num::NonZeroUsize;
use haematite::db::{ObservedChange, ReadOnlyDatabase};
use serde::Serialize;
use super::super::{Captured, Catalogue, Failure, ForkProvenance};
use super::{choice, compare, values};
use crate::serve::protocol::ReviewPage;
#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
enum State {
SourceOnly,
SameOnBoth,
Conflict,
}
#[derive(Serialize)]
pub(in crate::serve) struct Line {
#[serde(skip_serializing_if = "Option::is_none")]
choice_token: Option<String>,
collection: String,
document_id: Option<String>,
what: String,
ancestor: String,
target: String,
source: String,
state: State,
}
pub(in crate::serve) struct Page {
pub(in crate::serve) selection: Captured,
pub(in crate::serve) from: ForkProvenance,
pub(in crate::serve) parent: Captured,
pub(in crate::serve) lines: Vec<Line>,
pub(in crate::serve) next_after: Option<String>,
}
pub(in crate::serve) fn page(
reader: &ReadOnlyDatabase,
catalogue: &Catalogue,
request: &ReviewPage,
limit: NonZeroUsize,
) -> Result<Page, Failure> {
let compared = compare::page(reader, catalogue, request, limit)?;
let pins = choice::Pins::new(&compared.origin, &compared.parent, &compared.selection)?;
let lines = compared
.source
.iter()
.map(|change| line(reader, &compared, &pins, change))
.filter_map(Result::transpose)
.collect::<Result<Vec<_>, _>>()?;
Ok(Page {
selection: compared.selection,
from: compared.from,
parent: compared.parent,
lines,
next_after: compared.next_after,
})
}
fn line(
reader: &ReadOnlyDatabase,
compared: &compare::Page,
pins: &choice::Pins,
change: &ObservedChange,
) -> Result<Option<Line>, Failure> {
let (ancestor, source) = compare::sides(change);
if ancestor == source {
return Ok(None);
}
let Some(identity) = values::identity(change.key())? else {
return Ok(None);
};
let target = compare::target_value(change, &compared.target)?;
let state = if target == ancestor {
State::SourceOnly
} else if target == source {
State::SameOnBoth
} else {
State::Conflict
};
let (collection, document_id, what) = identity.description();
Ok(Some(Line {
choice_token: if choice::embedded_field(change.key(), ancestor, target, source)?.is_some() {
None
} else {
Some(pins.token(change.key(), ancestor, target, source)?)
},
collection,
document_id,
what,
ancestor: identity.value(reader, &compared.origin, ancestor)?,
target: identity.value(reader, &compared.parent, target)?,
source: identity.value(reader, &compared.selection, source)?,
state,
}))
}