Project::preview#

Applies hypothetical changes to a copy of the solved baseline of a Project and schedules the copy. The project isn't modified. Use it to show what an edit would do before committing it with apply.

Signature#

pub fn preview(
    &self,
    changes: &SchedulingChanges,
    options: &PreviewOptions,
) -> Result<ScheduleResult, Error>

Takes &self. Synchronous and CPU-only.

Parameters#

Name Type Description
changes &SchedulingChanges The edits to try
options &PreviewOptions Subtree, analytics, reasons, baseline check and an optional load profile

Behavior#

  • updates are relative to the baseline. An empty change set gives empty updates.
  • The result's baseline_id is the baseline the preview read, so it's accepted by a following preview.
  • The change set isn't partially accepted: one rejected operation fails the whole call.
  • options.baseline_id and changes.baseline_id are checked separately. None skips a check.

Returns#

A ScheduleResult for the previewed state.

Errors#

Error When
Error::Project(ProjectError::NoBaseline) The project is unsolved, including after apply
Error::Project(ProjectError::StaleBaseline) options.baseline_id or changes.baseline_id doesn't name the current baseline
Error::Input An operation in changes is rejected (the error reports the first one), or the profile query is invalid
Note

A start date that falls on non-working time is kept unsnapped when the task has no predecessor. The end is computed forward from it on the working calendar.

Example#

Preview a drag of task b and check that the project keeps its dates.

use plen_core::{
    DateTime, Patch, PreviewOptions, Project, ProjectData, SchedulingChanges, SolveOptions,
    TaskData, TaskUpdate,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let day = |d| DateTime::from_ymd_hm(2026, 1, d, 0, 0).ok_or("invalid date");

    let mut task = TaskData::new("b");
    task.start_date = Some(day(1)?);
    task.duration = Some(1.0);
    let mut data = ProjectData {
        tasks: vec![task],
        ..Default::default()
    };
    data.config.use_calendars = false;

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

    let mut moved = TaskUpdate::new("b");
    moved.start_date = Patch::Value(day(5)?);
    let mut drag = SchedulingChanges::default();
    drag.tasks.update.push(moved);

    let options = PreviewOptions {
        baseline_id: solved.baseline_id.clone(),
        ..Default::default()
    };
    let preview = project.preview(&drag, &options)?;
    for update in &preview.updates {
        if let (Some(start), Some(end)) = (update.start_date, update.end_date) {
            println!("task {}: {start} .. {end}", update.id); // task b: 2026-01-05 00:00 .. 2026-01-06 00:00
        }
    }

    // The project is unchanged: an empty preview reports no updates.
    let unchanged = project.preview(&SchedulingChanges::default(), &PreviewOptions::default())?;
    assert!(unchanged.updates.is_empty());
    Ok(())
}