onetaskgraph_core/engine/
metadata.rs1use onetaskgraph_plugin_api::{Location, MetadataKey, MetadataRecord, SourceError};
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15use serde_json::Value;
16
17use super::delivery::source_failed;
18use super::{Engine, EngineError};
19use crate::GlobalId;
20use crate::resolve::ResolvedSource;
21
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
24pub struct MetadataSet {
25 pub id: GlobalId,
27 pub key: MetadataKey,
29 pub value: Value,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub location: Option<Location>,
35}
36
37impl Engine {
38 pub async fn set_task_metadata(
48 &self,
49 id: &GlobalId,
50 key: &MetadataKey,
51 value: &Value,
52 ) -> Result<MetadataSet, EngineError> {
53 let source = self.metadata_writable(id, MetadataRecord::Task)?;
54 let task = source
55 .source()
56 .set_task_metadata(&id.native, key, value)
57 .await
58 .map_err(|error| source_failed(source, error))?
59 .ok_or_else(|| EngineError::NoSuchTask { id: id.to_string() })?;
60 answer(source, id, key, &task.metadata, task.location)
61 }
62
63 pub async fn set_project_metadata(
70 &self,
71 id: &GlobalId,
72 key: &MetadataKey,
73 value: &Value,
74 ) -> Result<MetadataSet, EngineError> {
75 let source = self.metadata_writable(id, MetadataRecord::Project)?;
76 let project = source
77 .source()
78 .set_project_metadata(&id.native, key, value)
79 .await
80 .map_err(|error| source_failed(source, error))?
81 .ok_or_else(|| EngineError::NoSuchProject { id: id.to_string() })?;
82 answer(source, id, key, &project.metadata, project.location)
83 }
84
85 pub async fn set_document_metadata(
93 &self,
94 id: &GlobalId,
95 key: &MetadataKey,
96 value: &Value,
97 ) -> Result<MetadataSet, EngineError> {
98 let source = self.metadata_writable(id, MetadataRecord::Document)?;
99 let document = source
100 .source()
101 .set_document_metadata(&id.native, key, value)
102 .await
103 .map_err(|error| source_failed(source, error))?
104 .ok_or_else(|| EngineError::NoSuchDocument { id: id.to_string() })?;
105 answer(source, id, key, &document.metadata, document.location)
106 }
107
108 fn metadata_writable(
110 &self,
111 id: &GlobalId,
112 record: MetadataRecord,
113 ) -> Result<&ResolvedSource, EngineError> {
114 let source = self.built(&id.source)?;
115 if !source.source().writes().is_supported() {
116 return Err(EngineError::MetadataNotWritable {
117 name: source.name().to_string(),
118 kind: source.kind().to_owned(),
119 record,
120 });
121 }
122 if record == MetadataRecord::Document
123 && !source.source().capabilities().documents.is_native()
124 {
125 return Err(EngineError::NoDocuments {
126 name: source.name().to_string(),
127 kind: source.kind().to_owned(),
128 });
129 }
130 Ok(source)
131 }
132}
133
134fn answer(
139 source: &ResolvedSource,
140 id: &GlobalId,
141 key: &MetadataKey,
142 metadata: &std::collections::BTreeMap<String, Value>,
143 location: Option<Location>,
144) -> Result<MetadataSet, EngineError> {
145 let value = metadata.get(key.as_str()).cloned().ok_or_else(|| {
146 source_failed(
147 source,
148 SourceError::Malformed {
149 message: format!(
150 "the record {id} it answered the write with does not hold the key {key} it \
151 was just written under"
152 ),
153 },
154 )
155 })?;
156 Ok(MetadataSet {
157 id: id.clone(),
158 key: key.clone(),
159 value,
160 location,
161 })
162}