Skip to content

Commit ddecfeb

Browse files
committed
[spr] initial version
Created using spr 1.3.6-beta.1
2 parents 092c10c + d199591 commit ddecfeb

15 files changed

+149
-34
lines changed

daft-derive/src/internals/imp.rs

+20-25
Original file line numberDiff line numberDiff line change
@@ -7,48 +7,43 @@ use syn::{
77
LifetimeParam, Path, Token, WhereClause, WherePredicate,
88
};
99

10-
pub fn derive_diffable(input: syn::DeriveInput) -> TokenStream {
10+
pub struct DeriveDiffableOutput {
11+
pub out: Option<TokenStream>,
12+
pub errors: Vec<syn::Error>,
13+
}
14+
15+
impl ToTokens for DeriveDiffableOutput {
16+
fn to_tokens(&self, tokens: &mut TokenStream) {
17+
tokens.extend(self.out.clone());
18+
tokens.extend(self.errors.iter().map(|error| error.to_compile_error()));
19+
}
20+
}
21+
22+
pub fn derive_diffable(input: syn::DeriveInput) -> DeriveDiffableOutput {
1123
let mut error_store = ErrorStore::new();
1224

1325
match &input.data {
1426
Data::Enum(_) => {
1527
// Implement all Enums as `Leaf`s
1628
let out = make_leaf(&input, AttrPosition::Enum, error_store.sink());
17-
// Errors might have occurred while parsing attributes.
18-
let errors = error_store
19-
.into_inner()
20-
.into_iter()
21-
.map(|error| error.into_compile_error());
22-
quote! {
23-
#out
24-
#(#errors)*
29+
DeriveDiffableOutput {
30+
out: Some(out),
31+
errors: error_store.into_inner(),
2532
}
2633
}
2734
Data::Struct(s) => {
2835
// This might be None if there are errors.
2936
let out = make_struct_impl(&input, s, error_store.sink());
30-
let errors = error_store
31-
.into_inner()
32-
.into_iter()
33-
.map(|error| error.into_compile_error());
34-
quote! {
35-
#out
36-
#(#errors)*
37-
}
37+
DeriveDiffableOutput { out, errors: error_store.into_inner() }
3838
}
3939

4040
Data::Union(_) => {
4141
// Implement all unions as `Leaf`s
4242
let out =
4343
make_leaf(&input, AttrPosition::Union, error_store.sink());
44-
// Errors might have occurred while parsing attributes.
45-
let errors = error_store
46-
.into_inner()
47-
.into_iter()
48-
.map(|error| error.into_compile_error());
49-
quote! {
50-
#out
51-
#(#errors)*
44+
DeriveDiffableOutput {
45+
out: Some(out),
46+
errors: error_store.into_inner(),
5247
}
5348
}
5449
}

daft-derive/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#![doc(html_root_url = "https://docs.rs/daft-derive/0.1.1")]
77
mod internals;
88

9+
use quote::ToTokens;
910
use syn::parse_macro_input;
1011

1112
// NOTE: We do not define documentation here -- only in daft while re-exporting
@@ -16,5 +17,5 @@ pub fn derive_diffable(
1617
input: proc_macro::TokenStream,
1718
) -> proc_macro::TokenStream {
1819
let input = parse_macro_input!(input as syn::DeriveInput);
19-
internals::derive_diffable(input).into()
20+
internals::derive_diffable(input).into_token_stream().into()
2021
}

daft-derive/tests/fixtures/README.md

+12-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ These fixtures ensure that:
99

1010
Each file in `valid` is automatically picked up by the snapshot and UI tests.
1111

12-
Currently, `snapshot_test.rs` only tests the first struct or enum in the file.
13-
The test can be extended to test multiple macro invocations per file if
14-
necessary.
12+
`snapshot_test.rs` tests all macro invocations annotated with `#[derive(Diffable)]`.
13+
14+
## Invalid fixtures
15+
16+
These fixtures ensure that:
17+
18+
* the macro's success output, if any, is stable, via `snapshot_test.rs`.
19+
* the macro's output fails with a good error message, via `ui_test.rs`.
20+
21+
Each file in `invalid` is automatically picked up by the snapshot and UI tests.
22+
23+
Like with valid fixtures, `snapshot_test.rs` tests all macro invocations annotated with `#[derive(Diffable)]`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
impl ::daft::Diffable for MyEnum {
2+
type Diff<'__daft> = ::daft::Leaf<&'__daft Self> where Self: '__daft;
3+
fn diff<'__daft>(&'__daft self, other: &'__daft Self) -> Self::Diff<'__daft> {
4+
::daft::Leaf {
5+
before: self,
6+
after: other,
7+
}
8+
}
9+
}
10+
impl ::daft::Diffable for MyEnum2 {
11+
type Diff<'__daft> = ::daft::Leaf<&'__daft Self> where Self: '__daft;
12+
fn diff<'__daft>(&'__daft self, other: &'__daft Self) -> Self::Diff<'__daft> {
13+
::daft::Leaf {
14+
before: self,
15+
after: other,
16+
}
17+
}
18+
}

daft-derive/tests/fixtures/invalid/output/field-attribute-with-value.output.rs

Whitespace-only changes.

daft-derive/tests/fixtures/invalid/output/field-leaf-and-ignore.output.rs

Whitespace-only changes.

daft-derive/tests/fixtures/invalid/output/field-specified-multiple-times.output.rs

Whitespace-only changes.

daft-derive/tests/fixtures/invalid/output/field-unknown-attribute.output.rs

Whitespace-only changes.

daft-derive/tests/fixtures/invalid/output/struct-attribute-with-value.output.rs

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
impl ::daft::Diffable for MyStruct {
2+
type Diff<'__daft> = ::daft::Leaf<&'__daft Self> where Self: '__daft;
3+
fn diff<'__daft>(&'__daft self, other: &'__daft Self) -> Self::Diff<'__daft> {
4+
::daft::Leaf {
5+
before: self,
6+
after: other,
7+
}
8+
}
9+
}

daft-derive/tests/fixtures/invalid/output/struct-specified-multiple-times.output.rs

Whitespace-only changes.

daft-derive/tests/fixtures/invalid/output/struct-unknown-attribute.output.rs

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
impl ::daft::Diffable for MyUnion {
2+
type Diff<'__daft> = ::daft::Leaf<&'__daft Self> where Self: '__daft;
3+
fn diff<'__daft>(&'__daft self, other: &'__daft Self) -> Self::Diff<'__daft> {
4+
::daft::Leaf {
5+
before: self,
6+
after: other,
7+
}
8+
}
9+
}
10+
impl ::daft::Diffable for MyUnion2 {
11+
type Diff<'__daft> = ::daft::Leaf<&'__daft Self> where Self: '__daft;
12+
fn diff<'__daft>(&'__daft self, other: &'__daft Self) -> Self::Diff<'__daft> {
13+
::daft::Leaf {
14+
before: self,
15+
after: other,
16+
}
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
impl ::daft::Diffable for Inner {
2+
type Diff<'__daft> = ::daft::Leaf<&'__daft Self> where Self: '__daft;
3+
fn diff<'__daft>(&'__daft self, other: &'__daft Self) -> Self::Diff<'__daft> {
4+
::daft::Leaf {
5+
before: self,
6+
after: other,
7+
}
8+
}
9+
}

daft-derive/tests/snapshot_test.rs

+61-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ mod internals;
1111

1212
datatest_stable::harness! {
1313
// The pattern matches all .rs files that aren't .output.rs files.
14-
{ test = daft_snapshot, root = "tests/fixtures/valid", pattern = r"^.*(?<!\.output)\.rs$" }
14+
{ test = daft_snapshot, root = "tests/fixtures/valid", pattern = r"^.*(?<!\.output)\.rs$" },
15+
{ test = daft_snapshot_invalid, root = "tests/fixtures/invalid", pattern = r"^.*(?<!\.output)\.rs$" },
1516
}
1617

1718
/// Snapshot tests for valid inputs.
@@ -21,10 +22,42 @@ fn daft_snapshot(
2122
) -> datatest_stable::Result<()> {
2223
let data = syn::parse_str::<syn::File>(&input)?;
2324

25+
let output = run_derive_macro(&data);
26+
assert_derive_output(path, output);
27+
28+
Ok(())
29+
}
30+
31+
/// Snapshot tests for invalid inputs.
32+
fn daft_snapshot_invalid(
33+
path: &Utf8Path,
34+
input: String,
35+
) -> datatest_stable::Result<()> {
36+
let data = syn::parse_str::<syn::File>(&input)?;
37+
38+
let output = run_derive_macro(&data).map(|output| {
39+
// Drop the errors for snapshot tests -- only use the output.
40+
output.out
41+
});
42+
assert_derive_output(path, output);
43+
44+
Ok(())
45+
}
46+
47+
fn run_derive_macro(
48+
data: &syn::File,
49+
) -> impl Iterator<Item = internals::DeriveDiffableOutput> + '_ {
2450
// Look for structs and enums in the input -- give them to the derive macro.
2551
let items = data.items.iter().filter_map(|item| match item {
26-
syn::Item::Struct(item) => Some(item.to_token_stream()),
27-
syn::Item::Enum(item) => Some(item.to_token_stream()),
52+
syn::Item::Struct(item) => {
53+
has_derive_diffable(&item.attrs).then(|| item.to_token_stream())
54+
}
55+
syn::Item::Enum(item) => {
56+
has_derive_diffable(&item.attrs).then(|| item.to_token_stream())
57+
}
58+
syn::Item::Union(item) => {
59+
has_derive_diffable(&item.attrs).then(|| item.to_token_stream())
60+
}
2861
_ => None,
2962
});
3063

@@ -35,8 +68,33 @@ fn daft_snapshot(
3568
});
3669
internals::derive_diffable(data)
3770
});
71+
output
72+
}
73+
74+
fn has_derive_diffable(attrs: &[syn::Attribute]) -> bool {
75+
attrs.iter().any(|attr| {
76+
if !attr.path().is_ident("derive") {
77+
return false;
78+
}
3879

80+
let mut is_diffable = false;
81+
attr.parse_nested_meta(|meta| {
82+
if meta.path.is_ident("Diffable") {
83+
is_diffable = true;
84+
}
85+
Ok(())
86+
})
87+
.expect("derive attributes parsed correctly");
88+
is_diffable
89+
})
90+
}
91+
92+
fn assert_derive_output<T: ToTokens>(
93+
path: &Utf8Path,
94+
output: impl IntoIterator<Item = T>,
95+
) {
3996
// Read the output as a `syn::File`.
97+
let output = output.into_iter();
4098
let file = parse_quote! {
4199
#(#output)*
42100
};
@@ -52,6 +110,4 @@ fn daft_snapshot(
52110
output_path.set_extension("output.rs");
53111

54112
expectorate::assert_contents(&output_path, &output);
55-
56-
Ok(())
57113
}

0 commit comments

Comments
 (0)