clean up parse_text_signature_attrs and get_doc

This commit is contained in:
Jacob Lifshay 2019-12-03 16:42:48 -08:00
parent 07611b0358
commit 95c2e2f585
1 changed files with 48 additions and 66 deletions

View File

@ -28,17 +28,10 @@ pub fn is_text_signature_attr(attr: &syn::Attribute) -> bool {
fn parse_text_signature_attr<T: Display + quote::ToTokens + ?Sized>( fn parse_text_signature_attr<T: Display + quote::ToTokens + ?Sized>(
attr: &syn::Attribute, attr: &syn::Attribute,
python_name: &T, python_name: &T,
text_signature_line: &mut Option<syn::LitStr>, ) -> syn::Result<Option<syn::LitStr>> {
) -> syn::Result<Option<()>> {
if !is_text_signature_attr(attr) { if !is_text_signature_attr(attr) {
return Ok(None); return Ok(None);
} }
if text_signature_line.is_some() {
return Err(syn::Error::new_spanned(
attr,
"text_signature attribute already specified previously",
));
}
let python_name_str = python_name.to_string(); let python_name_str = python_name.to_string();
let python_name_str = python_name_str let python_name_str = python_name_str
.rsplit('.') .rsplit('.')
@ -57,56 +50,46 @@ fn parse_text_signature_attr<T: Display + quote::ToTokens + ?Sized>(
.. ..
}) => { }) => {
let value = lit.value(); let value = lit.value();
if !value.starts_with('(') { if value.starts_with('(') && value.ends_with(')') {
return Err(syn::Error::new_spanned( Ok(Some(syn::LitStr::new(
lit,
"text_signature must start with \"(\"",
));
}
if !value.ends_with(')') {
return Err(syn::Error::new_spanned(
lit,
"text_signature must end with \")\"",
));
}
*text_signature_line = Some(syn::LitStr::new(
&(python_name_str.to_owned() + &value), &(python_name_str.to_owned() + &value),
lit.span(), lit.span(),
)); )))
} else {
Err(syn::Error::new_spanned(
lit,
"text_signature must start with \"(\" and end with \")\"",
))
} }
meta => { }
return Err(syn::Error::new_spanned( meta => Err(syn::Error::new_spanned(
meta, meta,
"text_signature must be of the form #[text_signature = \"\"]", "text_signature must be of the form #[text_signature = \"\"]",
)); )),
} }
};
Ok(Some(()))
} }
pub fn parse_text_signature_attrs<T: Display + quote::ToTokens + ?Sized>( pub fn parse_text_signature_attrs<T: Display + quote::ToTokens + ?Sized>(
attrs: &mut Vec<syn::Attribute>, attrs: &mut Vec<syn::Attribute>,
python_name: &T, python_name: &T,
) -> syn::Result<Option<syn::LitStr>> { ) -> syn::Result<Option<syn::LitStr>> {
let mut parse_error: Option<syn::Error> = None;
let mut text_signature = None; let mut text_signature = None;
attrs.retain(|attr| { let mut attrs_out = Vec::with_capacity(attrs.len());
match parse_text_signature_attr(attr, python_name, &mut text_signature) { for attr in attrs.drain(..) {
Ok(None) => return true, if let Some(value) = parse_text_signature_attr(&attr, python_name)? {
Ok(Some(_)) => {} if text_signature.is_some() {
Err(err) => { return Err(syn::Error::new_spanned(
if let Some(parse_error) = &mut parse_error { attr,
parse_error.combine(err); "text_signature attribute already specified previously",
));
} else { } else {
parse_error = Some(err); text_signature = Some(value);
}
} else {
attrs_out.push(attr);
} }
} }
} *attrs = attrs_out;
false
});
if let Some(parse_error) = parse_error {
return Err(parse_error);
}
Ok(text_signature) Ok(text_signature)
} }
@ -116,31 +99,35 @@ pub fn get_doc(
text_signature: Option<syn::LitStr>, text_signature: Option<syn::LitStr>,
null_terminated: bool, null_terminated: bool,
) -> syn::Result<syn::Lit> { ) -> syn::Result<syn::Lit> {
let mut doc = Vec::new(); let mut doc = String::new();
let mut needs_terminating_newline = false; let mut span = Span::call_site();
if let Some(text_signature) = text_signature { if let Some(text_signature) = text_signature {
doc.push(text_signature.value()); // create special doc string lines to set `__text_signature__`
doc.push("--".to_string()); span = text_signature.span();
doc.push(String::new()); doc.push_str(&text_signature.value());
needs_terminating_newline = true; doc.push_str("\n--\n\n");
} }
// TODO(althonos): set span on produced doc str literal let mut separator = "";
// let mut span = None; let mut first = true;
for attr in attrs.iter() { for attr in attrs.iter() {
if let Ok(syn::Meta::NameValue(ref metanv)) = attr.parse_meta() { if let Ok(syn::Meta::NameValue(ref metanv)) = attr.parse_meta() {
if metanv.path.is_ident("doc") { if metanv.path.is_ident("doc") {
// span = Some(metanv.span());
if let syn::Lit::Str(ref litstr) = metanv.lit { if let syn::Lit::Str(ref litstr) = metanv.lit {
if first {
first = false;
span = litstr.span();
}
let d = litstr.value(); let d = litstr.value();
doc.push(if d.starts_with(' ') { doc.push_str(separator);
d[1..d.len()].to_string() if d.starts_with(' ') {
doc.push_str(&d[1..d.len()]);
} else { } else {
d doc.push_str(&d);
}); };
needs_terminating_newline = false; separator = "\n";
} else { } else {
return Err(syn::Error::new_spanned(metanv, "Invalid doc comment")); return Err(syn::Error::new_spanned(metanv, "Invalid doc comment"));
} }
@ -148,14 +135,9 @@ pub fn get_doc(
} }
} }
if needs_terminating_newline {
doc.push(String::new());
}
let mut docstr = doc.join("\n");
if null_terminated { if null_terminated {
docstr.push('\0'); doc.push('\0');
} }
Ok(syn::Lit::Str(syn::LitStr::new(&docstr, Span::call_site()))) Ok(syn::Lit::Str(syn::LitStr::new(&doc, span)))
} }