use alloc::{
borrow::ToOwned,
format,
string::{String, ToString},
vec::Vec,
};
use core::any;
use core::fmt::{Display, Write};
pub trait ModuleDisplayDefault {
fn content(&self, _content: Content) -> Option<Content>;
fn num_params(&self) -> usize {
0
}
}
pub trait ModuleDisplay: ModuleDisplayDefault {
fn format(&self, passed_settings: DisplaySettings) -> String {
let settings = if let Some(custom_settings) = self.custom_settings() {
custom_settings.inherit(passed_settings)
} else {
passed_settings
};
let indent = " ".repeat(settings.level * settings.indentation_size());
let indent_close_braces = " ".repeat((settings.level - 1) * settings.indentation_size());
let settings = settings.level_up();
let self_type = extract_type_name::<Self>();
let content = if !settings.show_all_attributes() {
self.custom_content(Content::new(settings.clone()))
.unwrap_or_else(|| {
self.content(Content::new(settings.clone()))
.unwrap_or_else(|| {
panic!("Default content should be implemented for {self_type}.")
})
})
} else {
self.content(Content::new(settings.clone()))
.unwrap_or_else(|| panic!("Default content should be implemented for {self_type}."))
};
let top_level_type = if let Some(top_level_type) = content.top_level_type {
top_level_type.to_owned()
} else {
self_type.to_owned()
};
if let Some(item) = content.single_item {
return item;
} else if content.attributes.is_empty() {
return top_level_type.to_string();
}
let mut result = String::new();
if settings.new_line_after_attribute() {
writeln!(result, "{top_level_type} {{").unwrap();
} else {
write!(result, "{top_level_type} {{").unwrap();
}
for (i, attribute) in content.attributes.iter().enumerate() {
if settings.new_line_after_attribute() {
writeln!(result, "{indent}{}: {}", attribute.name, attribute.value).unwrap();
} else if i == 0 {
write!(result, "{}: {}", attribute.name, attribute.value).unwrap();
} else {
write!(result, ", {}: {}", attribute.name, attribute.value).unwrap();
}
}
if settings.show_num_parameters() {
let num_params = self.num_params();
if num_params > 0 {
if settings.new_line_after_attribute() {
writeln!(result, "{indent}params: {num_params}").unwrap();
} else {
write!(result, ", params: {num_params}").unwrap();
}
}
}
if settings.new_line_after_attribute() {
write!(result, "{indent_close_braces}}}").unwrap();
} else {
write!(result, "}}").unwrap();
}
result
}
fn custom_settings(&self) -> Option<DisplaySettings> {
None
}
fn custom_content(&self, _content: Content) -> Option<Content> {
None
}
}
#[derive(Debug, Clone)]
pub struct DisplaySettings {
show_param_id: Option<bool>,
show_all_attributes: Option<bool>,
show_num_parameters: Option<bool>,
new_line_after_attribute: Option<bool>,
indentation_size: Option<usize>,
level: usize,
}
impl Default for DisplaySettings {
fn default() -> Self {
DisplaySettings {
show_param_id: None,
show_all_attributes: None,
show_num_parameters: None,
new_line_after_attribute: None,
indentation_size: None,
level: 1,
}
}
}
impl DisplaySettings {
pub fn new() -> Self {
Default::default()
}
pub fn with_show_param_id(mut self, flag: bool) -> Self {
self.show_param_id = Some(flag);
self
}
pub fn with_show_all_attributes(mut self, flag: bool) -> Self {
self.show_all_attributes = Some(flag);
self
}
pub fn with_show_num_parameters(mut self, flag: bool) -> Self {
self.show_num_parameters = Some(flag);
self
}
pub fn with_new_line_after_attribute(mut self, flag: bool) -> Self {
self.new_line_after_attribute = Some(flag);
self
}
pub fn with_indentation_size(mut self, size: usize) -> Self {
self.indentation_size = Some(size);
self
}
pub fn inherit(self, top: Self) -> Self {
let mut updated = self.clone();
if let Some(show_param_id) = top.show_param_id {
updated.show_param_id = Some(show_param_id);
};
if let Some(show_all_attributes) = top.show_all_attributes {
updated.show_all_attributes = Some(show_all_attributes);
}
if let Some(show_num_parameters) = top.show_num_parameters {
updated.show_num_parameters = Some(show_num_parameters);
}
if let Some(new_line_after_attribute) = top.new_line_after_attribute {
updated.new_line_after_attribute = Some(new_line_after_attribute);
}
if let Some(indentation_size) = top.indentation_size {
updated.indentation_size = Some(indentation_size);
}
updated.level = top.level;
updated
}
pub fn optional(self) -> Option<Self> {
Some(self)
}
pub fn level_up(mut self) -> Self {
self.level += 1;
self
}
pub fn show_param_id(&self) -> bool {
self.show_param_id.unwrap_or(false)
}
pub fn show_all_attributes(&self) -> bool {
self.show_all_attributes.unwrap_or(false)
}
pub fn show_num_parameters(&self) -> bool {
self.show_num_parameters.unwrap_or(true)
}
pub fn new_line_after_attribute(&self) -> bool {
self.new_line_after_attribute.unwrap_or(true)
}
pub fn indentation_size(&self) -> usize {
self.indentation_size.unwrap_or(2)
}
}
#[derive(Clone, Debug)]
pub struct Content {
pub attributes: Vec<Attribute>,
pub single_item: Option<String>,
pub display_settings: DisplaySettings,
pub top_level_type: Option<String>,
}
impl Content {
pub fn new(display_settings: DisplaySettings) -> Self {
Content {
attributes: Vec::new(),
single_item: None,
display_settings,
top_level_type: None,
}
}
pub fn add<T: ModuleDisplay + ?Sized>(mut self, name: &str, value: &T) -> Self {
if self.single_item.is_some() {
panic!("Cannot add multiple attributes when single item is set.");
}
let attribute = Attribute {
name: name.to_owned(),
value: value.format(self.display_settings.clone()), ty: any::type_name::<T>().to_string(),
};
self.attributes.push(attribute);
self
}
pub fn add_single<T: ModuleDisplay + ?Sized>(mut self, value: &T) -> Self {
if !self.attributes.is_empty() {
panic!("Cannot add single item when attributes are set.");
}
self.single_item = Some(value.format(self.display_settings.clone()));
self
}
pub fn add_formatted<T: Display>(mut self, value: &T) -> Self {
if !self.attributes.is_empty() {
panic!("Cannot add single item when attributes are set.");
}
self.single_item = Some(format!("{value}"));
self
}
pub fn optional(self) -> Option<Self> {
if self.attributes.is_empty() && self.single_item.is_none() && self.top_level_type.is_none()
{
None
} else {
Some(self)
}
}
pub fn set_top_level_type(mut self, ty: &str) -> Self {
self.top_level_type = Some(ty.to_owned());
self
}
}
#[derive(Clone, Debug)]
pub struct Attribute {
pub name: String,
pub value: String,
pub ty: String,
}
pub fn extract_type_name<T: ?Sized>() -> &'static str {
let ty = any::type_name::<T>();
let end = ty.find('<').unwrap_or(ty.len());
let ty = &ty[0..end];
let start = ty.rfind("::").map(|i| i + 2).unwrap_or(0);
let end = ty.rfind('<').unwrap_or(ty.len());
if start < end { &ty[start..end] } else { ty }
}