burn_core/module/display.rs
1use alloc::{
2 borrow::ToOwned,
3 format,
4 string::{String, ToString},
5 vec::Vec,
6};
7use core::any;
8use core::fmt::{Debug, Display, Write};
9
10/// Default display settings for a module.
11pub trait ModuleDisplayDefault {
12 /// Attributes of the module used for display purposes.
13 ///
14 /// # Arguments
15 ///
16 /// * `_content` - The content object that contains display settings and attributes.
17 ///
18 /// # Returns
19 ///
20 /// An optional content object containing the display attributes.
21 fn content(&self, _content: Content) -> Option<Content>;
22
23 /// Gets the number of the parameters of the module.
24 fn num_params(&self) -> usize {
25 0
26 }
27}
28
29/// Trait to implement custom display settings for a module.
30///
31/// In order to implement custom display settings for a module,
32/// 1. Add #[module(custom_display)] attribute to the module struct after #[derive(Module)]
33/// 2. Implement ModuleDisplay trait for the module
34#[diagnostic::on_unimplemented(
35 note = "If `{Self}` is used as configuration or runtime state in a `#[derive(Module)]` type, mark that field with `#[module(skip)]`."
36)]
37pub trait ModuleDisplay: ModuleDisplayDefault {
38 /// Formats the module with provided display settings.
39 ///
40 /// # Arguments
41 ///
42 /// * `passed_settings` - Display settings passed to the module.
43 ///
44 /// # Returns
45 ///
46 /// A string representation of the formatted module.
47 fn format(&self, passed_settings: DisplaySettings) -> String {
48 let settings = if let Some(custom_settings) = self.custom_settings() {
49 custom_settings.inherit(passed_settings)
50 } else {
51 passed_settings
52 };
53
54 let indent = " ".repeat(settings.level * settings.indentation_size());
55 let indent_close_braces = " ".repeat((settings.level - 1) * settings.indentation_size());
56
57 let settings = settings.level_up();
58
59 let self_type = extract_type_name::<Self>();
60
61 // Use custom content if it is implemented and show_all_attributes is false,
62 // otherwise use default content
63 let content = if !settings.show_all_attributes() {
64 self.custom_content(Content::new(settings.clone()))
65 .unwrap_or_else(|| {
66 self.content(Content::new(settings.clone()))
67 .unwrap_or_else(|| {
68 panic!("Default content should be implemented for {self_type}.")
69 })
70 })
71 } else {
72 self.content(Content::new(settings.clone()))
73 .unwrap_or_else(|| panic!("Default content should be implemented for {self_type}."))
74 };
75
76 let top_level_type = if let Some(top_level_type) = content.top_level_type {
77 top_level_type.to_owned()
78 } else {
79 self_type.to_owned()
80 };
81
82 // If there is only one item in the content, return it or no attributes
83 if let Some(item) = content.single_item {
84 return item;
85 } else if content.attributes.is_empty() {
86 return top_level_type.to_string();
87 }
88
89 let mut result = String::new();
90
91 // Print the struct name
92 if settings.new_line_after_attribute() {
93 writeln!(result, "{top_level_type} {{").unwrap();
94 } else {
95 write!(result, "{top_level_type} {{").unwrap();
96 }
97
98 for (i, attribute) in content.attributes.iter().enumerate() {
99 if settings.new_line_after_attribute() {
100 writeln!(result, "{indent}{}: {}", attribute.name, attribute.value).unwrap();
101 } else if i == 0 {
102 write!(result, "{}: {}", attribute.name, attribute.value).unwrap();
103 } else {
104 write!(result, ", {}: {}", attribute.name, attribute.value).unwrap();
105 }
106 }
107
108 if settings.show_num_parameters() {
109 let num_params = self.num_params();
110 if num_params > 0 {
111 if settings.new_line_after_attribute() {
112 writeln!(result, "{indent}params: {num_params}").unwrap();
113 } else {
114 write!(result, ", params: {num_params}").unwrap();
115 }
116 }
117 }
118
119 if settings.new_line_after_attribute() {
120 write!(result, "{indent_close_braces}}}").unwrap();
121 } else {
122 write!(result, "}}").unwrap();
123 }
124
125 result
126 }
127
128 /// Custom display settings for the module.
129 ///
130 /// # Returns
131 ///
132 /// An optional display settings object.
133 fn custom_settings(&self) -> Option<DisplaySettings> {
134 None
135 }
136
137 /// Custom attributes for the module.
138 ///
139 /// # Arguments
140 ///
141 /// * `_content` - The content object that contains display settings and attributes.
142 ///
143 /// # Returns
144 ///
145 /// An optional content object containing the custom attributes.
146 fn custom_content(&self, _content: Content) -> Option<Content> {
147 None
148 }
149}
150
151/// Custom module display settings.
152#[derive(Debug, Clone)]
153pub struct DisplaySettings {
154 /// Whether to print the module parameter ids.
155 show_param_id: Option<bool>,
156
157 /// Whether to print the module attributes.
158 show_all_attributes: Option<bool>,
159
160 /// Whether to print the module number of parameters.
161 show_num_parameters: Option<bool>,
162
163 /// Print new line after an attribute.
164 new_line_after_attribute: Option<bool>,
165
166 /// Indentation size.
167 indentation_size: Option<usize>,
168
169 /// Level of indentation.
170 level: usize,
171}
172
173impl Default for DisplaySettings {
174 fn default() -> Self {
175 DisplaySettings {
176 show_param_id: None,
177 show_all_attributes: None,
178 show_num_parameters: None,
179 new_line_after_attribute: None,
180 indentation_size: None,
181 level: 1,
182 }
183 }
184}
185
186impl DisplaySettings {
187 /// Create a new format settings.
188 ///
189 /// # Returns
190 ///
191 /// A new instance of `DisplaySettings`.
192 pub fn new() -> Self {
193 Default::default()
194 }
195
196 /// Sets a flag to show module parameters.
197 ///
198 /// # Arguments
199 ///
200 /// * `flag` - Boolean flag to show module parameters.
201 ///
202 /// # Returns
203 ///
204 /// Updated `DisplaySettings` instance.
205 pub fn with_show_param_id(mut self, flag: bool) -> Self {
206 self.show_param_id = Some(flag);
207 self
208 }
209
210 /// Sets a flag to show module attributes.
211 ///
212 /// # Arguments
213 ///
214 /// * `flag` - Boolean flag to show all module attributes.
215 ///
216 /// # Returns
217 ///
218 /// Updated `DisplaySettings` instance.
219 pub fn with_show_all_attributes(mut self, flag: bool) -> Self {
220 self.show_all_attributes = Some(flag);
221 self
222 }
223
224 /// Sets a flag to show the number of module parameters.
225 ///
226 /// # Arguments
227 ///
228 /// * `flag` - Boolean flag to show the number of module parameters.
229 ///
230 /// # Returns
231 ///
232 /// Updated `DisplaySettings` instance.
233 pub fn with_show_num_parameters(mut self, flag: bool) -> Self {
234 self.show_num_parameters = Some(flag);
235 self
236 }
237
238 /// Sets a flag to print a new line after an attribute.
239 ///
240 /// # Arguments
241 ///
242 /// * `flag` - Boolean flag to print a new line after an attribute.
243 ///
244 /// # Returns
245 ///
246 /// Updated `DisplaySettings` instance.
247 pub fn with_new_line_after_attribute(mut self, flag: bool) -> Self {
248 self.new_line_after_attribute = Some(flag);
249 self
250 }
251
252 /// Sets the indentation size.
253 ///
254 /// # Arguments
255 ///
256 /// * `size` - The size of the indentation.
257 ///
258 /// # Returns
259 ///
260 /// Updated `DisplaySettings` instance.
261 pub fn with_indentation_size(mut self, size: usize) -> Self {
262 self.indentation_size = Some(size);
263 self
264 }
265
266 /// Inherits settings from the provided settings and return a new settings object.
267 ///
268 /// # Arguments
269 ///
270 /// * `top` - The top level `DisplaySettings` to inherit from.
271 ///
272 /// # Returns
273 ///
274 /// Updated `DisplaySettings` instance.
275 pub fn inherit(self, top: Self) -> Self {
276 let mut updated = self.clone();
277
278 if let Some(show_param_id) = top.show_param_id {
279 updated.show_param_id = Some(show_param_id);
280 };
281
282 if let Some(show_all_attributes) = top.show_all_attributes {
283 updated.show_all_attributes = Some(show_all_attributes);
284 }
285
286 if let Some(show_num_parameters) = top.show_num_parameters {
287 updated.show_num_parameters = Some(show_num_parameters);
288 }
289
290 if let Some(new_line_after_attribute) = top.new_line_after_attribute {
291 updated.new_line_after_attribute = Some(new_line_after_attribute);
292 }
293
294 if let Some(indentation_size) = top.indentation_size {
295 updated.indentation_size = Some(indentation_size);
296 }
297
298 updated.level = top.level;
299
300 updated
301 }
302
303 /// A convenience method to wrap the DisplaySettings struct in an option.
304 ///
305 /// # Returns
306 ///
307 /// An optional `DisplaySettings`.
308 pub fn optional(self) -> Option<Self> {
309 Some(self)
310 }
311
312 /// Increases the level of indentation.
313 ///
314 /// # Returns
315 ///
316 /// Updated `DisplaySettings` instance with increased indentation level.
317 pub fn level_up(mut self) -> Self {
318 self.level += 1;
319 self
320 }
321
322 /// Gets `show_param_id` flag, substitutes false if not set.
323 ///
324 /// This flag is used to print the module parameter ids.
325 ///
326 /// # Returns
327 ///
328 /// A boolean value indicating whether to show parameter ids.
329 pub fn show_param_id(&self) -> bool {
330 self.show_param_id.unwrap_or(false)
331 }
332
333 /// Gets `show_all_attributes`, substitutes false if not set.
334 ///
335 /// This flag is used to force to print all module attributes, overriding custom attributes.
336 ///
337 /// # Returns
338 ///
339 /// A boolean value indicating whether to show all attributes.
340 pub fn show_all_attributes(&self) -> bool {
341 self.show_all_attributes.unwrap_or(false)
342 }
343
344 /// Gets `show_num_parameters`, substitutes true if not set.
345 ///
346 /// This flag is used to print the number of module parameters.
347 ///
348 /// # Returns
349 ///
350 /// A boolean value indicating whether to show the number of parameters.
351 pub fn show_num_parameters(&self) -> bool {
352 self.show_num_parameters.unwrap_or(true)
353 }
354
355 /// Gets `new_line_after_attribute`, substitutes true if not set.
356 ///
357 /// This flag is used to print a new line after an attribute.
358 ///
359 /// # Returns
360 ///
361 /// A boolean value indicating whether to print a new line after an attribute.
362 pub fn new_line_after_attribute(&self) -> bool {
363 self.new_line_after_attribute.unwrap_or(true)
364 }
365
366 /// Gets `indentation_size`, substitutes 2 if not set.
367 ///
368 /// This flag is used to set the size of indentation.
369 ///
370 /// # Returns
371 ///
372 /// An integer value indicating the size of indentation.
373 pub fn indentation_size(&self) -> usize {
374 self.indentation_size.unwrap_or(2)
375 }
376}
377
378/// Struct to store the attributes of a module for formatting.
379#[derive(Clone, Debug)]
380pub struct Content {
381 /// List of attributes.
382 pub attributes: Vec<Attribute>,
383
384 /// Single item content.
385 pub single_item: Option<String>,
386
387 /// Display settings.
388 pub display_settings: DisplaySettings,
389
390 /// Top level type name.
391 pub top_level_type: Option<String>,
392}
393
394impl Content {
395 /// Creates a new attributes struct.
396 ///
397 /// # Arguments
398 ///
399 /// * `display_settings` - Display settings for the content.
400 ///
401 /// # Returns
402 ///
403 /// A new instance of `Content`.
404 pub fn new(display_settings: DisplaySettings) -> Self {
405 Content {
406 attributes: Vec::new(),
407 single_item: None,
408 display_settings,
409 top_level_type: None,
410 }
411 }
412
413 /// Adds an attribute to the format settings. The value will be formatted and stored as a string.
414 ///
415 /// # Arguments
416 ///
417 /// * `name` - Name of the attribute.
418 /// * `value` - Value of the attribute.
419 ///
420 /// # Returns
421 ///
422 /// Updated `Content` instance with the new attribute added.
423 pub fn add<T: ModuleDisplay + ?Sized>(mut self, name: &str, value: &T) -> Self {
424 if self.single_item.is_some() {
425 panic!("Cannot add multiple attributes when single item is set.");
426 }
427
428 let attribute = Attribute {
429 name: name.to_owned(),
430 value: value.format(self.display_settings.clone()), // TODO level + 1
431 ty: any::type_name::<T>().to_string(),
432 };
433 self.attributes.push(attribute);
434 self
435 }
436
437 /// Adds an attribute using its `Debug` representation.
438 ///
439 /// This is intended for fields that do not implement [`ModuleDisplay`].
440 ///
441 /// # Arguments
442 ///
443 /// * `name` - Name of the attribute.
444 /// * `value` - Value of the attribute.
445 ///
446 /// # Returns
447 ///
448 /// Updated `Content` instance with the new attribute added.
449 pub fn add_debug_attribute<T: Debug>(mut self, name: &str, value: &T) -> Self {
450 if self.single_item.is_some() {
451 panic!("Cannot add multiple attributes when single item is set.");
452 }
453 self.attributes.push(Attribute {
454 name: name.to_owned(),
455 value: DisplayAdapter(value).format(self.display_settings.clone()),
456 ty: any::type_name::<T>().to_string(),
457 });
458 self
459 }
460
461 /// Adds a single item.
462 ///
463 /// # Arguments
464 ///
465 /// * `value` - Rendered string of the single item.
466 ///
467 /// # Returns
468 ///
469 /// Updated `Content` instance with the single item added.
470 pub fn add_single<T: ModuleDisplay + ?Sized>(mut self, value: &T) -> Self {
471 if !self.attributes.is_empty() {
472 panic!("Cannot add single item when attributes are set.");
473 }
474
475 self.single_item = Some(value.format(self.display_settings.clone()));
476
477 self
478 }
479
480 /// Adds a single item.
481 ///
482 /// # Arguments
483 ///
484 /// * `value` - Formatted display value.
485 ///
486 /// # Returns
487 ///
488 /// Updated `Content` instance with the formatted single item added.
489 pub fn add_formatted<T: Display>(mut self, value: &T) -> Self {
490 if !self.attributes.is_empty() {
491 panic!("Cannot add single item when attributes are set.");
492 }
493
494 self.single_item = Some(format!("{value}"));
495 self
496 }
497
498 /// A convenience method to wrap the Attributes struct in an option
499 /// because it is often used as an optional field.
500 ///
501 /// # Returns
502 ///
503 /// An optional `Content`.
504 pub fn optional(self) -> Option<Self> {
505 if self.attributes.is_empty() && self.single_item.is_none() && self.top_level_type.is_none()
506 {
507 None
508 } else {
509 Some(self)
510 }
511 }
512
513 /// Sets the top level type name.
514 ///
515 /// # Arguments
516 ///
517 /// * `ty` - The type name to set.
518 ///
519 /// # Returns
520 ///
521 /// Updated `Content` instance with the top level type name set.
522 pub fn set_top_level_type(mut self, ty: &str) -> Self {
523 self.top_level_type = Some(ty.to_owned());
524 self
525 }
526}
527
528/// Minimal display adapter for non-module types.
529struct DisplayAdapter<'a, T: Debug>(&'a T);
530
531impl<'a, T: Debug> ModuleDisplayDefault for DisplayAdapter<'a, T> {
532 fn content(&self, content: Content) -> Option<Content> {
533 content.add_single(&format!("{:?}", self.0)).optional()
534 }
535}
536
537impl<'a, T: Debug> ModuleDisplay for DisplayAdapter<'a, T> {}
538
539/// Attribute to print in the display method.
540#[derive(Clone, Debug)]
541pub struct Attribute {
542 /// Name of the attribute.
543 pub name: String,
544
545 /// Value of the attribute.
546 pub value: String,
547
548 /// Type of the attribute.
549 pub ty: String,
550}
551
552/// Extracts the short name of a type T
553///
554/// # Returns
555///
556/// A string slice representing the short name of the type.
557pub fn extract_type_name<T: ?Sized>() -> &'static str {
558 // Get the full type name of T, including module path and generic parameters
559 let ty = any::type_name::<T>();
560
561 // Find the first occurrence of '<' in the full type name
562 // If not found, use the length of the type name
563 let end = ty.find('<').unwrap_or(ty.len());
564
565 // Slice the type name up to the first '<' or the end
566 let ty = &ty[0..end];
567
568 // Find the last occurrence of "::" in the sliced type name
569 // If found, add 2 to skip the "::" itself
570 // If not found, start from the beginning of the type name
571 let start = ty.rfind("::").map(|i| i + 2).unwrap_or(0);
572
573 // Find the last occurrence of '<' in the sliced type name
574 // If not found, use the length of the type name
575 let end = ty.rfind('<').unwrap_or(ty.len());
576
577 // If the start index is less than the end index,
578 // return the slice of the type name from start to end
579 // Otherwise, return the entire sliced type name
580 if start < end { &ty[start..end] } else { ty }
581}