Skip to content

Commit

Permalink
Release 0.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
VictorKoenders committed Dec 4, 2021
1 parent 6a7c052 commit 99d64c4
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 3 deletions.
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
[package]
name = "virtue"
version = "0.1.0"
version = "0.0.1"
edition = "2021"
description = "A sinless derive macro helper"
license-file = "LICENSE.md"
documentation = "https://docs.rs/virtue"
repository = "https://github.com/bincode-org/virtue"
readme = "README.md"

[dependencies]

Expand Down
21 changes: 21 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2021 Victor Koenders

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Virtue, a sinless derive macro helper

## Goals

- Zero dependencies, so fast compile times
- No other dependencies needed
- Declarative code generation
- As much typesystem checking as possible
- Build for modern rust: 1.57 and up
- Build with popular crates in mind:
- [bincode](https://docs.rs/bincode)
- Will always respect semver. Minor releases will never have:
- Breaking API changes
- MSRV changes

## Example

```rust
use virtue::prelude::*;

#[proc_macro_derive(YourDerive, attributes(some, attributes, go, here))]
pub fn derive_your_derive(input: TokenStream) -> TokenStream {
derive_your_derive_inner(input)
.unwrap_or_else(|error| error.into_token_stream())
}

fn derive_your_derive_inner(input: TokenStream) -> Result<TokenStream> {
// Parse the struct or enum you want to implement a derive for
let parse = Parse::new(input)?;
// Get a reference to the generator
let (mut generator, body) = parse.into_generator();
match body {
Body::Struct(body) => {
// Implement your struct body here
// See `Generator` for more information
generator.impl_for("YourTrait")?
.generate_fn("your_fn")
.with_self_arg(FnSelfArg::RefSelf)
.body(|fn_body| {
fn_body.push_parsed("println!(\"Hello world\");");
})?;
},
Body::Enum(body) => {
// Implement your enum body here
// See `Generator` for more information
generator.impl_for("YourTrait")?
.generate_fn("your_fn")
.with_self_arg(FnSelfArg::RefSelf)
.body(|fn_body| {
fn_body.push_parsed("println!(\"Hello world\");");
})?;
},
}
generator.finish()
}
```

Will generate

```ignore
impl YourTrait for <Struct or Enum> {
fn your_fn(&self) { // .generate_fn("your_fn").with_self_arg(FnSelfArg::RefSelf)
println!("Hello world"); // fn_body.push_parsed(...)
}
}
```
2 changes: 1 addition & 1 deletion src/generate/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl Generator {
/// ```no_run
/// # use virtue::prelude::*;
/// # let mut generator: Generator = unsafe { std::mem::zeroed() };
/// generator.impl_for_with_lifetime("Foo", &["a", "b"]);
/// generator.impl_for_with_lifetimes("Foo", &["a", "b"]);
///
/// // will output:
/// // impl<'a, 'b> Foo<'a, 'b> for StructOrEnum { }
Expand Down
2 changes: 2 additions & 0 deletions src/parse/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ fn test_attributes_try_take() {
/// - [`IdentOrIndex::has_field_attribute`]
///
/// This can be implemented on your own type to make parsing easier.
///
/// [`IdentOrIndex::has_field_attribute`]: enum.IdentOrIndex.html#has_field_attribute
pub trait FromAttribute: Sized {
/// Try to parse the given group into your own type. Return `None` if the parsing failed or if the attribute was not this type.
fn parse(group: &Group) -> Option<Self>;
Expand Down
21 changes: 21 additions & 0 deletions src/parse/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,27 +469,45 @@ impl UnnamedField {
}
}

/// Reference to an enum variant's field. Either by index or by ident.
///
/// ```
/// enum Foo {
/// Bar(u32), // will be IdentOrIndex::Index { index: 0, .. }
/// Baz {
/// a: u32, // will be IdentOrIndex::Ident { ident: "a", .. }
/// },
/// }
#[derive(Debug)]
pub enum IdentOrIndex<'a> {
/// The variant is a named field
Ident {
/// The name of the field
ident: &'a Ident,
/// The attributes of the field
attributes: &'a Vec<Attribute>,
},
/// The variant is an unnamed field
Index {
/// The field index
index: usize,
/// The span of the field type
span: Span,
/// The attributes of this field
attributes: &'a Vec<Attribute>,
},
}

impl<'a> IdentOrIndex<'a> {
/// Get the ident. Will panic if this is an `IdentOrIndex::Index`
pub fn unwrap_ident(&self) -> &'a Ident {
match self {
Self::Ident { ident, .. } => ident,
x => panic!("Expected ident, found {:?}", x),
}
}

/// Convert this ident into a TokenTree. If this is an `Index`, will return `prefix + index` instead.
pub fn to_token_tree_with_prefix(&self, prefix: &str) -> TokenTree {
TokenTree::Ident(match self {
IdentOrIndex::Ident { ident, .. } => (*ident).clone(),
Expand All @@ -499,6 +517,8 @@ impl<'a> IdentOrIndex<'a> {
}
})
}

/// Return either the index or the ident of this field with a fixed prefix. The prefix will always be added.
pub fn to_string_with_prefix(&self, prefix: &str) -> String {
match self {
IdentOrIndex::Ident { ident, .. } => ident.to_string(),
Expand All @@ -508,6 +528,7 @@ impl<'a> IdentOrIndex<'a> {
}
}

/// Check to see if the field has the given attribute. See [`FromAttribute`] for more information.
pub fn has_field_attribute<T: FromAttribute + PartialEq<T>>(&self, attrib: T) -> bool {
let attributes = match self {
Self::Ident { attributes, .. } => attributes,
Expand Down
2 changes: 1 addition & 1 deletion src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mod utils;
mod visibility;

pub use self::attributes::{Attribute, AttributeLocation, FromAttribute};
pub use self::body::{EnumBody, EnumVariant, Fields, StructBody, UnnamedField};
pub use self::body::{EnumBody, EnumVariant, Fields, IdentOrIndex, StructBody, UnnamedField};
pub(crate) use self::data_type::DataType;
pub use self::generics::{Generic, GenericConstraints, Generics, Lifetime, SimpleGeneric};
pub use self::visibility::Visibility;
Expand Down

0 comments on commit 99d64c4

Please sign in to comment.