--- /dev/null
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum NonUtilityActivity {
+ MainMenu,
+ HomeTimelineFile,
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum UtilityActivity {
+ UtilsMenu,
+ ReadMentions,
+ LogsMenu1,
+ LogsMenu2,
+ ExitMenu,
+ ExamineUser(String),
+ ListUserFollowers(String),
+ ListUserFollowees(String),
+ InfoStatus(String),
+ ListStatusFavouriters(String),
+ ListStatusBoosters(String),
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum Activity {
+ NonUtil(NonUtilityActivity),
+ Util(UtilityActivity),
+}
+
+impl From<NonUtilityActivity> for Activity {
+ fn from(value: NonUtilityActivity) -> Self { Activity::NonUtil(value) }
+}
+impl From<UtilityActivity> for Activity {
+ fn from(value: UtilityActivity) -> Self { Activity::Util(value) }
+}
+
+#[derive(PartialEq, Eq, Debug)]
+pub struct ActivityStack {
+ nonutil: Vec<NonUtilityActivity>, // not counting MainMenu at the top
+ util: Option<UtilityActivity>,
+}
+
+impl ActivityStack {
+ pub fn new() -> Self {
+ ActivityStack {
+ nonutil: Vec::new(),
+ util: None,
+ }
+ }
+
+ pub fn pop(&mut self) {
+ if self.util.is_some() {
+ self.util = None;
+ } else {
+ // deliberately ignore failed pop at root
+ let _ = self.nonutil.pop();
+ }
+ }
+
+ pub fn goto(&mut self, act: Activity) {
+ match act {
+ Activity::Util(x) => self.util = Some(x),
+ Activity::NonUtil(x) => {
+ self.util = None;
+ match x {
+ NonUtilityActivity::MainMenu => self.nonutil.clear(),
+ y => {
+ let trunc = match self.nonutil.iter()
+ .position(|z| *z == y) {
+ Some(pos) => pos,
+ None => self.nonutil.len(),
+ };
+ self.nonutil.truncate(trunc);
+ self.nonutil.push(y);
+ },
+ }
+ }
+ }
+ }
+
+ pub fn top(&self) -> Activity {
+ match &self.util {
+ Some(x) => Activity::Util(x.clone()),
+ _ => match self.nonutil.last() {
+ Some(y) => Activity::NonUtil(y.clone()),
+ _ => Activity::NonUtil(NonUtilityActivity::MainMenu),
+ },
+ }
+ }
+}
+
+#[test]
+fn test() {
+ let mut stk = ActivityStack::new();
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {},
+ util: None,
+ });
+
+ stk.goto(NonUtilityActivity::HomeTimelineFile.into());
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {
+ NonUtilityActivity::HomeTimelineFile,
+ },
+ util: None,
+ });
+
+ stk.goto(NonUtilityActivity::HomeTimelineFile.into());
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {
+ NonUtilityActivity::HomeTimelineFile,
+ },
+ util: None,
+ });
+
+ stk.goto(NonUtilityActivity::MainMenu.into());
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {},
+ util: None,
+ });
+
+ stk.goto(NonUtilityActivity::HomeTimelineFile.into());
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {
+ NonUtilityActivity::HomeTimelineFile,
+ },
+ util: None,
+ });
+
+ stk.goto(UtilityActivity::UtilsMenu.into());
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {
+ NonUtilityActivity::HomeTimelineFile,
+ },
+ util: Some(UtilityActivity::UtilsMenu),
+ });
+
+ stk.goto(UtilityActivity::ReadMentions.into());
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {
+ NonUtilityActivity::HomeTimelineFile,
+ },
+ util: Some(UtilityActivity::ReadMentions),
+ });
+
+ stk.pop();
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {
+ NonUtilityActivity::HomeTimelineFile,
+ },
+ util: None,
+ });
+
+ stk.goto(UtilityActivity::ReadMentions.into());
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {
+ NonUtilityActivity::HomeTimelineFile,
+ },
+ util: Some(UtilityActivity::ReadMentions),
+ });
+
+ stk.goto(NonUtilityActivity::HomeTimelineFile.into());
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {
+ NonUtilityActivity::HomeTimelineFile,
+ },
+ util: None,
+ });
+
+ stk.pop();
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {},
+ util: None,
+ });
+
+ stk.pop();
+
+ assert_eq!(stk, ActivityStack {
+ nonutil: vec! {},
+ util: None,
+ });
+}