pub struct Store {
pub issues_dir: PathBuf,
pub config: Config,
}Fields§
§issues_dir: PathBuf§config: ConfigImplementations§
Source§impl Store
impl Store
Sourcepub fn reference_epic(
&self,
issue_selector: &str,
epic_reference: &str,
) -> Result<EpicReferenceOutcome>
pub fn reference_epic( &self, issue_selector: &str, epic_reference: &str, ) -> Result<EpicReferenceOutcome>
Reference an epic from an issue.
The service resolves the epic (by id, or by name as a convenience),
enforces (issue, epic_id) uniqueness, and returns the resulting list
plus the History event; this method writes them through the existing
Store::update path. A refused reference writes nothing and appends
nothing, and an already-present reference is an idempotent no-op that does
the same.
Sourcepub fn dereference_epic(
&self,
issue_selector: &str,
epic_id: &str,
) -> Result<EpicReferenceOutcome>
pub fn dereference_epic( &self, issue_selector: &str, epic_id: &str, ) -> Result<EpicReferenceOutcome>
Remove an issue’s reference to an epic id.
epic_id is compared verbatim against the stored list, as the service
documents: a name is not resolved here, so removing a stale id that is no
longer a valid epic shape still works — the repair path C-005 promises.
Dereferencing an absent reference is an idempotent no-op.
Sourcepub fn bulk_reference(
&self,
epic_reference: &str,
selectors: &[String],
) -> Result<Vec<BulkReferenceOutcome>>
pub fn bulk_reference( &self, epic_reference: &str, selectors: &[String], ) -> Result<Vec<BulkReferenceOutcome>>
Reference an epic from each selector independently, writing every accepted change.
One rejection must not abort the rest (contracts/epic-service.md §5): the
returned vector has one entry per selector, in input order. Each accepted
change is written to its own file and appends exactly one History event; a
refusal writes nothing. An unresolvable selector becomes that entry’s own
error rather than failing the run.
§Two selectors for one issue
Selectors are resolved and applied one at a time against a working copy
of the issue set, advanced after every accepted write — the same reasoning
as Store::bulk_associate. Applying each selector to the one immutable
read would let a second selector naming the same issue miss the first
write, so the service’s already-present branch would not fire and a second
epic_referenced entry would be appended for a reference that is already
there. With the working copy the second commit is seen, so idempotence
stays the service’s decision rather than a membership rule re-implemented
here.
Source§impl Store
impl Store
Sourcepub fn create(
&self,
issue: &mut Issue,
body: &str,
filename_stem: Option<&str>,
) -> Result<PathBuf>
pub fn create( &self, issue: &mut Issue, body: &str, filename_stem: Option<&str>, ) -> Result<PathBuf>
Create an issue file atomically and assign its number by scanning the
live issue set (issue #52): the highest existing number plus one,
with a floor of 0 for an empty store.
The number is resolved at creation time rather than read from a
persisted [state].next_number counter, so tix.toml is never
rewritten by create. A deleted high-numbered issue’s number can be
reused by a later create; the frontmatter number stays stable for
every surviving issue (C-007).
The file is written into the assignee segment derived from
issue.assignee (unassigned/ fallback, FR-029/FR-030), creating the
segment directory as needed, and issue.filename is set to the
relative path so create output and later edits agree. Atomic-write
discipline (temp + rename, NFR-006) is preserved.
Sourcepub fn update(&self, filename: &str, issue: &Issue) -> Result<String>
pub fn update(&self, filename: &str, issue: &Issue) -> Result<String>
Write issue over the file at filename, then reconcile the file’s
location with the configured [layout] (move_on_change).
Returns the store-relative path the issue lives at afterwards — the same one in the common case, a new one when a templated field (status, type, assignee, cycle, …) now renders a different directory. Every write path goes through here, so no surface (CLI, MCP, TUI, triage, sync) can leave an issue at a path that disagrees with the layout.
pub fn delete(&self, filename: &str) -> Result<()>
Sourcepub fn archive(&self, sel: &str) -> Result<Issue>
pub fn archive(&self, sel: &str) -> Result<Issue>
Archive an issue: rename its file to a dot-prefixed name in place.
The marker is the filename (issue #1492), so this is a rename within the
same assignee segment (x.md ↔ .x.md) plus exactly one ## History
event. number, status, created and every other frontmatter field
are left untouched, no data is dropped, and the file is never deleted or
moved between directories.
An already-archived issue is refused with the already_archived
diagnostic rather than re-archived: the operation is not idempotent by
design, because a second call would append a second audit entry for a
mutation that did not happen.
Sourcepub fn restore(&self, sel: &str) -> Result<Issue>
pub fn restore(&self, sel: &str) -> Result<Issue>
Restore an archived issue: the inverse of Store::archive.
An active issue is refused with the not_archived diagnostic.
Sourcepub fn reassign(
&self,
filename: &str,
new_assignee: Option<&str>,
) -> Result<PathBuf>
pub fn reassign( &self, filename: &str, new_assignee: Option<&str>, ) -> Result<PathBuf>
Physically move an issue file into the assignee segment of a new
assignee (FR-031, contracts/file-format.md rule 9).
filename is a path relative to the issues dir: either a legacy flat
basename (2026-05-10-fix-login.md, C-010) or a grouped path
(alice/2026-05-10-fix-login.md). It is validated via the relaxed
[validate_filename] before anything touches the filesystem, so
traversal attempts are refused here as everywhere else.
The old assignee segment is read from the current relative path, the
new one derived with [derive_segment]; equal segments are a no-op.
The target filename keeps the original created-slug stem; when the
target already holds <stem>.md, a deterministic -1, -2, …
suffix is appended by testing existence — an existing file is never
overwritten or deleted. number/created live in the frontmatter and
are untouched, so issue-number stability is guaranteed by construction
(C-007).
The move is a single std::fs::rename (atomic-ish, same filesystem).
Returns the new relative path (matching how issue.filename is
stored) on success, including the unchanged relative path for the
equal-segment no-op.
Sourcepub fn migrate_issue(&self, filename: &str, target: &str) -> Result<()>
pub fn migrate_issue(&self, filename: &str, target: &str) -> Result<()>
Relocate an issue file to an exact store-relative target (used by
tix layout migrate). Frontmatter and number are preserved because
nothing is rewritten — it is a rename; an occupied target is refused; the
<slug>-assets/ dir moves alongside.
Sourcepub fn move_to_layout(&self, issue: &mut Issue) -> Result<bool>
pub fn move_to_layout(&self, issue: &mut Issue) -> Result<bool>
Move an issue to the directory its templated fields now render to, when
move_on_change is set. The basename is preserved. Returns whether it
moved; a no-op when already in place or when move_on_change is off.
Sourcepub fn add_asset(
&self,
filename: &str,
source_path: &Path,
) -> Result<AssetAdded>
pub fn add_asset( &self, filename: &str, source_path: &Path, ) -> Result<AssetAdded>
Copy an asset into the issue’s <slug>-assets/ directory (FR-006) and
insert a relative markdown link at the end of the body, persisted via
Store::update (NFR-004).
The asset dir is derived from the issue filename per
contracts/comments-format.md: <slug>-assets/ where <slug> is the
basename stem minus the leading YYYY-MM-DD- date prefix. The copy is
atomic (temp + rename, NFR-004); on a name collision the file is stored
as <stem>-<n>.<ext> (first free n) and the inserted link points at
the actual written name. Image extensions (.png .jpg .jpeg
.gif .svg .webp .bmp) get the  form, everything else
the […](…) form. The link path is relative to the issue file, so the
segment prefix is never part of it.
Source§impl Store
impl Store
Sourcepub fn set_parent(&self, child: &str, parent: &str) -> Result<ParentOutcome>
pub fn set_parent(&self, child: &str, parent: &str) -> Result<ParentOutcome>
Point child at parent, or repoint it if it already has one.
Both selectors go through Store::resolve_issue, so #4, 4 and a title
fragment all work and an unresolvable one produces the CLI’s own message. The
canonical issue id is what lands in the file, because neither a title nor
a branch-local number is a stable identity.
Sourcepub fn change_parent(&self, child: &str, parent: &str) -> Result<ParentOutcome>
pub fn change_parent(&self, child: &str, parent: &str) -> Result<ParentOutcome>
Repoint child at a new parent. A no-op when the parent is unchanged.
Sourcepub fn clear_parent(&self, child: &str) -> Result<ParentOutcome>
pub fn clear_parent(&self, child: &str) -> Result<ParentOutcome>
Remove child’s parent, yielding the absent state.
Sourcepub fn hierarchy_index(&self) -> Result<(HierarchyIndex, Vec<Issue>)>
pub fn hierarchy_index(&self) -> Result<(HierarchyIndex, Vec<Issue>)>
The in-memory snapshot the hierarchy service reads.
pub because the --parent edit flag (WP03) must validate through the
service before any write, exactly as this module does. It is a read, so
exposing it costs no more than a duplicate list() would.
One IssueView per issue, shared with the relationship service rather than
a second projection — two views of one issue type is how two services end up
disagreeing about the same snapshot.
The Vec is returned rather than discarded because the write path needs the
child from this same snapshot. Re-reading it through a second list()
would give the write and the validation two different notions of “current”:
two files carrying the same number: (reachable by hand-edit, since
HierarchyIndex::build keys by number and last-write-wins while
Store::get is first-match), or a concurrent writer, would make the
service decide on one parent and the writer emit another. The invariant is
then not merely unasserted, it is false.
Sourcepub fn relationship_done_column(&self) -> String
pub fn relationship_done_column(&self) -> String
Build a RelationshipGraph over every issue in the store.
The service is the only place relationship semantics live, so the write path validates through it rather than re-deriving direction handling, cycle detection, or blocked state locally (C-003). The board’s configured completion column.
Read from the first configured board. Completion is always resolved
from configuration, never by matching the literal string done; when no
board configures one, the service’s done fallback applies.
Resolved from the active board — the one named default, falling back
to the first declared, which is exactly what
Config::default_config returns
and what every other single-surface consumer already uses.
It used to take the first declared board instead
(configurations.values().next()). That is a different board whenever a
configuration is declared before default with a different
done_column, and it put two different boards inside a single decision:
blocked_move_guard asked “is the target the completion column?” via
completion_column(config) — the active board — and then “is this issue
blocked?” via this function’s graph — the first declared board. The
guard was reasoning about two different workflows in one call.
data-model.md §4 says completion “is always resolved through the board’s
configured done_column” and that the predicate is “the only definition
used by FR-003, FR-004, the move guard, and every surface”. That requires
one board, not “whichever is declared first”.
Source§impl Store
impl Store
Sourcepub fn associate(
&self,
issue_selector: &str,
project_reference: &str,
) -> Result<ProjectAssociationOutcome>
pub fn associate( &self, issue_selector: &str, project_reference: &str, ) -> Result<ProjectAssociationOutcome>
Associate an issue with a project.
The service resolves the project (by id, or by name as a convenience),
enforces uniqueness, and returns the resulting list plus the History event;
this method writes them through the existing Store::update path. A
refused association writes nothing and appends nothing, and an
already-present association is an idempotent no-op that does the same.
Sourcepub fn dissociate(
&self,
issue_selector: &str,
project_id: &str,
) -> Result<ProjectAssociationOutcome>
pub fn dissociate( &self, issue_selector: &str, project_id: &str, ) -> Result<ProjectAssociationOutcome>
Remove an issue’s association with a project id.
project_id is compared verbatim against the stored list, as the
service documents: a name is not resolved here, so removing a stale id that
is no longer a valid project shape still works — the repair path C-005
promises. Dissociating an absent association is an idempotent no-op.
Sourcepub fn bulk_associate(
&self,
project_reference: &str,
selectors: &[String],
) -> Result<Vec<BulkAssociationOutcome>>
pub fn bulk_associate( &self, project_reference: &str, selectors: &[String], ) -> Result<Vec<BulkAssociationOutcome>>
Associate each selector independently, writing every accepted change.
One rejection must not abort the rest (contract §3): the returned vector has one entry per selector, in input order. Each accepted change is written to its own file and appends exactly one History event; a refusal writes nothing. An unresolvable selector becomes that entry’s own error rather than failing the run.
§Two selectors for one issue
Selectors are resolved and applied one at a time against a working copy
of the issue set, advanced after every accepted write. Applying each
selector to the one immutable read instead would let a second selector
naming the same issue (say 1 and 2026-01-01-a.md) miss the first write:
the service’s already-present branch would not fire and a second
project_associated entry would be appended for an association that is
already there. With the working copy the second commit is seen, so
idempotence stays the service’s decision rather than a membership rule
re-implemented here, and the repeated selector is reported per selector as
the no-op it is (written: None, event: None, changed: false).
Source§impl Store
impl Store
pub fn open(config: &Config) -> Result<Store>
Sourcepub fn transaction<T>(&self, f: impl FnOnce() -> Result<T>) -> Result<T>
pub fn transaction<T>(&self, f: impl FnOnce() -> Result<T>) -> Result<T>
Run f, undoing every filesystem mutation it made if it returns Err.
A command like tix edit is several writes in sequence — a reassign that
moves the file, the merged frontmatter, a --status transition, a triage
stamp — and the writes are not individually atomic with each other. Without
this, an I/O failure between two of them commits the earlier one and leaves
the store half-mutated (tix #1302). Wrapping the command in
Store::transaction makes the sequence all-or-nothing: on any Err, every
file the command touched is restored to the bytes it had before.
The journal is recorded by the write primitives themselves
([write_file], the renames in move_issue_file, delete), so any
store method participates without knowing about transactions — the layer is
command-level, not a per-method flag.
Nesting: only the outermost call opens a journal, so an inner
transaction joins the outer one and a failure still rolls the whole outer
unit back. This is what makes it safe to wrap a public command that itself
calls other transactional commands.
Not covered, stated rather than implied: writes that bypass the primitives
(a future fs::write written by hand) and mutations made after f returns.
The rollback is best-effort per file and the original error is always the
one returned — a rollback that itself fails must not mask the cause.
Sourcepub fn list(&self) -> Result<Vec<Issue>>
pub fn list(&self) -> Result<Vec<Issue>>
The active issue set: every issue file that is not archived.
Archived issues are marked by a dot-prefixed markdown filename
(docs/issues/<assignee>/.<file>.md, issue #1492). They are skipped
from the directory entry, before the file is opened, so the default
path never parses an archived issue — the performance requirement that
keeps a repository with thousands of archived issues fast.
Use Store::list_all when an archived issue must still be reachable.
Sourcepub fn list_all(&self) -> Result<Vec<Issue>>
pub fn list_all(&self) -> Result<Vec<Issue>>
The exhaustive issue set: active issues plus archived ones.
Archived issues carry archived == true, derived from the leading dot
of their filename. This is the enumeration for selector resolution and
number allocation, both of which must see an archived issue; it is
deliberately not the default view.
Sourcepub fn assignee_segments(&self) -> Result<Vec<String>>
pub fn assignee_segments(&self) -> Result<Vec<String>>
The assignee storage folders under the issues directory, sorted.
These are the docs/issues/<username>/ segments Self::scan walks —
the ground truth for who has issues. The reserved segments are excluded:
unassigned is the fallback for “no assignee” and is not a person, and a
<slug>-assets/ or .tix-tmp-* directory is not a person either. Used by
the Triage mode’s assignee picker (design D6); never creates a directory.
pub fn get(&self, selector: IssueSelector) -> Result<Issue>
Sourcepub fn read_issue_at(&self, rel: &Path) -> Result<Issue>
pub fn read_issue_at(&self, rel: &Path) -> Result<Issue>
Read one issue by its store-relative filename (issue #10 follow-up).
The single-file counterpart of Store::scan, for the incremental
corpus path: it sets filename and archived exactly as scan does
(the archived marker is the leading dot of the file name) and applies
the same parse-error context, so an incrementally updated corpus holds
byte-identical issues to a full rebuild. It deliberately does not
linear-scan IssueSelector — the caller already has the relative
path from the watcher.
A path that is not a file is NotFound; the caller decides whether
that means “removed”.
Sourcepub fn resolve_issue(&self, raw: &str) -> Result<Issue>
pub fn resolve_issue(&self, raw: &str) -> Result<Issue>
Resolve a selector string exactly like the selector-taking commands
(parse_selector) and fetch the issue (D2: “Resolver SHALL match
tix edit/tix move”).
pub because the --parent flags (WP03) resolve a parent selector to its
number through this same path, so #4, 4 and a title fragment all work
on a parent exactly as they do on any other selector — and a second
resolver in the command layer is how two surfaces end up disagreeing about
which issue a selector names.
Sourcepub fn path_is_laid_out(&self, filename: &str, issue: &Issue) -> bool
pub fn path_is_laid_out(&self, filename: &str, issue: &Issue) -> bool
Whether filename already sits at the directory its templated fields
render to. Used to decide whether a mutation may relocate the file: only a
file already on the layout is moved, so a legacy or mismatched path is
left to tix layout migrate.
pub fn config_path(&self) -> Result<PathBuf>
Source§impl Store
impl Store
Sourcepub fn link(
&self,
a: &str,
b: &str,
kind: RelationKind,
) -> Result<RelationshipOutcome>
pub fn link( &self, a: &str, b: &str, kind: RelationKind, ) -> Result<RelationshipOutcome>
Create the typed relationship a --kind--> b.
Selectors resolve with the same grammar as the selector-taking
commands. The edge is stored once, in the frontmatter of whichever
issue owns the canonical edge — the from side for a directional kind,
and the lower-numbered side for related, which is order-normalized so
both spellings of an association are one edge. No back-reference is
written on the other issue: that symmetry was the retired model.
Validation runs through the service first, so a self-link, a missing target, a conflicting kind, or a cycle is refused before any file is touched (SC-005). Re-asserting an edge that already exists is an idempotent no-op: no file is written and no History event is appended.
Sourcepub fn unlink(
&self,
a: &str,
b: &str,
kind: RelationKind,
) -> Result<RelationshipOutcome>
pub fn unlink( &self, a: &str, b: &str, kind: RelationKind, ) -> Result<RelationshipOutcome>
Remove the typed relationship a --kind--> b.
Removing an edge that is not there is an idempotent no-op: no file is written and no History event is appended.
Sourcepub fn edit_relation(
&self,
id: &str,
new_to: Option<u32>,
new_kind: Option<RelationKind>,
) -> Result<RelationshipOutcome>
pub fn edit_relation( &self, id: &str, new_to: Option<u32>, new_kind: Option<RelationKind>, ) -> Result<RelationshipOutcome>
Retarget and/or re-kind the relationship with this stable id.
The id is generated once and survives the edit, so a relationship a
user has already referenced elsewhere does not change identity when its
target moves (contract §1). new_to and new_kind are both optional and
naming neither is the identity edit: nothing is written and no History
event is appended.
Validation goes through RelationshipGraph::validate_edit, so an edit
that would self-link, point at a missing issue, duplicate an existing
edge, or close a cycle is refused before any file is touched
(SC-005). An edit may change the kind but never the source: the source
is the edge’s owner, and moving it would mean deleting a relationship and
creating another, which is what remove plus link is for.
Sourcepub fn remove_relation(&self, id: &str) -> Result<RelationshipOutcome>
pub fn remove_relation(&self, id: &str) -> Result<RelationshipOutcome>
Remove the relationship with this stable id.
An id that is not stored is an idempotent no-op: no file is written
and no History event is appended, exactly as Store::unlink treats an
absent edge. tix link remove is therefore safe to retry.
Sourcepub fn graph_source(&self) -> Result<(Vec<Issue>, String)>
pub fn graph_source(&self) -> Result<(Vec<Issue>, String)>
The issue set plus the board’s configured completion column, for
building a RelationshipGraph over the whole store.
The issue set is returned alongside the column rather than as a
finished graph because the graph borrows the issues: a caller that wants
three derived reads (relationships_for, blocked_state,
dependency_chain) over one snapshot would otherwise pay three
separate O(V + E) builds and risk the three disagreeing.
This is the single place a read surface obtains its graph, so no surface can reach past the service to re-derive direction handling, cycle detection, or blocked state (C-003).
Reads the active set (Store::list): it is the graph a default
view renders — tix list --show relationships, tix relationship list
— so archived issues are deliberately absent from it (issue #1492).
Per-issue lookups that must still resolve an archived issue go through
Store::relationships_for, which reads Store::list_all.
Sourcepub fn set_relationships(
&self,
filename: &str,
targets: &[(u32, RelationKind)],
) -> Result<Issue>
pub fn set_relationships( &self, filename: &str, targets: &[(u32, RelationKind)], ) -> Result<Issue>
Make the issue’s relationship set exactly targets, with kind.
A typed relationship is stored once, in its canonical source’s file.
For a directional kind that is the declaring issue; for related, which
is order-normalized, it is the lower-numbered of the pair. So this method
may have to write more than one file: an edge requested from an issue
that is not its canonical source belongs to the other issue’s frontmatter.
That is the same ownership rule Store::link already follows, and it
is what “stored once, no mirrored edge” requires.
Every target number must already resolve (NotFound otherwise) and a
self-link is refused — both before anything is written (SC-005).
Relationships that survive keep their stable id and their History
entries; removals are recorded.
Sourcepub fn relationships_for(&self, number: u32) -> Result<Vec<Relationship>>
pub fn relationships_for(&self, number: u32) -> Result<Vec<Relationship>>
Every relationship touching number, as the service resolves it.
This is what a read surface must render, not issue.relationships.
A relationship is stored once, in its canonical source’s frontmatter, so
the stored field is a partial view of one endpoint: for 2 related 1
the edge lives in #1’s file, so #2’s own relationships is empty even
though #2 plainly has a relationship. Reading the raw field made tix view 2 and the TUI detail pane show nothing where the service reports
one, which is a user-visible regression against the retired symmetric
model — the old field was mirrored on both sides by construction.
Routing every surface through one accessor is also what keeps C-003 honest: derived relationship state is computed in exactly one place.
Sourcepub fn all_relationships(&self) -> Result<Vec<Relationship>>
pub fn all_relationships(&self) -> Result<Vec<Relationship>>
Every stored relationship, in deterministic order — the whole graph in one pass.
Store::relationships_for answers the same question for one issue, and
each of its calls reads the whole tree and rebuilds the graph. A caller
that wants every edge must therefore ask once: an N-issue loop over
relationships_for is N full scans of N files, which at this
repository’s size is millions of file reads for one export. This is the
accessor for that caller.
Archived issues are included, exactly as Store::relationships_for
includes them — the graph is the same one, built the same way, so the two
agree about which edges exist.
Source§impl Store
impl Store
Sourcepub fn set_status(
&self,
filename: &str,
request: StatusChange<'_>,
) -> Result<StatusOutcome>
pub fn set_status( &self, filename: &str, request: StatusChange<'_>, ) -> Result<StatusOutcome>
The status write path (WP03, C-003, FR-002, NFR-002).
Every surface that changes an issue’s column comes through here, so the
transition service is on the path structurally rather than by convention:
CLI (tix mv, tix edit --status), the TUI’s move and edit actions, MCP’s
move_issue and update_issue, and sync’s import. A caller that judges a
transition itself is a parity defect, and the two that used to are the
reason this function had no production caller at all.
§The three outcomes
| Result | Meaning | Written |
|---|---|---|
Ok(outcome), outcome.written | accepted, changed column | file + one decision |
Ok(outcome), !outcome.written | accepted, same column | nothing (idempotent) |
Err(TixError::Workflow(_)) | refused | nothing at all |
The separation is in the type, not in a runtime check: a refusal is an
Err carrying the named diagnostic, so a caller cannot read it as a
write, and cannot mistake unknown_column (a typo) for invalid_transition
(policy) — one message shape for both would lose the distinction the
contract’s shared vocabulary exists to keep (contract §6).
§Rejection is inert
Nothing is touched before the service has accepted: no issue file, no
tix.toml, no History entry. The service is pure (src/workflow.rs is
filesystem-free by design), and the only write in this function is the
single update at the end — so there is no window in which a refusal
leaves a partial change behind.
§No repair path
A file hand-edited into an inconsistent status is read, judged, and
reported. This function never corrects one (C-005, decision
01M3C3XBMTR5WFV7059NXM04AJ); WP06 owns the diagnosis. An auto-repair
sneaking in here would be an escalation trigger.
Routes through Store::update rather than serialising directly, so the
retired links: disclosure happens on this path too. Cycle-5 Minor 9: this
used to write the file itself, which drops the retired links: key
because the key is not in the model — with no disclosure, since the
disclosure lives in update. It was dead code reachable only from tests,
so nothing caught it, and wiring it up would have silently destroyed the
key on every surface at once.
Source§impl Store
impl Store
Sourcepub fn mention_store(&self) -> Option<MentionStore>
pub fn mention_store(&self) -> Option<MentionStore>
The mention store, or None when [mentions] enabled = false.
One constructor for every call site — the write seam, the TUI’s indicator and the CLI’s
tix mentions — so “disabled” cannot mean different things in different places: no
store, no scan, no write.
The directory resolves against the project root, the same rule issues_dir follows,
so a tix run from a subdirectory reads and writes the same files.
Auto Trait Implementations§
impl Freeze for Store
impl RefUnwindSafe for Store
impl Send for Store
impl Sync for Store
impl Unpin for Store
impl UnsafeUnpin for Store
impl UnwindSafe for Store
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> 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