perf(help): replace help() with an associated const

Signed-off-by: Changyuan Lyu <changyuanl@google.com>
This commit is contained in:
Changyuan Lyu 2024-08-30 03:22:19 -07:00 committed by Lencerf
parent 816804aa53
commit 03ee6c42ef
3 changed files with 99 additions and 132 deletions

View file

@ -22,9 +22,7 @@ use zerocopy::{AsBytes, FromBytes, FromZeroes};
pub struct MacAddr([u8; 6]);
impl Help for MacAddr {
fn help() -> TypedHelp {
TypedHelp::Custom { desc: "mac-addr" }
}
const HELP: TypedHelp = TypedHelp::Custom { desc: "mac-addr" };
}
struct MacAddrVisitor;

View file

@ -21,8 +21,8 @@ use quote::quote;
use syn::meta::ParseNestedMeta;
use syn::punctuated::Punctuated;
use syn::{
parse_macro_input, Attribute, Data, DataEnum, DataStruct, DeriveInput, Expr, ExprLit, Fields,
FieldsNamed, FieldsUnnamed, Ident, Lit, Meta, MetaNameValue, Token,
parse_macro_input, Attribute, Data, DataEnum, DataStruct, DeriveInput, Expr, ExprLit, Field,
Fields, FieldsNamed, FieldsUnnamed, Ident, Lit, Meta, MetaNameValue, Token,
};
fn get_doc_from_attrs(attrs: &[Attribute]) -> String {
@ -126,64 +126,39 @@ fn is_flattened(attrs: &[Attribute]) -> bool {
}
fn derive_named_struct_help(name: &Ident, fields: &FieldsNamed) -> TokenStream2 {
let mut field_doc = vec![];
let mut flatten_fields = vec![];
for field in fields.named.iter() {
let ident = field.ident.as_ref().unwrap();
let aliases = get_serde_aliases_from_attrs(ident, &field.attrs);
let ident = &aliases[0];
let field_help_fn = |field: &Field| {
let aliases;
let ident = if is_flattened(&field.attrs) {
""
} else {
aliases = get_serde_aliases_from_attrs(field.ident.as_ref().unwrap(), &field.attrs);
&aliases[0]
};
let ty = &field.ty;
let doc = get_doc_from_attrs(&field.attrs);
let field_help = quote! {
quote! {
FieldHelp {
ident: #ident,
doc: #doc,
ty: <#ty as Help>::help(),
}
};
if is_flattened(&field.attrs) {
flatten_fields.push(field_help);
} else {
field_doc.push(field_help);
}
}
if flatten_fields.is_empty() {
quote! {
TypedHelp::Struct{
name: stringify!(#name),
fields: vec![#(#field_doc,)*],
ty: <#ty as Help>::HELP,
}
}
} else {
quote! {{
let mut fields = vec![#(#field_doc,)*];
let mut flatted_fields = vec![#(#flatten_fields,)*];
for mut field in flatted_fields {
match field.ty {
TypedHelp::Enum { variants, .. } => field.ty = TypedHelp::FlattenedEnum { variants },
TypedHelp::Option(mut o) => match *o {
TypedHelp::Enum { variants, .. } => {
*o = TypedHelp::FlattenedEnum { variants };
field.ty = TypedHelp::Option(o);
}
_ => unreachable!(),
},
_ => unreachable!(),
};
fields.push(field);
}
TypedHelp::Struct{
name: stringify!(#name),
fields,
}
}}
};
let field_docs: Vec<_> = fields.named.iter().map(field_help_fn).collect();
quote! {
TypedHelp::Struct{
name: stringify!(#name),
fields: &[#(#field_docs,)*],
}
}
}
fn derive_unnamed_struct_help(fields: &FieldsUnnamed) -> TokenStream2 {
if let Some(first) = fields.unnamed.first() {
let ty = &first.ty;
quote! { <#ty as Help>::help() }
quote! { <#ty as Help>::HELP }
} else if fields.unnamed.is_empty() {
quote! { TypedHelp::Unit }
} else {
@ -221,7 +196,7 @@ fn derive_enum_help(name: &Ident, data: &DataEnum) -> TokenStream2 {
quote! {
TypedHelp::Enum {
name: stringify!(#name),
variants: vec![#(#variants,)*],
variants: &[#(#variants,)*],
}
}
}
@ -238,9 +213,7 @@ pub fn derive_help(input: TokenStream) -> TokenStream {
const _:() = {
use ::serde_aco::{Help, TypedHelp, FieldHelp};
impl Help for #ty_name {
fn help() -> TypedHelp {
#body
}
const HELP: TypedHelp = #body;
}
};
})

View file

@ -30,14 +30,11 @@ pub struct FieldHelp {
pub enum TypedHelp {
Struct {
name: &'static str,
fields: Vec<FieldHelp>,
fields: &'static [FieldHelp],
},
Enum {
name: &'static str,
variants: Vec<FieldHelp>,
},
FlattenedEnum {
variants: Vec<FieldHelp>,
variants: &'static [FieldHelp],
},
String,
Int,
@ -47,24 +44,20 @@ pub enum TypedHelp {
Custom {
desc: &'static str,
},
Option(Box<TypedHelp>),
Option(&'static TypedHelp),
}
pub trait Help {
fn help() -> TypedHelp;
const HELP: TypedHelp;
}
macro_rules! impl_help_for_num_types {
($help_type:ident, $($ty:ty),+) => {
$(impl Help for $ty {
fn help() -> TypedHelp {
TypedHelp::$help_type
}
const HELP: TypedHelp = TypedHelp::$help_type;
})+
$(impl Help for NonZero<$ty> {
fn help() -> TypedHelp {
TypedHelp::$help_type
}
const HELP: TypedHelp = TypedHelp::$help_type;
})+
};
}
@ -72,9 +65,7 @@ macro_rules! impl_help_for_num_types {
macro_rules! impl_help_for_types {
($help_type:ident, $($ty:ty),+) => {
$(impl Help for $ty {
fn help() -> TypedHelp {
TypedHelp::$help_type
}
const HELP: TypedHelp = TypedHelp::$help_type;
})+
};
}
@ -88,9 +79,7 @@ impl<T> Help for Option<T>
where
T: Help,
{
fn help() -> TypedHelp {
TypedHelp::Option(Box::new(T::help()))
}
const HELP: TypedHelp = TypedHelp::Option(&T::HELP);
}
#[derive(Debug, Default)]
@ -110,7 +99,6 @@ fn value_type(v: &TypedHelp) -> &'static str {
TypedHelp::Struct { name, .. } => name,
TypedHelp::Enum { name, .. } => name,
TypedHelp::Option(o) => value_type(o),
TypedHelp::FlattenedEnum { .. } => unreachable!(),
}
}
@ -125,7 +113,7 @@ fn add_extra_help<'a>(extra: &mut ExtraHelp<'a>, v: &'a TypedHelp) {
};
if extra.types.insert(name) {
extra.helps.push(v);
for f in fields {
for f in fields.iter() {
add_extra_help(extra, &f.ty);
}
}
@ -152,12 +140,30 @@ fn next_line(s: &mut String, indent: usize) {
}
fn one_key_val<'a>(s: &mut String, extra: &mut Option<&mut ExtraHelp<'a>>, f: &'a FieldHelp) {
s.push_str(f.ident);
s.push_str("=<");
s.push_str(value_type(&f.ty));
s.push('>');
if let Some(extra) = extra {
add_extra_help(extra, &f.ty)
if f.ident.is_empty() {
let fields = match f.ty {
TypedHelp::Enum { variants, .. } => variants,
_ => unreachable!(),
};
s.push('(');
let mut need_separator = false;
for field in fields {
if need_separator {
s.push('|');
} else {
need_separator = true;
}
one_key_val(s, extra, field)
}
s.push(')');
} else {
s.push_str(f.ident);
s.push_str("=<");
s.push_str(value_type(&f.ty));
s.push('>');
if let Some(extra) = extra {
add_extra_help(extra, &f.ty)
}
}
}
@ -173,72 +179,62 @@ fn key_val_pairs<'a>(
add_comma = true;
}
for f in fields.iter() {
let ty = if let TypedHelp::Option(b) = &f.ty {
b.as_ref()
} else {
&f.ty
};
if add_comma {
s.push(',');
} else {
add_comma = true;
}
if let TypedHelp::FlattenedEnum { variants } = ty {
s.push('(');
let mut need_separator = false;
for v in variants.iter() {
if need_separator {
s.push('|');
} else {
need_separator = true;
}
one_key_val(s, extra, v);
}
s.push(')');
one_key_val(s, extra, f);
}
}
fn value_helps(s: &mut String, indent: usize, width: usize, fields: &[FieldHelp]) {
for f in fields.iter() {
if f.ident.is_empty() {
let fields = match f.ty {
TypedHelp::Enum { variants, .. } => variants,
_ => unreachable!(),
};
value_helps(s, indent, width, fields)
} else if f.doc.is_empty() {
continue;
} else {
one_key_val(s, extra, f);
next_line(s, indent);
let mut first_line = true;
for line in f.doc.lines() {
if first_line {
s.push_str(&format!("- {:width$}\t{}", f.ident, line, width = width));
first_line = false;
} else {
next_line(s, indent + width + 2);
s.push('\t');
s.push_str(line);
}
}
}
}
}
fn value_helps(s: &mut String, indent: usize, width: usize, f: &FieldHelp) {
next_line(s, indent);
let mut first_line = true;
for line in f.doc.lines() {
if first_line {
s.push_str(&format!("- {:width$}\t{}", f.ident, line, width = width));
first_line = false;
} else {
next_line(s, indent + width + 2);
s.push('\t');
s.push_str(line);
fn fields_ident_len_max(fields: &[FieldHelp]) -> Option<usize> {
let ident_len = |field: &FieldHelp| {
if !field.ident.is_empty() {
return Some(field.ident.len());
}
}
match field.ty {
TypedHelp::Enum { variants, .. } => fields_ident_len_max(variants),
TypedHelp::Struct { fields, .. } => fields_ident_len_max(fields),
_ => unreachable!(),
}
};
fields.iter().flat_map(ident_len).max()
}
fn field_helps(s: &mut String, indent: usize, fields: &[FieldHelp]) {
let field_len = |f: &FieldHelp| {
if let TypedHelp::FlattenedEnum { variants } = &f.ty {
variants.iter().map(|v| v.ident.len()).max().unwrap_or(0)
} else {
f.ident.len()
}
};
let Some(width) = fields.iter().map(field_len).max() else {
let Some(width) = fields_ident_len_max(fields) else {
return;
};
for f in fields.iter() {
if f.doc.is_empty() {
continue;
}
if let TypedHelp::FlattenedEnum { variants } = &f.ty {
for v in variants {
value_helps(s, indent, width, v);
}
} else {
value_helps(s, indent, width, f);
}
}
value_helps(s, indent, width, fields)
}
fn struct_help<'a>(
@ -322,7 +318,7 @@ fn enum_help<'a>(
}
pub fn help_text<T: Help>(doc: &str) -> String {
let help = T::help();
let help = T::HELP;
let mut s = String::new();
let mut extra = ExtraHelp::default();
match &help {