Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Structable::get for accessing fields-by-name. #102

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions tests/src/hello_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,13 @@ impl Structable for HelloWorld {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static("HelloWorld", Fields::Named(HELLO_WORLD_FIELDS))
}

fn get(&self, field: Field<'_>) -> Option<Value<'_>> {
if let Field::Named(field) = field {
if field.name() == HELLO_WORLD_FIELDS[0].name() {
return Some(Value::U32(self.id));
}
}
None
}
}
24 changes: 24 additions & 0 deletions tests/tests/structable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ fn test_manual_static_impl() {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static("MyStruct", Fields::Named(MY_STRUCT_FIELDS))
}

fn get(&self, field: Field<'_>) -> Option<Value<'_>> {
match field {
Field::Named(field) if field.name() == "num" => Some(Value::U32(self.num)),
Field::Named(field) if field.name() == "list" => Some(Value::Listable(&self.list)),
Field::Named(field) if field.name() == "sub" => Some(Value::Structable(&self.sub)),
_ => None,
}
}
}

impl Valuable for SubStruct {
Expand All @@ -60,6 +69,13 @@ fn test_manual_static_impl() {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static("SubStruct", Fields::Named(SUB_STRUCT_FIELDS))
}

fn get(&self, field: Field<'_>) -> Option<Value<'_>> {
match field {
Field::Named(field) if field.name() == "sub" => Some(Value::String(&self.message)),
_ => None,
}
}
}

let my_struct = MyStruct {
Expand Down Expand Up @@ -118,6 +134,14 @@ fn test_manual_dyn_impl() {
fn definition(&self) -> StructDef<'_> {
StructDef::new_dynamic("MyStruct", Fields::Named(&[]))
}

fn get(&self, field: Field) -> Option<Value<'_>> {
match field {
Field::Named(field) if field.name() == "foo" => Some(Value::U32(1)),
Field::Named(field) if field.name() == "bar" => Some(Value::String("two")),
_ => None,
}
}
}

let my_struct = MyStruct;
Expand Down
29 changes: 29 additions & 0 deletions valuable-derive/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fn derive_struct(input: &syn::DeriveInput, data: &syn::DataStruct) -> TokenStrea
let name_literal = name.to_string();
let visit_fields;
let struct_def;
let get_branches: TokenStream;
let mut named_fields_statics = None;

match &data.fields {
Expand All @@ -33,6 +34,19 @@ fn derive_struct(input: &syn::DeriveInput, data: &syn::DataStruct) -> TokenStrea
)
};

get_branches =
data.fields.iter().map(|field| {
let field_name_ident = field.ident.as_ref();
let field_name_str = field_name_ident.unwrap().to_string();
quote! {
::valuable::Field::Named(field) if field.name() == #field_name_str => Some(::valuable::Valuable::as_value(&self.#field_name_ident)),
}
}).collect();

quote! {
::valuable::Field::Named(field) if field.name() == ""
};

let fields = data.fields.iter().map(|field| {
let f = field.ident.as_ref();
let tokens = quote! {
Expand All @@ -58,6 +72,14 @@ fn derive_struct(input: &syn::DeriveInput, data: &syn::DataStruct) -> TokenStrea
)
};

get_branches =
data.fields.iter().enumerate().map(|(i, _)| {
let i = syn::Index::from(i);
quote! {
::valuable::Field::Unnamed(f) if f == #i => Some(::valuable::Valuable::as_value(&self.#i)),
}
}).collect();

let indices = data.fields.iter().enumerate().map(|(i, field)| {
let index = syn::Index::from(i);
let tokens = quote! {
Expand All @@ -82,6 +104,13 @@ fn derive_struct(input: &syn::DeriveInput, data: &syn::DataStruct) -> TokenStrea
fn definition(&self) -> ::valuable::StructDef<'_> {
#struct_def
}

fn get(&self, field: ::valuable::Field<'_>) -> Option<::valuable::Value<'_>> {
match field {
#get_branches
_ => None,
}
}
}
};

Expand Down
16 changes: 16 additions & 0 deletions valuable-serde/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,14 @@ fn test_dyn_struct() {
fn definition(&self) -> StructDef<'_> {
StructDef::new_dynamic("Named", Fields::Named(&[]))
}

fn get(&self, field: Field<'_>) -> Option<Value<'_>> {
match field {
Field::Unnamed(0) => Some(Value::U32(1)),
Field::Unnamed(1) => Some(Value::I32(-1)),
_ => None,
}
}
}

struct Unnamed;
Expand All @@ -426,6 +434,14 @@ fn test_dyn_struct() {
fn definition(&self) -> StructDef<'_> {
StructDef::new_dynamic("Unnamed", Fields::Unnamed(2))
}

fn get(&self, field: Field<'_>) -> Option<Value<'_>> {
match field {
Field::Unnamed(0) => Some(Value::U32(1)),
Field::Unnamed(1) => Some(Value::I32(-1)),
_ => None,
}
}
}

assert_ser_tokens(
Expand Down
15 changes: 15 additions & 0 deletions valuable/examples/hello_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ impl Structable for HelloWorld {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static("HelloWorld", Fields::Named(HELLO_WORLD_FIELDS))
}

fn get(&self, field: Field<'_>) -> Option<Value<'_>> {
match field {
Field::Named(field) if field.name() == "hello" => Some(Value::String(self.hello)),
Field::Named(field) if field.name() == "world" => Some(Value::Structable(&self.world)),
_ => None,
}
}
}

impl Valuable for HelloWorld {
Expand Down Expand Up @@ -50,6 +58,13 @@ impl Structable for World {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static("World", Fields::Named(WORLD_FIELDS))
}

fn get(&self, field: Field<'_>) -> Option<Value<'_>> {
match field {
Field::Named(field) if field.name() == "answer" => Some(Value::Usize(self.answer)),
_ => None,
}
}
}

fn main() {
Expand Down
12 changes: 12 additions & 0 deletions valuable/examples/indexing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
fn main() {
use valuable::{NamedField, NamedValues, Value};

let fields = [NamedField::new("foo"), NamedField::new("bar")];
let values = [Value::U32(123), Value::U32(456)];

let named_values = NamedValues::new(&fields, &values);

let field = &fields[0];

assert_eq!(named_values.get(field).and_then(Value::as_u32), Some(123));
}
2 changes: 1 addition & 1 deletion valuable/examples/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl Visit for Print {
}

fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) {
for (field, value) in named_values {
for (field, value) in named_values.into_iter() {
print!("{}- {}: ", self.0, field.name());
value.visit(self);
}
Expand Down
10 changes: 10 additions & 0 deletions valuable/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ pub enum Fields<'a> {
Unnamed(usize),
}

/// A field, either named or positional, in a `Structable`.
#[derive(Debug)]
pub enum Field<'a> {
/// Named field.
Named(NamedField<'a>),

/// Unnamed (positional) field.
Unnamed(usize),
}

/// A named field
#[derive(Debug, Clone, Copy)]
pub struct NamedField<'a>(&'a str);
Expand Down
2 changes: 1 addition & 1 deletion valuable/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ mod enumerable;
pub use enumerable::{EnumDef, Enumerable, Variant, VariantDef};

mod field;
pub use field::{Fields, NamedField};
pub use field::{Field, Fields, NamedField};

mod listable;
pub use listable::Listable;
Expand Down
29 changes: 27 additions & 2 deletions valuable/src/structable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ pub trait Structable: Valuable {
///
/// assert_eq!("MyStruct", my_struct.definition().name());
fn definition(&self) -> StructDef<'_>;

/// Returns the value of the given field, if any.
fn get(&self, field: Field<'_>) -> Option<Value<'_>>;
}

/// A struct's name, fields, and other struct-level information.
Expand Down Expand Up @@ -178,7 +181,7 @@ pub enum StructDef<'a> {
/// The struct stores field values in a `HashMap`.
///
/// ```
/// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit};
/// use valuable::{Field, Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit};
/// use std::collections::HashMap;
///
/// /// A dynamic struct
Expand Down Expand Up @@ -210,13 +213,20 @@ pub enum StructDef<'a> {
/// fn definition(&self) -> StructDef<'_> {
/// StructDef::new_dynamic(&self.name, Fields::Named(&[]))
/// }
///
/// fn get(&self, field: Field<'_>) -> Option<Value<'_>> {
/// match field {
/// Field::Named(field) => self.values.get(field.name()).map(|v| v.as_value()),
/// _ => None,
/// }
/// }
/// }
/// ```
///
/// Some fields are known statically.
///
/// ```
/// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit};
/// use valuable::{Field, Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit};
/// use std::collections::HashMap;
///
/// struct HalfStatic {
Expand Down Expand Up @@ -259,6 +269,17 @@ pub enum StructDef<'a> {
/// "HalfStatic",
/// Fields::Named(FIELDS))
/// }
///
/// fn get(&self, field: Field<'_>) -> Option<Value<'_>> {
/// match field {
/// Field::Named(field) if field.name() == "foo" => Some(self.foo.as_value()),
/// Field::Named(field) if field.name() == "bar" => Some(self.bar.as_value()),
/// Field::Named(field) => {
/// self.extra_values.get(field.name()).map(|v| v.as_value())
/// },
/// _ => None,
/// }
/// }
/// }
/// ```
#[non_exhaustive]
Expand Down Expand Up @@ -478,6 +499,10 @@ macro_rules! deref {
fn definition(&self) -> StructDef<'_> {
T::definition(&**self)
}

fn get(&self, field: Field<'_>) -> Option<Value<'_>> {
T::get(&**self, field)
}
}
)*
};
Expand Down