Properly interpolate #[openapi]-annotation with TokenStreams#48
Conversation
|
(Had to fix a doc test I missed.) |
|
Thank you for your contribution. I am gonna have look at it at the weekend and do some testing. |
|
Hey I tested it. It works great and actually fixes: #46 I just have to make a code review. Just as an update |
DenuxPlays
left a comment
There was a problem hiding this comment.
Just one comment but it looks great.
But in the extract_* functions I am not sure if we should use nested for loops or if we should work with iterators.
Ofcourse both work so it's just a preferene.
But I had something like this in my mind:
fn extract_schemas(nested_attributes: &Punctuated<Meta, Token![,]>) -> TokenStream {
nested_attributes
.iter()
.find_map(|meta| {
let Meta::List(list) = meta else { return None };
if !list.path.is_ident("components") {
return None;
}
let nested = list
.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
.expect("Expected a list of attributes inside components(...)!");
nested.iter().find_map(|meta| {
let Meta::List(list) = meta else { return None };
if list.path.is_ident("schemas") {
Some(list.tokens.clone())
} else {
None
}
})
})
.unwrap_or_else(TokenStream::new)
}Maybe @ProbablyClem can say what he wants as I don't really care.
Both work and are readable.
|
Addressed your comments and just went ahead with changing the Happy to hear that this actually fixes the other issue as well |
|
LGTM Thank you for your contribution. FYI: |
Replaces the String-based interpolation of the auto-discovered tokens into the manually annotated attributes with quoting via
TokenStreams.What this PR fixes
When bumping to
utoipav5 /utoipautov0.2, I noticed I was getting an error in my IDE (VS Code) on the#[utoipauto]annotation. Everything built fine when running tests / just building withcargo, but the macro crashed byunwrapping anErrwhen run via rust-analyzer.I was able to narrow the issue down to your handling of the original
#[openapi]annotations on the struct derivingOpenAPI:utoipautodoes its discovery and then pieces together the automatically discovered components with the existing annotations likemodifiers,tags, etc. To do this,update_openapi_macro_attributestakes the whole attribute, converts it to a string viato_token_streamand then tries to strip#[openapi(at the start and)]at the end of the attribute, inserting everything in between into the newly generated annotation that is created as the output of the macro, containing also the automatically discovered paths.With
cargo, this works because therustcproc-macro server is relatively exact in itsto_string()representation of the attribute:#[openapi(modifiers(&Addon1, &Addon2),\ninfo(title = \"Title\", version = \"v1\",),\nvariables((\"var\" =\n(description = \"Description\")),)),))](I've left out a bunch of stuff but you get the idea.)
Compare this to the
to_string()representation we get when running under rust-analyzer:As you can see, rust-analyzer's proc-macro server inserts additional whitespace in between the tokens inside that make up the token stream, which causes the substrings to no longer match and not be removed, and therefore causes the macro to output something like
#[openapi(paths(...), #[openapi(modifiers(...))] )]containing first the auto-discovered paths, followed by the entire original attribute, which fails to re-parse as aTokenStreambecause attributes are not valid inside attributes.Given that the
impl Display for TokenStreamexplicitly cautions against matching against its output because it isn't stable by sayingI'm gonna suggest that this should be considered a bug in
utoipauto.Changes made to fix this issue
The PR changes the interpolation inside
update_openapi_macro_attributesto work onTokenStreams instead ofStrings. To do this, I had to create a larger change than I'd like, because I had to go back tofile_utils::exctract_module_name_from_pathto return an actualPath(this also fixes a bug where hyphens were only replaced with underscores in the crate name, but not module names, which for some reason existed in one of the tests), such thatdiscover_from_filecan nowparse_module_itemsasPaths, such thatupdate_openapi_macro_attributescan interpolate those paths into the original macro input on the level ofTokenStreams /quote. This necessitated also rewriting theextract_andremove_functions inattribute_utilsto work onsyn'sAttribute/Meta.I've ran the changes in the PR against your test suite, including the
acceptancetests, and further verified the macro output is unchanged / correct in a regular build in the project I was having the issue in originally. I'll leave a comment for some test changes I had to make as a review.