1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use std::fs::read_to_string;
6use syn::meta::parser;
7use syn::Fields::{Named, Unnamed};
8use syn::{
9 parse_macro_input, parse_quote, parse_str, Field, Fields, ItemImpl, ItemStruct, LitStr, Type,
10 TypeTuple,
11};
12
13use crate::utils::config_id_to_enum;
14use cu29_runtime::config::read_configuration;
15use cu29_runtime::config::CuConfig;
16use cu29_runtime::curuntime::{
17 compute_runtime_plan, find_task_type_for_id, CuExecutionLoop, CuExecutionUnit, CuTaskType,
18};
19use cu29_traits::CuResult;
20
21#[cfg(feature = "macro_debug")]
22use format::{highlight_rust_code, rustfmt_generated_code};
23use proc_macro2::{Ident, Span};
24
25mod format;
26mod utils;
27
28const DEFAULT_CLNB: usize = 10;
30
31#[inline]
32fn int2sliceindex(i: u32) -> syn::Index {
33 syn::Index::from(i as usize)
34}
35
36#[inline(always)]
37fn return_error(msg: String) -> TokenStream {
38 syn::Error::new(Span::call_site(), msg)
39 .to_compile_error()
40 .into()
41}
42
43#[proc_macro]
47pub fn gen_cumsgs(config_path_lit: TokenStream) -> TokenStream {
48 let config = parse_macro_input!(config_path_lit as LitStr).value();
49 if !std::path::Path::new(&config_full_path(&config)).exists() {
50 return return_error(format!(
51 "The configuration file `{}` does not exist. Please provide a valid path.",
52 config
53 ));
54 }
55 #[cfg(feature = "macro_debug")]
56 eprintln!("[gen culist support with {:?}]", config);
57 let cuconfig = match read_config(&config) {
58 Ok(cuconfig) => cuconfig,
59 Err(e) => return return_error(e.to_string()),
60 };
61 let runtime_plan: CuExecutionLoop = match compute_runtime_plan(&cuconfig) {
62 Ok(plan) => plan,
63 Err(e) => return return_error(format!("Could not compute runtime plan: {}", e)),
64 };
65
66 let all_tasks_member_ids: Vec<String> = cuconfig
68 .get_all_nodes()
69 .iter()
70 .map(|(_, node)| utils::config_id_to_struct_member(node.get_id().as_str()))
71 .collect();
72
73 let taskid_order: Vec<usize> = runtime_plan
76 .steps
77 .iter()
78 .filter_map(|unit| match unit {
79 CuExecutionUnit::Step(step) => Some(step.node_id as usize),
80 _ => None,
81 })
82 .collect();
83
84 #[cfg(feature = "macro_debug")]
85 eprintln!(
86 "[The CuMsgs matching tasks ids are {:?}]",
87 taskid_order
88 .iter()
89 .map(|i| all_tasks_member_ids[*i].clone())
90 .collect::<Vec<_>>()
91 );
92
93 let support = gen_culist_support(&runtime_plan, &taskid_order, &all_tasks_member_ids);
94
95 let with_uses = quote! {
96 mod cumsgs {
97 use cu29::bincode::Encode as _Encode;
98 use cu29::bincode::enc::Encoder as _Encoder;
99 use cu29::bincode::error::EncodeError as _EncodeError;
100 use cu29::bincode::Decode as _Decode;
101 use cu29::bincode::de::Decoder as _Decoder;
102 use cu29::bincode::error::DecodeError as _DecodeError;
103 use cu29::copperlist::CopperList as _CopperList;
104 use cu29::cutask::CuMsgMetadata as _CuMsgMetadata;
105 use cu29::cutask::CuMsg as _CuMsg;
106 #support
107 }
108 use cumsgs::CuMsgs;
109 };
110 with_uses.into()
111}
112
113fn gen_culist_support(
115 runtime_plan: &CuExecutionLoop,
116 taskid_call_order: &[usize],
117 all_tasks_as_struct_member_name: &Vec<String>,
118) -> proc_macro2::TokenStream {
119 #[cfg(feature = "macro_debug")]
120 eprintln!("[Extract msgs types]");
121 let all_msgs_types_in_culist_order = extract_msg_types(runtime_plan);
122
123 let culist_size = all_msgs_types_in_culist_order.len();
124 let task_indices: Vec<_> = taskid_call_order
125 .iter()
126 .map(|i| syn::Index::from(*i))
127 .collect();
128
129 #[cfg(feature = "macro_debug")]
130 eprintln!("[build the copperlist tuple]");
131 let msgs_types_tuple: TypeTuple = build_culist_tuple(&all_msgs_types_in_culist_order);
132
133 #[cfg(feature = "macro_debug")]
134 eprintln!("[build the copperlist tuple bincode support]");
135 let msgs_types_tuple_encode = build_culist_tuple_encode(&all_msgs_types_in_culist_order);
136 let msgs_types_tuple_decode = build_culist_tuple_decode(&all_msgs_types_in_culist_order);
137
138 #[cfg(feature = "macro_debug")]
139 eprintln!("[build the copperlist tuple debug support]");
140 let msgs_types_tuple_debug = build_culist_tuple_debug(&all_msgs_types_in_culist_order);
141
142 let collect_metadata_function = quote! {
143 pub fn collect_metadata<'a>(culist: &'a CuList) -> [&'a _CuMsgMetadata; #culist_size] {
144 [#( &culist.msgs.0.#task_indices.metadata, )*]
145 }
146 };
147
148 let methods = itertools::multizip((all_tasks_as_struct_member_name, taskid_call_order)).map(
149 |(name, output_position)| {
150 let fn_name = format_ident!("get_{}_output", name);
151 let payload_type = all_msgs_types_in_culist_order[*output_position].clone();
152 let index = syn::Index::from(*output_position);
153 quote! {
154 pub fn #fn_name(&self) -> &_CuMsg<#payload_type> {
155 &self.0.#index
156 }
157 }
158 },
159 );
160
161 quote! {
163 #collect_metadata_function
164
165 pub struct CuMsgs(#msgs_types_tuple);
166 pub type CuList = _CopperList<CuMsgs>;
167
168 impl CuMsgs {
169 #(#methods)*
170
171 fn get_tuple(&self) -> &#msgs_types_tuple {
172 &self.0
173 }
174
175 fn get_tuple_mut(&mut self) -> &mut #msgs_types_tuple {
176 &mut self.0
177 }
178 }
179
180 #msgs_types_tuple_encode
182 #msgs_types_tuple_decode
183
184 #msgs_types_tuple_debug
186 }
187}
188
189fn gen_sim_support(runtime_plan: &CuExecutionLoop) -> proc_macro2::TokenStream {
190 #[cfg(feature = "macro_debug")]
191 eprintln!("[Sim: Build SimEnum]");
192 let plan_enum: Vec<proc_macro2::TokenStream> = runtime_plan
193 .steps
194 .iter()
195 .map(|unit| match unit {
196 CuExecutionUnit::Step(step) => {
197 let enum_entry_name = config_id_to_enum(step.node.get_id().as_str());
198 let enum_ident = Ident::new(&enum_entry_name, proc_macro2::Span::call_site());
199 let inputs: Vec<Type> = step
200 .input_msg_indices_types
201 .iter()
202 .map(|(_, t)| parse_str::<Type>(format!("_CuMsg<{t}>").as_str()).unwrap())
203 .collect();
204 let output: Option<Type> = step
205 .output_msg_index_type
206 .as_ref()
207 .map(|(_, t)| parse_str::<Type>(format!("_CuMsg<{t}>").as_str()).unwrap());
208 let no_output = parse_str::<Type>("_CuMsg<()>").unwrap();
209 let output = output.as_ref().unwrap_or(&no_output);
210 quote! {
211 #enum_ident(cu29::simulation::CuTaskCallbackState<'cl, (#(&'cl #inputs),*), &'cl mut #output>)
212 }
213 }
214 CuExecutionUnit::Loop(_) => {
215 todo!("Needs to be implemented")
216 }
217 })
218 .collect();
219 quote! {
220 pub enum SimStep<'cl> {
221 #(#plan_enum),*
222 }
223 }
224}
225
226#[proc_macro_attribute]
230pub fn copper_runtime(args: TokenStream, input: TokenStream) -> TokenStream {
231 #[cfg(feature = "macro_debug")]
232 eprintln!("[entry]");
233 let mut item_struct = parse_macro_input!(input as ItemStruct);
234 let mut config_file: Option<LitStr> = None;
235 let mut sim_mode = false;
236
237 let attribute_config_parser = parser(|meta| {
239 if meta.path.is_ident("config") {
240 config_file = Some(meta.value()?.parse()?);
241 Ok(())
242 } else if meta.path.is_ident("sim_mode") {
243 if meta.input.peek(syn::Token![=]) {
245 meta.input.parse::<syn::Token![=]>()?;
246 let value: syn::LitBool = meta.input.parse()?;
247 sim_mode = value.value();
248 Ok(())
249 } else {
250 sim_mode = true;
252 Ok(())
253 }
254 } else {
255 Err(meta.error("unsupported property"))
256 }
257 });
258
259 #[cfg(feature = "macro_debug")]
260 eprintln!("[parse]");
261 parse_macro_input!(args with attribute_config_parser);
263
264 let config_file = match config_file {
266 Some(file) => file.value(),
267 None => {
268 return return_error(
269 "Expected config file attribute like #[CopperRuntime(config = \"path\")]"
270 .to_string(),
271 )
272 }
273 };
274
275 if !std::path::Path::new(&config_full_path(&config_file)).exists() {
276 return return_error(format!(
277 "The configuration file `{}` does not exist. Please provide a valid path.",
278 config_file
279 ));
280 }
281
282 let copper_config = match read_config(&config_file) {
283 Ok(cuconfig) => cuconfig,
284 Err(e) => return return_error(e.to_string()),
285 };
286 let copper_config_content = match read_to_string(config_full_path(config_file.as_str())) {
287 Ok(ok) => ok,
288 Err(e) => return return_error(format!("Could not read the config file (should not happen because we just succeeded just before). {}", e))
289 };
290
291 #[cfg(feature = "macro_debug")]
292 eprintln!("[runtime plan]");
293 let runtime_plan: CuExecutionLoop = match compute_runtime_plan(&copper_config) {
294 Ok(plan) => plan,
295 Err(e) => return return_error(format!("Could not compute runtime plan: {}", e)),
296 };
297 #[cfg(feature = "macro_debug")]
298 eprintln!("{:?}", runtime_plan);
299
300 #[cfg(feature = "macro_debug")]
301 eprintln!("[extract tasks ids & types]");
302 let (all_tasks_ids, all_tasks_cutype, all_tasks_types_names, all_tasks_types) =
303 extract_tasks_types(&copper_config);
304
305 let all_sim_tasks_types: Vec<Type> = all_tasks_ids
306 .iter()
307 .zip(&all_tasks_cutype)
308 .zip(&all_tasks_types)
309 .map(|((task_id, cutype), stype)| match cutype {
310 CuTaskType::Source => {
311 let msg_type = copper_config
312 .get_node_output_msg_type(task_id.as_str())
313 .unwrap_or_else(|| panic!("CuSrcTask {task_id} should have an outgoing connection with a valid output msg type"));
314 let sim_task_name = format!("cu29::simulation::CuSimSrcTask<{msg_type}>");
315 parse_str(sim_task_name.as_str()).unwrap_or_else(|_| panic!("Could not build the placeholder for simulation: {sim_task_name}"))
316 }
317 CuTaskType::Regular => stype.clone(),
318 CuTaskType::Sink => {
319 let msg_type = copper_config
320 .get_node_input_msg_type(task_id.as_str())
321 .unwrap_or_else(|| panic!("CuSinkTask {task_id} should have an incoming connection with a valid input msg type"));
322 let sim_task_name = format!("cu29::simulation::CuSimSinkTask<{msg_type}>");
323 parse_str(sim_task_name.as_str()).unwrap_or_else(|_| panic!("Could not build the placeholder for simulation: {sim_task_name}"))
324 }
325 })
326 .collect();
327
328 #[cfg(feature = "macro_debug")]
329 eprintln!("[build task tuples]");
330 let task_types_tuple: TypeTuple = parse_quote! {
333 (#(#all_tasks_types),*,)
334 };
335
336 let task_types_tuple_sim: TypeTuple = parse_quote! {
337 (#(#all_sim_tasks_types),*,)
338 };
339
340 #[cfg(feature = "macro_debug")]
341 eprintln!("[build monitor type]");
342 let monitor_type = if let Some(monitor_config) = copper_config.get_monitor_config() {
343 let monitor_type = parse_str::<Type>(monitor_config.get_type())
344 .expect("Could not transform the monitor type name into a Rust type.");
345 quote! { #monitor_type }
346 } else {
347 quote! { _NoMonitor }
348 };
349
350 #[cfg(feature = "macro_debug")]
351 eprintln!("[build runtime field]");
352 let runtime_field: Field = if sim_mode {
354 parse_quote! {
355 copper_runtime: _CuRuntime<CuSimTasks, CuMsgs, #monitor_type, #DEFAULT_CLNB>
356 }
357 } else {
358 parse_quote! {
359 copper_runtime: _CuRuntime<CuTasks, CuMsgs, #monitor_type, #DEFAULT_CLNB>
360 }
361 };
362
363 let name = &item_struct.ident;
364
365 #[cfg(feature = "macro_debug")]
366 eprintln!("[match struct anonymity]");
367 match &mut item_struct.fields {
368 Named(fields_named) => {
369 fields_named.named.push(runtime_field);
370 }
371 Unnamed(fields_unnamed) => {
372 fields_unnamed.unnamed.push(runtime_field);
373 }
374 Fields::Unit => {
375 panic!("This struct is a unit struct, it should have named or unnamed fields. use struct Something {{}} and not struct Something;")
376 }
377 };
378
379 #[cfg(feature = "macro_debug")]
380 eprintln!("[gen instances]");
381
382 let task_sim_instances_init_code = all_sim_tasks_types.iter().enumerate().map(|(index, ty)| {
383 let additional_error_info = format!(
384 "Failed to get create instance for {}, instance index {}.",
385 all_tasks_types_names[index], index
386 );
387
388 quote! {
389 <#ty>::new(all_instances_configs[#index]).map_err(|e| e.add_cause(#additional_error_info))?
390 }
391 }).collect::<Vec<_>>();
392
393 let (task_instances_init_code,
396 start_calls,
397 stop_calls,
398 preprocess_calls,
399 postprocess_calls): (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = itertools::multiunzip(all_tasks_types
400 .iter()
401 .enumerate()
402 .map(|(index, ty)| {
403 let task_index = int2sliceindex(index as u32);
404 let task_enum_name = config_id_to_enum(&all_tasks_ids[index]);
405 let enum_name = Ident::new(&task_enum_name, proc_macro2::Span::call_site());
406 let additional_error_info = format!(
407 "Failed to get create instance for {}, instance index {}.",
408 all_tasks_types_names[index], index
409 );
410 (
411 quote! {
412 #ty::new(all_instances_configs[#index]).map_err(|e| e.add_cause(#additional_error_info))?
413 },
414 {
415 let monitoring_action = quote! {
416 let decision = self.copper_runtime.monitor.process_error(#index, _CuTaskState::Start, &error);
417 match decision {
418 _Decision::Abort => {
419 debug!("Start: ABORT decision from monitoring. Task '{}' errored out \
420 during start. Aborting all the other starts.", TASKS_IDS[#index]);
421 return Ok(());
422
423 }
424 _Decision::Ignore => {
425 debug!("Start: IGNORE decision from monitoring. Task '{}' errored out \
426 during start. The runtime will continue.", TASKS_IDS[#index]);
427 }
428 _Decision::Shutdown => {
429 debug!("Start: SHUTDOWN decision from monitoring. Task '{}' errored out \
430 during start. The runtime cannot continue.", TASKS_IDS[#index]);
431 return Err(_CuError::new_with_cause("Task errored out during start.", error));
432 }
433 }
434 };
435
436 let call_sim_callback = if sim_mode {
437 quote! {
438 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Start));
440
441 let doit = if let cu29::simulation::SimOverride::Errored(reason) = ovr {
442 let error: _CuError = reason.into();
443 #monitoring_action
444 false
445 }
446 else {
447 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
448 };
449 }
450 } else {
451 quote! {
452 let doit = true; }
454 };
455
456
457 quote! {
458 #call_sim_callback
459 if doit {
460 let task = &mut self.copper_runtime.tasks.#task_index;
461 if let Err(error) = task.start(&self.copper_runtime.clock) {
462 #monitoring_action
463 }
464 }
465 }
466 },
467 {
468 let monitoring_action = quote! {
469 let decision = self.copper_runtime.monitor.process_error(#index, _CuTaskState::Stop, &error);
470 match decision {
471 _Decision::Abort => {
472 debug!("Stop: ABORT decision from monitoring. Task '{}' errored out \
473 during stop. Aborting all the other starts.", TASKS_IDS[#index]);
474 return Ok(());
475
476 }
477 _Decision::Ignore => {
478 debug!("Stop: IGNORE decision from monitoring. Task '{}' errored out \
479 during stop. The runtime will continue.", TASKS_IDS[#index]);
480 }
481 _Decision::Shutdown => {
482 debug!("Stop: SHUTDOWN decision from monitoring. Task '{}' errored out \
483 during stop. The runtime cannot continue.", TASKS_IDS[#index]);
484 return Err(_CuError::new_with_cause("Task errored out during stop.", error));
485 }
486 }
487 };
488 let call_sim_callback = if sim_mode {
489 quote! {
490 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Stop));
492
493 let doit = if let cu29::simulation::SimOverride::Errored(reason) = ovr {
494 let error: _CuError = reason.into();
495 #monitoring_action
496 false
497 }
498 else {
499 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
500 };
501 }
502 } else {
503 quote! {
504 let doit = true; }
506 };
507 quote! {
508 #call_sim_callback
509 if doit {
510 let task = &mut self.copper_runtime.tasks.#task_index;
511 if let Err(error) = task.stop(&self.copper_runtime.clock) {
512 #monitoring_action
513 }
514 }
515 }
516 },
517 {
518 let monitoring_action = quote! {
519 let decision = self.copper_runtime.monitor.process_error(#index, _CuTaskState::Preprocess, &error);
520 match decision {
521 _Decision::Abort => {
522 debug!("Preprocess: ABORT decision from monitoring. Task '{}' errored out \
523 during preprocess. Aborting all the other starts.", TASKS_IDS[#index]);
524 return Ok(());
525
526 }
527 _Decision::Ignore => {
528 debug!("Preprocess: IGNORE decision from monitoring. Task '{}' errored out \
529 during preprocess. The runtime will continue.", TASKS_IDS[#index]);
530 }
531 _Decision::Shutdown => {
532 debug!("Preprocess: SHUTDOWN decision from monitoring. Task '{}' errored out \
533 during preprocess. The runtime cannot continue.", TASKS_IDS[#index]);
534 return Err(_CuError::new_with_cause("Task errored out during preprocess.", error));
535 }
536 }
537 };
538 let call_sim_callback = if sim_mode {
539 quote! {
540 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Preprocess));
542
543 let doit = if let cu29::simulation::SimOverride::Errored(reason) = ovr {
544 let error: _CuError = reason.into();
545 #monitoring_action
546 false
547 } else {
548 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
549 };
550 }
551 } else {
552 quote! {
553 let doit = true; }
555 };
556 quote! {
557 #call_sim_callback
558 if doit {
559 let task = &mut self.copper_runtime.tasks.#task_index;
560 if let Err(error) = task.preprocess(&self.copper_runtime.clock) {
561 #monitoring_action
562 }
563 }
564 }
565 },
566 {
567 let monitoring_action = quote! {
568 let decision = self.copper_runtime.monitor.process_error(#index, _CuTaskState::Postprocess, &error);
569 match decision {
570 _Decision::Abort => {
571 debug!("Postprocess: ABORT decision from monitoring. Task '{}' errored out \
572 during postprocess. Aborting all the other starts.", TASKS_IDS[#index]);
573 return Ok(());
574
575 }
576 _Decision::Ignore => {
577 debug!("Postprocess: IGNORE decision from monitoring. Task '{}' errored out \
578 during postprocess. The runtime will continue.", TASKS_IDS[#index]);
579 }
580 _Decision::Shutdown => {
581 debug!("Postprocess: SHUTDOWN decision from monitoring. Task '{}' errored out \
582 during postprocess. The runtime cannot continue.", TASKS_IDS[#index]);
583 return Err(_CuError::new_with_cause("Task errored out during postprocess.", error));
584 }
585 }
586 };
587 let call_sim_callback = if sim_mode {
588 quote! {
589 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Postprocess));
591
592 let doit = if let cu29::simulation::SimOverride::Errored(reason) = ovr {
593 let error: _CuError = reason.into();
594 #monitoring_action
595 false
596 } else {
597 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
598 };
599 }
600 } else {
601 quote! {
602 let doit = true; }
604 };
605 quote! {
606 #call_sim_callback
607 if doit {
608 let task = &mut self.copper_runtime.tasks.#task_index;
609 if let Err(error) = task.postprocess(&self.copper_runtime.clock) {
610 #monitoring_action
611 }
612 }
613 }
614 }
615 )
616 })
617 );
618
619 let mut taskid_call_order: Vec<usize> = Vec::new();
622
623 let runtime_plan_code: Vec<proc_macro2::TokenStream> = runtime_plan.steps
624 .iter()
625 .map(|unit| {
626 match unit {
627 CuExecutionUnit::Step(step) => {
628 #[cfg(feature = "macro_debug")]
629 eprintln!(
630 "{} -> {} as {:?}. task_id: {} Input={:?}, Output={:?}",
631 step.node.get_id(),
632 step.node.get_type(),
633 step.task_type,
634 step.node_id,
635 step.input_msg_indices_types,
636 step.output_msg_index_type
637 );
638
639 let node_index = int2sliceindex(step.node_id);
640 let task_instance = quote! { self.copper_runtime.tasks.#node_index };
641 let comment_str = format!(
642 "/// {} ({:?}) Id:{} I:{:?} O:{:?}",
643 step.node.get_id(),
644 step.task_type,
645 step.node_id,
646 step.input_msg_indices_types,
647 step.output_msg_index_type
648 );
649 let comment_tokens: proc_macro2::TokenStream = parse_str(&comment_str).unwrap();
650 let tid = step.node_id as usize;
651 taskid_call_order.push(tid);
652
653 let task_enum_name = config_id_to_enum(&all_tasks_ids[tid]);
654 let enum_name = Ident::new(&task_enum_name, proc_macro2::Span::call_site());
655
656 let process_call = match step.task_type {
657 CuTaskType::Source => {
658 if let Some((index, _)) = &step.output_msg_index_type {
659 let output_culist_index = int2sliceindex(*index);
660
661 let monitoring_action = quote! {
662 let decision = self.copper_runtime.monitor.process_error(#tid, _CuTaskState::Process, &error);
663 match decision {
664 _Decision::Abort => {
665 debug!("Process: ABORT decision from monitoring. Task '{}' errored out \
666 during process. Skipping the processing of CL {}.", TASKS_IDS[#tid], id);
667 self.copper_runtime.monitor.process_copperlist(&collect_metadata(&culist))?;
668 self.copper_runtime.end_of_processing(id);
669 return Ok(()); }
672 _Decision::Ignore => {
673 debug!("Process: IGNORE decision from monitoring. Task '{}' errored out \
674 during process. The runtime will continue with a forced empty message.", TASKS_IDS[#tid]);
675 cumsg_output.clear_payload();
676 }
677 _Decision::Shutdown => {
678 debug!("Process: SHUTDOWN decision from monitoring. Task '{}' errored out \
679 during process. The runtime cannot continue.", TASKS_IDS[#tid]);
680 return Err(_CuError::new_with_cause("Task errored out during process.", error));
681 }
682 }
683 };
684 let call_sim_callback = if sim_mode {
685 quote! {
686 let doit = {
687 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Process((), cumsg_output)));
688 if let cu29::simulation::SimOverride::Errored(reason) = ovr {
689 let error: _CuError = reason.into();
690 #monitoring_action
691 false
692 } else {
693 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
694 }
695 };
696 }
697 } else {
698 quote! {
699 let doit = true; }
701 };
702
703 quote! {
704 {
705 #comment_tokens
706 {
707 let cumsg_output = &mut msgs.#output_culist_index;
708 #call_sim_callback
709 cumsg_output.metadata.process_time.start = self.copper_runtime.clock.now().into();
710 let maybe_error = if doit {
711 #task_instance.process(&self.copper_runtime.clock, cumsg_output)
712 } else {
713 Ok(())
714 };
715 cumsg_output.metadata.process_time.end = self.copper_runtime.clock.now().into();
716 if let Err(error) = maybe_error {
717 #monitoring_action
718 }
719 }
720 }
721 }
722 } else {
723 panic!("Source task should have an output message index.");
724 }
725 }
726 CuTaskType::Sink => {
727 let indices = step.input_msg_indices_types.iter().map(|(index, _)| int2sliceindex(*index));
729 if let Some((output_index, _)) = &step.output_msg_index_type {
730 let output_culist_index = int2sliceindex(*output_index);
731
732 let monitoring_action = quote! {
733 let decision = self.copper_runtime.monitor.process_error(#tid, _CuTaskState::Process, &error);
734 match decision {
735 _Decision::Abort => {
736 debug!("Process: ABORT decision from monitoring. Task '{}' errored out \
737 during process. Skipping the processing of CL {}.", TASKS_IDS[#tid], id);
738 self.copper_runtime.monitor.process_copperlist(&collect_metadata(&culist))?;
739 self.copper_runtime.end_of_processing(id);
740 return Ok(()); }
743 _Decision::Ignore => {
744 debug!("Process: IGNORE decision from monitoring. Task '{}' errored out \
745 during process. The runtime will continue with a forced empty message.", TASKS_IDS[#tid]);
746 cumsg_output.clear_payload();
747 }
748 _Decision::Shutdown => {
749 debug!("Process: SHUTDOWN decision from monitoring. Task '{}' errored out \
750 during process. The runtime cannot continue.", TASKS_IDS[#tid]);
751 return Err(_CuError::new_with_cause("Task errored out during process.", error));
752 }
753 }
754 };
755
756 let call_sim_callback = if sim_mode {
757 quote! {
758 let doit = {
759 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Process(cumsg_input, cumsg_output)));
760
761 if let cu29::simulation::SimOverride::Errored(reason) = ovr {
762 let error: _CuError = reason.into();
763 #monitoring_action
764 false
765 } else {
766 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
767 }
768 };
769 }
770 } else {
771 quote! {
772 let doit = true; }
774 };
775 quote! {
776 {
777 #comment_tokens
778 let cumsg_input = (#(&msgs.#indices),*);
779 let cumsg_output = &mut msgs.#output_culist_index;
781 #call_sim_callback
782 cumsg_output.metadata.process_time.start = self.copper_runtime.clock.now().into();
783 let maybe_error = if doit {#task_instance.process(&self.copper_runtime.clock, cumsg_input)} else {Ok(())};
784 cumsg_output.metadata.process_time.end = self.copper_runtime.clock.now().into();
785 if let Err(error) = maybe_error {
786 #monitoring_action
787 }
788 }
789 }
790 } else {
791 panic!("Sink tasks should have a virtual output message index.");
792 }
793 }
794 CuTaskType::Regular => {
795 let indices = step.input_msg_indices_types.iter().map(|(index, _)| int2sliceindex(*index));
796 if let Some((output_index, _)) = &step.output_msg_index_type {
797 let output_culist_index = int2sliceindex(*output_index);
798
799 let monitoring_action = quote! {
800 let decision = self.copper_runtime.monitor.process_error(#tid, _CuTaskState::Process, &error);
801 match decision {
802 _Decision::Abort => {
803 debug!("Process: ABORT decision from monitoring. Task '{}' errored out \
804 during process. Skipping the processing of CL {}.", TASKS_IDS[#tid], id);
805 self.copper_runtime.monitor.process_copperlist(&collect_metadata(&culist))?;
806 self.copper_runtime.end_of_processing(id);
807 return Ok(()); }
810 _Decision::Ignore => {
811 debug!("Process: IGNORE decision from monitoring. Task '{}' errored out \
812 during process. The runtime will continue with a forced empty message.", TASKS_IDS[#tid]);
813 cumsg_output.clear_payload();
814 }
815 _Decision::Shutdown => {
816 debug!("Process: SHUTDOWN decision from monitoring. Task '{}' errored out \
817 during process. The runtime cannot continue.", TASKS_IDS[#tid]);
818 return Err(_CuError::new_with_cause("Task errored out during process.", error));
819 }
820 }
821 };
822
823 let call_sim_callback = if sim_mode {
824 quote! {
825 let doit = {
826 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Process(cumsg_input, cumsg_output)));
827
828 if let cu29::simulation::SimOverride::Errored(reason) = ovr {
829 let error: _CuError = reason.into();
830 #monitoring_action
831 false
832 }
833 else {
834 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
835 }
836 };
837 }
838 } else {
839 quote! {
840 let doit = true; }
842 };
843 quote! {
844 {
845 #comment_tokens
846 let cumsg_input = (#(&msgs.#indices),*);
847 let cumsg_output = &mut msgs.#output_culist_index;
848 #call_sim_callback
849 cumsg_output.metadata.process_time.start = self.copper_runtime.clock.now().into();
850 let maybe_error = if doit {#task_instance.process(&self.copper_runtime.clock, cumsg_input, cumsg_output)} else {Ok(())};
851 cumsg_output.metadata.process_time.end = self.copper_runtime.clock.now().into();
852 if let Err(error) = maybe_error {
853 #monitoring_action
854 }
855 }
856 }
857 } else {
858 panic!("Regular task should have an output message index.");
859 }
860 }
861 };
862
863 process_call
864 }
865 CuExecutionUnit::Loop(_) => todo!("Needs to be implemented"),
866 }
867 }).collect();
868 #[cfg(feature = "macro_debug")]
869 eprintln!("[Culist access order: {:?}]", taskid_call_order);
870
871 let all_tasks_member_ids: Vec<String> = all_tasks_ids
873 .iter()
874 .map(|name| utils::config_id_to_struct_member(name.as_str()))
875 .collect();
876
877 #[cfg(feature = "macro_debug")]
878 eprintln!("[build the copperlist support]");
879 let culist_support: proc_macro2::TokenStream =
880 gen_culist_support(&runtime_plan, &taskid_call_order, &all_tasks_member_ids);
881
882 #[cfg(feature = "macro_debug")]
883 eprintln!("[build the sim support]");
884 let sim_support: proc_macro2::TokenStream = gen_sim_support(&runtime_plan);
885
886 let (new, run_one_iteration, start_all_tasks, stop_all_tasks, run) = if sim_mode {
887 (
888 quote! {
889 pub fn new<F>(clock:_RobotClock, unified_logger: _Arc<_Mutex<_UnifiedLoggerWrite>>, config_override: Option<_CuConfig>, sim_callback: &mut F) -> _CuResult<Self>
890 where F: FnMut(SimStep) -> cu29::simulation::SimOverride,
891 },
892 quote! {
893 pub fn run_one_iteration<F>(&mut self, sim_callback: &mut F) -> _CuResult<()>
894 where F: FnMut(SimStep) -> cu29::simulation::SimOverride,
895 },
896 quote! {
897 pub fn start_all_tasks<F>(&mut self, sim_callback: &mut F) -> _CuResult<()>
898 where F: FnMut(SimStep) -> cu29::simulation::SimOverride,
899 },
900 quote! {
901 pub fn stop_all_tasks<F>(&mut self, sim_callback: &mut F) -> _CuResult<()>
902 where F: FnMut(SimStep) -> cu29::simulation::SimOverride,
903 },
904 quote! {
905 pub fn run<F>(&mut self, sim_callback: &mut F) -> _CuResult<()>
906 where F: FnMut(SimStep) -> cu29::simulation::SimOverride,
907 },
908 )
909 } else {
910 (
911 quote! {
912 pub fn new(clock:_RobotClock, unified_logger: _Arc<_Mutex<_UnifiedLoggerWrite>>, config_override: Option<_CuConfig>) -> _CuResult<Self>
913 },
914 quote! {
915 pub fn run_one_iteration(&mut self) -> _CuResult<()>
916 },
917 quote! {
918 pub fn start_all_tasks(&mut self) -> _CuResult<()>
919 },
920 quote! {
921 pub fn stop_all_tasks(&mut self) -> _CuResult<()>
922 },
923 quote! {
924 pub fn run(&mut self) -> _CuResult<()>
925 },
926 )
927 };
928
929 let sim_callback_arg = if sim_mode {
930 Some(quote!(sim_callback))
931 } else {
932 None
933 };
934
935 let sim_callback_on_new_calls = all_tasks_ids.iter().enumerate().map(|(i, id)| {
936 let enum_name = config_id_to_enum(id);
937 let enum_ident = Ident::new(&enum_name, proc_macro2::Span::call_site());
938 quote! {
939 sim_callback(SimStep::#enum_ident(cu29::simulation::CuTaskCallbackState::New(all_instances_configs[#i].cloned())));
941 }
942 });
943
944 let sim_callback_on_new = if sim_mode {
945 Some(quote! {
946 let all_instances_configs: Vec<Option<&_ComponentConfig>> = config
947 .get_all_nodes()
948 .iter()
949 .map(|(_, node)| node.get_instance_config())
950 .collect();
951 #(#sim_callback_on_new_calls)*
952 })
953 } else {
954 None
955 };
956
957 #[cfg(feature = "macro_debug")]
958 eprintln!("[build the run method]");
959 let run_method = quote! {
960
961 #run_one_iteration {
962 #(#preprocess_calls)*
963 {
964 let mut culist: &mut _ = &mut self.copper_runtime.copper_lists_manager.create().expect("Ran out of space for copper lists"); let id = culist.id;
966 culist.change_state(cu29::copperlist::CopperListState::Processing);
967 {
968 let msgs = &mut culist.msgs.0;
969 #(#runtime_plan_code)*
970 } {
973 let md = collect_metadata(&culist);
975 let e2e = md.last().unwrap().process_time.end.unwrap() - md.first().unwrap().process_time.start.unwrap();
976 let e2en: u64 = e2e.into();
977 } self.copper_runtime.monitor.process_copperlist(&collect_metadata(&culist))?;
980 self.copper_runtime.end_of_processing(id);
981
982 }#(#postprocess_calls)*
984 Ok(())
985 }
986
987 #start_all_tasks {
988 #(#start_calls)*
989 self.copper_runtime.monitor.start(&self.copper_runtime.clock)?;
990 Ok(())
991 }
992
993 #stop_all_tasks {
994 #(#stop_calls)*
995 self.copper_runtime.monitor.stop(&self.copper_runtime.clock)?;
996 Ok(())
997 }
998
999 #run {
1000 self.start_all_tasks(#sim_callback_arg)?;
1001 let error = loop {
1002 let error = self.run_one_iteration(#sim_callback_arg);
1003 if error.is_err() {
1004 break error;
1005 }
1006 };
1007 debug!("A task errored out: {}", &error);
1008 self.stop_all_tasks(#sim_callback_arg)?;
1009 error
1010 }
1011 };
1012
1013 let tasks_type = if sim_mode {
1014 quote!(CuSimTasks)
1015 } else {
1016 quote!(CuTasks)
1017 };
1018
1019 let tasks_instanciator = if sim_mode {
1020 quote!(tasks_instanciator_sim)
1021 } else {
1022 quote!(tasks_instanciator)
1023 };
1024
1025 let runtime_impl = quote! {
1026 impl #name {
1027
1028 #new {
1029 let config_filename = #config_file;
1030 let config = if config_override.is_some() {
1031 let overridden_config = config_override.unwrap();
1032 debug!("CuConfig: Overridden programmatically: {}", &overridden_config.serialize_ron());
1033 overridden_config
1034 } else if std::path::Path::new(config_filename).exists() {
1035 debug!("CuConfig: Reading configuration from file: {}", config_filename);
1036 _read_configuration(config_filename)?
1037 } else {
1038 let original_config = Self::get_original_config();
1039 debug!("CuConfig: Using the original configuration the project was compiled with: {}", &original_config);
1040 _read_configuration_str(original_config)?
1041 };
1042
1043 let mut default_section_size = std::mem::size_of::<CuList>() * 64;
1046 if let Some(section_size_mib) = config.logging.as_ref().and_then(|l| l.section_size_mib) {
1048 default_section_size = section_size_mib as usize * 1024usize * 1024usize;
1050 }
1051 let copperlist_stream = _stream_write::<CuList>(
1052 unified_logger.clone(),
1053 _UnifiedLogType::CopperList,
1054 default_section_size,
1055 );
1059
1060 let runtime = Ok(#name {
1061 copper_runtime: _CuRuntime::<#tasks_type, CuMsgs, #monitor_type, #DEFAULT_CLNB>::new(
1062 clock,
1063 &config,
1064 #tasks_instanciator,
1065 monitor_instanciator,
1066 copperlist_stream)?,
1067 });
1068
1069 #sim_callback_on_new
1070
1071 runtime
1072 }
1073
1074 pub fn get_original_config() -> String {
1075 #copper_config_content.to_string()
1076 }
1077
1078 #run_method
1079 }
1080 };
1081
1082 let builder_name = format_ident!("{}Builder", name);
1083 let (
1084 builder_struct,
1085 builder_new,
1086 builder_impl,
1087 builder_sim_callback_method,
1088 builder_build_sim_callback_arg,
1089 ) = if sim_mode {
1090 (
1091 quote! {
1092 pub struct #builder_name <'a, F> {
1093 clock: Option<_RobotClock>,
1094 unified_logger: Option<_Arc<_Mutex<_UnifiedLoggerWrite>>>,
1095 config_override: Option<_CuConfig>,
1096 sim_callback: Option<&'a mut F>
1097 }
1098 },
1099 quote! {
1100 pub fn new() -> Self {
1101 Self {
1102 clock: None,
1103 unified_logger: None,
1104 config_override: None,
1105 sim_callback: None,
1106 }
1107 }
1108 },
1109 quote! {
1110 impl<'a, F> #builder_name <'a, F>
1111 where
1112 F: FnMut(SimStep) -> cu29::simulation::SimOverride,
1113 },
1114 Some(quote! {
1115 fn with_sim_callback(mut self, sim_callback: &'a mut F) -> Self
1116 {
1117 self.sim_callback = Some(sim_callback);
1118 self
1119 }
1120 }),
1121 Some(quote! {
1122 self.sim_callback
1123 .ok_or(_CuError::from("Sim callback missing from builder"))?,
1124 }),
1125 )
1126 } else {
1127 (
1128 quote! {
1129 pub struct #builder_name {
1130 clock: Option<_RobotClock>,
1131 unified_logger: Option<_Arc<_Mutex<_UnifiedLoggerWrite>>>,
1132 config_override: Option<_CuConfig>,
1133 }
1134 },
1135 quote! {
1136 pub fn new() -> Self {
1137 Self {
1138 clock: None,
1139 unified_logger: None,
1140 config_override: None,
1141 }
1142 }
1143 },
1144 quote! {
1145 impl #builder_name
1146 },
1147 None,
1148 None,
1149 )
1150 };
1151
1152 let builder = quote! {
1153 #builder_struct
1154
1155 #builder_impl
1156 {
1157 #builder_new
1158
1159 pub fn with_clock(mut self, clock: _RobotClock) -> Self {
1160 self.clock = Some(clock);
1161 self
1162 }
1163
1164 pub fn with_unified_logger(mut self, unified_logger: _Arc<_Mutex<_UnifiedLoggerWrite>>) -> Self {
1165 self.unified_logger = Some(unified_logger);
1166 self
1167 }
1168
1169 pub fn with_context(mut self, copper_ctx: &_CopperContext) -> Self {
1170 self.clock = Some(copper_ctx.clock.clone());
1171 self.unified_logger = Some(copper_ctx.unified_logger.clone());
1172 self
1173 }
1174
1175 pub fn with_config(mut self, config_override: _CuConfig) -> Self {
1176 self.config_override = Some(config_override);
1177 self
1178 }
1179
1180 #builder_sim_callback_method
1181
1182 pub fn build(self) -> _CuResult<#name> {
1183 #name::new(
1184 self.clock
1185 .ok_or(_CuError::from("Clock missing from builder"))?,
1186 self.unified_logger
1187 .ok_or(_CuError::from("Unified logger missing from builder"))?,
1188 self.config_override,
1189 #builder_build_sim_callback_arg
1190 )
1191 }
1192 }
1193 };
1194
1195 #[cfg(feature = "macro_debug")]
1196 eprintln!("[build result]");
1197 let result = quote! {
1199 use cu29::bincode::Encode as _Encode;
1201 use cu29::bincode::enc::Encoder as _Encoder;
1202 use cu29::bincode::error::EncodeError as _EncodeError;
1203 use cu29::bincode::Decode as _Decode;
1204 use cu29::bincode::de::Decoder as _Decoder;
1205 use cu29::bincode::error::DecodeError as _DecodeError;
1206 use cu29::clock::RobotClock as _RobotClock;
1207 use cu29::clock::OptionCuTime as _OptionCuTime;
1208 use cu29::clock::ClockProvider as _ClockProvider;
1209 use cu29::config::CuConfig as _CuConfig;
1210 use cu29::config::ComponentConfig as _ComponentConfig;
1211 use cu29::config::MonitorConfig as _MonitorConfig;
1212 use cu29::config::read_configuration as _read_configuration;
1213 use cu29::config::read_configuration_str as _read_configuration_str;
1214 use cu29::curuntime::CuRuntime as _CuRuntime;
1215 use cu29::curuntime::CopperContext as _CopperContext;
1216 use cu29::CuResult as _CuResult;
1217 use cu29::CuError as _CuError;
1218 use cu29::cutask::CuSrcTask as _CuSrcTask;
1219 use cu29::cutask::CuSinkTask as _CuSinkTask;
1220 use cu29::cutask::CuTask as _CuTask;
1221 use cu29::cutask::CuMsg as _CuMsg;
1222 use cu29::cutask::CuMsgMetadata as _CuMsgMetadata;
1223 use cu29::copperlist::CopperList as _CopperList;
1224 use cu29::monitoring::CuMonitor as _CuMonitor; use cu29::monitoring::NoMonitor as _NoMonitor;
1226 use cu29::monitoring::CuTaskState as _CuTaskState;
1227 use cu29::monitoring::Decision as _Decision;
1228 use cu29::prelude::stream_write as _stream_write;
1229 use cu29::prelude::UnifiedLoggerWrite as _UnifiedLoggerWrite;
1230 use cu29::prelude::UnifiedLogType as _UnifiedLogType;
1231 use std::sync::Arc as _Arc;
1232 use std::sync::Mutex as _Mutex;
1233
1234 pub type CuTasks = #task_types_tuple;
1238
1239 pub type CuSimTasks = #task_types_tuple_sim;
1241
1242 const TASKS_IDS: &'static [&'static str] = &[#( #all_tasks_ids ),*];
1243
1244 #culist_support
1245
1246 #sim_support
1247
1248 fn tasks_instanciator(all_instances_configs: Vec<Option<&_ComponentConfig>>) -> _CuResult<CuTasks> {
1249 Ok(( #(#task_instances_init_code),*, ))
1250 }
1251
1252 fn tasks_instanciator_sim(all_instances_configs: Vec<Option<&_ComponentConfig>>) -> _CuResult<CuSimTasks> {
1253 Ok(( #(#task_sim_instances_init_code),*, ))
1254 }
1255
1256 fn monitor_instanciator(config: &_CuConfig) -> #monitor_type {
1257 #monitor_type::new(config, TASKS_IDS).expect("Failed to create the given monitor.")
1258 }
1259
1260 pub #item_struct
1261
1262 #runtime_impl
1263
1264 #builder
1265 };
1266 let tokens: TokenStream = result.into();
1267
1268 #[cfg(feature = "macro_debug")]
1270 {
1271 let formatted_code = rustfmt_generated_code(tokens.to_string());
1272 eprintln!("\n === Gen. Runtime ===\n");
1273 eprintln!("{}", highlight_rust_code(formatted_code));
1274 eprintln!("\n === === === === === ===\n");
1275 }
1276
1277 tokens
1278}
1279
1280fn read_config(config_file: &str) -> CuResult<CuConfig> {
1281 let filename = config_full_path(config_file);
1282
1283 read_configuration(filename.as_str())
1284}
1285
1286fn config_full_path(config_file: &str) -> String {
1287 let mut config_full_path = utils::caller_crate_root();
1288 config_full_path.push(config_file);
1289 let filename = config_full_path
1290 .as_os_str()
1291 .to_str()
1292 .expect("Could not interpret the config file name");
1293 filename.to_string()
1294}
1295
1296fn extract_tasks_types(
1298 copper_config: &CuConfig,
1299) -> (Vec<String>, Vec<CuTaskType>, Vec<String>, Vec<Type>) {
1300 let all_id_nodes = copper_config.get_all_nodes();
1301
1302 let all_tasks_ids: Vec<String> = all_id_nodes
1304 .iter()
1305 .map(|(_, node)| node.get_id().to_string())
1306 .collect();
1307
1308 let all_task_cutype: Vec<CuTaskType> = all_id_nodes
1309 .iter()
1310 .map(|(id, _)| find_task_type_for_id(&copper_config.graph, *id))
1311 .collect();
1312
1313 let all_types_names: Vec<String> = all_id_nodes
1315 .iter()
1316 .map(|(_, node)| node.get_type().to_string())
1317 .collect();
1318
1319 let all_types: Vec<Type> = all_types_names
1321 .iter()
1322 .map(|name| {
1323 parse_str(name)
1324 .unwrap_or_else(|_| panic!("Could not transform {name} into a Task Rust type."))
1325 })
1326 .collect();
1327 (all_tasks_ids, all_task_cutype, all_types_names, all_types)
1328}
1329
1330fn extract_msg_types(runtime_plan: &CuExecutionLoop) -> Vec<Type> {
1331 runtime_plan
1332 .steps
1333 .iter()
1334 .filter_map(|unit| match unit {
1335 CuExecutionUnit::Step(step) => {
1336 if let Some((_, output_msg_type)) = &step.output_msg_index_type {
1337 Some(
1338 parse_str::<Type>(output_msg_type.as_str()).unwrap_or_else(|_| {
1339 panic!(
1340 "Could not transform {output_msg_type} into a message Rust type."
1341 )
1342 }),
1343 )
1344 } else {
1345 None
1346 }
1347 }
1348 CuExecutionUnit::Loop(_) => todo!("Needs to be implemented"),
1349 })
1350 .collect()
1351}
1352
1353fn build_culist_tuple(all_msgs_types_in_culist_order: &[Type]) -> TypeTuple {
1355 if all_msgs_types_in_culist_order.is_empty() {
1356 parse_quote! {()}
1357 } else {
1358 parse_quote! { (#(_CuMsg<#all_msgs_types_in_culist_order>),*,)}
1359 }
1360}
1361
1362fn build_culist_tuple_encode(all_msgs_types_in_culist_order: &[Type]) -> ItemImpl {
1364 let indices: Vec<usize> = (0..all_msgs_types_in_culist_order.len()).collect();
1365
1366 let encode_fields: Vec<_> = indices
1368 .iter()
1369 .map(|i| {
1370 let idx = syn::Index::from(*i);
1371 quote! { self.0.#idx.encode(encoder)?; }
1372 })
1373 .collect();
1374
1375 parse_quote! {
1376 impl _Encode for CuMsgs {
1377 fn encode<E: _Encoder>(&self, encoder: &mut E) -> Result<(), _EncodeError> {
1378 #(#encode_fields)*
1379 Ok(())
1380 }
1381 }
1382 }
1383}
1384
1385fn build_culist_tuple_decode(all_msgs_types_in_culist_order: &[Type]) -> ItemImpl {
1387 let indices: Vec<usize> = (0..all_msgs_types_in_culist_order.len()).collect();
1388
1389 let decode_fields: Vec<_> = indices
1391 .iter()
1392 .map(|i| {
1393 let t = &all_msgs_types_in_culist_order[*i];
1394 quote! { _CuMsg::<#t>::decode(decoder)? }
1395 })
1396 .collect();
1397
1398 parse_quote! {
1399 impl _Decode for CuMsgs {
1400 fn decode<D: _Decoder>(decoder: &mut D) -> Result<Self, _DecodeError> {
1401 Ok(CuMsgs ((
1402 #(#decode_fields),*
1403 )))
1404 }
1405 }
1406 }
1407}
1408
1409fn build_culist_tuple_debug(all_msgs_types_in_culist_order: &[Type]) -> ItemImpl {
1410 let indices: Vec<usize> = (0..all_msgs_types_in_culist_order.len()).collect();
1411
1412 let debug_fields: Vec<_> = indices
1413 .iter()
1414 .map(|i| {
1415 let idx = syn::Index::from(*i);
1416 quote! { .field(&self.0.#idx) }
1417 })
1418 .collect();
1419
1420 parse_quote! {
1421 impl std::fmt::Debug for CuMsgs {
1422 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1423 f.debug_tuple("CuMsgs")
1424 #(#debug_fields)*
1425 .finish()
1426 }
1427 }
1428 }
1429}
1430
1431#[cfg(test)]
1432mod tests {
1433 #[test]
1435 fn test_compile_fail() {
1436 let t = trybuild::TestCases::new();
1437 t.compile_fail("tests/compile_fail/*/*.rs");
1438 }
1439}