Add derive macro for structs

Signed-off-by: trivernis <trivernis@protonmail.com>
main
trivernis 2 years ago
commit fa4e847e72
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

2
.gitignore vendored

@ -0,0 +1,2 @@
/target
/Cargo.lock

@ -0,0 +1,20 @@
[workspace]
members = [".", "derive"]
[package]
name = "rusty-value"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies.rusty-value-derive]
path = "./derive"
optional = true
[features]
default = []
derive = ["rusty-value-derive"]
[dev-dependencies.rusty-value-derive]
path = "./derive"

@ -0,0 +1,14 @@
[package]
name = "rusty-value-derive"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
proc-macro2 = "1.0.46"
quote = "1.0.21"
syn = "1.0.101"

@ -0,0 +1,84 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DataStruct, DeriveInput, FieldsNamed, FieldsUnnamed};
#[proc_macro_derive(RustyValue)]
pub fn derive_value(input: TokenStream) -> TokenStream {
derive(parse_macro_input!(input as DeriveInput))
}
fn derive(input: DeriveInput) -> TokenStream {
match &input.data {
syn::Data::Struct(s) => derive_struct(&input, s),
syn::Data::Enum(_) => todo!(),
syn::Data::Union(_) => panic!("unions are currently unsupported"),
}
}
fn derive_struct(input: &DeriveInput, struct_data: &DataStruct) -> TokenStream {
let ident = &input.ident;
let name = ident.to_string();
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
match &struct_data.fields {
syn::Fields::Named(FieldsNamed { named, .. }) => {
let field_idents = named.iter().map(|f| f.ident.as_ref()).collect::<Vec<_>>();
let field_names = named
.iter()
.map(|f| f.ident.as_ref().unwrap().to_string())
.collect::<Vec<_>>();
let field_count = named.len();
TokenStream::from(quote! {
impl #impl_generics rusty_value::RustyValue for #ident #ty_generics #where_clause {
fn into_rusty_value(self) -> rusty_value::Value {
use rusty_value::*;
let mut values = std::collections::HashMap::with_capacity(#field_count);
#(
values.insert(#field_names.to_string(), self.#field_idents.into_rusty_value());
)*
Value::Struct(Struct{
name: #name.to_string(),
fields: StructFields::Named(values),
})
}
}
})
}
syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
let field_indices = unnamed
.iter()
.enumerate()
.map(|(i, _)| syn::Index::from(i))
.collect::<Vec<_>>();
let field_count = unnamed.len();
TokenStream::from(quote! {
impl #impl_generics rusty_value::RustyValue for #ident #ty_generics #where_clause {
fn into_rusty_value(self) -> rusty_value::Value {
use rusty_value::*;
let mut values = Vec::with_capacity(#field_count);
#(
values.push(self.#field_indices.into_rusty_value());
)*
Value::Struct(Struct{
name: #name.to_string(),
fields: StructFields::Unnamed(values),
})
}
}
})
}
syn::Fields::Unit => TokenStream::from(quote! {
impl #impl_generics rusty_value::RustyValue for #ident #ty_generics #where_clause {
fn into_rusty_value(self) -> rusty_value::Value {
Value::Unit(#name.to_string())
}
}
}),
}
}

@ -0,0 +1,9 @@
pub(crate) mod value;
pub(crate) mod value_trait;
pub use value::*;
pub use value_trait::*;
#[doc(inline)]
#[cfg(feature = "derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
pub use rusty_value_derive::*;

@ -0,0 +1,71 @@
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Primitive(Primitive),
Struct(Struct),
Enum(Enum),
Map(HashMap<HashablePrimitive, Value>),
List(Vec<Value>),
Unit(String),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Enum {
pub name: String,
pub variant: String,
pub value: Box<Value>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Struct {
pub name: String,
pub fields: StructFields,
}
#[derive(Clone, Debug, PartialEq)]
pub enum StructFields {
Named(HashMap<String, Value>),
Unnamed(Vec<Value>),
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum Primitive {
Integer(Integer),
Float(Float),
String(String),
Char(char),
Bool(bool),
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum Integer {
U8(u8),
I8(i8),
U16(u16),
I16(i16),
U32(u32),
I32(i32),
U64(u64),
I64(i64),
U128(u128),
I128(i128),
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum Float {
F32(f32),
F64(f64),
}
pub enum HashableValue {
Primitive(HashablePrimitive),
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum HashablePrimitive {
Integer(Integer),
String(String),
Char(char),
Bool(bool),
}

@ -0,0 +1,101 @@
use std::collections::HashMap;
use crate::{HashablePrimitive, Primitive, Value};
pub trait RustyValue {
fn into_rusty_value(self) -> Value;
}
impl RustyValue for u8 {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Integer(crate::Integer::U8(self)))
}
}
impl RustyValue for i8 {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Integer(crate::Integer::I8(self)))
}
}
impl RustyValue for u16 {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Integer(crate::Integer::U16(self)))
}
}
impl RustyValue for i16 {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Integer(crate::Integer::I16(self)))
}
}
impl RustyValue for u32 {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Integer(crate::Integer::U32(self)))
}
}
impl RustyValue for i32 {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Integer(crate::Integer::I32(self)))
}
}
impl RustyValue for u64 {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Integer(crate::Integer::U64(self)))
}
}
impl RustyValue for i64 {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Integer(crate::Integer::I64(self)))
}
}
impl RustyValue for u128 {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Integer(crate::Integer::U128(self)))
}
}
impl RustyValue for i128 {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Integer(crate::Integer::I128(self)))
}
}
impl RustyValue for String {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::String(self))
}
}
impl RustyValue for bool {
fn into_rusty_value(self) -> Value {
Value::Primitive(Primitive::Bool(self))
}
}
impl<R: RustyValue> RustyValue for Vec<R> {
fn into_rusty_value(self) -> Value {
let value_vec = self
.into_iter()
.map(|v| v.into_rusty_value())
.collect::<Vec<_>>();
Value::List(value_vec)
}
}
impl<R: RustyValue> RustyValue for HashMap<String, R> {
fn into_rusty_value(self) -> Value {
let map = self
.into_iter()
.map(|(k, v)| (HashablePrimitive::String(k), v.into_rusty_value()))
.collect::<HashMap<_, _>>();
Value::Map(map)
}
}

@ -0,0 +1,68 @@
use rusty_value::{RustyValue, StructFields, Value};
use rusty_value_derive::*;
#[derive(RustyValue)]
struct TestStructNamed {
foo: String,
bar: u64,
}
#[test]
fn it_handles_named_fields() {
let test_struct = TestStructNamed {
foo: String::from("Hello World"),
bar: 12,
};
let value = test_struct.into_rusty_value();
dbg!(&value);
if let Value::Struct(s) = value {
assert_eq!(&s.name, "TestStructNamed");
if let StructFields::Named(fields) = s.fields {
assert_eq!(fields.len(), 2);
} else {
panic!("Struct wasn't serialized as named struct")
}
} else {
panic!("Struct wasn't serialized as struct");
}
}
#[derive(RustyValue)]
struct TestStructUnnamed(String, u64);
#[test]
fn it_handles_unnamed_fields() {
let test_struct = TestStructUnnamed(String::from("Hello World"), 12);
let value = test_struct.into_rusty_value();
dbg!(&value);
if let Value::Struct(s) = value {
assert_eq!(&s.name, "TestStructUnnamed");
if let StructFields::Unnamed(fields) = s.fields {
assert_eq!(fields.len(), 2);
} else {
panic!("Struct wasn't serialized as unnamed struct")
}
} else {
panic!("Struct wasn't serialized as struct");
}
}
#[derive(RustyValue)]
struct TestStructUnit;
#[test]
fn it_handles_unit_structs() {
let test_struct = TestStructUnit;
let value = test_struct.into_rusty_value();
dbg!(&value);
if let Value::Unit(s) = value {
assert_eq!(&s, "TestStructUnit");
} else {
panic!("Struct wasn't serialized as struct");
}
}
Loading…
Cancel
Save