use onetaskgraph_plugin_api::{Location, MetadataKey, MetadataRecord, SourceError};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use super::delivery::source_failed;
use super::{Engine, EngineError};
use crate::GlobalId;
use crate::resolve::ResolvedSource;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct MetadataSet {
pub id: GlobalId,
pub key: MetadataKey,
pub value: Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub location: Option<Location>,
}
impl Engine {
pub async fn set_task_metadata(
&self,
id: &GlobalId,
key: &MetadataKey,
value: &Value,
) -> Result<MetadataSet, EngineError> {
let source = self.metadata_writable(id, MetadataRecord::Task)?;
let task = source
.source()
.set_task_metadata(&id.native, key, value)
.await
.map_err(|error| source_failed(source, error))?
.ok_or_else(|| EngineError::NoSuchTask { id: id.to_string() })?;
answer(source, id, key, &task.metadata, task.location)
}
pub async fn set_project_metadata(
&self,
id: &GlobalId,
key: &MetadataKey,
value: &Value,
) -> Result<MetadataSet, EngineError> {
let source = self.metadata_writable(id, MetadataRecord::Project)?;
let project = source
.source()
.set_project_metadata(&id.native, key, value)
.await
.map_err(|error| source_failed(source, error))?
.ok_or_else(|| EngineError::NoSuchProject { id: id.to_string() })?;
answer(source, id, key, &project.metadata, project.location)
}
pub async fn set_document_metadata(
&self,
id: &GlobalId,
key: &MetadataKey,
value: &Value,
) -> Result<MetadataSet, EngineError> {
let source = self.metadata_writable(id, MetadataRecord::Document)?;
let document = source
.source()
.set_document_metadata(&id.native, key, value)
.await
.map_err(|error| source_failed(source, error))?
.ok_or_else(|| EngineError::NoSuchDocument { id: id.to_string() })?;
answer(source, id, key, &document.metadata, document.location)
}
fn metadata_writable(
&self,
id: &GlobalId,
record: MetadataRecord,
) -> Result<&ResolvedSource, EngineError> {
let source = self.built(&id.source)?;
if !source.source().writes().is_supported() {
return Err(EngineError::MetadataNotWritable {
name: source.name().to_string(),
kind: source.kind().to_owned(),
record,
});
}
if record == MetadataRecord::Document
&& !source.source().capabilities().documents.is_native()
{
return Err(EngineError::NoDocuments {
name: source.name().to_string(),
kind: source.kind().to_owned(),
});
}
Ok(source)
}
}
fn answer(
source: &ResolvedSource,
id: &GlobalId,
key: &MetadataKey,
metadata: &std::collections::BTreeMap<String, Value>,
location: Option<Location>,
) -> Result<MetadataSet, EngineError> {
let value = metadata.get(key.as_str()).cloned().ok_or_else(|| {
source_failed(
source,
SourceError::Malformed {
message: format!(
"the record {id} it answered the write with does not hold the key {key} it \
was just written under"
),
},
)
})?;
Ok(MetadataSet {
id: id.clone(),
key: key.clone(),
value,
location,
})
}