SchedulingChanges#

One set of edits to a live Project: tasks, links, resources and assignments to add, update or remove. Project::apply commits it, Project::preview tries it on a copy, and each Candidate passed to Project::evaluate carries one. Project::suggest_assignments returns one in its SuggestProposal.

Start from SchedulingChanges::default(), where every bucket is empty, and push records into the buckets the edit needs.

Declaration#

#[derive(Clone, Debug, Default)]
pub struct SchedulingChanges {
    pub baseline_id: Option<String>,
    pub tasks: Changes<TaskData, TaskUpdate>,
    pub links: Changes<LinkAdd, LinkUpdate>,
    pub resources: Changes<ResourceData, ResourceUpdate>,
    pub assignments: Changes<AssignmentData, AssignmentUpdate>,
}

Fields#

Field Type Default Description
baseline_id Option<String> None Baseline token the edits were made against. None skips the check. Some(token) must name the project's current baseline, or the call fails with ProjectError::StaleBaseline.
tasks Changes<TaskData, TaskUpdate> empty Tasks to add, update or remove by id.
links Changes<LinkAdd, LinkUpdate> empty Links to add, update or remove by id.
resources Changes<ResourceData, ResourceUpdate> empty Resources to add, update or remove by id.
assignments Changes<AssignmentData, AssignmentUpdate> empty Assignments to add, update or remove by id.

Take the token for baseline_id from the latest solve result (ScheduleResult::baseline_id). Tokens are opaque: don't compare them across runs.

A set has no calendar bucket. Calendars can't be added or edited through SchedulingChanges; that takes a full solve(Some(data)).

Operation order#

Operations run in a fixed order, not in the order they were pushed. Kinds run in the order tasks, links, resources, assignments. Within a kind:

Kind Order
tasks remove, add, update
links remove, update, add
resources remove, add, update
assignments remove, update, add

Within a bucket, records run in vector order. Each operation is identified by its bucket and index, such as tasks.update[0], in AppliedOperation and RejectedOperation.

Example#

Move a task, add a link and check the edit against the current baseline.

use plen_core::{
    DateTime, LinkAdd, LinkType, Patch, Project, ProjectData, ProjectOptions,
    SchedulingChanges, SolveOptions, TaskData, TaskUpdate,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let monday = DateTime::from_ymd_hm(2026, 8, 10, 8, 0).ok_or("invalid date")?;
    let wednesday = DateTime::from_ymd_hm(2026, 8, 12, 8, 0).ok_or("invalid date")?;

    let data = ProjectData {
        tasks: vec![
            TaskData {
                start_date: Some(monday),
                duration: Some(1.0),
                ..TaskData::new(1)
            },
            TaskData {
                start_date: Some(monday),
                duration: Some(2.0),
                ..TaskData::new(2)
            },
        ],
        ..Default::default()
    };

    let mut project = Project::new(ProjectOptions::default());
    let solved = project.solve(Some(data), &SolveOptions::default())?;

    let mut changes = SchedulingChanges {
        baseline_id: solved.baseline_id.clone(),
        ..Default::default()
    };

    let mut moved = TaskUpdate::new(1);
    moved.start_date = Patch::Value(wednesday);
    changes.tasks.update.push(moved);

    changes.links.add.push(LinkAdd {
        id: None,
        source: 1.into(),
        target: 2.into(),
        link_type: LinkType::Fs,
        lag: 0.0,
        lag_unit: None,
    });

    let report = project.apply(&changes)?;
    for op in &report.rejected {
        println!("rejected {}: {}", op.path, op.message);
    }
    Ok(())
}