pub struct FileIssueStore { /* private fields */ }Expand description
File-backed issue store.
One instance is shared (via Arc) between the TUI indicator, the agent
issue tool, and the oxi issue CLI subcommand. All mutations go through
FileIssueStore::create / FileIssueStore::update which serialize per-file
content-hash CAS (cross-process / external edits).
Implementations§
Source§impl FileIssueStore
impl FileIssueStore
Sourcepub fn open(issues_dir: PathBuf) -> Result<Self>
pub fn open(issues_dir: PathBuf) -> Result<Self>
Open (or create lazily) the issue store rooted at issues_dir.
Sourcepub fn open_from_cwd(start: &Path) -> Result<Self>
pub fn open_from_cwd(start: &Path) -> Result<Self>
Open using project-root discovery from start (cwd).
Sourcepub fn issues_dir(&self) -> PathBuf
pub fn issues_dir(&self) -> PathBuf
The issues directory.
Sourcepub fn open_count(&self) -> usize
pub fn open_count(&self) -> usize
Number of open issues, for the status-bar indicator. Refreshes the cache if the directory mtime changed. Cheap (O(1) when fresh).
Sourcepub fn latest_open_title(&self) -> Option<String>
pub fn latest_open_title(&self) -> Option<String>
Title of the most recently updated open issue, for the status-bar
indicator. Cached alongside open_count, so this is also O(1) on a
warm cache. Returns None if there are no open issues.
Sourcepub fn summary(&self) -> IssueSummary
pub fn summary(&self) -> IssueSummary
Aggregate summary for the footer indicator / panels. Pulled from the in-memory cache, so it’s cheap (O(1) on a warm cache).
Sourcepub fn top_free_priority(&self) -> Option<Priority>
pub fn top_free_priority(&self) -> Option<Priority>
Highest priority among open, unassigned issues — the most actionable
thing a free agent could pick up right now (#10). Distinct from a
plain “top priority” (overall open max): this excludes issues someone
is already working on. Returns None when no open issue is free.
Cached alongside Self::open_count; O(1) on a warm cache.
Sourcepub fn has_any(&self) -> bool
pub fn has_any(&self) -> bool
True iff the issues directory has any issues at all (suppresses the indicator when the project has never used the feature).
Sourcepub fn invalidate(&self)
pub fn invalidate(&self)
Invalidate the cache (force next read to rescan).
Sourcepub fn list(&self, filter: &IssueFilter) -> Result<Vec<Issue>>
pub fn list(&self, filter: &IssueFilter) -> Result<Vec<Issue>>
List all issues, optionally filtered. Sorted by updated_at desc.
Sourcepub fn read(&self, id: u32) -> Result<(Issue, String)>
pub fn read(&self, id: u32) -> Result<(Issue, String)>
Read a single issue by id. Returns the issue and its current content hash (for optimistic-concurrency writes).
Sourcepub fn next_id(&self) -> Result<u32>
pub fn next_id(&self) -> Result<u32>
Allocate the next issue id by scanning existing filenames.
Cross-process allocation races are possible (two sessions create the
next id simultaneously) but bounded: the loser’s create write hits
an existing file and we bump to the next free id. No lock needed for
correctness, only for avoiding rare retries.
Sourcepub fn create(
&self,
title: String,
body: String,
priority: Priority,
labels: Vec<String>,
caller_session: Option<&str>,
) -> Result<Issue>
pub fn create( &self, title: String, body: String, priority: Priority, labels: Vec<String>, caller_session: Option<&str>, ) -> Result<Issue>
Create a new issue. caller_session is linked into sessions.
Sourcepub async fn update<F>(
&self,
id: u32,
expected_hash: Option<String>,
mutator: F,
) -> Result<Issue, IssueError>
pub async fn update<F>( &self, id: u32, expected_hash: Option<String>, mutator: F, ) -> Result<Issue, IssueError>
Update an issue with optimistic concurrency.
expected_hash should be the hash returned by FileIssueStore::read. If the
on-disk content changed since, returns IssueError::Conflict.
mutator receives the loaded issue and returns the new state.
All writes go through file_mutation_queue for in-process
serialization, exactly like the edit tool.
Sourcepub async fn close(
&self,
id: u32,
caller: &str,
expected_hash: Option<String>,
) -> Result<Issue, IssueError>
pub async fn close( &self, id: u32, caller: &str, expected_hash: Option<String>, ) -> Result<Issue, IssueError>
Convenience: close an issue (assignee only).
Sourcepub async fn reopen(
&self,
id: u32,
expected_hash: Option<String>,
) -> Result<Issue, IssueError>
pub async fn reopen( &self, id: u32, expected_hash: Option<String>, ) -> Result<Issue, IssueError>
Reopen a closed issue. No ownership required (reopening doesn’t assign the issue to anyone; it goes back to the unassigned pool).
Errors with NotFound if the id doesn’t exist, or with no special
error if the issue is already open — that case is a no-op.
Sourcepub async fn start(
&self,
id: u32,
caller: &str,
expected_hash: Option<String>,
) -> Result<Issue, IssueError>
pub async fn start( &self, id: u32, caller: &str, expected_hash: Option<String>, ) -> Result<Issue, IssueError>
Try to claim an issue for caller (the start action).
If already assigned to a live session, returns IssueError::Assigned.
If assigned to a dead session (process exited), reclaims and assigns
to the caller. If free, assigns to the caller.
Sourcepub async fn release(
&self,
id: u32,
caller: &str,
expected_hash: Option<String>,
) -> Result<Issue, IssueError>
pub async fn release( &self, id: u32, caller: &str, expected_hash: Option<String>, ) -> Result<Issue, IssueError>
Release an assignment (the release action). Caller must be the owner.
Sourcepub async fn link_session(
&self,
id: u32,
session: &str,
expected_hash: Option<String>,
) -> Result<Issue, IssueError>
pub async fn link_session( &self, id: u32, session: &str, expected_hash: Option<String>, ) -> Result<Issue, IssueError>
Link a session to an issue (append-only; idempotent).
Sourcepub async fn apply_patch(
&self,
id: u32,
patch: IssuePatch,
caller: Option<String>,
expected_hash: Option<String>,
) -> Result<Issue, IssueError>
pub async fn apply_patch( &self, id: u32, patch: IssuePatch, caller: Option<String>, expected_hash: Option<String>, ) -> Result<Issue, IssueError>
Apply a precise IssuePatch under strict CAS, preserving the existing
ownership policy.
If caller is Some, a different non-empty assignee blocks the
update with IssueError::NotAssigned — identical to the legacy
update tool action. Setting status = Open also clears closed_at,
fixing the latent reopen bug (#4: previously update { status: open }
left a stale closed_at on a reopened issue). Prefer the dedicated
FileIssueStore::reopen for clarity.
No-op patches (nothing meaningful changed) are detected inside
FileIssueStore::update and skip the write entirely.
Trait Implementations§
Source§impl Clone for FileIssueStore
impl Clone for FileIssueStore
Source§fn clone(&self) -> FileIssueStore
fn clone(&self) -> FileIssueStore
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for FileIssueStore
impl !UnwindSafe for FileIssueStore
impl Freeze for FileIssueStore
impl Send for FileIssueStore
impl Sync for FileIssueStore
impl Unpin for FileIssueStore
impl UnsafeUnpin for FileIssueStore
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> GetSetFdFlags for T
impl<T> GetSetFdFlags for T
Source§fn get_fd_flags(&self) -> Result<FdFlags, Error>where
T: AsFilelike,
fn get_fd_flags(&self) -> Result<FdFlags, Error>where
T: AsFilelike,
self file descriptor.Source§fn new_set_fd_flags(&self, fd_flags: FdFlags) -> Result<SetFdFlags<T>, Error>where
T: AsFilelike,
fn new_set_fd_flags(&self, fd_flags: FdFlags) -> Result<SetFdFlags<T>, Error>where
T: AsFilelike,
Source§fn set_fd_flags(&mut self, set_fd_flags: SetFdFlags<T>) -> Result<(), Error>where
T: Sized + AsFilelike,
fn set_fd_flags(&mut self, set_fd_flags: SetFdFlags<T>) -> Result<(), Error>where
T: Sized + AsFilelike,
self file descriptor. Read moreSource§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
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 moreimpl<T> MaybeSendSync for T
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.