Skip to content

Commit

Permalink
add SerJson and DeJson derive macro for serialization and deserializa…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
Shourya742 committed Dec 28, 2024
1 parent ac4b728 commit 3f957e6
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 12 deletions.
36 changes: 26 additions & 10 deletions utils/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ members = [
"error-handling",
"key-utils",
"bip32-key-derivation",
]
"sv2_serde_json"
, "sv2_serde_json_macros"]

exclude = [
"message-generator",
Expand Down
2 changes: 1 addition & 1 deletion utils/sv2_serde_json/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]

sv2_serde_json_macros = {path = "../sv2_serde_json_macros"}
[[example]]
path = "examples/json.rs"
name = "json"
37 changes: 37 additions & 0 deletions utils/sv2_serde_json/examples/derive_macro_example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use sv2_serde_json_macros::{SerJson, DeJson};
use sv2_serde_json::value::{Value, Number};

#[derive(SerJson, DeJson, Debug)]
struct Person {
name: String,
age: i64,
is_student: bool,
}

#[derive(SerJson, DeJson, Debug)]
enum Status {
Active,
Inactive,
Pending,
}

fn main() {
let person = Person {
name: "Alice".to_string(),
age: 25,
is_student: true,
};

let person_json = person.to_json_value();
println!("Serialized: {:?}", person_json.to_json_string());

let deserialized_person = Person::from_json_value(person_json).unwrap();
println!("Deserialized: {:?}", deserialized_person);

let status = Status::Active;
let status_json = status.to_json_value();
println!("Serialized Enum: {:?}", status_json);

let deserialized_status = Status::from_json_value(status_json).unwrap();
println!("Deserialized Enum: {:?}", deserialized_status);
}
35 changes: 35 additions & 0 deletions utils/sv2_serde_json/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,38 @@ impl<'a> TryFrom<&'a Value> for &'a HashMap<String, Value> {
}
}

impl From<String> for Value {
fn from(value: String) -> Self {
Value::String(value)
}
}

impl From<i64> for Value {
fn from(value: i64) -> Self {
Value::Number(Number::I64(value))
}
}

impl From<f64> for Value {
fn from(value: f64) -> Self {
Value::Number(Number::F64(value))
}
}

impl From<bool> for Value {
fn from(value: bool) -> Self {
Value::Boolean(value)
}
}

impl From<Vec<Value>> for Value {
fn from(value: Vec<Value>) -> Self {
Value::Array(value)
}
}

impl From<HashMap<String, Value>> for Value {
fn from(value: HashMap<String, Value>) -> Self {
Value::Object(value)
}
}
14 changes: 14 additions & 0 deletions utils/sv2_serde_json_macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "sv2_serde_json_macros"
version = "0.1.0"
edition = "2021"

[dependencies]
proc-macro2 = "1.0.92"
quote = "1.0.38"
syn = "2.0.92"



[lib]
proc-macro = true
141 changes: 141 additions & 0 deletions utils/sv2_serde_json_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput};

#[proc_macro_derive(SerJson)]
pub fn ser_json_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;

let gen = match &input.data {
Data::Struct(data) => {
let fields = data.fields.iter().map(|field| {
let field_name = field
.ident
.as_ref()
.expect("Expected named fields in the struct");
let field_ty = &field.ty;

// Handle standard types explicitly
let value_conversion = match field_ty {
syn::Type::Path(path) => {
let type_ident = &path.path.segments.last().unwrap().ident;
match type_ident.to_string().as_str() {
"String" => quote! { Value::String(self.#field_name.clone()) },
"i64"|"u32"|"u16"|"u8"|"i32"|"i16"|"i8" => quote! { Value::Number(Number::I64(self.#field_name)) },
"f64"|"f32" => quote! { Value::Number(Number::F64(self.#field_name)) },
"bool" => quote! { Value::Boolean(self.#field_name) },
_ => quote! { self.#field_name.to_json_value() },
}
}
_ => quote! { self.#field_name.to_json_value() },
};

quote! {
map.insert(
stringify!(#field_name).to_string(),
#value_conversion,
);
}
});

quote! {
impl #name {
pub fn to_json_value(&self) -> Value {
let mut map = std::collections::HashMap::new();
#(#fields)*
Value::Object(map)
}
}
}
}
Data::Enum(data) => {
let variants = data.variants.iter().map(|variant| {
let var_name = &variant.ident;

// Handle enum variants as string representations
quote! {
#name::#var_name => Value::String(stringify!(#var_name).to_string()),
}
});

quote! {
impl #name {
pub fn to_json_value(&self) -> Value {
match self {
#(#variants)*
}
}
}
}
}
_ => unimplemented!("SerJson is only implemented for structs and enums"),
};

TokenStream::from(gen)
}

#[proc_macro_derive(DeJson)]
pub fn de_json_derive(input:TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;

let gen = match &input.data {
Data::Struct(data) => {
let fields = data.fields.iter().map(|field| {
let field_name = field
.ident
.as_ref()
.expect("Expected named fields in the struct");
quote! {
#field_name: obj.get(stringify!(#field_name))
.ok_or(())?
.try_into()
.map_err(|_| ())?,
}
});
quote! {
impl #name {
pub fn from_json_value(value: Value) -> Result<Self, ()> {
if let Value::Object(obj) = value {
Ok(Self {
#(#fields)*
})
} else {
Err(())
}
}
}
}
}
Data::Enum(data) => {
let variants = data.variants.iter().map(|variant| {
let var_name = &variant.ident;
quote! {
stringify!(#var_name) => Ok(#name::#var_name),
}
});

quote! {
impl #name {
pub fn from_json_value(value: Value) -> Result<Self, ()> {
if let Value::String(s) = value {
match s.as_str() {
#(#variants)*
_ => Err(()),
}
} else {
Err(())
}
}
}
}
}
_ => unimplemented!("DeJson is only implemented for struct and enums")

};

TokenStream::from(gen)
}

0 comments on commit 3f957e6

Please sign in to comment.