TaskSlack#

The total and free slack of one task. It isn't built by hand. Read it from Analytics::slack, which is Some when the run asked for it with AnalyticsOptions::slack. For how slack is computed and which tasks get an entry, see Critical path and slack.

Declaration#

#[derive(Clone, Debug, PartialEq)]
pub struct TaskSlack {
    pub task: Id,
    pub total: f64,
    pub free: f64,
}

With the serde feature, it also implements Serialize and Deserialize.

Fields#

Name Type Description
task Id Id of the task.
total f64 Total slack: how far the task can slip before the project finish moves. Zero or negative on a critical task.
free f64 Free slack: how far the task can slip before it pushes a successor. Never more than total, and equal to total when the task has no successors.

Both values are in the configured TimeUnit (duration_unit in SchedulingConfig) and count working time on the task's calendar. A completed task (with use_progress and progress == 1) reports 0.0 for both.

Serialization#

With the serde feature, a TaskSlack serializes as:

{ "task": "docs", "total": 2.0, "free": 2.0 }

Example#

Schedule two tasks that both wait for a third, then print each task's slack.

use plen_core::{
    schedule, AnalyticsOptions, DateTime, LinkData, LinkType, ProjectData, SolveOptions, TaskData,
};

fn task(id: &str, start: DateTime, duration: f64) -> TaskData {
    let mut task = TaskData::new(id);
    task.start_date = Some(start);
    task.duration = Some(duration);
    task
}

fn fs(id: &str, source: &str, target: &str) -> LinkData {
    LinkData {
        id: id.into(),
        source: source.into(),
        target: target.into(),
        link_type: LinkType::Fs,
        lag: 0.0,
        lag_unit: None,
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let monday = DateTime::from_ymd_hm(2026, 8, 10, 8, 0).ok_or("invalid date")?;
    let data = ProjectData {
        tasks: vec![
            task("design", monday, 2.0),
            task("build", monday, 3.0),
            task("docs", monday, 1.0),
        ],
        links: vec![fs("l1", "design", "build"), fs("l2", "design", "docs")],
        ..Default::default()
    };

    let options = SolveOptions {
        analytics: AnalyticsOptions {
            slack: true,
            ..Default::default()
        },
        ..Default::default()
    };
    let result = schedule(data, &options)?;

    let analytics = result.analytics.ok_or("analytics not requested")?;
    for s in analytics.slack.iter().flatten() {
        println!("{}: total {} free {}", s.task, s.total, s.free);
    }
    // design: total 0 free 0
    // build: total 0 free 0
    // docs: total 2 free 2
    Ok(())
}