Whether the child's side of the app may show: the first-run gate.
The child's name and age are entered on the parents' side and nowhere else, and everything
Whiskers says is written for them (the greeting included), so a device that has no
[Child] has nothing it may say yet. Until both halves are filled in the child's side stays
shut and the app opens on the parents' "About your child" screen.
"No profile" is judged after a sync with the service, not before: a reinstall that gets the
household document back is not a first run. So the decision has three answers. With a profile
the child's side opens at once (a device that already knows who it talks to never waits for the
network). With none and no sync tried yet, the shell is told to sync first
([Entrance::SyncFirst]). With none after a sync, or after a sync that could not reach the
service, it is a first run; [Entrance::FirstRun::service_reached] says which, so the screen can
tell a parent that the fields will be shared once the service is back.
The guard's floor is unchanged (Age::YOUNGEST with no profile). This
gate is why it is not reachable in normal use, not a replacement for it.
19use crate::profile::Child;
What the shell knows about syncing with the service, when it asks.
No sync has been tried since the app opened.
25 NotTried,
The service answered; whatever it holds has been merged in.
27 Reached,
A profile exists: the child's side may show.
36 ChildSide,
There is no profile and no sync has been tried: try one, then ask again.
38 SyncFirst,
There is still no profile after trying: the child's side stays shut and the parents fill in the name and the age.
44impl Entrance {
The one place the question is answered. child is the profile this device holds now.
46 pub fn of(child: Option<&Child>, sync: SyncOutcome) -> Entrance { 47 let entrance = match (child, sync) { 48 (Some(_), _) => Entrance::ChildSide, 49 (None, SyncOutcome::NotTried) => Entrance::SyncFirst, 50 (None, SyncOutcome::Reached) => Entrance::FirstRun { service_reached: true }, 51 (None, SyncOutcome::Unreachable) => Entrance::FirstRun { service_reached: false }, 52 }; 53 ::log::info!("entrance: profile set = {}, sync {sync:?} -> {entrance:?}", child.is_some()); 54 entrance 55 }
Whether the child's side may show. The only way to be told yes is a profile.
63#[cfg(test)] 64mod tests { 65 use super::*; 66 use crate::profile::{Age, ChildName}; 67 68 fn ada() -> Child { 69 Child { name: ChildName::new("Ada").unwrap(), age: Age::new(6).unwrap() } 70 } 71 72 #[test] 73 fn a_profile_opens_the_child_side_without_waiting_for_the_network() { 74 for sync in [SyncOutcome::NotTried, SyncOutcome::Reached, SyncOutcome::Unreachable] { 75 assert_eq!(Entrance::of(Some(&ada()), sync), Entrance::ChildSide, "{sync:?}"); 76 } 77 } 78 79 #[test] 80 fn no_profile_and_no_sync_yet_means_sync_first() { 81 assert_eq!(Entrance::of(None, SyncOutcome::NotTried), Entrance::SyncFirst); 82 } 83 84 #[test] 85 fn no_profile_after_reaching_the_service_is_a_first_run() { 86 assert_eq!(Entrance::of(None, SyncOutcome::Reached), Entrance::FirstRun { service_reached: true }); 87 } 88 89 #[test] 90 fn no_profile_with_the_service_unreachable_is_still_a_first_run_and_says_so() { 91 assert_eq!(Entrance::of(None, SyncOutcome::Unreachable), Entrance::FirstRun { service_reached: false }); 92 } 93 94 #[test] 95 fn only_a_profile_opens_the_child_side() { 96 assert!(Entrance::ChildSide.child_side_open()); 97 assert!(!Entrance::SyncFirst.child_side_open()); 98 assert!(!Entrance::FirstRun { service_reached: true }.child_side_open()); 99 assert!(!Entrance::FirstRun { service_reached: false }.child_side_open()); 100 } 101}