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