crate::ix!();
#[derive(Debug, Clone)]
pub struct StructSignatureGenerator(ast::Struct);
impl GenerateSignature for ast::Struct {
fn generate_signature_with_opts(&self, opts: &SignatureOptions) -> String {
trace!("Generating signature for ast::Struct with opts: {:?}", opts);
let doc_text = if *opts.include_docs() {
extract_docs(&self.syntax())
.map(|d| format!("{}\n", d))
.unwrap_or_default()
} else {
"".to_string()
};
let vis_str = self
.visibility()
.map(|v| format!("{} ", v.syntax().text()))
.unwrap_or_default();
let name_str = self
.name()
.map(|n| n.to_string())
.unwrap_or_else(|| "<unknown_struct>".to_string());
let generic_params_raw = self
.generic_param_list()
.map(|g| g.syntax().text().to_string())
.unwrap_or_default();
let where_clause = full_clean_where_clause(&self.where_clause());
let fields_text = {
if let Some(fl) = self.field_list() {
match fl {
ast::FieldList::RecordFieldList(rfl) => {
align_record_fields(&rfl)
}
ast::FieldList::TupleFieldList(tfl) => {
align_tuple_fields(&tfl)
}
}
} else {
";".to_string()
}
};
let core = format!("{vis_str}struct {name_str}{generic_params_raw} {where_clause} {fields_text}");
let final_sig = format!("{doc_text}{core}");
post_process_spacing(&final_sig)
}
}
fn align_record_fields(rfl: &ast::RecordFieldList) -> String {
let mut fields_info = Vec::new();
for field in rfl.fields() {
let fname = field
.name()
.map(|n| n.text().to_string())
.unwrap_or_default();
let fty = field
.ty()
.map(|t| t.syntax().text().to_string())
.unwrap_or_default();
fields_info.push((fname, fty));
}
if fields_info.is_empty() {
return "{ }".to_string();
}
let max_name_len = fields_info.iter().map(|(n, _)| n.len()).max().unwrap_or(0);
let mut lines = Vec::new();
for (name, ftype) in fields_info {
let space_count = if max_name_len > name.len() {
max_name_len - name.len()
} else {
0
};
let spacing = " ".repeat(space_count);
let line = format!(" {name}:{spacing} {ftype},");
lines.push(line);
}
let joined = lines.join("\n");
format!("{{\n{}\n}}", joined)
}
fn align_tuple_fields(tfl: &ast::TupleFieldList) -> String {
let mut fields_info = Vec::new();
for field in tfl.fields() {
let vis = field
.visibility()
.map(|v| format!("{} ", v.syntax().text()))
.unwrap_or_default();
let fty = field
.ty()
.map(|t| t.syntax().text().to_string())
.unwrap_or_default();
let combined = format!("{}{}", vis, fty);
fields_info.push(combined);
}
if fields_info.is_empty() {
return "();".to_string();
}
let max_field_len = fields_info.iter().map(|s| s.len()).max().unwrap_or(0);
let mut lines = Vec::new();
for combined in fields_info {
let space_count = if max_field_len > combined.len() {
max_field_len - combined.len()
} else {
0
};
let spacing = " ".repeat(space_count);
let line = format!(" {combined}{spacing},");
lines.push(line);
}
let joined = lines.join("\n");
format!("(\n{}\n);", joined)
}
fn post_process_spacing(sig: &str) -> String {
sig.to_string()
}