pub struct BulkModal {
pub action: BatchAction,
pub flags: PatchFlags,
pub target: Option<String>,
pub kind: Option<String>,
pub cursor: usize,
pub focus: BulkFocus,
pub draft: String,
pub selection: BulkSelection,
pub state: BulkState,
pub note: Option<String>,
pub plan_stale: bool,
}Expand description
The batch modal.
Fields§
§action: BatchAction§flags: PatchFlagsThe CLI’s flag struct, not a locally built patch (see the module docs).
target: Option<String>The other end of a pair.
kind: Option<String>The relationship kind, as the user will set it.
cursor: usizeWhich row the cursor is on.
focus: BulkFocus§draft: StringThe text being typed, while focus is BulkFocus::Edit.
A draft is discarded on esc, never committed. An earlier draft of this
modal snapshotted the row on begin_edit and restored it on esc; the
snapshot was dead — commit_edit is the only thing that writes a row, so the
restore and a clear were indistinguishable from outside. The revert-and-red
run is what proved it: a mutation replacing the restore with a clear left all
141 cases green.
selection: BulkSelection§state: BulkState§note: Option<String>The last message, for the status bar.
plan_stale: boolWhether the plan no longer matches the screen.
Set on the first preview and on any change to the selection or the flags, so a confirm can never apply a plan the user has not looked at.
Implementations§
Source§impl BulkModal
impl BulkModal
Sourcepub fn new(action: BatchAction) -> Self
pub fn new(action: BatchAction) -> Self
A fresh modal for action.
Sourcepub fn row_names(&self) -> Vec<&'static str>
pub fn row_names(&self) -> Vec<&'static str>
The row names, in the order tab walks them.
Depends on the action, and that is the point: an archive has no rows,
because ArchiveRequest::archive() names no target status and inventing a
field for one would be offering a restore that #190’s C-003 does not allow.
A pair has no field rows either — it has a target and a kind.
Sourcepub fn row_name(&self, index: usize) -> &'static str
pub fn row_name(&self, index: usize) -> &'static str
The name of the row at index, for the render.
Sourcepub fn row_value(&self, index: usize) -> String
pub fn row_value(&self, index: usize) -> String
The value of the row at index.
An absent value is the em dash, not an empty string, so a blank row is
visibly blank rather than looking like a field set to "". The distinction is
the whole WP06 finding: a blank --estimate "" is Unchanged.
Sourcepub fn set_row(&mut self, index: usize, value: String) -> Result<()>
pub fn set_row(&mut self, index: usize, value: String) -> Result<()>
Store value on the row at index, or say why it cannot be stored.
A blank value is stored as absent (None), which is what makes “the user
cleared the field” and “the user never touched it” the same request — the
contract’s Unchanged. The clear row is the explicit way to empty
something, and it is the only row that may name a field clear does not own.
Sourcepub fn mark_stale(&mut self)
pub fn mark_stale(&mut self)
Mark that the plan no longer matches the screen.
contracts/patch-plan.md §4: “Apply requires the exact confirmed plan … any
change to selection or patch requires a new preview.” So a confirm is refused
while stale, and the refusal says why rather than silently re-previewing — a
silent re-preview would apply something the user did not look at.
Sourcepub fn begin_edit(&mut self)
pub fn begin_edit(&mut self)
Begin editing the row under the cursor.
Sourcepub fn commit_edit(&mut self) -> Result<()>
pub fn commit_edit(&mut self) -> Result<()>
Commit the draft, or report why it cannot be committed.
Sourcepub fn abort_edit(&mut self)
pub fn abort_edit(&mut self)
Abandon the draft. The row keeps whatever it had.
Discarding rather than committing is the point: an abandoned edit that still
wrote the half-typed value is an abandoned edit that changed state. The draft
is thrown away rather than left in place, because begin_edit rebuilds it
from the row every time — so a leftover draft is a draft nothing can read.
Sourcepub fn move_cursor(&mut self, delta: isize)
pub fn move_cursor(&mut self, delta: isize)
Move the cursor, wrapping. A no-op when the action has no rows (an archive).
Sourcepub fn patch(&self) -> Result<IssuePatch>
pub fn patch(&self) -> Result<IssuePatch>
The typed patch for a field action, or say why it cannot be built.
Delegates to the CLI’s own conversion, so a TUI field and a CLI flag mean the
same thing — including the “a blank field is Unchanged, not Clear” rule
WP06 found. A TUI that built its own patch would have its own blank rule.
Sourcepub fn patch_for(&self) -> Result<IssuePatch>
pub fn patch_for(&self) -> Result<IssuePatch>
The typed patch for this action, or say why it cannot be built.
One method, because the preview and the confirm both need it, and two
match self.action blocks are two places for an action to be handled
differently.
Sourcepub fn preview(&mut self, store: &Store, issues: &[(u32, Issue)]) -> Result<()>
pub fn preview(&mut self, store: &Store, issues: &[(u32, Issue)]) -> Result<()>
Build a plan for the current selection. Read-only by construction: it resolves, validates and renders a preview, and writes nothing.
Sourcepub fn relationship_for(
&self,
numbers: &[u32],
kind_override: Option<String>,
) -> Result<RelationshipOp>
pub fn relationship_for( &self, numbers: &[u32], kind_override: Option<String>, ) -> Result<RelationshipOp>
The relationship operation for numbers, or the field the user has not filled
in.
A pair’s from is per selected issue, not the whole selection: from is a
single issue in the relationship vocabulary, and the batch is the set of
edges. Passing the whole selection as from would build a plan for an edge
that does not exist.
Sourcepub fn archive_request(&self) -> ArchiveRequest
pub fn archive_request(&self) -> ArchiveRequest
The archive request.
The TUI’s archive action is archive only — there is no restore verb on this modal, because a restore needs an explicit target status (#190’s C-003) and there is no row for one. A modal that offered a restore with no target would be offering the request #190 rejects.
Sourcepub fn arm(&mut self)
pub fn arm(&mut self)
Go from a shown plan to the confirming state.
Two steps rather than one, because “I saw it” and “I approved it” are two
different decisions and a single y conflates them. The confirming state
renders a different affordance, so the user can see which one they are in.
Sourcepub fn confirm(&mut self, store: &Store) -> Result<()>
pub fn confirm(&mut self, store: &Store) -> Result<()>
Confirm the plan that is on screen and apply it.
Refuses while the plan is stale, and the refusal says why. That is contract §4’s “any change to selection or patch requires a new preview”, and it is checked here rather than inside the apply so a TUI user gets a TUI message.
Sourcepub fn cancel(&mut self)
pub fn cancel(&mut self)
Back to choosing, from any state.
confirm is the only method here that writes, so cancelling from any other
state cannot have changed a file. The confirm state keeps its plan so a
mis-aimed esc does not throw away a preview the user wanted to look at
again.
Sourcepub fn state_label(&self) -> &'static str
pub fn state_label(&self) -> &'static str
The state, in the words a user reads.
Sourcepub fn rows(&self) -> Vec<(bool, String, String)>
pub fn rows(&self) -> Vec<(bool, String, String)>
The rows, as the modal shows them: a cursor, a name and a value.
The footer, naming the keys that mean something right now.
The list is per-focus and per-state, because a footer that names y while y
types a letter is a footer that teaches the wrong thing.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for BulkModal
impl RefUnwindSafe for BulkModal
impl Send for BulkModal
impl Sync for BulkModal
impl Unpin for BulkModal
impl UnsafeUnpin for BulkModal
impl UnwindSafe for BulkModal
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more