LevelingOptions#

Options for a leveling run. Pass them to Project::level, or set them as level in EvaluateOptions and SuggestOptions to score candidates at the leveled stage. LevelingOptions::default() levels the whole project with the standard order. Override single fields with struct update syntax.

Declaration#

#[derive(Clone, Debug)]
pub struct LevelingOptions {
    pub scope: Option<LevelingScope>,
    pub basis: Option<Basis>,
    pub order: LevelingOrder,
    pub within_slack_only: bool,
    pub clear_first: bool,
    pub allow_splits: bool,
    pub step: LevelingStep,
}

impl Default for LevelingOptions

Fields#

scope#

pub scope: Option<LevelingScope>

Resources whose contention is resolved, tasks that may move, and the date range that triggers leveling. None covers the whole project.

Default: None.

basis#

pub basis: Option<Basis>

Bucket size for detecting overallocation. None derives it from SchedulingConfig::duration_unit: Day for a day unit, Hour for hour and minute.

Default: None.

order#

pub order: LevelingOrder

Order in which contenders are picked to move.

Default: LevelingOrder::Standard.

within_slack_only#

pub within_slack_only: bool

true never moves a task past its total slack, so the project end of the base plan doesn't move. A window that can't be cleared this way stays with reason slack-exhausted.

Default: false.

clear_first#

pub clear_first: bool

true resets leveling_delay and pauses on in-scope tasks before leveling. false keeps them, and leveling adds to them.

Default: true.

allow_splits#

pub allow_splits: bool

true lets leveling split a task by adding or extending pauses. false makes every correction a whole-task leveling_delay; existing pauses are kept unless clear_first clears them.

Default: true.

step#

pub step: LevelingStep

How far one correction moves a task.

Default: LevelingStep::Minimal.

See Project::level for the errors.

Example#

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

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

    // Two tasks on the same resource at the same time.
    let mut tasks = Vec::new();
    let mut assignments = Vec::new();
    for id in ["a", "b"] {
        let mut task = TaskData::new(id);
        task.start_date = Some(start);
        task.duration = Some(2.0);
        tasks.push(task);
        assignments.push(AssignmentData {
            id: None,
            task: id.into(),
            resource: "r1".into(),
            value: 1.0,
            offset: None,
            duration: None,
        });
    }

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

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

    // Only task "b" may move, picked by priority, without splits.
    let options = LevelingOptions {
        scope: Some(LevelingScope {
            tasks: Some(vec!["b".into()]),
            ..Default::default()
        }),
        order: LevelingOrder::Priority,
        allow_splits: false,
        ..Default::default()
    };

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