Skip to content

Commit

Permalink
Add #[obake(serde(...))] helper
Browse files Browse the repository at this point in the history
  • Loading branch information
doctorn committed Aug 31, 2021
1 parent a65b8f2 commit 5b2b998
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 6 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl From<Foo!["0.1.0"]> for Foo!["0.2.0"] {
}

// an enumeration of all versions of `Foo` is accessed using the `obake::AnyVersion` type
// alias.
// alias
let versioned_example: obake::AnyVersion<Foo> = (Foo { bar: 42 }).into();

// this enumeration implements `Into<Foo>`, where `Foo` is the latest declared
Expand All @@ -78,6 +78,9 @@ assert_eq!(example, Foo { bar: 42 });

- `#[obake(inherit)]`: allows nesting of versioned data-structures.
- `#[obake(derive(...))]`: allows derive attributes to be applied to generated enums.
- `#[obake(serde(...))]`: allows [`serde`](https://serde.rs) attributes to be applied to
generated `enum`s.
- Note: requires the feature `serde`.

## Limitations

Expand Down
7 changes: 6 additions & 1 deletion obake/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "obake"
authors = ["Nathan Corbyn <[email protected]>"]
version = "1.0.3"
version = "1.0.4"
edition = "2018"
license = "MIT OR Apache-2.0"
description = "Versioned data-structures for Rust"
Expand All @@ -21,3 +21,8 @@ obake_macros = { path = "../obake_macros", version = "1.0" }

[dev-dependencies]
trybuild = "1.0"
obake_macros = { path = "../obake_macros", version = "1.0", features = ["serde"] }

[features]
default = []
serde = ["obake_macros/serde"]
10 changes: 9 additions & 1 deletion obake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
//! }
//!
//! // an enumeration of all versions of `Foo` is accessed using the `obake::AnyVersion` type
//! // alias.
//! // alias
//! let versioned_example: obake::AnyVersion<Foo> = (Foo { bar: 42 }).into();
//!
//! // this enumeration implements `Into<Foo>`, where `Foo` is the latest declared
Expand All @@ -52,6 +52,9 @@
//!
//! - `#[obake(inherit)]`: allows nesting of versioned data-structures.
//! - `#[obake(derive(...))]`: allows derive attributes to be applied to generated `enum`s.
//! - `#[obake(serde(...))]`: allows [`serde`](https://serde.rs) attributes to be applied to
//! generated `enum`s.
//! - Note: requires the feature `serde`.
//!
//! ## Limitations
//!
Expand Down Expand Up @@ -80,9 +83,14 @@
/// attributes are treated as a disjunctively).
/// - `#[obake(derive(...))]` - Apply a derive to the version-tagged enum generated for the
/// data-structre.
/// - `#[obake(serde(...))]` - Apply a [serde] attribute to the version-tagged enum generated
/// for the data-structre.
/// - Note: requires the feature `serde`.
/// - `#[obake(inherit)]` - Marks a field as having an inherited version (i.e., given a field of
/// type `Bar`, when marked with `inherit`, this field will be expanded to a field of type
/// `Bar![{version}]` in every version).
///
/// [serde]: https://serde.rs
// TODO(@doctorn) document generated types and trait implementations
pub use obake_macros::versioned;

Expand Down
25 changes: 25 additions & 0 deletions obake/tests/ui/bad_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,29 @@ mod derives {
}
}

mod serdes {
#[obake::versioned]
#[obake(version("0.1.0"))]
struct Foo {
#[obake(serde(skip_serializing))]
field_0: u32,
}

#[obake::versioned]
#[obake(version("0.1.0"))]
enum Bar {
#[obake(serde(skip_serializing))]
X,
}

#[obake::versioned]
#[obake(version("0.1.0"))]
enum Baz {
X {
#[obake(serde(skip_serializing))]
field_0: u32,
},
}
}

fn main() {}
18 changes: 18 additions & 0 deletions obake/tests/ui/bad_helpers.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,21 @@ error: `#[obake(derive(...))]` not valid in this context
|
53 | #[obake(derive(Clone))]
| ^^^^^^

error: `#[obake(serde(...))]` not valid in this context
--> $DIR/bad_helpers.rs:63:17
|
63 | #[obake(serde(skip_serializing))]
| ^^^^^

error: `#[obake(serde(...))]` not valid in this context
--> $DIR/bad_helpers.rs:70:17
|
70 | #[obake(serde(skip_serializing))]
| ^^^^^

error: `#[obake(serde(...))]` not valid in this context
--> $DIR/bad_helpers.rs:78:21
|
78 | #[obake(serde(skip_serializing))]
| ^^^^^
6 changes: 5 additions & 1 deletion obake_macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "obake_macros"
authors = ["Nathan Corbyn <[email protected]>"]
version = "1.0.3"
version = "1.0.4"
edition = "2018"
license = "MIT OR Apache-2.0"
description = "Macros for versioned data-structures"
Expand All @@ -22,3 +22,7 @@ proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0", features = ["full"] }
semver = "1.0"

[features]
default = []
serde = []
25 changes: 23 additions & 2 deletions obake_macros/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ impl VersionedField {
));
}

#[cfg(feature = "serde")]
if let Some(serde) = self.attrs.serdes().next() {
return Err(syn::Error::new(
serde.span,
"`#[obake(serde(...))]` not valid in this context",
));
}

let mut reqs: Vec<_> = self.attrs.cfgs().map(|attr| attr.req.clone()).collect();

// If we have no `#[obake(cfg(...))]` attributes, default to `#[obake(cfg("*"))]`
Expand Down Expand Up @@ -122,13 +130,21 @@ impl VersionedVariant {
));
}

if let Some(inherit) = self.attrs.derives().next() {
if let Some(derive) = self.attrs.derives().next() {
return Err(syn::Error::new(
inherit.span,
derive.span,
"`#[obake(derive(...))]` not valid in this context",
));
}

#[cfg(feature = "serde")]
if let Some(serde) = self.attrs.serdes().next() {
return Err(syn::Error::new(
serde.span,
"`#[obake(serde(...))]` not valid in this context",
));
}

let mut reqs: Vec<_> = self.attrs.cfgs().map(|attr| attr.req.clone()).collect();

// If we have no `#[obake(cfg(...))]` attributes, default to `#[obake(cfg("*"))]`
Expand Down Expand Up @@ -303,6 +319,11 @@ impl VersionedItem {
let tokens = &attr.tokens;
quote!(#[derive(#tokens)])
});
#[cfg(feature = "serde")]
let derives = derives.chain(self.attrs.serdes().map(|attr| {
let tokens = &attr.tokens;
quote!(#[serde(#tokens)])
}));

quote! {
#[doc(hidden)]
Expand Down
23 changes: 23 additions & 0 deletions obake_macros/src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,21 @@ pub struct DeriveAttr {
pub tokens: TokenStream2,
}

#[cfg(feature = "serde")]
#[derive(Clone)]
pub struct SerdeAttr {
pub span: Span,
pub tokens: TokenStream2,
}

#[derive(Clone)]
pub enum ObakeAttribute {
Version(VersionAttr),
Cfg(CfgAttr),
Inherit(InheritAttr),
Derive(DeriveAttr),
#[cfg(feature = "serde")]
Serde(SerdeAttr),
}

#[derive(Clone)]
Expand Down Expand Up @@ -108,6 +117,15 @@ impl ObakeAttribute {
_ => None,
}
}

#[cfg(feature = "serde")]
pub fn serde(&self) -> Option<&SerdeAttr> {
#![allow(clippy::match_wildcard_for_single_variants)]
match &self {
ObakeAttribute::Serde(serde) => Some(serde),
_ => None,
}
}
}

impl VersionedAttribute {
Expand Down Expand Up @@ -149,6 +167,11 @@ impl VersionedAttributes {
self.obake().filter_map(ObakeAttribute::derive)
}

#[cfg(feature = "serde")]
pub fn serdes(&self) -> impl Iterator<Item = &SerdeAttr> + '_ {
self.obake().filter_map(ObakeAttribute::serde)
}

pub fn attrs(&self) -> impl Iterator<Item = &syn::Attribute> + '_ {
self.attrs.iter().filter_map(VersionedAttribute::attr)
}
Expand Down
9 changes: 9 additions & 0 deletions obake_macros/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ impl Parse for ObakeAttribute {
tokens: content.parse()?,
})
}
#[cfg(feature = "serde")]
_ if ident == "serde" => {
let content;
parenthesized!(content in input);
Self::Serde(SerdeAttr {
span: ident.span(),
tokens: content.parse()?,
})
}
_ => {
return Err(syn::Error::new(
ident.span(),
Expand Down

0 comments on commit 5b2b998

Please sign in to comment.