AssignmentData#

One resource booked on one task at a given rate. Assignments go in ProjectData::assignments, and new ones are added to a live project through SchedulingChanges::assignments .add. Build it with a struct literal; it has no constructor and no Default.

Declaration#

#[derive(Clone, Debug)]
pub struct AssignmentData {
    pub id: Option<Id>,
    pub task: Id,
    pub resource: Id,
    pub value: f64,
    pub offset: Option<f64>,
    pub duration: Option<f64>,
}

Fields#

Field Type Default Description
id Option<Id> None Assignment id. None gets a generated id on import.
task Id required The task being worked on.
resource Id required The resource doing the work.
value f64 required Utilization rate: 1.0 is 100 % of one unit of capacity.
offset Option<f64> None (0) Worked time from the task start where the slice begins.
duration Option<f64> None (to the task end) Worked time the slice books.

id#

pub id: Option<Id>

The key that AssignmentUpdate and SchedulingChanges::assignments.remove refer to. A generated id never collides with an explicit id in the same list. Give every assignment an explicit id if you plan to edit it later.

task#

pub task: Id

Id of a task in the project. An assignment on an unscheduled task stays in the project but books no load; it's reported as Note::DroppedAssignment and books again when the task is scheduled.

resource#

pub resource: Id

Id of a resource in ProjectData::resources.

value#

pub value: f64

Units of the resource the assignment uses while it works, in the same units as ResourceData::capacity. 0.5 is half-time; 2.0 is two full units. Must be finite and greater than 0.

offset#

pub offset: Option<f64>

Start of the slice, as worked time counted from the task start, in the task's duration_unit. Pause and non-working time don't count. None is the same as Some(0.0). A slice that starts at or after the end of the task's work books nothing.

duration#

pub duration: Option<f64>

Amount of worked time the slice books, in the task's duration_unit. None books up to the task end. A slice that runs past the end of the task's work is cut at the task end when load is booked, so it follows a task that shrinks later.

offset and duration change resource load only. They never change task dates. For how slices are measured and booked, see Resources and assignments.

Errors#

The snapshot is validated when it's passed to schedule or Project::solve(Some(data)). Under Validation::Strict, an assignment fails with Error::Input at these paths:

Path When
assignments[i].id the id repeats the id of an earlier assignment
assignments[i].task no task has this id
assignments[i].resource no resource has this id
assignments[i].value value isn't finite or is <= 0
assignments[i].offset offset isn't finite or is < 0
assignments[i].duration duration isn't finite or is <= 0
assignments[i] in CalendarMode::Resource, the task's own calendar and the resource's calendar share no weekly working time

The offset and duration checks apply to assignments on unscheduled tasks too.

Validation::Repair keeps the last of several assignments with the same id, and clears an invalid offset or duration to None. Each fix is reported as Note::Repaired.

An invalid AssignmentData in SchedulingChanges::assignments.add is a rejected operation in the ApplyReport, not an Err.

Example#

Two people share one 4-day task: anna books the first 2 days of work at full time, ben books the rest at half time.

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

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

    let data = ProjectData {
        tasks: vec![TaskData {
            start_date: Some(start),
            duration: Some(4.0),
            ..TaskData::new("build")
        }],
        resources: vec![
            ResourceData { id: "anna".into(), capacity: None, calendar_id: None },
            ResourceData { id: "ben".into(), capacity: None, calendar_id: None },
        ],
        assignments: vec![
            AssignmentData {
                id: Some("build:anna".into()),
                task: "build".into(),
                resource: "anna".into(),
                value: 1.0,
                offset: None,
                duration: Some(2.0),
            },
            AssignmentData {
                id: Some("build:ben".into()),
                task: "build".into(),
                resource: "ben".into(),
                value: 0.5,
                offset: Some(2.0),
                duration: None,
            },
        ],
        ..Default::default()
    };

    let result = schedule(data, &SolveOptions::default())?;
    println!("{:?}", result.overallocations);
    Ok(())
}

For a step-by-step handover between people, see Split assignment work.