pub struct Planner { /* private fields */ }Expand description
A working snapshot of the planning registry, bound to a repo root.
Implementations§
Source§impl Planner
impl Planner
Sourcepub fn load() -> Result<Self>
pub fn load() -> Result<Self>
Load the planning registry from tix.toml in the current directory,
on the crate-wide assumption that commands run from the repo root.
Sourcepub fn load_from(path: &Path) -> Result<Self>
pub fn load_from(path: &Path) -> Result<Self>
Load the planning registry from an explicit config path.
Sourcepub fn from_config(config: Config, root: PathBuf) -> Self
pub fn from_config(config: Config, root: PathBuf) -> Self
Build a planner from an already-loaded config (used by tix init and
tests). The config is not persisted here; mutators save on demand.
Sourcepub fn category_resolution(&self) -> CategoryResolution
pub fn category_resolution(&self) -> CategoryResolution
The resolution this planner’s board maps through.
Built here rather than stored, because a Planner is a short-lived view
over a config and caching it would be the per-object rebuild the service’s
“built once per config load” rule exists to avoid. The expensive part of a
sweep is not building this — it is a handful of hash inserts per column —
it is not re-deriving it per issue, and that is what callers do wrong.
Sourcepub fn schedule_index(&self) -> Result<ScheduleIndex>
pub fn schedule_index(&self) -> Result<ScheduleIndex>
The cycle-identity index this planner’s records resolve through (issue #181).
One read of docs/cycles/ — or of the legacy [cycles.<name>] tables when
there is no store, which is the loader’s own rule — through
crate::capacity::schedule_index. It is the same index
crate::cycle_schedule::registry::load_index builds, and the same one
crate::capacity::resolve_cycle resolves a capacity scope reference
against, so a caller never has to build a second one.
Sourcepub fn capacity_snapshot_from(&self, issues: &[Issue]) -> Result<IssueSnapshot>
pub fn capacity_snapshot_from(&self, issues: &[Issue]) -> Result<IssueSnapshot>
One indexed capacity snapshot over the issues a caller already read (WP02).
The cycle records are read once (Planner::schedule_index) and the issues are
indexed once (IssueSnapshot::build): cycle, team, and assignee membership,
#183’s completion answer, and every estimate parse and explicit conversion are
resolved there, so no calculation needs to read the store or re-parse an
estimate again (NFR-001). This is the entry point WP03’s calculation service
reads.
Sourcepub fn capacity_snapshot(&self, store: &Store) -> Result<IssueSnapshot>
pub fn capacity_snapshot(&self, store: &Store) -> Result<IssueSnapshot>
Planner::capacity_snapshot_from over a store, read once here.
The store read and the index build are the only I/O a capacity calculation performs; everything a surface then asks is a lookup into the returned snapshot.
Sourcepub fn milestones_dir(&self) -> PathBuf
pub fn milestones_dir(&self) -> PathBuf
The resolved milestones directory, relative to the repo root.
Sourcepub fn cycle(&self, name: &str) -> Option<&PlanningCycle>
pub fn cycle(&self, name: &str) -> Option<&PlanningCycle>
Look up a cycle by name.
The implicit Default cycle answers even when it has no file, so a
fresh project can name it.
Sourcepub fn cycle_names(&self) -> Vec<String>
pub fn cycle_names(&self) -> Vec<String>
Cycle names in declaration order.
The implicit Default cycle is not invented here: a project with no
store shows only what it has actually configured, and the legacy
tix.toml listing is preserved. CycleStore::list is where the
implicit default is surfaced (the cycle mode lists through it), and
Planner::cycle answers for the name regardless.
pub fn is_overdue(issue: &Issue, today: &str) -> bool
pub fn cycle_expired(cycle: &PlanningCycle, today: &str) -> bool
Sourcepub fn is_running(cycle: &PlanningCycle, today: &str) -> bool
pub fn is_running(cycle: &PlanningCycle, today: &str) -> bool
Whether cycle is running on today: its start_date is present and
<= today, and its due_date is either absent (open-ended) or
>= today. Both bounds are inclusive, so a cycle runs on its first and
its last day. The implicit, date-less Default cycle has no start date
and therefore never runs.
Sourcepub fn running_cycle(&self, today: &str) -> Option<String>
pub fn running_cycle(&self, today: &str) -> Option<String>
The single cycle running on today, if exactly one is.
None when no cycle runs — there is nothing to assign — and None when
two or more do, because an ambiguous date range must fall back to the
configured [planning].default_cycle rather than guess which one the
caller meant. See Planner::is_running for the running rule; the
implicit Default cycle is never a candidate.
Sourcepub fn overlapping_cycles(&self, name: &str) -> Vec<String>
pub fn overlapping_cycles(&self, name: &str) -> Vec<String>
Names of the cycles whose date window intersects name’s.
A cycle’s window is [start_date, due_date], both bounds inclusive; an
absent due_date leaves the window open-ended, so it meets every later
window. Touching endpoints intersect (the shared day is in both). A cycle
with no start_date has no window: it never overlaps anything, and a
name that has no start_date reports no overlaps. name is never
reported against itself, and the result follows config declaration order.
pub fn successor<'a>( &'a self, cycle: &str, today: &str, ) -> Option<(&'a str, &'a PlanningCycle)>
pub fn overdue_batch(&self, store: &Store, today: &str) -> Vec<Issue>
pub fn rollover_plan(&self, store: &Store, today: &str) -> Vec<RolloverTarget>
Sourcepub fn rollover_plan_from(
&self,
all: &[Issue],
today: &str,
) -> Vec<RolloverTarget>
pub fn rollover_plan_from( &self, all: &[Issue], today: &str, ) -> Vec<RolloverTarget>
Planner::rollover_plan over an already-read issue set (tix #1518).
The TUI reads the tree once at startup and hands the same slice to the
board, the corpus and this plan, instead of walking the store again for
every expired cycle. The result is identical to Planner::rollover_plan:
the enumeration is the only thing that moved out of the loop.
pub fn cycle_progress(&self, store: &Store, name: &str) -> Progress
pub fn milestone_progress(&self, store: &Store, name: &str) -> Progress
Sourcepub fn next_cycle_name(&mut self) -> String
pub fn next_cycle_name(&mut self) -> String
The next auto cycle name. Skips forward past any existing Cycle N
keys and permanently bumps [planning].cycle_number one past the
returned name, per the seed’s cycle_number = 1 successor semantics.
The bump is persisted by the mutator that calls this.
Sourcepub fn add_cycle(&mut self, name: Option<String>) -> Result<String>
pub fn add_cycle(&mut self, name: Option<String>) -> Result<String>
Create a named or auto cycle. An explicit name must not collide with
the current auto-counter successor (Cycle {cycle_number}), a config
error, so hand-authored keys and generated ones never overlap.
Sourcepub fn rename_cycle(&mut self, old: &str, new: &str) -> Result<()>
pub fn rename_cycle(&mut self, old: &str, new: &str) -> Result<()>
Rename a cycle, moving its declaration order slot and rebasing
default_cycle when it pointed at the old name.
pub fn delete_impact(&self, store: &Store, name: &str) -> Result<Impact>
pub fn rename_cycle_cascade( &mut self, store: &Store, old: &str, new: &str, ) -> Result<RenameImpact>
pub fn set_cycle_fields( &mut self, name: &str, description: Option<&str>, start_date: Option<&str>, due_date: Option<&str>, ) -> Result<()>
pub fn delete_cycle(&mut self, name: &str) -> Result<()>
Sourcepub fn add_team_member(&mut self, label: &str, member: &str) -> Result<()>
pub fn add_team_member(&mut self, label: &str, member: &str) -> Result<()>
Add a member to a team, creating the team when absent. Duplicate members and blank inputs are rejected.
Sourcepub fn list_milestones(&self) -> Result<Vec<Milestone>>
pub fn list_milestones(&self) -> Result<Vec<Milestone>>
All milestones, sorted by filename (which sorts by target_date then
slug — the registry is a directory listing, never a stored list).
Sourcepub fn milestone_names(&self) -> Result<Vec<String>>
pub fn milestone_names(&self) -> Result<Vec<String>>
Milestone names in listing order.
Sourcepub fn milestone(&self, name: &str) -> Result<Milestone>
pub fn milestone(&self, name: &str) -> Result<Milestone>
Look up a milestone by its frontmatter name.
Sourcepub fn create_milestone_file(
&self,
name: &str,
target_date: Option<&str>,
description: Option<&str>,
) -> Result<PathBuf>
pub fn create_milestone_file( &self, name: &str, target_date: Option<&str>, description: Option<&str>, ) -> Result<PathBuf>
Create a milestone registry file. The filename is
{created}-{slug}.md where created is today’s date; an optional
target_date is stored in the frontmatter and must be a valid
YYYY-MM-DD calendar date when given (FR-007). The markdown body is
the optional description. Refuses to overwrite an existing file.
Sourcepub fn rename_milestone_file(
&self,
old: &Milestone,
new_name: &str,
) -> Result<PathBuf>
pub fn rename_milestone_file( &self, old: &Milestone, new_name: &str, ) -> Result<PathBuf>
Rename a milestone file, keeping the created date prefix, bumping
updated and rebasing the frontmatter name; the body is preserved
verbatim (the cascade back into referencing issues is FR-009/IC-08
work that lives in the issue layer, not the registry).
pub fn milestone_delete_impact( &self, store: &Store, name: &str, ) -> Result<Impact>
pub fn rename_milestone_cascade( &self, store: &Store, old: &Milestone, new_name: &str, ) -> Result<RenameImpact>
pub fn update_milestone( &self, name: &str, target_date: Option<&str>, description: Option<&str>, ) -> Result<()>
pub fn delete_milestone(&self, name: &str) -> Result<String>
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Planner
impl RefUnwindSafe for Planner
impl Send for Planner
impl Sync for Planner
impl Unpin for Planner
impl UnsafeUnpin for Planner
impl UnwindSafe for Planner
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