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


# Project::solve

Schedules a [`Project`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md>) and accepts the scheduled dates as its baseline. Call it with `Some(data)` to load a project and with `None` to reschedule after [`apply`](<https://docs.dhtmlx.com/planning-engine/rust/api/project-apply.md>). Every other read-only call ([`preview`](<https://docs.dhtmlx.com/planning-engine/rust/api/project-preview.md>), [`evaluate`](<https://docs.dhtmlx.com/planning-engine/rust/api/project-evaluate.md>), [`suggest_assignments`](<https://docs.dhtmlx.com/planning-engine/rust/api/project-suggest-assignments.md>) and the rest) needs a solved project.

For a one-shot run without keeping state, use [`schedule`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/schedule.md>).

## Signature

```rust
pub fn solve(
    &mut self,
    data: Option<ProjectData>,
    options: &SolveOptions,
) -> Result<Arc<ScheduleResult>, Error>
```

Takes `&mut self`. Synchronous and CPU-only.

## Parameters

| Name | Type | Description |
|---|---|---|
| `data` | `Option<`[`ProjectData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/project-data.md>)`>` | Replacement data, or `None` to schedule the current state |
| `options` | `&`[`SolveOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/solve-options.md>) | Subtree, analytics and reporting switches |

## Behavior

- `Some(data)` validates the replacement data, installs it, schedules it and accepts the result. The previous data is discarded. How `data` is validated or repaired depends on [`ProjectOptions::validation`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project-options.md>).
- `None` schedules the current state when it has unsolved edits. When the project is already solved with equal `options`, it returns the cached result without scheduling again. The cache key is the whole `SolveOptions` value.

Tasks touched by successful [`apply`](<https://docs.dhtmlx.com/planning-engine/rust/api/project-apply.md>) calls since the last solve appear in `updates` as direct updates, even when the planner leaves them where the edit put them. A cyclic project returns unchanged dates and keeps those tasks pending.

The result's `baseline_id` names the accepted baseline. It changes only when the data or the dates change, so solving an unchanged state keeps it.

## Returns

The [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md>) behind an `Arc`, shared with the project's cache.

## Errors

| Error | When |
|---|---|
| [`Error::Input`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/error.md>) | `Some(data)` fails validation. The previous state and cached result are kept. |
| [`Error::Project(ProjectError::NeedsResync)`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/project-error.md>) | `None` after an `apply` rejected an operation and no `solve(Some(data))` followed |

## Example

Load a project, commit an edit and reschedule it.

```rust
use plen_core::{
    DateTime, Patch, Project, ProjectData, SchedulingChanges, SolveOptions, TaskData, TaskUpdate,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let monday = DateTime::from_ymd_hm(2026, 8, 10, 8, 0).ok_or("invalid date")?;
    let mut task = TaskData::new("a");
    task.start_date = Some(monday);
    task.duration = Some(2.0);
    let data = ProjectData {
        tasks: vec![task],
        ..Default::default()
    };

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

    // Nothing changed: the cached result comes back with the same token.
    let again = project.solve(None, &options)?;
    assert_eq!(again.baseline_id, loaded.baseline_id);

    let mut update = TaskUpdate::new("a");
    update.duration = Patch::Value(3.0);
    let mut changes = SchedulingChanges::default();
    changes.tasks.update.push(update);
    project.apply(&changes)?;

    let result = project.solve(None, &options)?;
    for update in &result.updates {
        if let (Some(start), Some(end)) = (update.start_date, update.end_date) {
            println!("task {}: {start} .. {end}", update.id); // task a: 2026-08-10 08:00 .. 2026-08-13 08:00
        }
    }
    Ok(())
}
```

## Related

- [`Project`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#solve>) - the type and its other methods
- [`SolveOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/solve-options.md>) - what to schedule and report
- [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md>) - the returned result
- [Scheduling a Project](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/scheduling-a-project.md#solving-a-project>) - loading and solving a project
- [Data Updates](<https://docs.dhtmlx.com/planning-engine/rust/live-projects/editing-a-project.md#rescheduling>) - rescheduling after `apply`
