[Canonical page](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/project-data/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# ProjectData

One complete snapshot of a project: its scheduling config, calendars, tasks, links, resources and assignments. It's passed by value to [`Project::solve`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#solve>) and [`schedule`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/schedule.md>), and by reference to [`Project::analyze`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#analyze>). `ProjectData` is an import format: the project keeps its own normalized copy, and later edits go through [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/scheduling-changes.md>), not through this struct.

Build it with a struct literal and `..Default::default()`. Every collection except `tasks` usually stays empty.

## Declaration

```rust
#[derive(Clone, Debug, Default)]
pub struct ProjectData {
    pub config: SchedulingConfig,
    pub calendars: Vec<CalendarData>,
    pub tasks: Vec<TaskData>,
    pub links: Vec<LinkData>,
    pub resources: Vec<ResourceData>,
    pub assignments: Vec<AssignmentData>,
}
```

## Fields

| Field | Type | Default | Description |
|---|---|---|---|
| `config` | [`SchedulingConfig`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/scheduling-config.md>) | `SchedulingConfig::default()` | Project-wide scheduling settings. |
| `calendars` | `Vec<`[`CalendarData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/calendar-data.md>)`>` | empty | Working-time calendars. Empty means only the built-in `"global"` calendar (Mon-Fri, 08:00-12:00 and 13:00-17:00). A calendar with id `"global"` replaces the built-in one. |
| `tasks` | `Vec<`[`TaskData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/task-data.md>)`>` | empty | Tasks, summaries and milestones. Hierarchy comes from each task's `parent`. |
| `links` | `Vec<`[`LinkData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/link-data.md>)`>` | empty | Dependencies between tasks. |
| `resources` | `Vec<`[`ResourceData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/resource-data.md>)`>` | empty | Resources that assignments book. |
| `assignments` | `Vec<`[`AssignmentData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/assignment-data.md>)`>` | empty | Bookings of a resource on a task. |

## Validation

Under the default [`Validation::Strict`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/validation.md>), the rules below tie the collections together. A broken rule returns [`Err(Error::Input)`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/error.md>) with the path of the offending field. Rules for a single record are on that record's page.

| Rule | Error path |
|---|---|
| Task ids are unique | `tasks[i].id` |
| Link ids are unique | `links[i].id` |
| Calendar ids are unique | `calendars[i]` |
| Resource ids are unique | `resources[i].id` |
| Assignment ids are unique | `assignments[i].id` |
| A task's `parent` names a task in `tasks` (or equals `config.root_id`) | `tasks[i].parent` |
| A task's `calendar_id` names a calendar in `calendars` or `"global"` | `tasks[i].calendar_id` |
| A resource's `calendar_id` names a calendar (checked only in [`CalendarMode::Resource`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/calendar-mode.md>)) | `resources[i].calendar_id` |
| An assignment's `task` names a task in `tasks` | `assignments[i].task` |
| An assignment's `resource` names a resource in `resources` | `assignments[i].resource` |

Uniqueness follows [`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md#equality-and-hashing>) equality.

Some references don't fail validation. They drop the record instead and report a [`Note`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/note.md>) in the result; see `Note::DroppedLink` and `Note::DroppedAssignment` for the cases.

[`Validation::Repair`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/validation.md>) fixes some of these cases instead of rejecting them.

## Example

```rust
use plen_core::{
    schedule, DateTime, LinkData, LinkType, ProjectData, 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(2.0),
                ..TaskData::new(1)
            },
            TaskData {
                start_date: Some(start),
                duration: Some(3.0),
                ..TaskData::new(2)
            },
        ],
        links: vec![LinkData {
            id: 1.into(),
            source: 1.into(),
            target: 2.into(),
            link_type: LinkType::Fs,
            lag: 0.0,
            lag_unit: None,
        }],
        ..Default::default()
    };

    let result = schedule(data, &SolveOptions::default())?;
    for update in &result.updates {
        println!("{update:?}");
    }
    Ok(())
}
```

## Related

- [`Project`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md>) - the live project built from a snapshot
- [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md>) - what scheduling a snapshot returns
- [`Validation`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/validation.md>) - whether invalid records fail the import or get repaired
- [Project data](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/project-data.md>) - building a snapshot field by field
- [Scheduling a Project](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/scheduling-a-project.md>) - passing a snapshot to `schedule` and `Project::solve`
