Skip to main content

ResourceDef

Struct ResourceDef 

Source
pub struct ResourceDef<R: Resource> { /* private fields */ }
Expand description

What a Resource declares: its names, navigation, policy, tenancy, table, form, detail page, relations and actions.

Resource::declare returns one, and Panel::resource_with adjusts it for one panel. Every setting has a default, so a resource sets only what differs:

use tablo_core::{ModelColumns, ReadOnly, Resource, ResourceDef, TernaryFilter, lens};

impl Resource for PostResource {
    type Model = Post;
    type Form = PostForm;

    fn declare() -> ResourceDef<Self> {
        ResourceDef::new()
            .label("Blog post")
            .policy(ReadOnly)
            .table(Post::table().filters(TernaryFilter::new(lens!(Post.featured))))
    }
}

The panel builds the def once when it mounts, binding the paths it names to the database schema, and serves the result to every request.

Implementations§

Source§

impl<R: Resource> ResourceDef<R>

Source

pub fn new() -> Self

A def with every default: the policy denies all, rows belong to no tenant, the model’s ModelColumns give the table and the detail page, and the record form gives the form.

Source

pub fn slug(self, slug: impl Into<String>) -> Self

The URL slug for the resource’s pages: "users" mounts the list at {panel prefix}/users.

Defaults to the resource type’s name without a trailing Resource, pluralized and kebab-cased: UserResource → users, BlogPostResource → blog-posts.

Source

pub fn label(self, label: impl Into<String>) -> Self

One record’s name, the noun in the “Create {label}” and “Edit {label}” titles.

Defaults to the model’s type name in sentence case: BlogPost → Blog post, APIKey → API key.

Source

pub fn plural_label(self, label: impl Into<String>) -> Self

The list title and sidebar label.

Defaults to the pluralized label: Category → Categories, Person → People.

Source

pub fn icon(self, icon: IconData) -> Self

The icon of the resource’s sidebar entry, replacing the icon of a navigation item.

Source

pub fn navigation_order(self, order: i32) -> Self

Where the sidebar entry sorts among the panel’s entries, lowest first; defaults to 0, and entries of equal order keep their registration order. It replaces the order of a navigation item.

Source

pub fn navigation_group(self, group: impl Into<String>) -> Self

The labelled sidebar group the entry renders in, such as "Content"; defaults to none, above every group. It replaces the group of a navigation item.

Source

pub fn navigation(self, item: NavigationItem) -> Self

Replaces the sidebar entry, for one that links somewhere other than the list page: NavigationItem::at("Drafts", "/admin/posts?f.status=draft"). icon, navigation_order and navigation_group still apply to it.

Source

pub fn policy(self, policy: impl Policy<R::Model>) -> Self

Authorizes every handler; defaults to Deny. Row scoping belongs in Resource::query, not the policy.

Source

pub fn policy_fn( self, policy: impl Fn(&Cx, Ability<'_, R::Model>) -> bool + Send + Sync + 'static, ) -> Self

Authorizes every handler with a closure, as policy does, inferring the closure’s argument types from the def’s model:

use tablo_core::{Ability, Panel};

Panel::new("portal").resource_with::<PostResource>(|def| {
    def.policy_fn(|_cx, ability| match ability {
        Ability::Update(post) | Ability::Delete(post) => !post.locked,
        _ => true,
    })
});

In Resource::declare, the chain on ResourceDef::new() learns its resource from the return type, after the closure is checked, so a closure that reads the record names the ability’s type, |_cx, ability: Ability<'_, Post>|, or the chain starts from ResourceDef::<Self>::new().

policy takes a closure too, but infers neither argument type: Rust infers a closure’s signature from an Fn bound, not from Policy’s blanket impl.

Source

pub fn tenancy(self, tenancy: Tenancy<R::Model>) -> Self

How rows belong to a tenant; defaults to Tenancy::none.

Source

pub fn table(self, table: Table<R::Model>) -> Self

The list table; defaults to the model’s (ModelColumns::table).

Source

pub fn form(self, form: Schema<R::Form>) -> Self

Arranges the create and edit forms: the record form’s own controls, from its controls(), in layout blocks.

A control the schema does not place follows the ones it does, in the record form’s declaration order and outside every layout block, so the default form renders one control per field, and a form that adjusts one control places only that one:

use tablo_core::{Resource, ResourceDef, Schema};

impl Resource for UserResource {
    type Model = User;
    type Form = UserForm;

    fn declare() -> ResourceDef<Self> {
        // `email`, then `name`.
        ResourceDef::new().form(Schema::new(UserForm::controls().email.email()))
    }
}
Source

pub fn detail(self, detail: Detail<R::Model>) -> Self

The detail page: columns in layout blocks; defaults to the model’s (ModelColumns::detail), and Detail::empty turns the page off.

The page loads the relations its columns declare.

Source

pub fn record_title<T>(self, lens: Lens<R::Model, T>) -> Self
where T: FormScalar + Field<Inner = String> + Send + Sync + 'static,

Titles each record with the text column lens reads, as in .record_title(lens!(Post.title)): the detail page’s heading, each option of a relationship choice over this resource, and each cell of a RelationColumn::of it.

A record whose column is empty, and every record by default, is titled with the label and its key, as in Blog Post 3f2a…. A relationship choice over this resource also searches the column, besides the table’s searchable columns.

A title is display text, not a key: two records can share one, so it never replaces the primary key that keys the table’s rows and the action routes. A lens names one of the model’s own columns, never a relation, so the title reads on every record however it was loaded; a title built from several columns is a column the model stores.

Links a record’s public page from its detail and edit pages, for each record link returns one for; defaults to no link.

The edit page loads the record without relations, so a link that reads one renders on the detail page only when its relation is loaded there.

Source

pub fn relation(self, relation: Relation<R::Model>) -> Self

Adds a related resource, rendered as its table narrowed to the record on the detail and edit pages.

Source

pub fn action<A: Action<R>>(self) -> Self

Adds the custom Action A after the ones already declared.

The policy decides who may run it, through Ability::RunAny and Ability::Run. An A::NAME that is not a single URL path segment does not compile.

Source

pub fn header_action<A: HeaderAction>(self) -> Self

Adds the HeaderAction A, which acts on no record, to the list page’s header, after the ones already declared.

The policy decides who may run it, through Ability::ViewAny and Ability::RunHeader with its name, which no other action of the resource may share. An A::NAME that is not a single URL path segment does not compile.

ⓘ
struct RecountTags;

impl HeaderAction for RecountTags {
    type Input = ();
    const NAME: &'static str = "recount/tags";

    async fn run(_: &Cx, _: (), _: &mut dyn toasty::Executor) -> Result<()> {
        Ok(())
    }
}

let _def = ResourceDef::<PostResource>::new().header_action::<RecountTags>();
Source

pub fn create_column<T>(self, path: impl Into<Path<R::Model, T>>) -> Self

Declares a column an overriding Resource::create_record sets itself, beyond the form’s fields: .create_column(lens!(Post.slug)). Call it once per column.

Mounting the panel refuses a path that names no one field of the model, and the tenant column, which the framework stamps.

Trait Implementations§

Source§

impl<R: Resource> Debug for ResourceDef<R>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<R: Resource> Default for ResourceDef<R>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<R> !Freeze for ResourceDef<R>

§

impl<R> !RefUnwindSafe for ResourceDef<R>

§

impl<R> !UnwindSafe for ResourceDef<R>

§

impl<R> Send for ResourceDef<R>
where Arc<dyn Policy<<R as Resource>::Model>>: Send, Tenancy<<R as Resource>::Model>: Send, Option<Table<<R as Resource>::Model>>: Send, Option<Schema<<R as Resource>::Form>>: Send, Option<Detail<<R as Resource>::Model>>: Send, Option<RecordTitle<<R as Resource>::Model>>: Send, Option<Arc<dyn Fn(&Cx, &<R as Resource>::Model) -> Option<PublicLink> + Sync + Send>>: Send, Vec<Relation<<R as Resource>::Model>>: Send, Actions<R>: Send,

§

impl<R> Sync for ResourceDef<R>
where Arc<dyn Policy<<R as Resource>::Model>>: Sync, Tenancy<<R as Resource>::Model>: Sync, Option<Table<<R as Resource>::Model>>: Sync, Option<Schema<<R as Resource>::Form>>: Sync, Option<Detail<<R as Resource>::Model>>: Sync, Option<RecordTitle<<R as Resource>::Model>>: Sync, Option<Arc<dyn Fn(&Cx, &<R as Resource>::Model) -> Option<PublicLink> + Sync + Send>>: Sync, Vec<Relation<<R as Resource>::Model>>: Sync, Actions<R>: Sync,

§

impl<R> Unpin for ResourceDef<R>
where Arc<dyn Policy<<R as Resource>::Model>>: Unpin, Tenancy<<R as Resource>::Model>: Unpin, Option<Table<<R as Resource>::Model>>: Unpin, Option<Schema<<R as Resource>::Form>>: Unpin, Option<Detail<<R as Resource>::Model>>: Unpin, Option<RecordTitle<<R as Resource>::Model>>: Unpin, Option<Arc<dyn Fn(&Cx, &<R as Resource>::Model) -> Option<PublicLink> + Sync + Send>>: Unpin, Vec<Relation<<R as Resource>::Model>>: Unpin, Actions<R>: Unpin,

§

impl<R> UnsafeUnpin for ResourceDef<R>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more