Skip to main content

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}