mirror of
https://github.com/google/alioth.git
synced 2024-10-22 22:46:38 +00:00
perf(help): replace help() with an associated const
Signed-off-by: Changyuan Lyu <changyuanl@google.com>
This commit is contained in:
parent
816804aa53
commit
03ee6c42ef
3 changed files with 99 additions and 132 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
})
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue