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, list widgets, form, detail page, record pages, 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 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<tablo_core::Outcome, tablo_core::WriteError<std::convert::Infallible>> {
        Ok(tablo_core::Outcome::done())
    }
}

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

pub fn header_action_with_badge<A: HeaderAction>( self, filter: Expr<bool>, ) -> Self

Declares the header action A, as header_action does, and badges its button with the number of rows of scoped_query that filter matches, as navigation_badge badges the sidebar. The count runs each time the list renders, and a zero shows no badge.

// "Publish drafts 3" while three drafts wait.
let _def = ResourceDef::<PostResource>::new()
    .header_action_with_badge::<PublishDrafts>(Post::fields().draft().eq(true));
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. Renaming the type then moves the resource’s URLs and drops the column choices its users saved, which are stored under the slug; set it to keep both.

Source

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

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

Defaults to the message resource-{model} of the panel’s catalogs, else the model’s type name in sentence case: BlogPost reads resource-blog-post, else Blog post, and APIKey reads resource-api-key, else API key.

Source

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

The list title and sidebar label.

Defaults to the plural of the label: a plain label pluralized in English, Category reading Categories and Person People, and a message’s attribute plural. The default label’s message is resource-{model}, so its plural is resource-{model}.plural, else the derived English noun pluralized.

resource-blog-post = Article
    .plural = Articles
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<Text>) -> 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, navigation_group and navigation_badge still apply to it.

Source

pub fn navigation_badge(self, filter: Expr<bool>) -> Self

Badge the sidebar entry with the number of rows of scoped_query that filter matches, so a tenant counts its own rows. The panel counts them each time its sidebar renders, on a page load and on each runtime rerun such as a sort or a page step, one query per badge run together, and shows no badge for zero. It replaces the badge of a navigation item.

fn declare() -> tablo_core::ResourceDef<Self> {
    // "Posts 3" while three drafts wait.
    tablo_core::ResourceDef::new().navigation_badge(Post::fields().draft().eq(true))
}
Source

pub fn without_navigation(self) -> Self

Keeps the resource out of the sidebar and the root’s redirect: hiding every resource needs a Panel::home (NoRoot). Its pages stay mounted at their URLs, and its policy still decides who may open them.

Source

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

Authorizes every handler; defaults to Deny. Row scoping belongs in 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| {
    // No update, delete, restore or record action on a locked post.
    def.policy_fn(|_cx, ability| ability.written_record().is_none_or(|post| !post.locked))
});

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 field_access<T>( self, path: impl Into<Path<R::Model, T>>, access: FieldAccess, ) -> Self

Restricts who views and who changes the field path names, as in .field_access(lens!(Employee.salary), FieldAccess::new().view(is_hr)): see FieldAccess.

The rule restricts the field of its model within the panel: every resource the panel mounts over the model drops what reads a field the request may not view, the table’s and the detail page’s columns, the filters and the grouping, the export’s columns, the searches of the list and of the relationship choices over those resources, and the relation columns showing it. Each form renders nothing for the field, or a disabled control for one it may view but not change, and refuses a submission posting either with 400. A create the field needs a value for, with no default, is refused.

Hiding a belongs_to relation hides its foreign keys, and hiding a key hides the relation. Mounting the panel refuses a path that is not one field of the model, a second declaration over one field, a primary key with a view rule, a record title or a form condition reading a restricted field, and a column or a filter that does not say which fields it reads, and the declarations FieldAccessFault lists that would show a restricted record no panel can mask.

A validation error on a field the request may not view shows as an alert that a hidden field refuses the save, without its message.

Source

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

How rows belong to a tenant. A resource that declares none is served to every tenant; Tenancy::none says when mounting the panel refuses it instead.

Source

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

The list table; defaults to the model’s (ModelColumns::table). Its columns are also the detail page’s, unless the resource declares one.

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 table’s columns, else to the model’s (ModelColumns::detail). The page adds a View action to each row; without_page(ResourcePage::View) turns it off, and mounting refuses a detail listing no column on a resource that serves it.

The page, at {list}/{key}, carries in its header the record’s title, a link back to the list, the actions placed on Places::DETAIL and Delete as the policy allows them on the record, and Edit when the resource has a form and the policy allows Update. The related tables follow its columns.

The record loads through the resource’s tenant-scoped query with every relation the columns declare, once each, wherever its block sits; no other relation loads. An unknown key and a key outside the request’s tenant are the same 404, and a record the policy refuses View is a 403. The form does not shape the page: a resource that arranges its form declares a detail to show the same arrangement.

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: 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 list_widget<W: Widget>(self) -> Self

Adds the Widget W to the grid above the list page’s table, after the ones already declared: a banner, a row of Stats, a chart.

The grid renders with the list, behind its sign-in and Ability::ViewAny, and leaves out each widget whose WidgetDef::access refuses the request. Its widgets render before the rows load, and an error from one fails the list’s response. A sort, filter or page change reruns the list and its widgets with it. Mounting refuses two widgets sharing a name.

Source

pub fn list_widget_columns(self, columns: u8) -> Self

How many columns the list page’s widget grid has on a wide screen, from 1 to 4; defaults to 2, as a page’s does.

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.

Source

pub fn importable(self) -> Self

Serves a CSV import beside the create page: the list links Import CSV to a page that takes a file whose header names the form’s keys, and creates one record per row through the create form, all in one transaction or none.

Each row is checked as a create submission is, under the same policy, field access and tenant. Mounting refuses an importable resource that serves no create page.

  • The first row names the columns, each by its form key or its field’s label in any case: the controls the request may change, but file fields, repeaters and controls disabled on the create page. A header naming any other column, or a label two fields carry, refuses the file. Download template at {list}/-/import/template returns the header row.
  • Each cell posts what the create form’s control would: an option’s value, true or false for a checkbox, a date and time in the request’s time zone. A blank cell posts a blank value, and a column the header leaves out takes the field’s default. A multiple choice takes one column per value, its key repeated; no other column may repeat.
  • The file is UTF-8 text with commas or semicolons between cells, of at most Limits::import_rows rows, 500 cells a row and Limits::body_bytes. A row whose every cell is blank is skipped.
  • Resource::before_commit receives each row’s Change::Create inside the transaction. A row Resource::create_record refuses rolls back to a savepoint taken before it, so the rows after it are checked without its writes. When any row is refused nothing is written, and the page lists each refusal by row number, as a spreadsheet numbers it, and column. Once every row passes, the import lands on the list and Resource::after_commit receives each row’s change, in file order.

The transaction lasts the whole file: on SQLite it holds the write lock that long. The import reads what the form posts, not what the export writes: an exported $1,234.50, Yes or option label does not import back.

Source§

impl<R: Resource> ResourceDef<R>

Declares how the resource’s records show in the panel’s global search: the details under each result’s title. Without it a result shows its title alone.

The search box in the panel’s topbar searches every resource the panel mounts unless the resource opts out with without_global_search. It matches a record when each word of the term is in one of the table’s searchable columns or in the record_title column, which also titles each result. It lists only the records the resource’s list would, and none to a request the list refuses: one the policy refuses ViewAny, or one without a tenant on a tenant-scoped resource. Each result opens the record’s detail page, or its edit page on a resource with an empty detail; a record the policy refuses View, or Update for the edit page, is left out.

See Panel::without_global_search for the panel’s side.

Leaves the resource’s records out of the panel’s global search.

Source§

impl<R: Resource> ResourceDef<R>

Source

pub fn without_page(self, page: ResourcePage) -> Self

Turns page off.

use tablo_core::{ResourceDef, ResourcePage};

ResourceDef::<PostResource>::new().without_page(ResourcePage::View);
Source§

impl<R: Resource> ResourceDef<R>

Source

pub fn query<F>(self, query: F) -> Self
where F: Fn(&Cx, Query<List<R::Model>>) -> Query<List<R::Model>> + Send + Sync + 'static,

Narrows the rows the resource serves within the panel: its own row scoping, such as row visibility. Every loader reads through it: the list and its counts, the export, the detail, edit and record pages, every write, relationship options and relation tables, the global search, widgets and the sidebar badge. A row it leaves out does not exist for the request: its pages answer 404.

use tablo_core::Panel;

// Each author lists and opens their own posts only.
Panel::new("portal").resource_with::<PostResource>(|def| {
    def.query(|cx, query| query.filter(Post::fields().author().eq(current_user(cx))))
});

Each call receives the query the calls before it return, so a panel’s resource_with narrows what the resource’s own declare narrows.

Tenancy and soft deletes are not this method’s job. The framework ANDs the tenancy filter and leaves out the trashed records after every narrowing, so none can drop them. Code outside the framework’s loaders starts from scoped_query, which reads through the same narrowings.

Relations are not its job either. The list, the export and the detail page load the relations their columns declare (Column::includes): a RelationColumn declares its own. Include a relation here only when a closure no column covers reads it on every loader’s rows: one the policy reads, or one a table’s group_by or row key reads without a column including it.

A list and its export replace any ordering a narrowing sets with the table’s own sort: order a list with Table::default_sort.

§Keep unique constraints in step with the narrowed rows

The app-side unique pre-check probes through the same query, so a #[unique] index broader than it is invisible: the probe misses the colliding row and the user gets a 500 instead of the inline “has already been taken”. Scope the constraint to match — #[unique(tenant_id, email)] — or, when the index must stay broader, probe it in create_record and update_record and refuse with WriteError::Refused. Not checkable at declaration time, since a query’s filters are not introspectable; tablo#117 tracks the fix.

Source§

impl<R: Resource> ResourceDef<R>

Source

pub fn record_page<P: RecordPage<R>>(self) -> Self

Adds the RecordPage P after the ones already declared, served at {list}/{key}/{slug} for each record and linked from the record’s navigation.

Source§

impl<R: Resource> ResourceDef<R>

Source

pub fn soft_delete(self, lens: Lens<R::Model, Option<Timestamp>>) -> Self

Deletes by stamping lens’s column with the time instead of removing the row, as in .soft_delete(lens!(Post.deleted_at)), and keeps the stamped records out of every loader unless it asks for them.

The list hides the trashed records behind a trash control, and offers Restore, which empties the column, and Force delete, which removes the row, on each one: the policy decides who may through Ability::Restore and Ability::ForceDelete. The detail page opens a trashed record; its edit page and every other action refuse one until it is restored. scoped_query admits the live records only, and scoped_query_in the ones a Trashed scope names, each under the tenant filter.

The unique pre-check probes the trashed records too, since a unique index covers them. Mounting the panel refuses a lens that is not one field of the model, and a record form that writes the column.

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> !RefUnwindSafe for ResourceDef<R>

§

impl<R> !UnwindSafe for ResourceDef<R>

§

impl<R> Forget for ResourceDef<R>

§

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

§

impl<R> Move for ResourceDef<R>

§

impl<R> Send for ResourceDef<R>
where Arc<dyn Policy<<R as Resource>::Model>>: Send, Vec<Arc<dyn Fn(&Cx, Query<List<<R as Resource>::Model>>) -> Query<List<<R as Resource>::Model>> + Sync + Send>>: 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<GlobalSearch<<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, Vec<RecordPageEntry<R>>: Send, Actions<R>: Send, Option<SoftDelete<<R as Resource>::Model>>: Send,

§

impl<R> Sync for ResourceDef<R>
where Arc<dyn Policy<<R as Resource>::Model>>: Sync, Vec<Arc<dyn Fn(&Cx, Query<List<<R as Resource>::Model>>) -> Query<List<<R as Resource>::Model>> + Sync + Send>>: 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<GlobalSearch<<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, Vec<RecordPageEntry<R>>: Sync, Actions<R>: Sync, Option<SoftDelete<<R as Resource>::Model>>: Sync,

§

impl<R> Unpin for ResourceDef<R>
where Arc<dyn Policy<<R as Resource>::Model>>: Unpin, Vec<Arc<dyn Fn(&Cx, Query<List<<R as Resource>::Model>>) -> Query<List<<R as Resource>::Model>> + Sync + Send>>: 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<GlobalSearch<<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, Vec<RecordPageEntry<R>>: Unpin, Actions<R>: Unpin, Option<SoftDelete<<R as Resource>::Model>>: Unpin,

§

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

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> ErasedDestructor for T
where T: 'static,

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