I try to implement a proc_macro Dump
, which is similar to serdes Serialize
.
For this purpose I have a crate foo
which contains my "primitive" structs (P1
and P2
in this case) which should only be dumpable.
Next I do have a foo_derive
crate which contains the procedural macro itself.
Because I want to support multiple formats I have a third crate foo_dump
which contains the trait definition of Dump
(e.g. this struct can be dumped) and Dumper
(this is something the backend should implement).
Very straight forward until this point.
When I now want to compile it, I get this error:
$ cargo build
error: cyclic package dependency: package `foo v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo)` depends on itself. Cycle:
package `foo v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo)`
... which is depended on by `foo_dump v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo_dump)`
... which is depended on by `foo_derive v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo_derive)`
I don't know what the correct way is, how to use dependencies in this crate. My current one is:
and this certainly is not possible.
What am I missing? What do I have to do to break the dependency circle?
(mcve@github)
/Cargo.toml
[workspace]
members = [
"foo",
"foo_derive",
"foo_dump",
]
/foo/Cargo.toml
[package]
name = "foo"
version = "0.1.0"
edition = "2018"
[dependencies]
foo_derive = { path = "../foo_derive" }
/foo/src/lib.rs
use foo_derive::Dump;
struct P1;
struct P2;
#[derive(Dump)]
struct Bar {
primitive_one: P1,
primitive_two: P2,
}
/foo_dump/Cargo.toml
[package]
name = "foo_dump"
version = "0.1.0"
edition = "2018"
[dependencies]
foo = { path = "../foo" }
/foo_dump/src/lib.rs
use foo::{P1, P2};
pub trait Dumper {
fn dump_p1(&mut self, value: &P1);
fn dump_p2(&mut self, value: &P2);
}
pub trait Dump {
fn dump<D: Dumper>(&self, d: D);
}
impl Dump for P1 {
fn dump<D: Dumper>(&self, d: D) {
d.dump_p1(self);
}
}
impl Dump for P2 {
fn dump<D: Dumper>(&self, d: D) {
d.dump_p2(self);
}
}
/foo_derive/Cargo.toml
[package]
name = "foo_derive"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
syn = "*"
quote = "*"
foo_dump = { path = "../foo_dump" }
/foo_derive/src/lib.rs
extern crate proc_macro;
use quote::quote;
use proc_macro::TokenStream;
use syn::DeriveInput;
#[proc_macro_derive(Dump)]
pub fn derive_dump(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as DeriveInput);
let name = &input.ident;
quote!(
impl foo_dump::Dump for #name {
fn dump<D: foo_dump::Dumper>(&self, d: D) {
unimplemented!()
}
}
).into()
}
Thanks to @Boiethious comment and his help in chat I was able to come up with a solution, which involes introducing a new crate foo_core
which contains the structs P1
and P2
.
So what I did was:
P1
and P2
from foo
and putting them into foo_core
foo_dump
from foo_derive
so it only depends on syn
and quote
anymorefoo_core
as a dependency in foo
and foo_dump
foo_dump
to foo
(you can see the full list of changes in the git history).
The final dependency chain now looks like this:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With