Directly parse data on nested proc macro - rust

Suppose this:
#[route(base_path="/api")]
pub struct Type;
impl Type {
#[route(path="/v1/handler")]
pub fn handler() {}
#[route(path="/v1/handler2")]
pub fn handler2() {}
}
I want to parse in any kind of data struct some mapping between the base_path and the path attached to the methods on the impl block. Imagine any HashMap<K, V> where base_path are the keys and path it's related values.
// pseudo struct internal data representation
[
{
struct: MyRestController,
base_path: "/api",
paths: [
"handler", "handler2"
]
},
// other types registered
]
Is it possible to know with the proc-macro attached to the struct, detect the attributes attached to the methods on the impl block and made the parse in one type? (When the macro attached to the struct runs).

You probably want to write your macro so that it expects the following:
pub struct Type;
#[route(base_path="/api")]
impl Type {
#[route(path="/v1/handler")]
pub fn handler() {}
#[route(path="/v1/handler2")]
pub fn handler2() {}
}
That is, it wouldn't know anything about the struct itself (i.e. its fields), but it will have full access to the impl block, including attributes on individual functions.

Related

Use a generic type as associated type, without phantom data

I have a trait defining an associated type:
pub trait View: Default {
type State;
fn draw(&mut self, state: &mut Self::State);
}
I have a type suitable for the State associated type, but it's generic:
pub struct MenuState<I> {
pub items: Vec<I>,
}
The following code can't compile (rust 1.66) because I isn't constrained:
#[derive(Default)]
pub struct MenuView {
// fields here
}
impl<I: ToString + Copy> View for MenuView {
type State = MenuState<I>;
fn draw(&mut self, _state: &mut Self::State) { }
}
(note: the ToString and Copy constraints are needed for the implementation of MenuView)
I know how to solve that by adding some phantom data to make MenuView generic on I (see playground) but it's ugly and seems too much complicated. I feel like the code pasted above just needs some syntactic fix.
Is there a simple solution, without changing the View trait and not involving a phantom ?
If a syntactic fix can't be defined for some fundamental reason, what is that reason ?
Without changing the View trait, PhantomData is the correct solution, your generic must appear somewhere either in the trait or in the struct. State is something that is "associate" to MenuView and so there is no other way than to include it.
However, your case could suggest View is not well defined to your usage, maybe a generic would be more appropriate than an associate type. Like:
pub trait View<State>: Default {
fn draw(&mut self, state: &mut State);
}
#[derive(Default)]
pub struct MenuState<I> {
pub items: Vec<I>,
}
#[derive(Default)]
pub struct MenuView {}
impl<I: ToString + Copy + Default> View<MenuState<I>> for MenuView {
fn draw(&mut self, _state: &mut MenuState<I>) {}
}
This more or less transforms your original trait, from, implement View for MenuView that have a State, to, implement View a State for MenuView.

How to generate Rust docs for type aliases?

In a library I have a compressor that supports two "modes" with different APIs, but the same data model some common logic. Here's how I've written it DRY:
struct BaseCompressor<M: Mode> {...}
impl<M: Mode> BaseCompressor<M> {
/// some docs...
pub fn header(...) -> ... {...}
}
pub type CompressorA = BaseCompressor<ModeA>;
impl CompressorA {
/// some docs...
pub fn chunk(...) -> ... {...}
}
pub type CompressorB = BaseCompressor<ModeB>
impl CompressorB {
/// some docs...
pub fn data_page(...) -> ... {...}
}
I'm quite happy with structuring the logic this way so that CompressorA and CompressorB share data members and logic for header. However, it screws up the generated docs, rendering absolutely nothing meaningful for CompressorA and CompressorB. I would rather not expose BaseCompressor in the public API. Is there any way I can get full docs for CompressorA and CompressorB? Or perhaps a better way I can structure this?
Trying your example locally (slightly adjusted), I get this result from cargo doc --open:
Would this satisfy your needs? I do have another way to structure your code which I would prefer:
Since you said the APIs for the modes are different, you could just have 2 separate structs that both contain a BaseCompressor:
// Private base type
struct BaseCompressor;
impl BaseCompressor {
// Some shared logic
pub fn header() {}
}
// Mode A
pub struct CompressorA {
inner: BaseCompressor
}
impl CompressorA {
// API for mode A
pub fn chunk() {}
}
// Mode B
pub struct CompressorB {
inner: BaseCompressor
}
impl CompressorB {
// API for mode B
pub fn data_page() {}
}

How do I access the token stream of a trait when writing a derive macro for a struct?

I have a struct and a trait:
struct Foo {
i: i32,
}
trait FooTrait {
fn foo(&self);
}
I want to create a derive macro for the struct which generates the impl:
impl FooTrait for Foo {
fn foo(&self) {
println!("generated code: {}", self.i);
}
}
When I attempted to achieve this, I'm facing the blocker that my derive macro doesn't seem to have a way to know the token stream of FooTrait, where I need to iterate through the methods of FooTrait, and generate the implementation for each trait method based on Foo!
How can I achieve that?
This question is not about how to use quote! to quote the trait impl and spell out foo directly - the hard part is that I want to procedurally iterate through the methods of FooTrait, and generate some boilerplate code for each trait method.
You don't.
By construction, macros are only provided with the source code of the item they are attached to. If you attach the macro to the struct, you don't get to see the trait.
Similar derive macros bake in the knowledge about the trait being implemented. They may also parse custom attributes that allow the user to configure details about the generated code. Your macro will likely do the same.
See also:
Is it possible to store state within Rust's procedural macros?
A possible workaround: instead of defining FooTrait directly, encode the information you need about it in some structure, store it in a const and access it both from the derive macro and another macro generating the definition of FooTrait.
EDIT: if you want exactly "the token stream of FooTrait", you don't need the const (this is just a skeleton and not tested):
The macros crate:
#[proc_macro]
pub fn foo_trait_definition(_input: TokenStream) -> TokenStream {
TokenStream::from(quote!(
trait FooTrait {
... // actual definition goes here
}
))
}
#[proc_macro_derive(FooTrait)]
pub fn foo_trait_derivation(input: TokenStream) -> TokenStream {
let foo_trait_tokens: TokenStream = foo_trait_definition(TokenStream::from(quote!()));
// you can parse foo_trait_tokens and use them just like input
TokenStream::from(quote!(
impl foo_mod::FooTrait for #name {
...
}
))
}
The trait crate:
mod foo_mod {
foo_trait_definition!();
}
and
#[derive(FooTrait)]
struct Foo {
i: i32,
}
But I expect that in most cases the macros crate can look like this instead of parsing foo_trait_tokens:
// lists method names here, if they are all (&self)
// but probably you want something more complex
const foo_trait_data = ["foo"];
#[proc_macro]
pub fn foo_trait_definition(_input: TokenStream) -> TokenStream {
// use foo_trait_data here
TokenStream::from(quote!(
trait FooTrait {
... // actual definition goes here
}
))
}
#[proc_macro_derive(FooTrait)]
pub fn foo_trait_derivation(input: TokenStream) -> TokenStream {
// use foo_trait_data here too
TokenStream::from(quote!(
impl foo_mod::FooTrait for #name {
...
}
))
}

Can you put struct impl blocks in a different file?

Say, for purposes of conjecture, that you have three files: main.rs, struct.rs, and impl.rs. Could you define a struct in struct.rs, put an impl there, put another impl in impl.rs, and then use both sets of impls from main.rs? If so, how?
Project structure:
main.rs:
use struct;
use impl;
main() {
let foobar = struct::Struct::new(); // defined in struct.rs
foobar.x(); // defined in impl.rs
}
struct.rs:
Define Struct, first impl
impl.rs:
Second impl
Yes, this is possible. You can provide implementations for your struct throughout the crate. You just can't provide impls for types from foreign crates. And you don't need to do anything special to make this work – just make sure the struct is visible in main. Of course you can't name your modules struct and impl, since these are reserved words.
Here's some example code:
fn main() {
use struct_::A;
A::foo();
A::bar();
}
pub mod struct_ {
pub struct A;
impl A {
pub fn foo() {}
}
}
mod impl_ {
impl crate::struct_::A {
pub fn bar() {}
}
}
(Playground)

How do I use a BorrowMut supertrait to access struct fields in a trait default method?

Consider the following Rust code
trait Trait {
fn show_name(&self) {}
}
struct Foo {
name: String,
things_and_stuff: usize,
}
impl Trait for Foo {
fn show_name(&self) {
println!("{}", self.name);
}
}
struct Bar {
name: String,
other_field: i32,
}
impl Trait for Bar {
fn show_name(&self) {
println!("{}", self.name);
}
}
The two show_name functions have exactly the same code. It would be convenient if I could put that method as a default method on Trait, but that's not possible because traits cannot access struct fields.
We could declare a get_name(&self) -> &str method on Trait and implement it on Foo and Bar, but that doesn't fix the problem of having duplicated code because both implementations of get_name will be the same.
It would be nice to avoid the code duplication. Another question has already asked if field access is possible in traits, and the answer was basically "no".
However, I found a comment in the rust-internals forum suggesting that it's already possible. Here's the code:
struct Fields {
name: String,
}
trait Trait: BorrowMut<Fields> {
// methods go here
}
impl<T> Trait for T where T: BorrowMut<Fields> {}
Presumably there's a way to make a type T be BorrowMut<Fields> and use that to allow Trait to access Fields's fields, but so far I'm not sure how that would work.
How is the code snippet shown above supposed to solve the problem of getting field access in traits?
I know there are discussions of adding field access in traits to the language (rust-internals, RFC, another RFC), but I'd like to know what's possible now.
the answer was basically "no"
The answer actually says (emphasis mine):
A default implementation can only use methods that are defined on the trait or in a super trait.
That's what your snippet does:
trait Trait: BorrowMut<Fields>
To make it work, follow the advice from the post you are referencing:
all types which choose to implement BorrowMut<Foo>
Thus, you need to implement the trait for each of your types. I switched to Borrow because you don't need mutability here:
use std::borrow::Borrow;
struct Fields {
name: String,
}
trait Trait: Borrow<Fields> {
fn show_name(&self) {
let fields: &Fields = self.borrow();
println!("{}", fields.name);
}
}
struct Foo {
fields: Fields,
things_and_stuff: usize,
}
impl Borrow<Fields> for Foo {
fn borrow(&self) -> &Fields {
&self.fields
}
}
struct Bar {
fields: Fields,
other_field: i32,
}
impl Borrow<Fields> for Bar {
fn borrow(&self) -> &Fields {
&self.fields
}
}
impl<T> Trait for T where T: Borrow<Fields> {}
I'm almost certain you won't like this solution because it
doesn't fix the problem of having duplicated code because both implementations [...] will be the same
You may prefer to write a macro if your goal is to reduce the number of duplicate characters in your code.
See also:
I implemented a trait for another trait but cannot call methods from both traits
Is it possible to access struct fields from within a trait?

Resources