Skip to content

Commit

Permalink
Merge pull request #174 from sval-rs/feat/derive-enhancements
Browse files Browse the repository at this point in the history
Derive enhancements
  • Loading branch information
KodrAus committed Oct 3, 2023
2 parents ed1fd18 + e93db07 commit 54e2cd3
Show file tree
Hide file tree
Showing 13 changed files with 313 additions and 180 deletions.
11 changes: 11 additions & 0 deletions derive/test/compile_fail/union.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use sval_derive::*;

#[derive(Value)]
pub union Union {
a: i32,
b: u32,
}

fn main() {

}
7 changes: 7 additions & 0 deletions derive/test/compile_fail/union.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: proc-macro derive panicked
--> compile_fail/union.rs:3:10
|
3 | #[derive(Value)]
| ^^^^^
|
= help: message: unsupported container type
35 changes: 30 additions & 5 deletions derive/test/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,35 @@ mod derive_struct {
})
}

#[test]
fn generic() {
#[derive(Value)]
struct RecordTuple<S> {
a: S,
}

assert_tokens(&RecordTuple { a: 42 }, {
use sval_test::Token::*;

&[
RecordTupleBegin(None, Some(sval::Label::new("RecordTuple")), None, Some(1)),
RecordTupleValueBegin(None, sval::Label::new("a"), sval::Index::new(0)),
I32(42),
RecordTupleValueEnd(None, sval::Label::new("a"), sval::Index::new(0)),
RecordTupleEnd(None, Some(sval::Label::new("RecordTuple")), None),
]
})
}

#[test]
fn indexed() {
const B_INDEX: sval::Index = sval::Index::new(3);

#[derive(Value)]
struct RecordTuple {
#[sval(index = 1)]
a: i32,
#[sval(index = B_INDEX)]
b: i32,
}

Expand All @@ -43,9 +66,9 @@ mod derive_struct {
RecordTupleValueBegin(None, sval::Label::new("a"), sval::Index::new(1)),
I32(42),
RecordTupleValueEnd(None, sval::Label::new("a"), sval::Index::new(1)),
RecordTupleValueBegin(None, sval::Label::new("b"), sval::Index::new(2)),
RecordTupleValueBegin(None, sval::Label::new("b"), sval::Index::new(3)),
I32(57),
RecordTupleValueEnd(None, sval::Label::new("b"), sval::Index::new(2)),
RecordTupleValueEnd(None, sval::Label::new("b"), sval::Index::new(3)),
RecordTupleEnd(None, Some(sval::Label::new("RecordTuple")), None),
]
})
Expand Down Expand Up @@ -120,7 +143,7 @@ mod derive_struct {
fn data_tagged() {
#[derive(Value)]
struct RecordTuple {
#[sval(data_tag = "sval::tags::NUMBER")]
#[sval(data_tag = sval::tags::NUMBER)]
a: i32,
}

Expand Down Expand Up @@ -216,7 +239,7 @@ mod derive_struct {
const FIELD: sval::Tag = sval::Tag::new("field");

#[derive(Value)]
#[sval(tag = "CONTAINER", label = "record", index = 0)]
#[sval(tag = CONTAINER, label = "record", index = 0)]
struct Record {
#[sval(tag = "FIELD", label = "field0")]
a: i32,
Expand Down Expand Up @@ -309,8 +332,10 @@ mod derive_tuple {

#[test]
fn labeled() {
const B_LABEL: sval::Label<'static> = sval::Label::new("B");

#[derive(Value)]
struct RecordTuple(#[sval(label = "A")] i32, #[sval(label = "B")] i32);
struct RecordTuple(#[sval(label = "A")] i32, #[sval(label = B_LABEL)] i32);

assert_tokens(&RecordTuple(42, 43), {
use sval_test::Token::*;
Expand Down
69 changes: 51 additions & 18 deletions derive_macros/src/attr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::collections::HashSet;

use syn::{spanned::Spanned, Attribute, Expr, ExprUnary, Lit, LitBool, Path, UnOp};
use syn::{Attribute, Expr, ExprUnary, Lit, Path, UnOp};

use crate::{index::IndexValue, label::LabelValue};

/**
The `tag` attribute.
Expand All @@ -13,6 +15,14 @@ pub(crate) struct TagAttr;
impl SvalAttribute for TagAttr {
type Result = syn::Path;

fn try_from_expr(&self, expr: &Expr) -> Option<Self::Result> {
match expr {
Expr::Lit(lit) => Some(self.from_lit(&lit.lit)),
Expr::Path(path) => Some(path.path.clone()),
_ => None,
}
}

fn from_lit(&self, lit: &Lit) -> Self::Result {
if let Lit::Str(ref s) = lit {
s.parse().expect("invalid value")
Expand All @@ -39,6 +49,14 @@ pub(crate) struct DataTagAttr;
impl SvalAttribute for DataTagAttr {
type Result = syn::Path;

fn try_from_expr(&self, expr: &Expr) -> Option<Self::Result> {
match expr {
Expr::Lit(lit) => Some(self.from_lit(&lit.lit)),
Expr::Path(path) => Some(path.path.clone()),
_ => None,
}
}

fn from_lit(&self, lit: &Lit) -> Self::Result {
if let Lit::Str(ref s) = lit {
s.parse().expect("invalid value")
Expand All @@ -63,11 +81,19 @@ to use for the annotated item.
pub(crate) struct LabelAttr;

impl SvalAttribute for LabelAttr {
type Result = String;
type Result = LabelValue;

fn try_from_expr(&self, expr: &Expr) -> Option<Self::Result> {
match expr {
Expr::Lit(lit) => Some(self.from_lit(&lit.lit)),
Expr::Path(path) => Some(LabelValue::Ident(quote!(#path))),
_ => None,
}
}

fn from_lit(&self, lit: &Lit) -> Self::Result {
if let Lit::Str(ref s) = lit {
s.value()
LabelValue::Const(s.value())
} else {
panic!("unexpected value")
}
Expand All @@ -88,10 +114,20 @@ to use for the annotated item.
*/
pub(crate) struct IndexAttr;

impl IndexAttr {
fn const_from_lit(&self, lit: &Lit) -> isize {
if let Lit::Int(ref n) = lit {
n.base10_parse().expect("invalid value")
} else {
panic!("unexpected value")
}
}
}

impl SvalAttribute for IndexAttr {
type Result = isize;
type Result = IndexValue;

fn from_expr(&self, expr: &Expr) -> Option<Self::Result> {
fn try_from_expr(&self, expr: &Expr) -> Option<Self::Result> {
match expr {
// Take `-` into account
Expr::Unary(ExprUnary {
Expand All @@ -100,22 +136,19 @@ impl SvalAttribute for IndexAttr {
..
}) => {
if let Expr::Lit(ref lit) = **expr {
Some(-(self.from_lit(&lit.lit)))
Some(IndexValue::Const(-(self.const_from_lit(&lit.lit))))
} else {
None
}
}
Expr::Lit(lit) => Some(self.from_lit(&lit.lit)),
Expr::Lit(lit) => Some(IndexValue::Const(self.const_from_lit(&lit.lit))),
Expr::Path(path) => Some(IndexValue::Ident(quote!(#path))),
_ => None,
}
}

fn from_lit(&self, lit: &Lit) -> Self::Result {
if let Lit::Int(ref n) = lit {
n.base10_parse().expect("invalid value")
} else {
panic!("unexpected value")
}
IndexValue::Const(self.const_from_lit(lit))
}
}

Expand Down Expand Up @@ -292,7 +325,7 @@ pub(crate) trait RawAttribute {
pub(crate) trait SvalAttribute: RawAttribute {
type Result: 'static;

fn from_expr(&self, expr: &Expr) -> Option<Self::Result> {
fn try_from_expr(&self, expr: &Expr) -> Option<Self::Result> {
if let Expr::Lit(lit) = expr {
Some(self.from_lit(&lit.lit))
} else {
Expand Down Expand Up @@ -363,7 +396,7 @@ pub(crate) fn get_unchecked<T: SvalAttribute>(
.flatten()
{
if value_key.is_ident(request_key) {
return Some(request.from_lit(&value));
return Some(request.try_from_expr(&value).expect("unexpected value"));
}
}

Expand All @@ -373,23 +406,23 @@ pub(crate) fn get_unchecked<T: SvalAttribute>(
fn sval_attr<'a>(
ctxt: &'a str,
attr: &'_ Attribute,
) -> Option<impl IntoIterator<Item = (Path, Lit)> + 'a> {
) -> Option<impl IntoIterator<Item = (Path, Expr)> + 'a> {
if !attr.path().is_ident("sval") {
return None;
}

let mut results = Vec::new();
attr.parse_nested_meta(|meta| {
let lit: Lit = match meta.value() {
let expr: Expr = match meta.value() {
Ok(value) => value.parse()?,
// If there isn't a value associated with the item
// then use the boolean `true`
Err(_) => Lit::Bool(LitBool::new(true, meta.path.span())),
Err(_) => syn::parse_quote!(true),
};

let path = meta.path;

results.push((path, lit));
results.push((path, expr));

Ok(())
})
Expand Down
39 changes: 38 additions & 1 deletion derive_macros/src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,43 @@ pub(crate) fn derive(input: DeriveInput) -> proc_macro2::TokenStream {

derive_enum(&input.ident, &input.generics, variants.iter(), &attrs)
}
_ => panic!("unimplemented"),
_ => panic!("unsupported container type"),
}
}

fn impl_tokens(
impl_generics: syn::ImplGenerics,
ident: &syn::Ident,
ty_generics: syn::TypeGenerics,
bounded_where_clause: &syn::WhereClause,
stream_body: proc_macro2::TokenStream,
tag_body: Option<proc_macro2::TokenStream>,
) -> proc_macro2::TokenStream {
let stream_fn = quote!(
fn stream<'sval, __SvalStream: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut __SvalStream) -> sval::Result {
#stream_body
}
);

let tag_fn = if let Some(tag_body) = tag_body {
quote!(
fn tag(&self) -> Option<sval::Tag> {
#tag_body
}
)
} else {
quote!()
};

quote! {
const _: () = {
extern crate sval;

impl #impl_generics sval::Value for #ident #ty_generics #bounded_where_clause {
#stream_fn

#tag_fn
}
};
}
}

0 comments on commit 54e2cd3

Please sign in to comment.