commit
fa4e847e72
@ -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…
Reference in New Issue