use crate::api::{BackState, Carry, FlipBack, FlipDonor, FlipReceiver, LayerSet, PortableViewState};
fn mask_forward(mut state: PortableViewState, carried: LayerSet) -> PortableViewState {
if !carried.contains(LayerSet::NAV) {
state.url = None;
state.scroll = (0.0, 0.0);
}
if !carried.contains(LayerSet::FORM) {
state.form = None;
}
if !carried.contains(LayerSet::SESSION) {
state.cookies.clear();
}
if !carried.contains(LayerSet::DOM) {
state.dom_snapshot = None;
}
if !carried.contains(LayerSet::VISUAL) {
state.visual = None;
}
state
}
fn mask_back(mut back: BackState, carried: LayerSet) -> BackState {
if !carried.contains(LayerSet::FORM) {
back.form = None;
}
if !carried.contains(LayerSet::SESSION) {
back.cookies.clear();
}
if !carried.contains(LayerSet::NAV) {
back.scroll = (0.0, 0.0);
}
back
}
pub fn forward_carried(donor: &dyn FlipDonor, receiver: &dyn FlipReceiver) -> LayerSet {
donor.donates().intersect(receiver.receives())
}
pub fn flip_forward(donor: &dyn FlipDonor, receiver: &mut dyn FlipReceiver) {
let carried = forward_carried(donor, receiver);
let state = mask_forward(donor.capture(), carried);
receiver.present(Carry::Forward(state));
}
pub fn flip_back(source: &dyn FlipBack, primary: &mut dyn FlipReceiver) -> bool {
let carried = primary.receives();
if !carried.contains(LayerSet::NAV) {
return false;
}
let back = mask_back(source.extract(), carried);
primary.present(Carry::Back(back));
true
}
#[cfg(test)]
mod tests {
use super::*;
use crate::api::{Cookie, FormValues};
struct MockDonor {
layers: LayerSet,
state: PortableViewState,
}
impl FlipDonor for MockDonor {
fn donates(&self) -> LayerSet {
self.layers
}
fn capture(&self) -> PortableViewState {
self.state.clone()
}
}
struct MockReceiver {
layers: LayerSet,
got: Option<Carry>,
}
impl FlipReceiver for MockReceiver {
fn receives(&self) -> LayerSet {
self.layers
}
fn present(&mut self, carry: Carry) {
self.got = Some(carry);
}
}
struct MockBack(BackState);
impl FlipBack for MockBack {
fn extract(&self) -> BackState {
self.0.clone()
}
}
fn full_state() -> PortableViewState {
PortableViewState {
url: Some("https://example.com/p".into()),
scroll: (0.0, 90.0),
form: Some(FormValues(vec![("q".into(), "x".into())])),
cookies: vec![Cookie {
name: "sid".into(),
..Cookie::default()
}],
dom_snapshot: Some("<html></html>".into()),
visual: None,
}
}
#[test]
fn forward_carries_only_the_intersection() {
let donor = MockDonor {
layers: LayerSet::all(),
state: full_state(),
};
let mut receiver = MockReceiver {
layers: LayerSet::NAV | LayerSet::SESSION,
got: None,
};
flip_forward(&donor, &mut receiver);
match receiver.got.unwrap() {
Carry::Forward(s) => {
assert_eq!(s.url.as_deref(), Some("https://example.com/p")); assert_eq!(s.cookies.len(), 1); assert!(s.form.is_none()); assert!(s.dom_snapshot.is_none()); }
Carry::Back(_) => panic!("expected a forward carry"),
}
}
#[test]
fn forward_carried_is_the_layer_intersection() {
let donor = MockDonor {
layers: LayerSet::NAV | LayerSet::FORM | LayerSet::DOM,
state: full_state(),
};
let receiver = MockReceiver {
layers: LayerSet::NAV | LayerSet::SESSION,
got: None,
};
let carried = forward_carried(&donor, &receiver);
assert!(carried.contains(LayerSet::NAV));
assert!(!carried.contains(LayerSet::FORM)); assert!(!carried.contains(LayerSet::SESSION)); }
#[test]
fn flip_back_masks_to_the_primary_appetite() {
let source = MockBack(BackState {
url: "https://example.com/after-login".into(),
scroll: (0.0, 40.0),
form: Some(FormValues(vec![("q".into(), "y".into())])),
cookies: vec![Cookie {
name: "sid".into(),
..Cookie::default()
}],
});
let mut primary = MockReceiver {
layers: LayerSet::NAV | LayerSet::SESSION,
got: None,
};
assert!(flip_back(&source, &mut primary));
match primary.got.unwrap() {
Carry::Back(b) => {
assert_eq!(b.url, "https://example.com/after-login");
assert_eq!(b.cookies.len(), 1); assert!(b.form.is_none()); }
Carry::Forward(_) => panic!("expected a back carry"),
}
}
#[test]
fn flip_back_declines_a_primary_that_cannot_re_root() {
let source = MockBack(BackState {
url: "https://example.com/x".into(),
..BackState::default()
});
let mut primary = MockReceiver {
layers: LayerSet::FORM,
got: None,
};
assert!(!flip_back(&source, &mut primary));
assert!(primary.got.is_none()); }
}