AssignmentUpdate#

A partial edit of one existing assignment, matched by its id. Create one with AssignmentUpdate::new, set the fields to change, and pass it in SchedulingChanges::assignments .update to Project::apply.

Declaration#

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

Constructors#

AssignmentUpdate::new#

pub fn new(id: impl Into<Id>) -> AssignmentUpdate

Creates an update for the assignment with this id. Every other field starts at Patch::Missing, so the update changes nothing until a field is set.

Parameters

Name Type Description
id impl Into<Id> Id of the assignment to edit.

Returns an AssignmentUpdate with only id set.

Fields#

Every field except id is a Patch. Each field maps to the AssignmentData field of the same name, which describes how the value is read.

Field Type Patch::Null
id Id not a patch
task Patch<Id> rejected (required field)
resource Patch<Id> rejected (required field)
value Patch<f64> rejected (required field)
offset Patch<f64> clears it: the slice starts at the task start
duration Patch<f64> clears it: the slice runs to the task end

id#

pub id: Id

Id of the assignment to edit. It must match the id of an assignment already in the project. The id itself can't be changed.

task#

pub task: Patch<Id>

Moves the assignment to another task. The new id must name a task in the project.

resource#

pub resource: Patch<Id>

Hands the assignment to another resource. The new id must name a resource in the project.

value#

pub value: Patch<f64>

New utilization rate. Must be finite and greater than 0.

offset#

pub offset: Patch<f64>

New start of the slice, as worked time from the task start. Must be finite and >= 0. Patch::Null clears it, which is the same as 0.

duration#

pub duration: Patch<f64>

New amount of worked time the slice books. Must be finite and greater than 0. Patch::Null clears it, so the slice runs to the task end.

Changes to offset and duration never move task dates. Changes to task and resource move dates only when a task ends up working on other calendars; see Resources and assignments.

Errors#

Project::apply lists a rejected update in ApplyReport::rejected at the path assignments.update[i]. An update is rejected when:

  • no assignment has this id
  • task, resource or value is Patch::Null
  • task or resource names an id that isn't in the project
  • value, offset or duration is out of the range given for that field above
  • in CalendarMode::Resource, the task's calendar and the resource's calendar share no weekly working time

Example#

Hand the second half of a 4-day task from anna to ben: anna keeps the first 2 days of work, and ben's assignment moves to start after them and run to the task end.

use plen_core::{
    AssignmentData, AssignmentUpdate, DateTime, Patch, Project, ProjectData, ResourceData,
    SchedulingChanges, 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: None,
            },
            AssignmentData {
                id: Some("build:ben".into()),
                task: "build".into(),
                resource: "ben".into(),
                value: 1.0,
                offset: None,
                duration: None,
            },
        ],
        ..Default::default()
    };

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

    let mut anna = AssignmentUpdate::new("build:anna");
    anna.duration = Patch::Value(2.0);

    let mut ben = AssignmentUpdate::new("build:ben");
    ben.offset = Patch::Value(2.0);
    ben.duration = Patch::Null;

    let mut changes = SchedulingChanges::default();
    changes.assignments.update = vec![anna, ben];

    let report = project.apply(&changes)?;
    assert!(report.rejected.is_empty());
    Ok(())
}