1use crate::serde::new_bounded_string;
33use tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec;
34use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::FieldType;
35use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::jobs::{
36 JobDefinition, JobMetadata,
37};
38use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::types::PricingModel;
39
40pub trait IntoJobMetadata {
44 fn into_job_metadata(self) -> JobMetadata;
50}
51
52impl<T> IntoJobMetadata for T {
53 fn into_job_metadata(self) -> JobMetadata {
54 JobMetadata {
55 name: new_bounded_string(core::any::type_name::<T>().to_string()),
56 description: None,
57 }
58 }
59}
60
61pub trait IntoJobDefinition<T> {
65 fn into_job_definition(self) -> JobDefinition;
71}
72
73pub trait IntoTangleFieldTypes {
77 fn into_tangle_fields() -> Vec<FieldType>;
83}
84
85impl IntoTangleFieldTypes for blueprint_core::job::result::Void {
86 fn into_tangle_fields() -> Vec<FieldType> {
87 Vec::from([FieldType::Void])
88 }
89}
90
91impl<F, Fut, Res> IntoJobDefinition<((),)> for F
95where
96 F: FnOnce() -> Fut + Clone + Send + Sync + 'static,
97 Fut: Future<Output = Res> + Send,
98 Res: IntoTangleFieldTypes,
99{
100 fn into_job_definition(self) -> JobDefinition {
101 JobDefinition {
102 metadata: self.into_job_metadata(),
103 params: BoundedVec(Vec::new()),
104 result: BoundedVec(Res::into_tangle_fields()),
105 pricing_model: PricingModel::PayOnce { amount: 0 },
106 }
107 }
108}
109
110macro_rules! impl_into_job_definition {
114 (
115 [$($ty:ident),*], $last:ident
116 ) => {
117 impl_into_job_definition!([$($ty),*], $last, PricingModel::PayOnce { amount: 0 });
118 };
119
120 (
121 [$($ty:ident),*], $last:ident, $pricing_model:expr
122 ) => {
123 impl<F, Fut, Res, $($ty,)* $last> IntoJobDefinition<((), $($ty,)* $last,)> for F
124 where
125 F: FnOnce($($ty,)* $last,) -> Fut + Clone + Send + Sync + 'static,
126 Fut: Future<Output = Res> + Send,
127 $last: IntoTangleFieldTypes,
128 Res: IntoTangleFieldTypes,
129 {
130 fn into_job_definition(self) -> JobDefinition {
131 JobDefinition {
132 metadata: self.into_job_metadata(),
133 params: BoundedVec($last::into_tangle_fields()),
134 result: BoundedVec(Res::into_tangle_fields()),
135 pricing_model: $pricing_model,
136 }
137 }
138 }
139 };
140}
141
142blueprint_core::all_the_tuples!(impl_into_job_definition);
145
146#[cfg(test)]
147#[allow(clippy::type_complexity)]
148mod tests {
149 use blueprint_core::extract::Context;
150
151 use super::*;
152 use crate::extract::{
153 TangleArg, TangleArgs2, TangleArgs3, TangleArgs4, TangleArgs5, TangleArgs6, TangleArgs7,
154 TangleArgs8, TangleArgs9, TangleArgs10, TangleArgs11, TangleArgs12, TangleArgs13,
155 TangleArgs14, TangleArgs15, TangleArgs16, TangleResult,
156 };
157
158 macro_rules! count {
159 ($val:ident, $($rest:tt)*) => {
160 1 + count!($($rest)*)
161 };
162 ($val:ident) => {
163 1
164 };
165 () => {
166 0
167 }
168 }
169
170 async fn empty() -> TangleResult<u64> {
171 TangleResult(0)
172 }
173
174 #[test]
175 fn empty_test() {
176 let empty = empty.into_job_definition();
177 assert_eq!(
178 empty.metadata.name.0.0,
179 b"blueprint_tangle_extra::metadata::job_definition::tests::empty",
180 "expected empty, got {}",
181 std::str::from_utf8(&empty.metadata.name.0.0).unwrap()
182 );
183 assert!(empty.params.0.is_empty());
184 assert_eq!(empty.result.0[0], FieldType::Uint64);
185 }
186
187 #[derive(Debug, Default, Clone, Copy)]
188 struct MyContext {
189 foo: u64,
190 }
191
192 #[derive(Debug)]
193 struct MyError;
194
195 impl std::fmt::Display for MyError {
196 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197 write!(f, "MyError")
198 }
199 }
200
201 impl std::error::Error for MyError {}
202
203 macro_rules! definition_tests {
204 ($($func:ident($args_ty:ident($($args:ident),*): $ty:ty));* $(;)?) => {
205 $(
206 #[allow(unused_variables)]
207 async fn $func($args_ty($($args),*): $ty) -> TangleResult<u64> {
208 todo!()
209 }
210
211 paste::paste! {
212 #[allow(unused_variables)]
213 #[allow(clippy::type_complexity)]
214 async fn [<$func _with_context>](Context(ctx): Context<MyContext>, $args_ty($($args),*): $ty) -> TangleResult<u64> {
215 eprintln!("ctx: {:?}", ctx.foo);
216 todo!()
217 }
218
219 #[allow(unused_variables)]
220 #[allow(clippy::type_complexity)]
221 async fn [<$func _with_context_ret_res>](Context(ctx): Context<MyContext>, $args_ty($($args),*): $ty) -> Result<TangleResult<u64>, MyError> {
222 eprintln!("ctx: {:?}", ctx.foo);
223 todo!()
224 }
225
226
227 #[allow(unused_variables)]
228 #[allow(clippy::type_complexity)]
229 async fn [<$func _with_context_ret_opt>](Context(ctx): Context<MyContext>, $args_ty($($args),*): $ty) -> Option<TangleResult<u64>> {
230 eprintln!("ctx: {:?}", ctx.foo);
231 todo!()
232 }
233
234 #[test]
235 fn [<$func _test>]() {
236 let def = $func.into_job_definition();
237 assert_eq!(
238 def.metadata.name.0.0,
239 format!("blueprint_tangle_extra::metadata::job_definition::tests::{}", stringify!($func)).as_bytes(),
240 );
241 assert_eq!(def.params.0.len(), count!($($args),*));
242 assert_eq!(def.result.0.len(), 1);
243 assert!(def.params.0.iter().all(|ty| ty.clone() == FieldType::Uint64));
244 assert_eq!(def.result.0[0], FieldType::Uint64);
245
246 let def2 = [<$func _with_context>].into_job_definition();
247 assert_eq!(
248 def2.metadata.name.0.0,
249 format!("blueprint_tangle_extra::metadata::job_definition::tests::{}", stringify!([<$func _with_context>])).as_bytes(),
250 );
251 assert_eq!(def2.params.0.len(), count!($($args),*));
252 assert_eq!(def2.result.0.len(), 1);
253 assert!(def2.params.0.iter().all(|ty| ty.clone() == FieldType::Uint64));
254 assert_eq!(def2.result.0[0], FieldType::Uint64);
255
256
257 let def3 = [<$func _with_context_ret_res>].into_job_definition();
258 assert_eq!(
259 def3.metadata.name.0.0,
260 format!("blueprint_tangle_extra::metadata::job_definition::tests::{}", stringify!([<$func _with_context_ret_res>])).as_bytes(),
261 );
262 assert_eq!(def3.params.0.len(), count!($($args),*));
263 assert_eq!(def3.result.0.len(), 1);
264 assert!(def3.params.0.iter().all(|ty| ty.clone() == FieldType::Uint64));
265 assert_eq!(def3.result.0[0], FieldType::Uint64);
266
267 let def4 = [<$func _with_context_ret_opt>].into_job_definition();
268 assert_eq!(
269 def4.metadata.name.0.0,
270 format!("blueprint_tangle_extra::metadata::job_definition::tests::{}", stringify!([<$func _with_context_ret_opt>])).as_bytes(),
271 );
272 assert_eq!(def4.params.0.len(), count!($($args),*));
273 assert_eq!(def4.result.0.len(), 1);
274 assert!(def4.params.0.iter().all(|ty| ty.clone() == FieldType::Uint64));
275 assert_eq!(def4.result.0[0], FieldType::Uint64);
276
277 }
278 }
279 )*
280 }
281 }
282
283 definition_tests!(
284 xsquare(TangleArg(x): TangleArg<u64>);
285 xsquare2(TangleArgs2(x, y): TangleArgs2<u64, u64>);
286 xsquare3(TangleArgs3(x, y, z): TangleArgs3<u64, u64, u64>);
287 xsquare4(TangleArgs4(x, y, z, w): TangleArgs4<u64, u64, u64, u64>);
288 xsquare5(TangleArgs5(x, y, z, w, v): TangleArgs5<u64, u64, u64, u64, u64>);
289 xsquare6(TangleArgs6(x, y, z, w, v, u): TangleArgs6<u64, u64, u64, u64, u64, u64>);
290 xsquare7(TangleArgs7(x, y, z, w, v, u, t): TangleArgs7<u64, u64, u64, u64, u64, u64, u64>);
291 xsquare8(TangleArgs8(x, y, z, w, v, u, t, s): TangleArgs8<u64, u64, u64, u64, u64, u64, u64, u64>);
292 xsquare9(TangleArgs9(x, y, z, w, v, u, t, s, r): TangleArgs9<u64, u64, u64, u64, u64, u64, u64, u64, u64>);
293 xsquare10(TangleArgs10(x, y, z, w, v, u, t, s, r, q): TangleArgs10<u64, u64, u64, u64, u64, u64, u64, u64, u64, u64>);
294 xsquare11(TangleArgs11(x, y, z, w, v, u, t, s, r, q, p): TangleArgs11<u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64>);
295 xsquare12(TangleArgs12(x, y, z, w, v, u, t, s, r, q, p, o): TangleArgs12<u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64>);
296 xsquare13(TangleArgs13(x, y, z, w, v, u, t, s, r, q, p, o, n): TangleArgs13<u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64>);
297 xsquare14(TangleArgs14(x, y, z, w, v, u, t, s, r, q, p, o, n, m): TangleArgs14<u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64>);
298 xsquare15(TangleArgs15(x, y, z, w, v, u, t, s, r, q, p, o, n, m, l): TangleArgs15<u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64>);
299 xsquare16(TangleArgs16(x, y, z, w, v, u, t, s, r, q, p, o, n, m, l, k): TangleArgs16<u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64>);
300 );
301}