EvaluateOptions#

Options passed to Project::evaluate and Project::evaluate_with. EvaluateOptions::default() scores only the scheduled stage, reports no per-task figures, skips the baseline check and keeps stored leveling delays. Set individual fields with struct update syntax.

Declaration#

#[derive(Clone, Debug, Default)]
pub struct EvaluateOptions {
    pub baseline_id: Option<String>,
    pub level: Option<LevelingOptions>,
    pub focus: Vec<Id>,
    pub clear_first: bool,
}

Fields#

Field Type Default Description
baseline_id Option<String> None Expected baseline token. Some makes the call fail with ProjectError::StaleBaseline unless it names the current baseline. Take it from the latest ScheduleResult's baseline_id.
level Option<LevelingOptions> None Some levels each candidate with these options after scheduling it and fills CandidateResult::leveled. None scores the scheduled stage only. Options that are invalid for the baseline make the call fail with Error::Input.
focus Vec<Id> empty Tasks to report finish and overload for. Each id adds one entry, in this order, to Finish::tasks and Overload::focus of every stage. An unknown task makes the call fail with Error::Input at path focus[i].
clear_first bool false true scores the baseline and every candidate against a copy of the accepted state with leveling_delay and pauses cleared on in-scope tasks. See clear_first below.

clear_first#

The scope is level.scope.tasks when level sets it, otherwise every task, also when level is None. The cleared copy is solved once and replaces the accepted state as the reference for the baseline and every candidate.

  • It's independent of LevelingOptions::clear_first, so it also applies to scheduled-stage scoring.
  • It adds no errors: baseline_id, candidate tokens, level and focus are checked against the accepted state before anything is cleared.
  • When nothing in scope has a delay or pause, the output is identical to clear_first: false.

See Evaluating alternatives for when to use it and how to combine it with level.

Example#

use plen_core::{
    AssignmentData, AssignmentUpdate, Candidate, DateTime, EvaluateOptions, LevelingOptions,
    Patch, Project, ProjectData, ResourceData, SchedulingChanges, SolveOptions, TaskData,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let monday = DateTime::from_ymd_hm(2026, 8, 10, 8, 0).ok_or("invalid date")?;
    let mut tasks = Vec::new();
    for id in ["A", "B"] {
        let mut task = TaskData::new(id);
        task.start_date = Some(monday);
        task.duration = Some(2.0);
        tasks.push(task);
    }
    let resource = |id: &str| ResourceData { id: id.into(), capacity: None, calendar_id: None };
    let assign = |id: &str, task: &str| AssignmentData {
        id: Some(id.into()),
        task: task.into(),
        resource: "dev1".into(),
        value: 1.0,
        offset: None,
        duration: None,
    };

    let mut project = Project::default();
    let solved = project.solve(
        Some(ProjectData {
            tasks,
            resources: vec![resource("dev1"), resource("dev2")],
            assignments: vec![assign("aA", "A"), assign("aB", "B")],
            ..Default::default()
        }),
        &SolveOptions::default(),
    )?;

    let mut changes = SchedulingChanges::default();
    let mut update = AssignmentUpdate::new("aB");
    update.resource = Patch::Value("dev2".into());
    changes.assignments.update.push(update);
    let candidates = [Candidate { id: "B-to-dev2".into(), changes }];

    let options = EvaluateOptions {
        baseline_id: solved.baseline_id.clone(),
        level: Some(LevelingOptions::default()),
        focus: vec!["B".into()],
        clear_first: true,
    };
    let result = project.evaluate(&candidates, &options)?;
    for candidate in &result.candidates {
        println!("{:?}: cost {:?}, leveled {:?}", candidate.id, candidate.cost, candidate.leveled);
    }
    Ok(())
}