use crate::action::Action;
use crate::audit::Audit;
use crate::changes::ValueMap;
#[derive(Clone, Debug, PartialEq)]
pub struct Revision {
pub attributes: ValueMap,
pub version: i32,
pub new_record: bool,
}
pub(crate) fn reconstruct(audits: &[Audit]) -> (ValueMap, i32, bool) {
let mut attrs = ValueMap::new();
let mut version = 0;
let mut destroyed = false;
for audit in audits {
for (k, v) in audit.new_attributes() {
attrs.insert(k, v);
}
version = audit.version;
destroyed = audit.action == Action::Destroy;
}
(attrs, version, destroyed)
}
pub(crate) fn revisions(all_ascending: &[Audit], from_version: i32) -> Vec<Revision> {
if !all_ascending.iter().any(|a| a.version >= from_version) {
return Vec::new();
}
let before: Vec<Audit> = all_ascending
.iter()
.filter(|a| a.version < from_version)
.cloned()
.collect();
let (mut attrs, _, _) = reconstruct(&before);
let mut out = Vec::new();
for audit in all_ascending.iter().filter(|a| a.version >= from_version) {
for (k, v) in audit.new_attributes() {
attrs.insert(k, v);
}
out.push(Revision {
attributes: attrs.clone(),
version: audit.version,
new_record: audit.action == Action::Destroy,
});
}
out
}
pub(crate) fn revision_at_version(all_ascending: &[Audit], version: i32) -> Option<Revision> {
let last = all_ascending.last()?;
if last.version < version {
return None;
}
let up_to: Vec<Audit> = all_ascending
.iter()
.filter(|a| a.version <= version)
.cloned()
.collect();
let (attrs, reconstructed_version, destroyed) = reconstruct(&up_to);
Some(Revision {
attributes: attrs,
version: if reconstructed_version == 0 {
version
} else {
reconstructed_version
},
new_record: destroyed,
})
}
pub(crate) fn revision_previous(all_ascending: &[Audit]) -> Option<Revision> {
if all_ascending.is_empty() {
return None;
}
let target_version = if all_ascending.len() >= 2 {
all_ascending[all_ascending.len() - 2].version
} else {
1
};
revision_at_version(all_ascending, target_version)
}
pub(crate) fn revision_up_until(up_until_ascending: &[Audit]) -> Option<Revision> {
if up_until_ascending.is_empty() {
return None;
}
let (attrs, version, destroyed) = reconstruct(up_until_ascending);
Some(Revision {
attributes: attrs,
version,
new_record: destroyed,
})
}