# M4b task 12: approval posts in the state file **Branch:** `m4b` (run `git switch m4b`; `git status --short` must be empty, otherwise stop) **Commit subject:** `gatewayd: state, the posts of open approvals, and the turn of a session` ## Goal Two things the serve loop needs from `state.json` (M4b spec, sections 6 and 8): - **Approval posts.** The posts of each approval shown and not yet ended, so that at the next start the ones left behind can be marked dead. The field is **optional**: an M4a state file loads unchanged. Like every other field, its ids must be valid, or the file is refused. - **The turn of a session**: where an approval of that session goes (its thread). ## Files - Copy: `crates/gatewayd/tests/state_approvals.rs` - Modify: `crates/gatewayd/src/state.rs`, `docs/implementer-log.md` ## The changes, exactly In `crates/gatewayd/src/state.rs`: **1.** In `struct StateFile`, after `in_flight: Vec,`: ```rust /// Optional, so an M4a state file loads unchanged. #[serde(default)] approvals: Vec, ``` and after `struct StateFile`: ```rust /// The posts of an approval shown in Mattermost and not yet ended (M4b spec, section 8). #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct ApprovalPosts { pub approval: u64, pub channel: String, /// First part first; the last is the one that is edited when it ends. pub posts: Vec, } ``` **2.** In `StateFile::problem`, after the `in_flight` loop and before `None`: each entry of `approvals` must have a `valid_id` channel and at least one post, all `valid_id`; otherwise `Some(format!("approval {}'s posts are not valid", shown.approval))`. **3.** Four methods at the end of `impl State`, each saving (`self.save()`) when it changes something, like the methods already there: - `pub fn turn_of(&self, session: &str) -> Option`: the entry of `in_flight` with that session, cloned. - `pub fn add_approval(&mut self, shown: ApprovalPosts) -> Result<(), StateError>`: remove any entry with the same `approval`, push this one, save. - `pub fn remove_approval(&mut self, approval: u64) -> Result<(), StateError>`: remove the entries with that `approval`; save only if one was removed. - `pub fn take_approvals(&mut self) -> Result, StateError>`: take them all out (`std::mem::take`); save only when there were some; return them. (As `take_in_flight` does.) ## Steps - [ ] **1. Copy.** `git switch m4b`, then `cp docs/plans/M4b/files/crates/gatewayd/tests/state_approvals.rs crates/gatewayd/tests/` - [ ] **2. See it fail.** `cargo test -p gatewayd --test state_approvals`. Expected: it does not compile. - [ ] **3. Make the three changes.** Run `cargo fmt --all`. - [ ] **4. See it pass.** `cargo test -p gatewayd --test state_approvals --test state --test serve_restart`. Expected: 4, 4 and 7 passed. - [ ] **5. Run the gate.** `make gate`. Expected last line: `gate: ok`. - [ ] **6. Log and commit.** `git add crates/gatewayd docs/implementer-log.md Cargo.lock && git commit` ## Done when - The three suites pass; `make gate` prints `gate: ok`.