use std::collections::BTreeSet;
use onetaskgraph_plugin_api::{
DependencyEdge, DependencyEndpoint, ItemKind, NativeId, SourceName, Task, TaskUpdate,
UpdatedField,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::copy::{Spent, readings, spent_between};
use super::delivery::{Delivered, qualified_task, source_failed, targets};
use super::narrow::holds_priority;
use super::{Engine, EngineError};
use crate::GlobalId;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct TaskUpdated {
pub id: GlobalId,
pub task: Task,
pub written: BTreeSet<UpdatedField>,
pub delivered: Vec<Delivered>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub spent: Option<Spent>,
}
impl Engine {
pub async fn update_task(
&self,
id: &GlobalId,
update: &TaskUpdate,
) -> Result<TaskUpdated, EngineError> {
let source = self.built(&id.source)?;
if !source.source().writes().is_supported() {
return Err(EngineError::UpdateNotWritable {
name: source.name().to_string(),
kind: source.kind().to_owned(),
});
}
update
.consistent()
.map_err(|error| source_failed(source, error))?;
if let Some(priority) = update.priority {
holds_priority(source, &id.to_string(), priority)?;
}
let update = TaskUpdate {
depends_on: update
.depends_on
.as_ref()
.map(|edges| near_edges(edges, &id.native, &id.source)),
..update.clone()
};
let before = readings(&[source]).await;
let outcome = source
.source()
.update_task(&id.native, &update)
.await
.map_err(|error| source_failed(source, error))?
.ok_or_else(|| EngineError::NoSuchTask { id: id.to_string() })?;
let delivered = if update.status.is_some() || update.delivers.is_some() {
let now = targets(&outcome.task.delivers, &id.source);
let dropped = targets(&outcome.delivers_before, &id.source);
self.deliver(id, outcome.task.status.category, &now, &dropped)
.await
} else {
Vec::new()
};
let spent = spent_between(&before, &readings(&[source]).await);
Ok(TaskUpdated {
id: id.clone(),
task: qualified_task(id.clone(), outcome.task).item,
written: outcome.written,
delivered,
spent,
})
}
}
fn near_edges(
edges: &[DependencyEdge],
near: &NativeId,
source: &SourceName,
) -> Vec<DependencyEdge> {
edges
.iter()
.map(|edge| {
let to = match edge.to.id().parse::<GlobalId>() {
Ok(far) if &far.source == source => {
DependencyEndpoint::from_native(far.native, edge.to.kind)
}
_ => edge.to.clone(),
};
DependencyEdge {
from: DependencyEndpoint::from_native(near.clone(), ItemKind::Task),
to,
kind: edge.kind,
}
})
.collect()
}