Trait Resource
pub trait Resource: Sized + 'static {
type State: 'static;
type Action: Action + Serialize + DeserializeOwned + Clone + 'static;
const KIND: &'static str;
const LABEL: &'static str;
const ACTION_TYPE: &'static str = Self::KIND;
const READABLE: bool = false;
const VERSION: u32 = 1;
const EDITS: bool = false;
Show 36 methods
// Required method
fn read(doc: &AutoCommit) -> Result<Self::State, ModelError>;
// Provided methods
fn refresh(
state: &mut Self::State,
doc: &AutoCommit,
delta: &Delta,
) -> Result<(), ModelError> { ... }
fn copy_state(_state: &Self::State) -> Option<Self::State> { ... }
fn publication_cache_epoch(_state: &Self::State) -> Option<String> { ... }
fn locate(state: &Self::State, obj: &ExId) -> Option<Vec<(ExId, Prop)>> { ... }
fn load(
state: &mut Self::State,
doc: &AutoCommit,
demand: &Demand,
) -> Result<(), ModelError> { ... }
fn initialize(_doc: &mut AutoCommit) -> Result<(), ModelError> { ... }
fn coalesces(previous: &Self::Action, next: &Self::Action) -> bool { ... }
fn continuable(action: &Self::Action) -> bool { ... }
fn lands(state: &Self::State, action: &Self::Action) -> Option<Place> { ... }
fn field_of(action: &Self::Action) -> Option<&'static str> { ... }
fn least_need() -> ActionNeed { ... }
fn declarations() -> Vec<ActionDecl<Self>> { ... }
fn action_schema() -> Option<Value> { ... }
fn children(_state: &Self::State) -> Vec<Resource> { ... }
fn item_effects(_field: &str, _item: &Id) -> Vec<ActionSpec> { ... }
fn item_views(_field: &str, _item: &Id) -> Vec<ResourceViewSpec> { ... }
fn children_parts(state: &Self::State) -> ChildrenParts { ... }
fn children_offer_bound(state: &Self::State) -> Vec<&'static str> { ... }
fn child(_state: &Self::State, _id: &str) -> Option<Resource> { ... }
fn local_operations() -> Vec<ActionSpec> { ... }
fn views(_state: &Self::State) -> Vec<ResourceViewSpec> { ... }
fn search_scopes(_state: &Self::State) -> Vec<ScopeDecl> { ... }
fn search(_state: &Self::State) -> Vec<SearchEntry> { ... }
fn read_content(
state: &Self::State,
doc: &AutoCommit,
item: Option<&str>,
) -> Result<Value, String> { ... }
fn answer(
_doc: &AutoCommit,
_action: &str,
_changes: &[Patch],
) -> Option<Value> { ... }
fn shape() -> Option<Shape> { ... }
fn migrate(doc: &mut AutoCommit, from: u32) -> Result<(), ModelError> { ... }
fn surface() -> Surface { ... }
fn edit_action(edit: Edit<Self>) -> Option<Self::Action> { ... }
fn stamp(action: &mut Self::Action, now_ms: i64) { ... }
fn op_schemas() -> Vec<(String, Value)> { ... }
fn availability(state: &Self::State, action: &str) -> Availability { ... }
fn seed() -> Option<Self::Action> { ... }
fn kind_of(action: &Self::Action) -> ActionKind { ... }
fn register(registry: &mut ActionRegistry) { ... }
}Expand description
A resource type. Implement it by hand for a type with its own storage
layout and merge semantics (a timeline, a rich-text document, a photo edit
stack); #[resource] implements it for a #[derive(Model)] record.
The contract is deliberately small:
read— materialise the state views render, from the live document or from any historical version (time travel is justreadon an older document);refresh— optionally, bring a state up to date after a change by re-reading only what the change touched, so an edit costs as much as the edit rather than as much as the resource;load— optionally, read on demand the parts of a large resource that views are looking at (the pages of a long document on screen), so opening or refreshing it never reads all of it;Action— the replayable mutation vocabulary: a serializable value whoseapplyis a pure function of the document;declarations— which mutations are offered by name to the palette, agents and scripts, with typed parameters.
Everything else has a default.
Required Associated Constants§
Provided Associated Constants§
const ACTION_TYPE: &'static str = Self::KIND
const ACTION_TYPE: &'static str = Self::KIND
The registry type id every action of this resource is replayed under.
const READABLE: bool = false
const READABLE: bool = false
Whether read_content answers. A resource that
does is published with the framework’s read operation, so a script,
an agent or an assistive tool reads what it holds as typed data through
the same door, permissions and audit as every other action — never
from a screenshot.
const VERSION: u32 = 1
const VERSION: u32 = 1
The storage version documents of this resource are written at;
#[resource(version = N)] raises it with each incompatible change
and Self::migrate brings older documents up to it.
const EDITS: bool = false
const EDITS: bool = false
Whether the resource’s actions include field edits (Edit),
published as FIELD_EDIT. #[resource] records have them.
Required Associated Types§
type State: 'static
type State: 'static
What views render. For a record resource this is the record itself.
Required Methods§
fn read(doc: &AutoCommit) -> Result<Self::State, ModelError>
fn read(doc: &AutoCommit) -> Result<Self::State, ModelError>
Read the state from a document.
§Errors
Returns [ModelError] when the document does not hold this resource.
Provided Methods§
fn refresh(
state: &mut Self::State,
doc: &AutoCommit,
delta: &Delta,
) -> Result<(), ModelError>
fn refresh( state: &mut Self::State, doc: &AutoCommit, delta: &Delta, ) -> Result<(), ModelError>
Bring state, read earlier from this document, up to date after the
changes in delta. Re-read only what they touched: this runs on every
keystroke, local or a collaborator’s. The default reads everything
again, which is right for small resources.
§Errors
Returns [ModelError] when the document does not hold this resource.
fn copy_state(_state: &Self::State) -> Option<Self::State>
fn copy_state(_state: &Self::State) -> Option<Self::State>
A copy of state for refresh to bring up to date
while the state the reader holds is shared (a view still holds the
last one): refreshing a copy reads only what a change touched, where
without one the state is read whole. #[resource] copies a Clone
record; the default has none.
fn publication_cache_epoch(_state: &Self::State) -> Option<String>
fn publication_cache_epoch(_state: &Self::State) -> Option<String>
Opt in to reusing the document-owned publication between changes.
Return an epoch for every non-document input used by declarations,
children, views or local operations (for example the local date of a
daily-note action). The framework also invalidates on state and locale
revisions; surface bindings and effect availability remain live.
None preserves publication on every request for dynamic resources.
fn locate(state: &Self::State, obj: &ExId) -> Option<Vec<(ExId, Prop)>>
fn locate(state: &Self::State, obj: &ExId) -> Option<Vec<(ExId, Prop)>>
Where obj sits in the document, when the state already knows: the
path from the root to it. A change to an object the state has read
(the paragraph being typed in) is then placed without asking the
document, which for a large document is the costly part of reading a
change. The default knows nothing, and the document is asked.
fn load(
state: &mut Self::State,
doc: &AutoCommit,
demand: &Demand,
) -> Result<(), ModelError>
fn load( state: &mut Self::State, doc: &AutoCommit, demand: &Demand, ) -> Result<(), ModelError>
Make sure the parts of the state views asked for are read, and drop
parts nobody asks for any more. For a resource too large to read
whole, read reads a spine (the order of a document’s
paragraphs, the rows of a sheet) and this reads the parts on screen.
Called after every read and refresh and whenever the demand changes,
so it must be cheap when everything demanded is already there. The
default reads nothing: the state is read whole.
§Errors
Returns [ModelError] when the document does not hold this resource.
fn initialize(_doc: &mut AutoCommit) -> Result<(), ModelError>
fn initialize(_doc: &mut AutoCommit) -> Result<(), ModelError>
Write the initial content of a brand-new record: its genesis.
The genesis is made under an actor derived from the record’s identity,
at time zero, so it is the same change byte for byte on every device:
two people who each create the same record before they sync (a room’s
default record, the record of one file) converge on one record rather
than two competing roots. It must therefore depend on nothing but the
resource type — not on who creates it, the device, the clock or a
file. Content that does depend on those (the creator as owner, a
first-run import of settings that already exist) is the app’s seed,
applied as actions right after the genesis (ResourceApp::seed in
tilo-app).
§Errors
Returns the document’s write error.
fn coalesces(previous: &Self::Action, next: &Self::Action) -> bool
fn coalesces(previous: &Self::Action, next: &Self::Action) -> bool
Whether next, applied right after previous by the same person,
continues it as one undo step — the characters of a word typed one
keystroke at a time, the values a slider passes through in one drag.
The default keeps every action its own step.
Immediate edits (Dispatcher::apply_now in tilo-app) and native
keyboard/IME edits from action-bound text fields may continue one
another. Programmatic/agent invocations remain separate steps.
A surface ends an immediate pointer gesture
(Dispatcher::settle) when the pointer is let go, so the next drag
of the same slider is a step of its own.
fn continuable(action: &Self::Action) -> bool
fn continuable(action: &Self::Action) -> bool
Whether a later action may continue action (coalesces),
so its undo step stays open for it. One that never continues — a
paragraph split after a typed word, a block moved — is a step of its
own, recorded at once. The default keeps every step open for what may
follow.
fn lands(state: &Self::State, action: &Self::Action) -> Option<Place>
fn lands(state: &Self::State, action: &Self::Action) -> Option<Place>
Where action, just applied, leaves the participant who took it, read
off the state after it: the paragraph an append wrote and the caret
at its end. Every collaborator’s view draws that participant there,
so one with no pointer or caret of its own (an agent) shows where it
works. The default places it nowhere in particular.
fn field_of(action: &Self::Action) -> Option<&'static str>
fn field_of(action: &Self::Action) -> Option<&'static str>
The one field of the record action writes, when it writes only one:
a reply or a note writes the comments, so a grant on that field
(what someone who may only comment holds) reaches it. None by
default. A declared action that builds it names the same field with
ActionDecl::below_field.
fn least_need() -> ActionNeed
fn least_need() -> ActionNeed
The least any of the resource’s actions is (kind_of,
field_of): what the generic APPLY and group
actions are offered under, each invocation then decided by what its
value is. An undoable change of the record by default; an undoable
change of its comments for a resource someone who may only comment
can still reply in.
fn declarations() -> Vec<ActionDecl<Self>>
fn declarations() -> Vec<ActionDecl<Self>>
Mutations offered by name. The generic APPLY action is always
offered in addition, so any typed value can reach the host.
fn action_schema() -> Option<Value>
fn action_schema() -> Option<Value>
The JSON Schema of Action’s serde form, published as
the schema of the generic APPLY operation’s argument, so agents
and scripts can read the edit vocabulary from the descriptor and
binding refuses a malformed edit by name. A variant that only
restates a declaration is published as a reference to it. Return Some(<Self::Action as JsonSchema>::json_schema()) for an action type deriving
JsonSchema; #[resource] describes its
operations from their declarations. The default publishes no schema.
fn children(_state: &Self::State) -> Vec<Resource>
fn children(_state: &Self::State) -> Vec<Resource>
Resources nested inside this one (a track’s clips, a list’s items, a page’s rich-text body), each exporting actions with their arguments already bound. Published beneath the root resource for the palette, agents and scripts — the same door as the root, not a second tree.
fn item_effects(_field: &str, _item: &Id) -> Vec<ActionSpec>
fn item_effects(_field: &str, _item: &Id) -> Vec<ActionSpec>
The effects offered on the item item of the collection field,
bound to it (#[resource(effects = E)] with #[effect(item = field)]
variants): published on the item resource beside its item verbs.
fn item_views(_field: &str, _item: &Id) -> Vec<ResourceViewSpec>
fn item_views(_field: &str, _item: &Id) -> Vec<ResourceViewSpec>
The views the item item of the collection field offers
(#[resource(item_views = path)]): published on the item resource,
so a link to the item opens it in one, each opened by one of the
item’s own actions (item_effects).
fn children_parts(state: &Self::State) -> ChildrenParts
fn children_parts(state: &Self::State) -> ChildrenParts
children split into the children that are not
items and the items of the record’s collections, so a publisher can
keep the item children it built for an earlier version and build
only those whose label changed. others followed by
item_children of the rest must be
exactly children. The default puts every child in others;
#[resource] splits off its item collections.
fn children_offer_bound(state: &Self::State) -> Vec<&'static str>
fn children_offer_bound(state: &Self::State) -> Vec<&'static str>
The ids of the declarations that
children offer on some child with arguments
already bound (an item action on each item of a list), which the
resource then publishes for scripts rather than the palette.
publish_head asks this to publish the resource’s own actions
without building its children, so a resource with many children
answers from what it knows about them: #[resource] from how many
items each collection holds. The default builds the children and
looks.
fn child(_state: &Self::State, _id: &str) -> Option<Resource>
fn child(_state: &Self::State, _id: &str) -> Option<Resource>
The nested resource id, when children leaves it
out. A resource with more children than a published tree should carry
(a book of contacts, a long history) publishes a bounded page from
children and resolves the rest here, so an action addressed to any
of them still reaches it through the same door. The default resolves
nothing beyond children.
fn local_operations() -> Vec<ActionSpec>
fn local_operations() -> Vec<ActionSpec>
View and effect operations on this resource: local presentation and
jobs that have no document delta (show a week, start a sync drain,
open a system file picker). They are published on the same tree as
declarations. A document mutation does not
belong here — it is a declarations entry or
APPLY. publish drops a Document spec so it cannot become a
second write path.
fn views(_state: &Self::State) -> Vec<ResourceViewSpec>
fn views(_state: &Self::State) -> Vec<ResourceViewSpec>
Named presentations of this resource (editor, reader) with stable ids.
Not an action called open: ordinary presentation toggles never become
composable views by accident. Defaults to none.
fn search_scopes(_state: &Self::State) -> Vec<ScopeDecl>
fn search_scopes(_state: &Self::State) -> Vec<ScopeDecl>
Entries for the system search index.
fn search(_state: &Self::State) -> Vec<SearchEntry>
fn read_content(
state: &Self::State,
doc: &AutoCommit,
item: Option<&str>,
) -> Result<Value, String>
fn read_content( state: &Self::State, doc: &AutoCommit, item: Option<&str>, ) -> Result<Value, String>
What the resource holds, as JSON a caller can act on: with no item,
the resource itself (a notebook’s pages); with item, the nested
resource of that id from children (one page’s
title and blocks). Read from doc, the committed document, so the
answer is never older than the action that preceded it. Called only
when READABLE is set.
§Errors
A message naming why: no such item, or a document that cannot be read.
fn answer(_doc: &AutoCommit, _action: &str, _changes: &[Patch]) -> Option<Value>
fn answer(_doc: &AutoCommit, _action: &str, _changes: &[Patch]) -> Option<Value>
What declared action action (one ActionDecl::answering) made,
answered to its caller once it applied to doc as changes: the
ids it minted, the area it selected, a warning that what it drew is
hidden. A compact summary, so the caller needs no second read to
see what happened. The default answers nothing.
fn shape() -> Option<Shape>
fn shape() -> Option<Shape>
The stored layout of this resource’s content, for services that act
on it generically: History restores a past version through it, field
by field and item by item. #[resource] returns the record’s
Model::SHAPE; the default opts out.
fn migrate(doc: &mut AutoCommit, from: u32) -> Result<(), ModelError>
fn migrate(doc: &mut AutoCommit, from: u32) -> Result<(), ModelError>
Bring a document stored at version from up to Self::VERSION,
reading its old shape with the model helpers. Called once per
document by [crate::evolution::migrate].
§Errors
Returns why the document cannot be migrated.
fn surface() -> Surface
fn surface() -> Surface
The public surface: verbs, their parameters and kinds, and the
stored fields ([crate::surface]).
fn edit_action(edit: Edit<Self>) -> Option<Self::Action>
fn edit_action(edit: Edit<Self>) -> Option<Self::Action>
The action a field edit of this resource is, when it has field edits
(Self::EDITS).
fn stamp(action: &mut Self::Action, now_ms: i64)
fn stamp(action: &mut Self::Action, now_ms: i64)
Stamp the origin’s clock on an action as the door admits it. A caller never supplies the time an action carries.
fn op_schemas() -> Vec<(String, Value)>
fn op_schemas() -> Vec<(String, Value)>
The schemas of the own vocabularies of the record’s fields
(Operations), by field path pattern (* for
every item): what a field edit in that vocabulary carries.
fn availability(state: &Self::State, action: &str) -> Availability
fn availability(state: &Self::State, action: &str) -> Availability
Whether the declared action action can be taken in state, and why
not when it cannot. #[resource(available = path)] answers it.
fn seed() -> Option<Self::Action>
fn seed() -> Option<Self::Action>
The first content of a new record, written by its creator once it is
created: nobody’s undo step. #[resource(seed = path)] provides it.
fn kind_of(action: &Self::Action) -> ActionKind
fn kind_of(action: &Self::Action) -> ActionKind
What kind of action action is: whether it only reads, changes only
the document, or reaches outside it. Derived by #[resource] from
what each verb is able to do, never declared.
fn register(registry: &mut ActionRegistry)
fn register(registry: &mut ActionRegistry)
Register this resource’s replay decoder. Override to register an existing vocabulary under additional type ids.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.