TaskFinish#

The finish of one focus task in one evaluated stage. It's an entry of Finish::tasks. It's returned by Project::evaluate and isn't built by hand.

Declaration#

#[derive(Clone, Debug, PartialEq)]
pub struct TaskFinish {
    pub id: Id,
    pub finish: Option<Shift>,
}

With the serde feature (cargo add plen-core --features serde), TaskFinish also implements Serialize and Deserialize, with field names in camelCase.

Fields#

Field Type Description
id Id The focus task's id, as given in EvaluateOptions::focus.
finish Option<Shift> The task's finish date in this stage and its shift from the baseline finish. None when the task isn't in the stage, for example because the candidate removed it.

Example#

Focus on task 2 and score two candidates: one makes task 2 longer, the other removes it.

use plen_core::{
    Candidate, DateTime, EvaluateOptions, 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 task = |id: i32| TaskData {
        start_date: Some(monday),
        duration: Some(2.0),
        ..TaskData::new(id)
    };
    let data = ProjectData {
        tasks: vec![task(1), task(2)],
        ..Default::default()
    };

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

    // Candidate "longer": task 2 takes 4 days instead of 2.
    let mut longer = SchedulingChanges::default();
    longer.tasks.update.push(TaskUpdate {
        duration: Patch::Value(4.0),
        ..TaskUpdate::new(2)
    });
    // Candidate "drop": task 2 is removed.
    let mut drop = SchedulingChanges::default();
    drop.tasks.remove.push(2.into());

    let candidates = [
        Candidate { id: "longer".into(), changes: longer },
        Candidate { id: "drop".into(), changes: drop },
    ];
    let options = EvaluateOptions {
        focus: vec![2.into()],
        ..Default::default()
    };

    let result = project.evaluate(&candidates, &options)?;
    for candidate in &result.candidates {
        let Some(scheduled) = &candidate.scheduled else { continue };
        for task in &scheduled.finish.tasks {
            match &task.finish {
                Some(shift) => println!(
                    "{}: task {} finishes {}, {:+} days",
                    candidate.id, task.id, shift.date, shift.delta
                ),
                None => println!("{}: task {} not in plan", candidate.id, task.id),
            }
        }
    }
    Ok(())
}

Output:

longer: task 2 finishes 2026-08-14 08:00, +2 days
drop: task 2 not in plan