Skip to content

Commit

Permalink
Add Structable::get for accessing fields-by-name.
Browse files Browse the repository at this point in the history
  • Loading branch information
jswrenn committed Aug 30, 2022
1 parent 6c45408 commit 255ae39
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 4 deletions.
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));
}
}
unimplemented!()
}
}
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

0 comments on commit 255ae39

Please sign in to comment.