Skip to content

Commit

Permalink
FSDS derive update: docs and fixed impl From<#struct> for Value
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzodellagiustina committed Sep 18, 2024
1 parent 2150665 commit e2e28dd
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 18 deletions.
2 changes: 1 addition & 1 deletion fsds-rs-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description = "Derive macro for fsds-rs."
keywords = ["simulator", "api", "api-client", "driverless", "formula-student"]
repository = "https://github.com/lorenzodellagiustina/fsds-rs"
readme = "README.md"
version = "0.1.0"
version = "0.1.1"
edition = "2021"

[lib]
Expand Down
76 changes: 59 additions & 17 deletions fsds-rs-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,81 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields, FieldsNamed};

/// Implements From for Value for an iterable struct.
/// Implements `TryFrom<Value>` for a #struct and `From<#struct>` for `Value`.
///
/// Note that [`rmpv::Value`] must be in scope for the derive to work.
///
/// ## From<#struct> for Value implementation
///
/// The implementation of `From<#struct>` for `Value` will create a `Value::Map`
/// with the field names as keys and the field values as values.
///
/// Every field of the struct must implement `Into<Value>`.
///
/// ## TryFrom<Value> for #struct implementation
///
/// The implementation of `TryFrom<Value>` for `#struct` will try to convert a
/// `Value::Map` to a struct.
///
/// Every field of the struct must implement `TryFrom<Value>`. The struct must
/// have the same fields as the `Value::Map` keys.
#[proc_macro_derive(FromIntoValue)]
pub fn from_and_into_for_value_derive(input: TokenStream) -> TokenStream {
// Parsing TokenStream into DeriveInput.
let input = parse_macro_input!(input as DeriveInput);

// Extracting the struct name.
let name = input.ident;

// Extracting the fields of the struct.
let fields = if let Data::Struct(DataStruct {
fields: Fields::Named(FieldsNamed { named, .. }),
..
}) = &input.data
{
named
} else {
// Works only for structs with named fields.
unimplemented!();
};

// ----------------------- //
// FROM<#struct> FOR VALUE //
// ----------------------- //

// Converting the struct fields into `Value`s.
let field_from_impl = fields.iter().map(|field| {
let field_name = &field.ident;
quote! {
vec.push((stringify!(#field_name).into(), value.#field_name.into()));
}
});

// From<#struct> for Value implementation.
let from_impl = quote! {
impl From<#name> for Value {
fn from(value: #name) -> Self {
let vec = value
.iter()
.map(|(k, v)| {
(k.into(), any_to_value(v))
})
.collect();
let mut vec = Vec::new();

#(#field_from_impl)*

Value::Map(vec)
}
}
};

let fields = if let Data::Struct(DataStruct { fields: Fields::Named(FieldsNamed { named, .. }), .. }) = &input.data {
named
} else {
// Works only for structs with named fields.
unimplemented!();
};
// -------------------------- //
// TRYFROM<VALUE> FOR #struct //
// -------------------------- //

let try_from_impl = {

// Converting the `Value::Map` fields into the struct fields.
let fields_def = fields.iter().map(|field| {
let field_name = &field.ident;
quote! {
let pos = map.iter().position(|(k, _)| k
.as_str()
.unwrap_or("") // TODO: throw instead this error for better
// debugging: Value::Map should be Vec<(String, _)> to be converted to a struct, but {} was found as a key
.unwrap_or("Value::Map should contain only String keys to be converted to a struct.")
== stringify!(#field_name)
).ok_or(anyhow::anyhow!("Field {} not found in Value::Map.", stringify!(#field_name)))?;
let #field_name = map
Expand All @@ -51,13 +89,15 @@ pub fn from_and_into_for_value_derive(input: TokenStream) -> TokenStream {
}
});

// Populating the struct fields.
let fields = fields.iter().map(|field| {
let field_name = &field.ident;
quote! {
#field_name
}
});

// TryFrom<Value> for #struct implementation.
quote! {
impl TryFrom<Value> for #name {
type Error = anyhow::Error;
Expand All @@ -81,10 +121,12 @@ pub fn from_and_into_for_value_derive(input: TokenStream) -> TokenStream {
}
};

// Expanding the macro.
let expanded = quote! {
#from_impl
#try_from_impl
};

// Returning the generated impl.
TokenStream::from(expanded)
}
}

0 comments on commit e2e28dd

Please sign in to comment.