max / goingson
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
18 files changed,
+459 insertions,
-371 deletions
| @@ -37,8 +37,8 @@ | |||
| 37 | 37 | //! are the first writes in this module, and they are what proves the `Act` on a | |
| 38 | 38 | //! row reaches a handler at all. | |
| 39 | 39 | ||
| 40 | - | // Handlers take their params by value because `quasi_router::Handler` is a | |
| 41 | - | // plain `fn(&S, Params)` pointer, so the signature is the router's and not a | |
| 40 | + | // Handlers take their request by value because `quasi_router::Handler` is a | |
| 41 | + | // plain `fn(&S, Request)` pointer, so the signature is the router's and not a | |
| 42 | 42 | // choice made here. Same allow, for the same reason, as quasi-axum's tests. | |
| 43 | 43 | #![allow(clippy::needless_pass_by_value)] | |
| 44 | 44 | ||
| @@ -175,10 +175,10 @@ | |||
| 175 | 175 | fn list_action(search: Option<&str>, tag: Option<&str>) -> Action { | |
| 176 | 176 | let mut action = Action::get("/contacts/list"); | |
| 177 | 177 | if let Some(search) = search { | |
| 178 | - | action = action.with("q", search); | |
| 178 | + | action = action.carrying("q", search); | |
| 179 | 179 | } | |
| 180 | 180 | if let Some(tag) = tag { | |
| 181 | - | action = action.with("tag", tag); | |
| 181 | + | action = action.carrying("tag", tag); | |
| 182 | 182 | } | |
| 183 | 183 | action | |
| 184 | 184 | } | |
| @@ -189,10 +189,11 @@ | |||
| 189 | 189 | /// crate's. Same reasoning as the projects screen: not worth adding one upstream | |
| 190 | 190 | /// for a handful of call sites. | |
| 191 | 191 | fn id_param<T: From<uuid::Uuid>>( | |
| 192 | - | params: &quasi_router::Params, | |
| 192 | + | request: &quasi_router::Request, | |
| 193 | 193 | name: &str, | |
| 194 | 194 | ) -> Result<T, RouteError> { | |
| 195 | - | let raw = params | |
| 195 | + | let raw = request | |
| 196 | + | .captures | |
| 196 | 197 | .get(name) | |
| 197 | 198 | .ok_or_else(|| RouteError::not_found("no id"))?; | |
| 198 | 199 | let uuid = uuid::Uuid::parse_str(raw).map_err(|_| RouteError::not_found("not an id"))?; | |
| @@ -200,9 +201,9 @@ | |||
| 200 | 201 | } | |
| 201 | 202 | ||
| 202 | 203 | /// The whole screen. | |
| 203 | - | fn index(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 204 | - | let search = text(¶ms, "q"); | |
| 205 | - | let tag = text(¶ms, "tag"); | |
| 204 | + | fn index(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 205 | + | let search = text(&request.carried, "q"); | |
| 206 | + | let tag = text(&request.carried, "tag"); | |
| 206 | 207 | ||
| 207 | 208 | let mut band = Slot::new("contacts-band", RegionKind::Band) | |
| 208 | 209 | .with(Node::page("Contacts")) | |
| @@ -226,8 +227,12 @@ | |||
| 226 | 227 | } | |
| 227 | 228 | ||
| 228 | 229 | /// The grid alone, which is what search and the tag filter replace. | |
| 229 | - | fn list(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 230 | - | let node = grid(state, text(¶ms, "q"), text(¶ms, "tag"))?; | |
| 230 | + | fn list(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 231 | + | let node = grid( | |
| 232 | + | state, | |
| 233 | + | text(&request.carried, "q"), | |
| 234 | + | text(&request.carried, "tag"), | |
| 235 | + | )?; | |
| 231 | 236 | Ok(Response::fragment("contacts-grid", node)) | |
| 232 | 237 | } | |
| 233 | 238 | ||
| @@ -412,8 +417,8 @@ | |||
| 412 | 417 | } | |
| 413 | 418 | ||
| 414 | 419 | /// One contact's detail pane. | |
| 415 | - | fn detail(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 416 | - | let contact = load(state, id_param(¶ms, "id")?)?; | |
| 420 | + | fn detail(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 421 | + | let contact = load(state, id_param(&request, "id")?)?; | |
| 417 | 422 | Ok(Response::fragment("contacts-detail", detail_pane(&contact))) | |
| 418 | 423 | } | |
| 419 | 424 | ||
| @@ -428,41 +433,41 @@ | |||
| 428 | 433 | } | |
| 429 | 434 | ||
| 430 | 435 | /// Remove one email address. | |
| 431 | - | fn remove_email(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 432 | - | let contact: ContactId = id_param(¶ms, "id")?; | |
| 436 | + | fn remove_email(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 437 | + | let contact: ContactId = id_param(&request, "id")?; | |
| 433 | 438 | state | |
| 434 | 439 | .contacts | |
| 435 | - | .remove_email(id_param(¶ms, "sub")?, DESKTOP_USER_ID) | |
| 440 | + | .remove_email(id_param(&request, "sub")?, DESKTOP_USER_ID) | |
| 436 | 441 | .map_err(|error| RouteError::internal(error.to_string()))?; | |
| 437 | 442 | removed(state, contact) | |
| 438 | 443 | } | |
| 439 | 444 | ||
| 440 | 445 | /// Remove one phone number. | |
| 441 | - | fn remove_phone(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 442 | - | let contact: ContactId = id_param(¶ms, "id")?; | |
| 446 | + | fn remove_phone(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 447 | + | let contact: ContactId = id_param(&request, "id")?; | |
| 443 | 448 | state | |
| 444 | 449 | .contacts | |
| 445 | - | .remove_phone(id_param(¶ms, "sub")?, DESKTOP_USER_ID) | |
| 450 | + | .remove_phone(id_param(&request, "sub")?, DESKTOP_USER_ID) | |
| 446 | 451 | .map_err(|error| RouteError::internal(error.to_string()))?; | |
| 447 | 452 | removed(state, contact) | |
| 448 | 453 | } | |
| 449 | 454 | ||
| 450 | 455 | /// Remove one social handle. | |
| 451 | - | fn remove_social(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 452 | - | let contact: ContactId = id_param(¶ms, "id")?; | |
| 456 | + | fn remove_social(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 457 | + | let contact: ContactId = id_param(&request, "id")?; | |
| 453 | 458 | state | |
| 454 | 459 | .contacts | |
| 455 | - | .remove_social_handle(id_param(¶ms, "sub")?, DESKTOP_USER_ID) | |
| 460 | + | .remove_social_handle(id_param(&request, "sub")?, DESKTOP_USER_ID) | |
| 456 | 461 | .map_err(|error| RouteError::internal(error.to_string()))?; | |
| 457 | 462 | removed(state, contact) | |
| 458 | 463 | } | |
| 459 | 464 | ||
| 460 | 465 | /// Remove one custom field. | |
| 461 | - | fn remove_field(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 462 | - | let contact: ContactId = id_param(¶ms, "id")?; | |
| 466 | + | fn remove_field(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 467 | + | let contact: ContactId = id_param(&request, "id")?; | |
| 463 | 468 | state | |
| 464 | 469 | .contacts | |
| 465 | - | .remove_custom_field(id_param(¶ms, "sub")?, DESKTOP_USER_ID) | |
| 470 | + | .remove_custom_field(id_param(&request, "sub")?, DESKTOP_USER_ID) | |
| 466 | 471 | .map_err(|error| RouteError::internal(error.to_string()))?; | |
| 467 | 472 | removed(state, contact) | |
| 468 | 473 | } |
| @@ -59,8 +59,8 @@ | |||
| 59 | 59 | //! it either. Recorded rather than quietly given a button, which would make the | |
| 60 | 60 | //! described screen offer something the screen it stands in for does not. | |
| 61 | 61 | ||
| 62 | - | // Handlers take their params by value because `quasi_router::Handler` is a | |
| 63 | - | // plain `fn(&S, Params)` pointer, so the signature is the router's and not a | |
| 62 | + | // Handlers take their request by value because `quasi_router::Handler` is a | |
| 63 | + | // plain `fn(&S, Request)` pointer, so the signature is the router's and not a | |
| 64 | 64 | // choice made here. Same allow, for the same reason, as quasi-axum's tests. | |
| 65 | 65 | #![allow(clippy::needless_pass_by_value)] | |
| 66 | 66 | ||
| @@ -123,17 +123,18 @@ | |||
| 123 | 123 | ||
| 124 | 124 | impl View { | |
| 125 | 125 | /// The view a route was addressed at. | |
| 126 | - | fn of(params: &quasi_router::Params) -> Self { | |
| 126 | + | fn of(request: &quasi_router::Request) -> Self { | |
| 127 | 127 | Self { | |
| 128 | - | folder: text(params, "folder"), | |
| 129 | - | label: text(params, "label"), | |
| 130 | - | archived: matches!(params.get("archived"), Some("1" | "true")), | |
| 128 | + | folder: text(&request.carried, "folder"), | |
| 129 | + | label: text(&request.carried, "label"), | |
| 130 | + | archived: matches!(request.carried.get("archived"), Some("1" | "true")), | |
| 131 | 131 | // A hand-typed `shown` is clamped rather than refused: this is an | |
| 132 | 132 | // address, and landing on the first page is a more useful answer | |
| 133 | 133 | // than an error page. The ceiling is the one the JS's own paging | |
| 134 | 134 | // would reach in ten scrolls and stops a typo asking for a million | |
| 135 | 135 | // rows. | |
| 136 | - | shown: params | |
| 136 | + | shown: request | |
| 137 | + | .carried | |
| 137 | 138 | .get("shown") | |
| 138 | 139 | .and_then(|raw| raw.parse::<i64>().ok()) | |
| 139 | 140 | .unwrap_or(PAGE) | |
| @@ -147,48 +148,51 @@ | |||
| 147 | 148 | /// That is [`super::projects::filtered_by`]'s rule, applied to four params | |
| 148 | 149 | /// instead of two. | |
| 149 | 150 | /// | |
| 150 | - | /// # The sixth finding, which this port walked into | |
| 151 | + | /// # The sixth finding, which this port walked into, and which is now closed | |
| 151 | 152 | /// | |
| 152 | - | /// **A write's parameters and the view's parameters share one namespace, and | |
| 153 | - | /// nothing warns when they collide.** | |
| 153 | + | /// **A write's parameters and the view's parameters shared one namespace, | |
| 154 | + | /// and nothing warned when they collided.** | |
| 154 | 155 | /// | |
| 155 | 156 | /// This screen writes to `POST /emails/{id}/folder` and reads a `folder` | |
| 156 | 157 | /// filter, and it archives through `POST /emails/{id}/archive` while reading | |
| 157 | 158 | /// an `archived` filter. Written the obvious way โ the destination under | |
| 158 | - | /// `folder`, the desired state under `archived` โ both routes compile, | |
| 159 | - | /// answer, and are wrong in the same silent way: the write lands correctly | |
| 160 | - | /// and then [`View::of`] reads the write's own parameter back as the view, | |
| 161 | - | /// so moving a message to Archive from the INBOX answers with the Archive | |
| 162 | - | /// folder as though the user had navigated there. Nothing is lost and | |
| 163 | - | /// nothing errors; the screen just moves under them. | |
| 159 | + | /// `folder`, the desired state under `archived` โ both routes compiled, | |
| 160 | + | /// answered, and were wrong in the same silent way: the write landed | |
| 161 | + | /// correctly and then [`View::of`] read the write's own parameter back as | |
| 162 | + | /// the view, so moving a message to Archive from the INBOX answered with the | |
| 163 | + | /// Archive folder as though the user had navigated there. Nothing was lost | |
| 164 | + | /// and nothing errored; the screen just moved under them. | |
| 164 | 165 | /// | |
| 165 | - | /// The fix here is naming โ the destination is `to` and the archive state is | |
| 166 | - | /// `on` โ and it is a convention held by hand, which is what makes it worth | |
| 167 | - | /// recording rather than just doing. Every screen that carries its view in | |
| 168 | - | /// the address has this hazard, it grows with the number of filters, and the | |
| 169 | - | /// two ports before this one had one and two filters and never met it. Filed | |
| 170 | - | /// against quasicoherent: the description layer knows which params are the | |
| 171 | - | /// view's, because the handler asked for them, and a `Params` that could say | |
| 172 | - | /// "these are the address and these are the payload" would make the collision | |
| 173 | - | /// a compile-time question instead of a convention. | |
| 166 | + | /// The fix was naming โ the destination was `to` and the archive state `on` | |
| 167 | + | /// โ a convention held by hand, which is why it was recorded rather than | |
| 168 | + | /// just done. The problems inbox hit the same wall the same day, on a screen | |
| 169 | + | /// with two filters rather than four, which killed the theory that this was | |
| 170 | + | /// about how many filters a screen carries. | |
| 171 | + | /// | |
| 172 | + | /// Closed 2026-08-10 in quasi-router. A request now arrives in three bags: | |
| 173 | + | /// `captures` from the path, `payload` from what the control sent, and | |
| 174 | + | /// `carried` from the address it was sent from. So this method writes the | |
| 175 | + | /// view with [`Action::carrying`], the writes send their values under their | |
| 176 | + | /// own honest names again โ `folder` and `archived`, not `to` and `on` โ | |
| 177 | + | /// and neither can be read as the other. | |
| 174 | 178 | fn carry(&self, action: Action) -> Action { | |
| 175 | 179 | let action = match &self.folder { | |
| 176 | - | Some(folder) => action.with("folder", folder.clone()), | |
| 180 | + | Some(folder) => action.carrying("folder", folder.clone()), | |
| 177 | 181 | None => action, | |
| 178 | 182 | }; | |
| 179 | 183 | let action = match &self.label { | |
| 180 | - | Some(label) => action.with("label", label.clone()), | |
| 184 | + | Some(label) => action.carrying("label", label.clone()), | |
| 181 | 185 | None => action, | |
| 182 | 186 | }; | |
| 183 | 187 | let action = if self.archived { | |
| 184 | - | action.with("archived", "1") | |
| 188 | + | action.carrying("archived", "1") | |
| 185 | 189 | } else { | |
| 186 | 190 | action | |
| 187 | 191 | }; | |
| 188 | 192 | if self.shown == PAGE { | |
| 189 | 193 | action | |
| 190 | 194 | } else { | |
| 191 | - | action.with("shown", self.shown.to_string()) | |
| 195 | + | action.carrying("shown", self.shown.to_string()) | |
| 192 | 196 | } | |
| 193 | 197 | } | |
| 194 | 198 | ||
| @@ -211,8 +215,9 @@ | |||
| 211 | 215 | } | |
| 212 | 216 | ||
| 213 | 217 | /// The email a route was addressed at. | |
| 214 | - | fn email_id(params: &quasi_router::Params) -> Result<EmailId, RouteError> { | |
| 215 | - | let raw = params | |
| 218 | + | fn email_id(request: &quasi_router::Request) -> Result<EmailId, RouteError> { | |
| 219 | + | let raw = request | |
| 220 | + | .captures | |
| 216 | 221 | .get("id") | |
| 217 | 222 | .ok_or_else(|| RouteError::not_found("no email id"))?; | |
| 218 | 223 | Ok(EmailId::from( | |
| @@ -342,9 +347,9 @@ | |||
| 342 | 347 | Act::new("Mark read", at("read").with("read", "true")) | |
| 343 | 348 | }, | |
| 344 | 349 | if email.is_archived { | |
| 345 | - | Act::new("Unarchive", at("archive").with("on", "false")).key("a") | |
| 350 | + | Act::new("Unarchive", at("archive").with("archived", "false")).key("a") | |
| 346 | 351 | } else { | |
| 347 | - | Act::new("Archive", at("archive").with("on", "true")).key("a") | |
| 352 | + | Act::new("Archive", at("archive").with("archived", "true")).key("a") | |
| 348 | 353 | }, | |
| 349 | 354 | Act::new("Create task", at("task")).key("t"), | |
| 350 | 355 | Act::new("Create event", at("event")).key("e"), | |
| @@ -807,7 +812,7 @@ | |||
| 807 | 812 | // `to`, not `folder`, for the reason `View::carry` records: `folder` | |
| 808 | 813 | // is the view's filter, and a destination under the same name | |
| 809 | 814 | // would move the message and the screen at once. | |
| 810 | - | Field::new(makeover_layout::FieldKind::Text, "to", "Folder") | |
| 815 | + | Field::new(makeover_layout::FieldKind::Text, "folder", "Folder") | |
| 811 | 816 | .value(latest.source_folder.clone().unwrap_or_default()) | |
| 812 | 817 | .required(), | |
| 813 | 818 | ], | |
| @@ -816,13 +821,13 @@ | |||
| 816 | 821 | } | |
| 817 | 822 | ||
| 818 | 823 | /// The whole screen. | |
| 819 | - | fn index(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 820 | - | Ok(screen(state, &View::of(¶ms), None)?.into()) | |
| 824 | + | fn index(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 825 | + | Ok(screen(state, &View::of(&request), None)?.into()) | |
| 821 | 826 | } | |
| 822 | 827 | ||
| 823 | 828 | /// The list alone, which is what a filter or another page replaces. | |
| 824 | - | fn list_only(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 825 | - | let view = View::of(¶ms); | |
| 829 | + | fn list_only(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 830 | + | let view = View::of(&request); | |
| 826 | 831 | let node = list(state, &view, None)?; | |
| 827 | 832 | Ok(Response::fragment(LIST, node)) | |
| 828 | 833 | } | |
| @@ -838,9 +843,9 @@ | |||
| 838 | 843 | /// The consequence is that the answer is the whole screen and not the pane: the | |
| 839 | 844 | /// row behind it just lost its unread badge, and the unread figure in the band | |
| 840 | 845 | /// changed with it. | |
| 841 | - | fn thread(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 842 | - | let id = email_id(¶ms)?; | |
| 843 | - | let view = View::of(¶ms); | |
| 846 | + | fn thread(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 847 | + | let id = email_id(&request)?; | |
| 848 | + | let view = View::of(&request); | |
| 844 | 849 | // Marked before the read, so the screen that comes back is the one after the | |
| 845 | 850 | // write rather than the one before it. | |
| 846 | 851 | state | |
| @@ -859,10 +864,10 @@ | |||
| 859 | 864 | } | |
| 860 | 865 | ||
| 861 | 866 | /// Read or unread. | |
| 862 | - | fn set_read(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 863 | - | let id = email_id(¶ms)?; | |
| 864 | - | let view = View::of(¶ms); | |
| 865 | - | let read = params.get("read") == Some("true"); | |
| 867 | + | fn set_read(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 868 | + | let id = email_id(&request)?; | |
| 869 | + | let view = View::of(&request); | |
| 870 | + | let read = request.payload.get("read") == Some("true"); | |
| 866 | 871 | ||
| 867 | 872 | let found = if read { | |
| 868 | 873 | state.emails.mark_read(id, DESKTOP_USER_ID) | |
| @@ -903,13 +908,13 @@ | |||
| 903 | 908 | /// screen that writes locally and wants to tell something else about it has | |
| 904 | 909 | /// nowhere to say so. An outbox the app drains is one answer and a handler that | |
| 905 | 910 | /// can return work is another, and picking between them wants a second consumer. | |
| 906 | - | fn set_archived(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 907 | - | let id = email_id(¶ms)?; | |
| 908 | - | let view = View::of(¶ms); | |
| 911 | + | fn set_archived(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 912 | + | let id = email_id(&request)?; | |
| 913 | + | let view = View::of(&request); | |
| 909 | 914 | // `on`, not `archived`: `archived` is the view's own filter, and a write | |
| 910 | 915 | // that reused the name would rewrite the view it answers with. See | |
| 911 | 916 | // `View::carry`. | |
| 912 | - | let archived = params.get("on") == Some("true"); | |
| 917 | + | let archived = request.payload.get("archived") == Some("true"); | |
| 913 | 918 | ||
| 914 | 919 | let found = if archived { | |
| 915 | 920 | state.emails.archive(id, DESKTOP_USER_ID) | |
| @@ -937,9 +942,9 @@ | |||
| 937 | 942 | /// | |
| 938 | 943 | /// A 404 for a message that is not there rather than a quiet success, which is | |
| 939 | 944 | /// the rule the projects delete set. | |
| 940 | - | fn remove(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 941 | - | let id = email_id(¶ms)?; | |
| 942 | - | let view = View::of(¶ms); | |
| 945 | + | fn remove(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 946 | + | let id = email_id(&request)?; | |
| 947 | + | let view = View::of(&request); | |
| 943 | 948 | let deleted = state | |
| 944 | 949 | .emails | |
| 945 | 950 | .delete(id, DESKTOP_USER_ID) | |
| @@ -957,10 +962,11 @@ | |||
| 957 | 962 | /// than a set of labels picked from what exists, and the JS knows it โ it prints | |
| 958 | 963 | /// the existing ones under the box as a hint. Kept as it is: replacing it with a | |
| 959 | 964 | /// multi-select is a change to the screen, and this port describes the screen. | |
| 960 | - | fn set_labels(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 961 | - | let id = email_id(¶ms)?; | |
| 962 | - | let view = View::of(¶ms); | |
| 963 | - | let labels: Vec<String> = params | |
| 965 | + | fn set_labels(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 966 | + | let id = email_id(&request)?; | |
| 967 | + | let view = View::of(&request); | |
| 968 | + | let labels: Vec<String> = request | |
| 969 | + | .payload | |
| 964 | 970 | .get("labels") | |
| 965 | 971 | .unwrap_or_default() | |
| 966 | 972 | .split(',') | |
| @@ -980,10 +986,15 @@ | |||
| 980 | 986 | /// Move it to another folder. | |
| 981 | 987 | /// | |
| 982 | 988 | /// The local half only; see [`set_archived`] for why. | |
| 983 | - | fn set_folder(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 984 | - | let id = email_id(¶ms)?; | |
| 985 | - | let view = View::of(¶ms); | |
| 986 | - | let folder = params.get("to").unwrap_or_default().trim().to_owned(); | |
| 989 | + | fn set_folder(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 990 | + | let id = email_id(&request)?; | |
| 991 | + | let view = View::of(&request); | |
| 992 | + | let folder = request | |
| 993 | + | .payload | |
| 994 | + | .get("folder") | |
| 995 | + | .unwrap_or_default() | |
| 996 | + | .trim() | |
| 997 | + | .to_owned(); | |
| 987 | 998 | if folder.is_empty() { | |
| 988 | 999 | return Err(RouteError::not_found("no folder")); | |
| 989 | 1000 | } | |
| @@ -1004,11 +1015,11 @@ | |||
| 1004 | 1015 | } | |
| 1005 | 1016 | ||
| 1006 | 1017 | /// Snooze it until a time, or bring it back now. | |
| 1007 | - | fn set_snooze(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 1008 | - | let id = email_id(¶ms)?; | |
| 1009 | - | let view = View::of(¶ms); | |
| 1018 | + | fn set_snooze(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 1019 | + | let id = email_id(&request)?; | |
| 1020 | + | let view = View::of(&request); | |
| 1010 | 1021 | ||
| 1011 | - | if params.get("clear") == Some("true") { | |
| 1022 | + | if request.payload.get("clear") == Some("true") { | |
| 1012 | 1023 | state | |
| 1013 | 1024 | .emails | |
| 1014 | 1025 | .unsnooze(id, DESKTOP_USER_ID) | |
| @@ -1023,7 +1034,8 @@ | |||
| 1023 | 1034 | // take it, `is_snoozed` would read false the moment it landed, and the user | |
| 1024 | 1035 | // would be told the message was hidden when it was not. The JS enforces the | |
| 1025 | 1036 | // same floor with the picker's `min`. | |
| 1026 | - | let until = params | |
| 1037 | + | let until = request | |
| 1038 | + | .payload | |
| 1027 | 1039 | .get("until") | |
| 1028 | 1040 | .and_then(|raw| DateTime::parse_from_rfc3339(raw).ok()) | |
| 1029 | 1041 | .map(|when| when.with_timezone(&Utc)) | |
| @@ -1070,9 +1082,9 @@ | |||
| 1070 | 1082 | /// from here, and that is the whole of what a described screen has to route | |
| 1071 | 1083 | /// around: the rules are in core, where the settings port's finding says host | |
| 1072 | 1084 | /// facts should be, and for the same reason. | |
| 1073 | - | fn to_task(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 1074 | - | let id = email_id(¶ms)?; | |
| 1075 | - | let view = View::of(¶ms); | |
| 1085 | + | fn to_task(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 1086 | + | let id = email_id(&request)?; | |
| 1087 | + | let view = View::of(&request); | |
| 1076 | 1088 | let email = load(state, id)?; | |
| 1077 | 1089 | ||
| 1078 | 1090 | let contact_id = sender_contact(state, &email.from); | |
| @@ -1106,9 +1118,9 @@ | |||
| 1106 | 1118 | } | |
| 1107 | 1119 | ||
| 1108 | 1120 | /// Make an event of it. | |
| 1109 | - | fn to_event(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 1110 | - | let id = email_id(¶ms)?; | |
| 1111 | - | let view = View::of(¶ms); | |
| 1121 | + | fn to_event(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 1122 | + | let id = email_id(&request)?; | |
| 1123 | + | let view = View::of(&request); | |
| 1112 | 1124 | let email = load(state, id)?; | |
| 1113 | 1125 | ||
| 1114 | 1126 | let contact_id = sender_contact(state, &email.from); |
| @@ -58,8 +58,8 @@ | |||
| 58 | 58 | //! yet; describing a control that reaches nothing would be worse than leaving | |
| 59 | 59 | //! it out. It returns when day planning does. | |
| 60 | 60 | ||
| 61 | - | // Handlers take their params by value because `quasi_router::Handler` is a | |
| 62 | - | // plain `fn(&S, Params)` pointer, so the signature is the router's and not a | |
| 61 | + | // Handlers take their request by value because `quasi_router::Handler` is a | |
| 62 | + | // plain `fn(&S, Request)` pointer, so the signature is the router's and not a | |
| 63 | 63 | // choice made here. Same allow, for the same reason, as quasi-axum's tests. | |
| 64 | 64 | #![allow(clippy::needless_pass_by_value)] | |
| 65 | 65 | ||
| @@ -92,8 +92,9 @@ | |||
| 92 | 92 | /// there a bad value is a client bug worth reporting, and here it is a | |
| 93 | 93 | /// hand-typed address, where landing on this month is the more useful answer | |
| 94 | 94 | /// than an error page. | |
| 95 | - | fn month_of(params: &quasi_router::Params) -> NaiveDate { | |
| 96 | - | params | |
| 95 | + | fn month_of(request: &quasi_router::Request) -> NaiveDate { | |
| 96 | + | request | |
| 97 | + | .carried | |
| 97 | 98 | .get("month") | |
| 98 | 99 | .and_then(monthly_review::parse_month) | |
| 99 | 100 | .unwrap_or_else(monthly_review::current_month_start) | |
| @@ -101,7 +102,7 @@ | |||
| 101 | 102 | ||
| 102 | 103 | /// The same action, still pointed at the month it was offered under. | |
| 103 | 104 | fn in_month(action: Action, month: NaiveDate) -> Action { | |
| 104 | - | action.with("month", month.format("%Y-%m").to_string()) | |
| 105 | + | action.carrying("month", month.format("%Y-%m").to_string()) | |
| 105 | 106 | } | |
| 106 | 107 | ||
| 107 | 108 | /// The month before this one, and the month after. | |
| @@ -514,8 +515,8 @@ | |||
| 514 | 515 | } | |
| 515 | 516 | ||
| 516 | 517 | /// The whole review. | |
| 517 | - | fn review(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 518 | - | Ok(screen(state, month_of(¶ms))?.into()) | |
| 518 | + | fn review(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 519 | + | Ok(screen(state, month_of(&request))?.into()) | |
| 519 | 520 | } | |
| 520 | 521 | ||
| 521 | 522 | /// The month a write names, as the repository spells it. | |
| @@ -524,8 +525,9 @@ | |||
| 524 | 525 | } | |
| 525 | 526 | ||
| 526 | 527 | /// The goal id in a path, or a 404. | |
| 527 | - | fn goal_id(params: &quasi_router::Params) -> Result<goingson_core::MonthlyGoalId, RouteError> { | |
| 528 | - | let raw = params | |
| 528 | + | fn goal_id(request: &quasi_router::Request) -> Result<goingson_core::MonthlyGoalId, RouteError> { | |
| 529 | + | let raw = request | |
| 530 | + | .captures | |
| 529 | 531 | .get("id") | |
| 530 | 532 | .ok_or_else(|| RouteError::not_found("no goal id"))?; | |
| 531 | 533 | Ok(goingson_core::MonthlyGoalId::from( | |
| @@ -539,9 +541,9 @@ | |||
| 539 | 541 | /// the JS: `addGoal(month, position)` is called from a specific empty slot, so | |
| 540 | 542 | /// the position is a fact about which slot was clicked. A described form has no | |
| 541 | 543 | /// slot, and "the next one" is what every one of those clicks meant. | |
| 542 | - | fn add_goal(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 543 | - | let month = month_of(¶ms); | |
| 544 | - | let text = params.get("text").unwrap_or_default().trim(); | |
| 544 | + | fn add_goal(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 545 | + | let month = month_of(&request); | |
| 546 | + | let text = request.payload.get("text").unwrap_or_default().trim(); | |
| 545 | 547 | if text.is_empty() { | |
| 546 | 548 | return Err(RouteError::conflict("A goal needs some text")); | |
| 547 | 549 | } | |
| @@ -570,10 +572,14 @@ | |||
| 570 | 572 | } | |
| 571 | 573 | ||
| 572 | 574 | /// Set a goal's status to the one the control named. | |
| 573 | - | fn set_goal_status(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 574 | - | let month = month_of(¶ms); | |
| 575 | - | let id = goal_id(¶ms)?; | |
| 576 | - | let status: MonthlyGoalStatus = params | |
| 575 | + | fn set_goal_status( | |
| 576 | + | state: &AppState, | |
| 577 | + | request: quasi_router::Request, | |
| 578 | + | ) -> Result<Response, RouteError> { | |
| 579 | + | let month = month_of(&request); | |
| 580 | + | let id = goal_id(&request)?; | |
| 581 | + | let status: MonthlyGoalStatus = request | |
| 582 | + | .payload | |
| 577 | 583 | .get("status") | |
| 578 | 584 | .ok_or_else(|| RouteError::not_found("no status"))? | |
| 579 | 585 | .parse() | |
| @@ -588,9 +594,9 @@ | |||
| 588 | 594 | } | |
| 589 | 595 | ||
| 590 | 596 | /// Drop a goal. | |
| 591 | - | fn delete_goal(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 592 | - | let month = month_of(¶ms); | |
| 593 | - | let id = goal_id(¶ms)?; | |
| 597 | + | fn delete_goal(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 598 | + | let month = month_of(&request); | |
| 599 | + | let id = goal_id(&request)?; | |
| 594 | 600 | ||
| 595 | 601 | if !state | |
| 596 | 602 | .monthly_reviews | |
| @@ -607,10 +613,10 @@ | |||
| 607 | 613 | /// Both answers empty still writes, and still marks the month reviewed: the | |
| 608 | 614 | /// completion is the act and the writing is optional, which is the rule the | |
| 609 | 615 | /// weekly review settled. | |
| 610 | - | fn complete(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 611 | - | let month = month_of(¶ms); | |
| 612 | - | let highlight = params.get("highlight").unwrap_or_default().trim(); | |
| 613 | - | let change = params.get("change").unwrap_or_default().trim(); | |
| 616 | + | fn complete(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 617 | + | let month = month_of(&request); | |
| 618 | + | let highlight = request.payload.get("highlight").unwrap_or_default().trim(); | |
| 619 | + | let change = request.payload.get("change").unwrap_or_default().trim(); | |
| 614 | 620 | ||
| 615 | 621 | state | |
| 616 | 622 | .monthly_reviews |
| @@ -34,20 +34,22 @@ | |||
| 34 | 34 | //! control that derives its target from what it was drawn with races anything | |
| 35 | 35 | //! that already moved the row. | |
| 36 | 36 | //! | |
| 37 | - | //! # The sixth finding again, and this time it bit before it was read | |
| 37 | + | //! # This screen is why the router has two bags | |
| 38 | 38 | //! | |
| 39 | - | //! The target travels as `to`, not as `status`, because `status` is already | |
| 40 | - | //! this screen's view filter. Written the obvious way the route works and the | |
| 41 | - | //! screen moves under the user: dismissing from the Open inbox answers with the | |
| 42 | - | //! Dismissed list, because the write's own parameter is what the next | |
| 43 | - | //! `status_filter` reads back. This port found it by a failing test rather than | |
| 44 | - | //! by reading, which is the argument for the finding the mail port filed | |
| 45 | - | //! against quasicoherent: the convention is held by hand, it is invisible until | |
| 46 | - | //! something collides, and `Params` knows enough to make it a compile-time | |
| 47 | - | //! question. Second screen to hit it, second one to work around it by naming. | |
| 39 | + | //! It filters on `status` and it writes a `status`, and for one afternoon those | |
| 40 | + | //! were the same name in one namespace: dismissing from the Open inbox wrote | |
| 41 | + | //! correctly and then answered with the Dismissed list, because the write's own | |
| 42 | + | //! parameter was what the next read of the filter found. The workaround was to | |
| 43 | + | //! call the target `to`, which is what the mail screen had already done twice | |
| 44 | + | //! under its own names. | |
| 45 | + | //! | |
| 46 | + | //! That convention is retired. A write's values arrive in `payload` and the | |
| 47 | + | //! view arrives in `carried`, so the target is called `status` here because | |
| 48 | + | //! that is what it is, and the filter is called `status` because that is what | |
| 49 | + | //! it is. Neither can reach the other. | |
| 48 | 50 | ||
| 49 | - | // Handlers take their params by value because `quasi_router::Handler` is a | |
| 50 | - | // plain `fn(&S, Params)` pointer, so the signature is the router's and not a | |
| 51 | + | // Handlers take their request by value because `quasi_router::Handler` is a | |
| 52 | + | // plain `fn(&S, Request)` pointer, so the signature is the router's and not a | |
| 51 | 53 | // choice made here. Same allow, for the same reason, as quasi-axum's tests. | |
| 52 | 54 | #![allow(clippy::needless_pass_by_value)] | |
| 53 | 55 | ||
| @@ -109,8 +111,8 @@ | |||
| 109 | 111 | /// `all` means unfiltered. Same three cases as `list_problems`, and an | |
| 110 | 112 | /// unrecognised word is a 404 rather than a silent fall back to `Open`, which | |
| 111 | 113 | /// would answer with a different list than the one asked for. | |
| 112 | - | fn status_filter(params: &quasi_router::Params) -> Result<Option<ProblemStatus>, RouteError> { | |
| 113 | - | match text(params, "status") { | |
| 114 | + | fn status_filter(request: &quasi_router::Request) -> Result<Option<ProblemStatus>, RouteError> { | |
| 115 | + | match text(&request.carried, "status") { | |
| 114 | 116 | None => Ok(Some(ProblemStatus::Open)), | |
| 115 | 117 | Some(word) if word.eq_ignore_ascii_case("all") => Ok(None), | |
| 116 | 118 | Some(word) => word | |
| @@ -128,9 +130,9 @@ | |||
| 128 | 130 | /// Carry the current filters on an action, so every control keeps the view it | |
| 129 | 131 | /// was pressed in. The same job `in_month` does on the monthly review. | |
| 130 | 132 | fn filtered(mut action: Action, status: Option<ProblemStatus>, source: Option<&str>) -> Action { | |
| 131 | - | action = action.with("status", status_word(status)); | |
| 133 | + | action = action.carrying("status", status_word(status)); | |
| 132 | 134 | if let Some(source) = source { | |
| 133 | - | action = action.with("source", source); | |
| 135 | + | action = action.carrying("source", source); | |
| 134 | 136 | } | |
| 135 | 137 | action | |
| 136 | 138 | } | |
| @@ -150,8 +152,9 @@ | |||
| 150 | 152 | } | |
| 151 | 153 | ||
| 152 | 154 | /// Parse the path id, or answer 404. | |
| 153 | - | fn problem_id(params: &quasi_router::Params) -> Result<ProblemId, RouteError> { | |
| 154 | - | let raw = params | |
| 155 | + | fn problem_id(request: &quasi_router::Request) -> Result<ProblemId, RouteError> { | |
| 156 | + | let raw = request | |
| 157 | + | .captures | |
| 155 | 158 | .get("id") | |
| 156 | 159 | .ok_or_else(|| RouteError::not_found("no id"))?; | |
| 157 | 160 | uuid::Uuid::parse_str(raw) | |
| @@ -289,7 +292,7 @@ | |||
| 289 | 292 | Act::new( | |
| 290 | 293 | "Dismiss", | |
| 291 | 294 | filtered( | |
| 292 | - | Action::post(format!("/problems/{id}/status")).with("to", "Dismissed"), | |
| 295 | + | Action::post(format!("/problems/{id}/status")).with("status", "Dismissed"), | |
| 293 | 296 | status, | |
| 294 | 297 | source, | |
| 295 | 298 | ), | |
| @@ -385,9 +388,9 @@ | |||
| 385 | 388 | } | |
| 386 | 389 | ||
| 387 | 390 | /// The whole screen. | |
| 388 | - | fn index(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 389 | - | let status = status_filter(¶ms)?; | |
| 390 | - | let source = text(¶ms, "source"); | |
| 391 | + | fn index(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 392 | + | let status = status_filter(&request)?; | |
| 393 | + | let source = text(&request.carried, "source"); | |
| 391 | 394 | ||
| 392 | 395 | let mut band = Slot::new("problems-band", RegionKind::Band).with(Node::page("Problems")); | |
| 393 | 396 | ||
| @@ -414,9 +417,9 @@ | |||
| 414 | 417 | } | |
| 415 | 418 | ||
| 416 | 419 | /// The list alone, which is what a filter chip replaces. | |
| 417 | - | fn list(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 418 | - | let status = status_filter(¶ms)?; | |
| 419 | - | let source = text(¶ms, "source"); | |
| 420 | + | fn list(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 421 | + | let status = status_filter(&request)?; | |
| 422 | + | let source = text(&request.carried, "source"); | |
| 420 | 423 | Ok(Response::fragment( | |
| 421 | 424 | "problems-list", | |
| 422 | 425 | ranked(state, status, source)?, | |
| @@ -430,11 +433,11 @@ | |||
| 430 | 433 | /// pressed under is usually `Open` and the press is what settles it. | |
| 431 | 434 | fn triaged( | |
| 432 | 435 | state: &AppState, | |
| 433 | - | params: &quasi_router::Params, | |
| 436 | + | request: &quasi_router::Request, | |
| 434 | 437 | message: &str, | |
| 435 | 438 | ) -> Result<Response, RouteError> { | |
| 436 | - | let status = status_filter(params)?; | |
| 437 | - | let source = text(params, "source"); | |
| 439 | + | let status = status_filter(request)?; | |
| 440 | + | let source = text(&request.carried, "source"); | |
| 438 | 441 | Ok( | |
| 439 | 442 | Response::fragment("problems-list", ranked(state, status, source)?) | |
| 440 | 443 | .toast(makeover_layout::Tone::Success, message), | |
| @@ -447,8 +450,8 @@ | |||
| 447 | 450 | /// good one: the description defaults to the problem's own text and the | |
| 448 | 451 | /// priority to its painhours band, and both are better than anything retyped at | |
| 449 | 452 | /// triage time. Shape the task afterwards if it needs it. | |
| 450 | - | fn promote_one(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 451 | - | let id = problem_id(¶ms)?; | |
| 453 | + | fn promote_one(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 454 | + | let id = problem_id(&request)?; | |
| 452 | 455 | let outcome = promote( | |
| 453 | 456 | state, | |
| 454 | 457 | id, | |
| @@ -461,7 +464,7 @@ | |||
| 461 | 464 | ||
| 462 | 465 | triaged( | |
| 463 | 466 | state, | |
| 464 | - | ¶ms, | |
| 467 | + | &request, | |
| 465 | 468 | if outcome.created { | |
| 466 | 469 | "Promoted to a task." | |
| 467 | 470 | } else { | |
| @@ -475,10 +478,11 @@ | |||
| 475 | 478 | /// The target is a param, never derived from what the row was drawn with. Two | |
| 476 | 479 | /// windows on the same inbox therefore cannot disagree about what "the next | |
| 477 | 480 | /// state" was. | |
| 478 | - | fn set_status(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 479 | - | let id = problem_id(¶ms)?; | |
| 480 | - | let target: ProblemStatus = params | |
| 481 | - | .get("to") | |
| 481 | + | fn set_status(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 482 | + | let id = problem_id(&request)?; | |
| 483 | + | let target: ProblemStatus = request | |
| 484 | + | .payload | |
| 485 | + | .get("status") | |
| 482 | 486 | .ok_or_else(|| RouteError::not_found("no status"))? | |
| 483 | 487 | .parse() | |
| 484 | 488 | .map_err(|_| RouteError::not_found("not a triage state"))?; | |
| @@ -493,7 +497,7 @@ | |||
| 493 | 497 | ||
| 494 | 498 | triaged( | |
| 495 | 499 | state, | |
| 496 | - | ¶ms, | |
| 500 | + | &request, | |
| 497 | 501 | match target { | |
| 498 | 502 | ProblemStatus::Open => "Back in triage.", | |
| 499 | 503 | ProblemStatus::Dismissed => "Dismissed. It stays down through the next pull.", |
| @@ -41,8 +41,8 @@ | |||
| 41 | 41 | //! under, or acting resets the view. [`filtered`] is that, applied to the | |
| 42 | 42 | //! detail address, the create form and both writes. | |
| 43 | 43 | ||
| 44 | - | // Handlers take their params by value because `quasi_router::Handler` is a | |
| 45 | - | // plain `fn(&S, Params)` pointer, so the signature is the router's and not a | |
| 44 | + | // Handlers take their request by value because `quasi_router::Handler` is a | |
| 45 | + | // plain `fn(&S, Request)` pointer, so the signature is the router's and not a | |
| 46 | 46 | // choice made here. Same allow, for the same reason, as quasi-axum's tests. | |
| 47 | 47 | #![allow(clippy::needless_pass_by_value)] | |
| 48 | 48 | ||
| @@ -249,8 +249,8 @@ | |||
| 249 | 249 | } | |
| 250 | 250 | ||
| 251 | 251 | /// Whether a param is on. Absent is off, which is what a URL without it means. | |
| 252 | - | fn flag(params: &quasi_router::Params, name: &str) -> bool { | |
| 253 | - | matches!(params.get(name), Some("1" | "true")) | |
| 252 | + | fn flag(request: &quasi_router::Request, name: &str) -> bool { | |
| 253 | + | matches!(request.carried.get(name), Some("1" | "true")) | |
| 254 | 254 | } | |
| 255 | 255 | ||
| 256 | 256 | /// The same action, carrying one flag if it is on. | |
| @@ -259,7 +259,11 @@ | |||
| 259 | 259 | /// never written. That is what keeps two addresses for the same view from | |
| 260 | 260 | /// existing. | |
| 261 | 261 | pub(super) fn filtered_by(action: Action, name: &str, on: bool) -> Action { | |
| 262 | - | if on { action.with(name, "1") } else { action } | |
| 262 | + | if on { | |
| 263 | + | action.carrying(name, "1") | |
| 264 | + | } else { | |
| 265 | + | action | |
| 266 | + | } | |
| 263 | 267 | } | |
| 264 | 268 | ||
| 265 | 269 | /// The same action, carrying the filters the screen was under. | |
| @@ -322,13 +326,13 @@ | |||
| 322 | 326 | } | |
| 323 | 327 | ||
| 324 | 328 | /// The whole screen. | |
| 325 | - | fn index(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 326 | - | Ok(screen(state, flag(¶ms, "shared"), flag(¶ms, "retired"))?.into()) | |
| 329 | + | fn index(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 330 | + | Ok(screen(state, flag(&request, "shared"), flag(&request, "retired"))?.into()) | |
| 327 | 331 | } | |
| 328 | 332 | ||
| 329 | 333 | /// The grid alone, which is what a filter toggle replaces. | |
| 330 | - | fn list(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 331 | - | let node = grid(state, flag(¶ms, "shared"), flag(¶ms, "retired"))?; | |
| 334 | + | fn list(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 335 | + | let node = grid(state, flag(&request, "shared"), flag(&request, "retired"))?; | |
| 332 | 336 | Ok(Response::fragment("projects-grid", node)) | |
| 333 | 337 | } | |
| 334 | 338 | ||
| @@ -337,9 +341,10 @@ | |||
| 337 | 341 | /// `ProjectId` has no `FromStr`, only `From<Uuid>`, so the parse is the uuid | |
| 338 | 342 | /// crate's. Not worth adding one upstream for two call sites. | |
| 339 | 343 | pub(super) fn project_id( | |
| 340 | - | params: &quasi_router::Params, | |
| 344 | + | request: &quasi_router::Request, | |
| 341 | 345 | ) -> Result<goingson_core::ProjectId, RouteError> { | |
| 342 | - | let raw = params | |
| 346 | + | let raw = request | |
| 347 | + | .captures | |
| 343 | 348 | .get("id") | |
| 344 | 349 | .ok_or_else(|| RouteError::not_found("no project id"))?; | |
| 345 | 350 | Ok(goingson_core::ProjectId::from( | |
| @@ -348,10 +353,10 @@ | |||
| 348 | 353 | } | |
| 349 | 354 | ||
| 350 | 355 | /// One project's detail pane. | |
| 351 | - | fn detail(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 352 | - | let id = project_id(¶ms)?; | |
| 353 | - | let shared_only = flag(¶ms, "shared"); | |
| 354 | - | let show_retired = flag(¶ms, "retired"); | |
| 356 | + | fn detail(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 357 | + | let id = project_id(&request)?; | |
| 358 | + | let shared_only = flag(&request, "shared"); | |
| 359 | + | let show_retired = flag(&request, "retired"); | |
| 355 | 360 | ||
| 356 | 361 | let project = state | |
| 357 | 362 | .projects | |
| @@ -491,10 +496,15 @@ | |||
| 491 | 496 | } | |
| 492 | 497 | ||
| 493 | 498 | /// The create form. | |
| 494 | - | fn new(_state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 499 | + | fn new(_state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 495 | 500 | Ok(Response::fragment( | |
| 496 | 501 | "projects-detail", | |
| 497 | - | form_pane(flag(¶ms, "shared"), flag(¶ms, "retired"), &[], None), | |
| 502 | + | form_pane( | |
| 503 | + | flag(&request, "shared"), | |
| 504 | + | flag(&request, "retired"), | |
| 505 | + | &[], | |
| 506 | + | None, | |
| 507 | + | ), | |
| 498 | 508 | )) | |
| 499 | 509 | } | |
| 500 | 510 | ||
| @@ -534,12 +544,18 @@ | |||
| 534 | 544 | } | |
| 535 | 545 | ||
| 536 | 546 | /// Create a project, or answer with the form saying why not. | |
| 537 | - | fn create(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 538 | - | let shared_only = flag(¶ms, "shared"); | |
| 539 | - | let show_retired = flag(¶ms, "retired"); | |
| 547 | + | fn create(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 548 | + | let shared_only = flag(&request, "shared"); | |
| 549 | + | let show_retired = flag(&request, "retired"); | |
| 540 | 550 | ||
| 541 | - | let name = params.get("name").unwrap_or_default().trim().to_owned(); | |
| 542 | - | let description = params | |
| 551 | + | let name = request | |
| 552 | + | .payload | |
| 553 | + | .get("name") | |
| 554 | + | .unwrap_or_default() | |
| 555 | + | .trim() | |
| 556 | + | .to_owned(); | |
| 557 | + | let description = request | |
| 558 | + | .payload | |
| 543 | 559 | .get("description") | |
| 544 | 560 | .unwrap_or_default() | |
| 545 | 561 | .trim() | |
| @@ -550,8 +566,8 @@ | |||
| 550 | 566 | // A select offers a fixed set, so an unparseable value did not come from the | |
| 551 | 567 | // form. Refused rather than defaulted: `from_str_or_default` would file a | |
| 552 | 568 | // typo as an `Other` project and say nothing. | |
| 553 | - | let project_type = parse_choice::<ProjectType>(¶ms, "project_type", &mut errors); | |
| 554 | - | let status = parse_choice::<ProjectStatus>(¶ms, "status", &mut errors) | |
| 569 | + | let project_type = parse_choice::<ProjectType>(&request.payload, "project_type", &mut errors); | |
| 570 | + | let status = parse_choice::<ProjectStatus>(&request.payload, "status", &mut errors) | |
| 555 | 571 | .filter(|status| NEW_STATUSES.contains(status)); | |
| 556 | 572 | if status.is_none() && !errors.iter().any(|(field, _)| *field == "status") { | |
| 557 | 573 | errors.push(("status", "Not a status a project starts in.".to_owned())); | |
| @@ -562,13 +578,13 @@ | |||
| 562 | 578 | let (Some(project_type), Some(status)) = (project_type, status) else { | |
| 563 | 579 | return Ok(Response::fragment( | |
| 564 | 580 | "projects-detail", | |
| 565 | - | form_pane(shared_only, show_retired, &errors, Some(¶ms)), | |
| 581 | + | form_pane(shared_only, show_retired, &errors, Some(&request.payload)), | |
| 566 | 582 | )); | |
| 567 | 583 | }; | |
| 568 | 584 | if !errors.is_empty() { | |
| 569 | 585 | return Ok(Response::fragment( | |
| 570 | 586 | "projects-detail", | |
| 571 | - | form_pane(shared_only, show_retired, &errors, Some(¶ms)), | |
| 587 | + | form_pane(shared_only, show_retired, &errors, Some(&request.payload)), | |
| 572 | 588 | )); | |
| 573 | 589 | } | |
| 574 | 590 | ||
| @@ -608,8 +624,8 @@ | |||
| 608 | 624 | /// A 404 for a project that is not there rather than a quiet success: the | |
| 609 | 625 | /// repository answers `false`, and a delete that reports done for something it | |
| 610 | 626 | /// never saw is how two panes end up disagreeing about what exists. | |
| 611 | - | fn remove(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 612 | - | let id = project_id(¶ms)?; | |
| 627 | + | fn remove(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 628 | + | let id = project_id(&request)?; | |
| 613 | 629 | let deleted = state | |
| 614 | 630 | .projects | |
| 615 | 631 | .delete(id, DESKTOP_USER_ID) | |
| @@ -617,7 +633,7 @@ | |||
| 617 | 633 | if !deleted { | |
| 618 | 634 | return Err(RouteError::not_found("no such project")); | |
| 619 | 635 | } | |
| 620 | - | wrote(state, flag(¶ms, "shared"), flag(¶ms, "retired")) | |
| 636 | + | wrote(state, flag(&request, "shared"), flag(&request, "retired")) | |
| 621 | 637 | } | |
| 622 | 638 | ||
| 623 | 639 | /// The projects screen's routes. |
| @@ -61,8 +61,8 @@ | |||
| 61 | 61 | //! no carrying: a write answers with the section it happened in, and the | |
| 62 | 62 | //! handler knows which that is from the key. | |
| 63 | 63 | ||
| 64 | - | // Handlers take their params by value because `quasi_router::Handler` is a | |
| 65 | - | // plain `fn(&S, Params)` pointer, so the signature is the router's and not a | |
| 64 | + | // Handlers take their request by value because `quasi_router::Handler` is a | |
| 65 | + | // plain `fn(&S, Request)` pointer, so the signature is the router's and not a | |
| 66 | 66 | // choice made here. Same allow, for the same reason, as quasi-axum's tests. | |
| 67 | 67 | #![allow(clippy::needless_pass_by_value)] | |
| 68 | 68 | ||
| @@ -379,13 +379,14 @@ | |||
| 379 | 379 | } | |
| 380 | 380 | ||
| 381 | 381 | /// Appearance, which is where the JS opens. | |
| 382 | - | fn index(state: &AppState, _params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 382 | + | fn index(state: &AppState, _request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 383 | 383 | Ok(screen(state, "appearance")?.into()) | |
| 384 | 384 | } | |
| 385 | 385 | ||
| 386 | 386 | /// One section. | |
| 387 | - | fn section(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 388 | - | let slug = params | |
| 387 | + | fn section(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 388 | + | let slug = request | |
| 389 | + | .captures | |
| 389 | 390 | .get("section") | |
| 390 | 391 | .ok_or_else(|| RouteError::not_found("no section"))?; | |
| 391 | 392 | Ok(screen(state, slug)?.into()) | |
| @@ -407,12 +408,14 @@ | |||
| 407 | 408 | /// this screen, so an undeclared key or a missing value is a bug in the | |
| 408 | 409 | /// description and not in what the user chose. `Field::error` is for the other | |
| 409 | 410 | /// case, which this screen does not have โ nothing here is typed. | |
| 410 | - | fn set(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 411 | - | let key = params | |
| 411 | + | fn set(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 412 | + | let key = request | |
| 413 | + | .captures | |
| 412 | 414 | .get("key") | |
| 413 | 415 | .ok_or_else(|| RouteError::not_found("no config key"))? | |
| 414 | 416 | .to_owned(); | |
| 415 | - | let value = params | |
| 417 | + | let value = request | |
| 418 | + | .payload | |
| 416 | 419 | .get(Node::SELECTED) | |
| 417 | 420 | .ok_or_else(|| RouteError::internal("the control sent no value"))?; | |
| 418 | 421 |
| @@ -29,8 +29,8 @@ | |||
| 29 | 29 | //! second arrangement this screen would have to describe before it could offer | |
| 30 | 30 | //! it. Recorded rather than faked. | |
| 31 | 31 | ||
| 32 | - | // Handlers take their params by value because `quasi_router::Handler` is a | |
| 33 | - | // plain `fn(&S, Params)` pointer, so the signature is the router's and not a | |
| 32 | + | // Handlers take their request by value because `quasi_router::Handler` is a | |
| 33 | + | // plain `fn(&S, Request)` pointer, so the signature is the router's and not a | |
| 34 | 34 | // choice made here. Same allow, for the same reason, as quasi-axum's tests. | |
| 35 | 35 | #![allow(clippy::needless_pass_by_value)] | |
| 36 | 36 | ||
| @@ -81,8 +81,9 @@ | |||
| 81 | 81 | } | |
| 82 | 82 | ||
| 83 | 83 | /// The task a route was addressed at. | |
| 84 | - | fn task_id(params: &quasi_router::Params) -> Result<TaskId, RouteError> { | |
| 85 | - | let raw = params | |
| 84 | + | fn task_id(request: &quasi_router::Request) -> Result<TaskId, RouteError> { | |
| 85 | + | let raw = request | |
| 86 | + | .captures | |
| 86 | 87 | .get("id") | |
| 87 | 88 | .ok_or_else(|| RouteError::not_found("no task id"))?; | |
| 88 | 89 | Ok(TaskId::from( | |
| @@ -520,8 +521,8 @@ | |||
| 520 | 521 | } | |
| 521 | 522 | ||
| 522 | 523 | /// The whole overview. | |
| 523 | - | fn overview(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 524 | - | Ok(screen(state, task_id(¶ms)?)?.into()) | |
| 524 | + | fn overview(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 525 | + | Ok(screen(state, task_id(&request)?)?.into()) | |
| 525 | 526 | } | |
| 526 | 527 | ||
| 527 | 528 | /// Answer a write with the screen it happened on, re-read. | |
| @@ -540,8 +541,8 @@ | |||
| 540 | 541 | /// this does not branch on recurrence. Answering with the same address then | |
| 541 | 542 | /// shows the completed instance rather than the new one, which matches what the | |
| 542 | 543 | /// JS does: it re-opens the task it was showing. | |
| 543 | - | fn complete(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 544 | - | let id = task_id(¶ms)?; | |
| 544 | + | fn complete(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 545 | + | let id = task_id(&request)?; | |
| 545 | 546 | state | |
| 546 | 547 | .tasks | |
| 547 | 548 | .complete(id, DESKTOP_USER_ID) | |
| @@ -571,8 +572,8 @@ | |||
| 571 | 572 | /// the same whether a task was deleted or the user simply navigated. The two | |
| 572 | 573 | /// were filed apart and had to land together: one enum member could not be | |
| 573 | 574 | /// both. | |
| 574 | - | fn remove(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 575 | - | let id = task_id(¶ms)?; | |
| 575 | + | fn remove(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 576 | + | let id = task_id(&request)?; | |
| 576 | 577 | let deleted = state | |
| 577 | 578 | .tasks | |
| 578 | 579 | .delete(id, DESKTOP_USER_ID) | |
| @@ -588,9 +589,9 @@ | |||
| 588 | 589 | } | |
| 589 | 590 | ||
| 590 | 591 | /// Add a subtask. | |
| 591 | - | fn add_subtask(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 592 | - | let id = task_id(¶ms)?; | |
| 593 | - | let text = params.get("text").unwrap_or_default().trim(); | |
| 592 | + | fn add_subtask(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 593 | + | let id = task_id(&request)?; | |
| 594 | + | let text = request.payload.get("text").unwrap_or_default().trim(); | |
| 594 | 595 | // An empty add is the JS's early return, not an error: the user pressed the | |
| 595 | 596 | // button with nothing typed and the screen should simply not change. | |
| 596 | 597 | if !text.is_empty() { | |
| @@ -604,9 +605,13 @@ | |||
| 604 | 605 | } | |
| 605 | 606 | ||
| 606 | 607 | /// Tick or untick a subtask. | |
| 607 | - | fn toggle_subtask(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 608 | - | let id = task_id(¶ms)?; | |
| 609 | - | let raw = params | |
| 608 | + | fn toggle_subtask( | |
| 609 | + | state: &AppState, | |
| 610 | + | request: quasi_router::Request, | |
| 611 | + | ) -> Result<Response, RouteError> { | |
| 612 | + | let id = task_id(&request)?; | |
| 613 | + | let raw = request | |
| 614 | + | .captures | |
| 610 | 615 | .get("sub") | |
| 611 | 616 | .ok_or_else(|| RouteError::not_found("no subtask id"))?; | |
| 612 | 617 | let sub = goingson_core::SubtaskId::from( | |
| @@ -621,9 +626,9 @@ | |||
| 621 | 626 | } | |
| 622 | 627 | ||
| 623 | 628 | /// Add a note. | |
| 624 | - | fn add_note(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 625 | - | let id = task_id(¶ms)?; | |
| 626 | - | let note = params.get("note").unwrap_or_default().trim(); | |
| 629 | + | fn add_note(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 630 | + | let id = task_id(&request)?; | |
| 631 | + | let note = request.payload.get("note").unwrap_or_default().trim(); | |
| 627 | 632 | if !note.is_empty() { | |
| 628 | 633 | state | |
| 629 | 634 | .tasks |
| @@ -41,8 +41,8 @@ | |||
| 41 | 41 | //! moves the user to the current week and writes there. [`in_week`] is that, | |
| 42 | 42 | //! applied to all five routes and to both arrows. | |
| 43 | 43 | ||
| 44 | - | // Handlers take their params by value because `quasi_router::Handler` is a | |
| 45 | - | // plain `fn(&S, Params)` pointer, so the signature is the router's and not a | |
| 44 | + | // Handlers take their request by value because `quasi_router::Handler` is a | |
| 45 | + | // plain `fn(&S, Request)` pointer, so the signature is the router's and not a | |
| 46 | 46 | // choice made here. Same allow, for the same reason, as quasi-axum's tests. | |
| 47 | 47 | #![allow(clippy::needless_pass_by_value)] | |
| 48 | 48 | ||
| @@ -90,8 +90,9 @@ | |||
| 90 | 90 | /// a bad value is a client bug worth reporting, and here it is a hand-typed | |
| 91 | 91 | /// address, where landing on this week is the more useful answer than an error | |
| 92 | 92 | /// page. | |
| 93 | - | fn week_of(params: &quasi_router::Params) -> NaiveDate { | |
| 94 | - | params | |
| 93 | + | fn week_of(request: &quasi_router::Request) -> NaiveDate { | |
| 94 | + | request | |
| 95 | + | .carried | |
| 95 | 96 | .get("week") | |
| 96 | 97 | .and_then(weekly_review::parse_week_start) | |
| 97 | 98 | .unwrap_or_else(weekly_review::current_week_start) | |
| @@ -99,7 +100,7 @@ | |||
| 99 | 100 | ||
| 100 | 101 | /// The same action, still pointed at the week it was offered under. | |
| 101 | 102 | fn in_week(action: Action, week: NaiveDate) -> Action { | |
| 102 | - | action.with("week", week.to_string()) | |
| 103 | + | action.carrying("week", week.to_string()) | |
| 103 | 104 | } | |
| 104 | 105 | ||
| 105 | 106 | /// Read the week. | |
| @@ -597,8 +598,8 @@ | |||
| 597 | 598 | } | |
| 598 | 599 | ||
| 599 | 600 | /// The whole review. | |
| 600 | - | fn review(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 601 | - | Ok(screen(state, week_of(¶ms))?.into()) | |
| 601 | + | fn review(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 602 | + | Ok(screen(state, week_of(&request))?.into()) | |
| 602 | 603 | } | |
| 603 | 604 | ||
| 604 | 605 | /// Put a task in the week's focus, or take it out. | |
| @@ -614,29 +615,30 @@ | |||
| 614 | 615 | /// JS has the same shape and the same hole; naming it here is what the port is | |
| 615 | 616 | /// for. Filed as a finding on the goingson task rather than on quasicoherent: | |
| 616 | 617 | /// nothing about the description layer is involved. | |
| 617 | - | fn set_focus(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 618 | - | let raw = params | |
| 618 | + | fn set_focus(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 619 | + | let raw = request | |
| 620 | + | .captures | |
| 619 | 621 | .get("id") | |
| 620 | 622 | .ok_or_else(|| RouteError::not_found("no task id"))?; | |
| 621 | 623 | let id = goingson_core::TaskId::from( | |
| 622 | 624 | uuid::Uuid::parse_str(raw).map_err(|_| RouteError::not_found("not a task id"))?, | |
| 623 | 625 | ); | |
| 624 | - | let on = params.get("focus") == Some("true"); | |
| 626 | + | let on = request.payload.get("focus") == Some("true"); | |
| 625 | 627 | state | |
| 626 | 628 | .tasks | |
| 627 | 629 | .set_focus(id, DESKTOP_USER_ID, on) | |
| 628 | 630 | .map_err(|error| RouteError::internal(error.to_string()))? | |
| 629 | 631 | .ok_or_else(|| RouteError::not_found("no such task"))?; | |
| 630 | - | wrote(state, week_of(¶ms)) | |
| 632 | + | wrote(state, week_of(&request)) | |
| 631 | 633 | } | |
| 632 | 634 | ||
| 633 | 635 | /// Take everything out of the week's focus. | |
| 634 | - | fn clear_focus(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 636 | + | fn clear_focus(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 635 | 637 | state | |
| 636 | 638 | .tasks | |
| 637 | 639 | .clear_all_focus(DESKTOP_USER_ID) | |
| 638 | 640 | .map_err(|error| RouteError::internal(error.to_string()))?; | |
| 639 | - | wrote(state, week_of(¶ms)) | |
| 641 | + | wrote(state, week_of(&request)) | |
| 640 | 642 | } | |
| 641 | 643 | ||
| 642 | 644 | /// Mark a weekday off, or on again. | |
| @@ -645,9 +647,13 @@ | |||
| 645 | 647 | /// write. That is the one place on this screen where two windows on the same | |
| 646 | 648 | /// week can lose an edit, and it is the storage's shape rather than the | |
| 647 | 649 | /// description's: `set_vacation_days` has no single-day form to call. | |
| 648 | - | fn toggle_vacation(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 649 | - | let week = week_of(¶ms); | |
| 650 | - | let day: u8 = params | |
| 650 | + | fn toggle_vacation( | |
| 651 | + | state: &AppState, | |
| 652 | + | request: quasi_router::Request, | |
| 653 | + | ) -> Result<Response, RouteError> { | |
| 654 | + | let week = week_of(&request); | |
| 655 | + | let day: u8 = request | |
| 656 | + | .captures | |
| 651 | 657 | .get("day") | |
| 652 | 658 | .and_then(|raw| raw.parse().ok()) | |
| 653 | 659 | .filter(|day| usize::from(*day) < WEEKDAYS.len()) | |
| @@ -676,10 +682,10 @@ | |||
| 676 | 682 | /// is what the JS's two `if` guards do. Both empty leaves the notes empty, and | |
| 677 | 683 | /// the review is still marked reviewed: the completion is the act, and the | |
| 678 | 684 | /// writing is optional. | |
| 679 | - | fn complete(state: &AppState, params: quasi_router::Params) -> Result<Response, RouteError> { | |
| 680 | - | let week = week_of(¶ms); | |
| 681 | - | let went_well = params.get("went-well").unwrap_or_default().trim(); | |
| 682 | - | let improve = params.get("improve").unwrap_or_default().trim(); | |
| 685 | + | fn complete(state: &AppState, request: quasi_router::Request) -> Result<Response, RouteError> { | |
| 686 | + | let week = week_of(&request); | |
| 687 | + | let went_well = request.payload.get("went-well").unwrap_or_default().trim(); | |
| 688 | + | let improve = request.payload.get("improve").unwrap_or_default().trim(); | |
| 683 | 689 | ||
| 684 | 690 | let mut notes = String::new(); | |
| 685 | 691 | if !went_well.is_empty() { |
| @@ -10,7 +10,7 @@ | |||
| 10 | 10 | use goingson_core::{NewContact, NewContactEmail, NewSocialHandle}; | |
| 11 | 11 | use quasi_http::Render as _; | |
| 12 | 12 | use quasi_router::Outcome; | |
| 13 | - | use quasi_router::{Method, Params, Response}; | |
| 13 | + | use quasi_router::{Params, Request, Response}; | |
| 14 | 14 | ||
| 15 | 15 | use super::super::router; | |
| 16 | 16 | use crate::state::{AppState, DESKTOP_USER_ID}; | |
| @@ -62,13 +62,13 @@ | |||
| 62 | 62 | ||
| 63 | 63 | fn get(state: &AppState, path: &str, params: Params) -> Response { | |
| 64 | 64 | router() | |
| 65 | - | .handle(state, Method::Get, path, params) | |
| 65 | + | .handle(state, Request::get(path).carrying(params)) | |
| 66 | 66 | .expect("the route answers") | |
| 67 | 67 | } | |
| 68 | 68 | ||
| 69 | 69 | fn post(state: &AppState, path: &str) -> Response { | |
| 70 | 70 | router() | |
| 71 | - | .handle(state, Method::Post, path, Params::new()) | |
| 71 | + | .handle(state, Request::post(path)) | |
| 72 | 72 | .expect("the route answers") | |
| 73 | 73 | } | |
| 74 | 74 | ||
| @@ -222,7 +222,7 @@ | |||
| 222 | 222 | // The tag is a token, in the token strip, and clicking it filters the grid. | |
| 223 | 223 | assert!(html.contains("class=\"row-tokens\"")); | |
| 224 | 224 | assert!(html.contains("friend")); | |
| 225 | - | assert!(html.contains("hx-vals=\"{"tag":"friend"}\"")); | |
| 225 | + | assert!(html.contains("hx-get=\"/contacts/list?tag=friend\"")); | |
| 226 | 226 | // And no longer joined into one string. | |
| 227 | 227 | assert!(!html.contains("ada@example.com ยท friend")); | |
| 228 | 228 | } | |
| @@ -368,9 +368,7 @@ | |||
| 368 | 368 | let error = router() | |
| 369 | 369 | .handle( | |
| 370 | 370 | &state, | |
| 371 | - | Method::Get, | |
| 372 | - | &format!("/contacts/{}", uuid::Uuid::nil()), | |
| 373 | - | Params::new(), | |
| 371 | + | Request::get(format!("/contacts/{}", uuid::Uuid::nil())), | |
| 374 | 372 | ) | |
| 375 | 373 | .expect_err("no such contact"); | |
| 376 | 374 | assert_eq!(error.class.http_status(), 404); | |
| @@ -380,7 +378,7 @@ | |||
| 380 | 378 | async fn an_id_that_is_not_a_uuid_is_a_not_found_rather_than_a_panic() { | |
| 381 | 379 | let state = state().await; | |
| 382 | 380 | let error = router() | |
| 383 | - | .handle(&state, Method::Get, "/contacts/nonsense", Params::new()) | |
| 381 | + | .handle(&state, Request::get("/contacts/nonsense")) | |
| 384 | 382 | .expect_err("not an id"); | |
| 385 | 383 | assert_eq!(error.class.http_status(), 404); | |
| 386 | 384 | } |