tablo_core/resource/def/header_actions.rs
1//! The header actions a [`ResourceDef`] declares, each with the badge its button may show.
2
3use toasty::stmt::Expr;
4
5use crate::{HeaderAction, Resource, ResourceDef};
6
7impl<R: Resource> ResourceDef<R> {
8 /// Adds the [`HeaderAction`] `A`, which acts on no record, to the list page's header, after
9 /// the ones already declared.
10 ///
11 /// The policy decides who may run it, through [`Ability::ViewAny`](crate::Ability::ViewAny)
12 /// and [`Ability::RunHeader`](crate::Ability::RunHeader) with its name, which no other action
13 /// of the resource may share. An `A::NAME` that is not a single URL path segment does not
14 /// compile.
15 ///
16 /// ```rust,compile_fail,E0080
17 /// # use tablo_core::{HeaderAction, NoForm, Resource, ResourceDef};
18 /// # use topcoat::{Result, context::Cx};
19 /// # #[derive(Debug, Clone, toasty::Model, tablo_core::ModelColumns)]
20 /// # struct Post { #[key] #[auto] id: uuid::Uuid, title: String }
21 /// # struct PostResource;
22 /// # impl Resource for PostResource {
23 /// # type Model = Post;
24 /// # type Form = NoForm<Post>;
25 /// # }
26 /// struct RecountTags;
27 ///
28 /// impl HeaderAction for RecountTags {
29 /// type Input = ();
30 /// const NAME: &'static str = "recount/tags";
31 ///
32 /// async fn run(_: &Cx, _: (), _: &mut dyn toasty::Executor) -> Result<tablo_core::Outcome, tablo_core::WriteError<std::convert::Infallible>> {
33 /// Ok(tablo_core::Outcome::done())
34 /// }
35 /// }
36 ///
37 /// let _def = ResourceDef::<PostResource>::new().header_action::<RecountTags>();
38 /// ```
39 #[must_use]
40 pub fn header_action<A: HeaderAction>(mut self) -> Self {
41 self.header_actions = self.header_actions.add::<A>();
42 self
43 }
44
45 /// Declares the header action `A`, as [`header_action`](Self::header_action) does, and badges
46 /// its button with the number of rows of [`scoped_query`](crate::scoped_query) that `filter`
47 /// matches, as [`navigation_badge`](Self::navigation_badge) badges the sidebar. The count runs
48 /// each time the list renders, and a zero shows no badge.
49 ///
50 /// ```rust
51 /// # use tablo_core::{HeaderAction, NoForm, Resource, ResourceDef};
52 /// # use topcoat::{Result, context::Cx};
53 /// # #[derive(Debug, Clone, toasty::Model, tablo_core::ModelColumns)]
54 /// # struct Post { #[key] #[auto] id: uuid::Uuid, title: String, draft: bool }
55 /// # struct PostResource;
56 /// # impl Resource for PostResource {
57 /// # type Model = Post;
58 /// # type Form = NoForm<Post>;
59 /// # }
60 /// # struct PublishDrafts;
61 /// # impl HeaderAction for PublishDrafts {
62 /// # type Input = ();
63 /// # const NAME: &'static str = "publish-drafts";
64 /// # async fn run(_: &Cx, _: (), _: &mut dyn toasty::Executor) -> Result<tablo_core::Outcome, tablo_core::WriteError<std::convert::Infallible>> {
65 /// # Ok(tablo_core::Outcome::done())
66 /// # }
67 /// # }
68 /// // "Publish drafts 3" while three drafts wait.
69 /// let _def = ResourceDef::<PostResource>::new()
70 /// .header_action_with_badge::<PublishDrafts>(Post::fields().draft().eq(true));
71 /// ```
72 #[must_use]
73 pub fn header_action_with_badge<A: HeaderAction>(mut self, filter: Expr<bool>) -> Self {
74 self.header_badges.push((A::NAME, filter));
75 self.header_action::<A>()
76 }
77}