LevelingResult#

The outcome of a leveling run: the leveled schedule and how many rounds proposed a correction. Project::level returns it.

Declaration#

#[derive(Clone, Debug, PartialEq)]
pub struct LevelingResult {
    pub schedule: Arc<ScheduleResult>,
    pub rounds: u32,
}

Arc is std::sync::Arc. Field access goes through it, so result.schedule.updates works directly.

Fields#

Field Type Description
schedule Arc<ScheduleResult> The schedule level accepted as the new baseline.
rounds u32 Number of leveling rounds that proposed a correction.

schedule#

pub schedule: Arc<ScheduleResult>

The final solved state after leveling. Its baseline_id is the accepted baseline.

Its updates are diffed against the state the level call started from. Entries include the leveling_delay and pauses values leveling set.

For the meaning of every field, see ScheduleResult.

rounds#

pub rounds: u32

Counts only rounds that proposed a delay or a pause. It's 0 when nothing needed leveling, and when the project is cyclic: then schedule is the initial solve's result, with its cycle diagnostics, and no delay or pause is edited.

rounds is separate from the planner's iteration count. Planner convergence is schedule.converged.

Serialization#

With the serde feature, LevelingResult implements Serialize and Deserialize. The fields of schedule are flattened into the top-level object, next to rounds:

{
  "baselineId": "...",
  "updates": [],
  "cycles": [],
  "conflicts": [],
  "notes": [],
  "converged": true,
  "rounds": 2
}

Example#

use plen_core::{
    AssignmentData, DateTime, LevelingOptions, Project, ProjectData, ResourceData, SolveOptions,
    TaskData,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Monday, August 10, 2026: two tasks book the same developer full time.
    let monday = DateTime::from_ymd_hm(2026, 8, 10, 8, 0).ok_or("invalid date")?;
    let task = |id: &str| {
        let mut task = TaskData::new(id);
        task.start_date = Some(monday);
        task.duration = Some(2.0);
        task
    };
    let booking = |task: &str| AssignmentData {
        id: None,
        task: task.into(),
        resource: "dev".into(),
        value: 1.0,
        offset: None,
        duration: None,
    };

    let data = ProjectData {
        tasks: vec![task("a"), task("b")],
        resources: vec![ResourceData { id: "dev".into(), capacity: None, calendar_id: None }],
        assignments: vec![booking("a"), booking("b")],
        ..Default::default()
    };

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

    let result = project.level(&LevelingOptions::default())?;
    println!("rounds: {}", result.rounds);
    for update in &result.schedule.updates {
        println!("{}: start {:?}, delay {:?}", update.id, update.start_date, update.leveling_delay);
    }
    Ok(())
}

For how leveling picks tasks and sizes delays, see Leveling resources.