name-index 0.2.1

Library for accessing struct fields by name at runtime
Documentation
//! **Dynamic struct field indexing library**
//!
//! name-index implements functionality to allow users to dynamically access
//! the fields of realitively-homogenous structs by name at runtime.
//!
//! This functionality may prove useful for problems where fixed key-value
//! pairs are mapped as fields of a struct (for example to aid performance and
//! allow compile-time type checking) but which need to be accessed sparingly
//! at runtime by their respective name.
//!
//! The lookup functionality is not optimized for performance. In cases where
//! this lookup is frequently used, it is likely better to use alternatives
//! like [std::collections::HashMap].
//!
//! ## Example
//! ```rust
//! use name_index::NameIndex;
//!
//! #[derive(Default, NameIndex)]
//! struct MyStruct {
//!     // The #[index] attributes tells the derive macro to
//!     // start indexing all u32 fields from here on
//!     #[index]
//!     first: u32,
//!     second: u32,
//!     third: u32,
//! }
//!
//! fn main() {
//!     let mut my_struct = MyStruct::default();
//!
//!     // The name of the field we want to index
//!     // Might come from user input for example
//!     let name = "second";
//!
//!     // Use NameIndex::get_ref_mut to get a mutable
//!     // reference to our field
//!     let field_ref: &mut u32 =
//!         NameIndex::get_ref_mut(&mut my_struct, name)
//!         .expect("second is a field of MyStruct");
//!
//!     *field_ref = 5;
//!
//!     assert_eq!(my_struct.second, 5);
//! }
//! ```
//!
//! For the previous example the [NameIndex] derive creates the following
//! (simplified[^simpl]) code:
//!
//! ```rust
//! # use name_index::{NameIndex, Field, FieldMut};
//! # struct MyStruct {
//! #     first: u32,
//! #     second: u32,
//! #     third: u32,
//! # }
//! impl NameIndex<u32> for MyStruct {
//!     fn get_ref_mut(&mut self, name: &str) -> Option<&mut u32> {
//!         match name {
//!             "first" => Some(&mut self.first),
//!             "second" => Some(&mut self.second),
//!             "third" => Some(&mut self.third),
//!             _ => None,
//!         }
//!     }
//!     // -- snip --
//!     # fn get_ref(&self, name: &str) -> Option<&u32> {unimplemented!()}
//!     # fn fields(&self) -> Vec<Field<u32>> {unimplemented!()}
//!     # fn fields_mut(&mut self) -> Vec<FieldMut<u32>> {unimplemented!()}
//!     # fn field_aliases(&self, name: &str) -> Vec<&'static str>
//!     #     {unimplemented!()}
//!     # fn resolve_alias(&self, name: &str) -> Option<&'static str>
//!     #     {unimplemented!()}
//! }
//! ```
//!
//! ## Second Example
//! This example is supposed to illustrate how [NameIndexCopy] can be used
//! to save some redundant typing when the generic type of [NameIndex]
//! implements [std::marker::Copy].
//!
//! ```rust
//! use name_index::{NameIndex, NameIndexCopy};
//!
//! #[derive(NameIndex)]
//! struct MyStruct {
//!     // When no `#[index]` attribute is given, the first field is
//!     // automatically chosen as the reference.
//!     first: u32,
//!     second: u32,
//!     third: u32,
//! }
//!
//! fn main() {
//!     let mut my_struct = MyStruct {
//!         first: 1,
//!         second: 2,
//!         third: 3,
//!     };
//!
//!     // We can use NameIndexCopy<u32> here because NameIndexCopy<T>
//!     // is automatically implemented for NameIndex<T> when T: Copy
//!
//!     // Get the value of my_struct.third
//!     let val: u32 = NameIndexCopy::get(&my_struct, "third")
//!         .expect("'third' is a field of MyStruct");
//!
//!     assert_eq!(val, my_struct.third);
//!
//!     // Set the value of my_struct.first to 7
//!     NameIndexCopy::set(&mut my_struct, "first", 7)
//!         .expect("'first' is a field of MyStruct");
//!
//!     assert_eq!(my_struct.first, 7);
//! }
//! ```
//!
//! [^simpl]: Only the [NameIndex::get_ref_mut] function is implemented here
//! for clarity.

/// Macro to automatically derive NameIndex
///
/// This derive can only be used for regular structs (i.e. not on tuple
/// or unit structs).
///
/// The `#[index]` attribute can only be placed on one of the fields.
/// If it isn't present, the first field is chosen as if it had the attribute.
/// All fields with the same type, starting at that field, will be indexed
/// by the macro and can then be found through [NameIndex].
///
/// The `#[alias(...)]` attribute can be used to define aliases for the field.
/// Aliases behave exactly like the actual name of the field when using
/// [NameIndex::get_ref] and [NameIndex::get_ref_mut].
///
/// ## Example
/// ```rust
/// use name_index::NameIndex;
///
/// #[derive(Default, NameIndex)]
/// struct SomeStruct {
///     one: u16, // not indexed
///     random: String, // not indexed
///     #[index]
///     two: u16, // FOUND
///     three: u16, // FOUND
///     other: Vec<u8>, // not indexed
///     #[alias(f)]
///     four: u16, // FOUND
/// }
///
/// // SomeStruct now implements NameIndex<u16>
///
/// let s: SomeStruct = SomeStruct::default();
/// assert_eq!(s.get_ref("one"), None);
/// assert_eq!(s.get_ref("other"), None);
///
/// let two_ref: &u16 = &s.two;
/// let found_ref: Option<&u16> = s.get_ref("two");
/// assert!(found_ref.is_some());
/// assert!(std::ptr::eq(found_ref.unwrap(), two_ref));
///
/// // The "four" field of SomeStruct has the alias "f" and get_ref treats them
/// // the exact same way.
/// let four_ref: &u16 = s.get_ref("four").unwrap();
/// let f_ref: &u16 = s.get_ref("f").unwrap();
/// assert!(std::ptr::eq(four_ref, f_ref));
/// ```
pub use name_index_derive::NameIndex;

/// Named reference to a struct field
///
/// The first entry of the tuple represents the name of the field and the
/// second holds a reference to the field.
///
/// Returned by [NameIndex::fields].
pub type Field<'a, T> = (&'static str, &'a T);

/// Named mutable reference to a struct field
///
/// The first entry of the tuple represents the name of the field and the
/// second holds a mutable reference to the field.
///
/// Returned by [NameIndex::fields_mut].
pub type FieldMut<'a, T> = (&'static str, &'a mut T);

/// Struct field access trait
///
/// Designed to be derived through [NameIndex][name_index_derive::NameIndex].
pub trait NameIndex<T> {
    /// Get a reference to a field by name
    ///
    /// When the field does not exist, the return value will be None.
    /// Otherwise it will be a reference to the field of the struct.
    ///
    /// The name may be an alias of the field instead of the true name.
    /// See `#[alias(...)]` in the [derive macro][name_index_derive::NameIndex].
    fn get_ref(&self, name: &str) -> Option<&T>;

    /// Get a mutable reference to a field by name
    ///
    /// When the field does not exist, the return value will be None.
    /// Otherwise it will be a mutable reference to the field of the struct.
    ///
    /// The name may be an alias of the field instead of the true name.
    /// See `#[alias(...)]` in the [derive macro][name_index_derive::NameIndex].
    fn get_ref_mut(&mut self, name: &str) -> Option<&mut T>;

    /// Get named references all indexed fields
    ///
    /// The returned vector contains the names and references for all of the
    /// indexed fields in tuple pairs. See [Field].
    fn fields(&self) -> Vec<Field<T>>;

    /// Get named mutable references all indexed fields
    ///
    /// The returned vector contains the names and mutable references for all
    /// of the indexed fields in tuple pairs. See [FieldMut].
    fn fields_mut(&mut self) -> Vec<FieldMut<T>>;

    /// Get all aliases of a field
    ///
    /// If the field doesn't exist, an empty vector will be returned.
    ///
    /// The name parameter has to be the true name of the field.
    /// See [resolve_alias][NameIndex::resolve_alias] for finding the true name
    /// from an alias.
    fn field_aliases(&self, name: &str) -> Vec<&'static str>;

    /// Get true name of a field
    ///
    /// The name parameter can be anything that can be passed to
    /// [get_ref][NameIndex::get_ref]. Aliases will be resolved to the actual
    /// name of the field and when given the true name, it is just resolved to
    /// itself.
    ///
    /// If the name doesn't match any field names or aliases of the struct,
    /// a None value will be returned.
    fn resolve_alias(&self, name: &str) -> Option<&'static str>;
}

/// Struct primitive field access trait
pub trait NameIndexCopy<T: Copy>: NameIndex<T> {
    /// Get the value of a field
    ///
    /// Will be None when the field does not exist, otherwise it will be the
    /// current value.
    fn get(&self, name: &str) -> Option<T>;

    /// Set the value of a field
    ///
    /// The returned Result will be an Err when the field does not exist.
    fn set(&mut self, name: &str, value: T) -> Result<(), ()>;
}

impl<T, U: NameIndex<T>> NameIndexCopy<T> for U
where
    T: Copy,
{
    fn get(&self, name: &str) -> Option<T> {
        self.get_ref(name).map(|r| *r)
    }

    fn set(&mut self, name: &str, value: T) -> Result<(), ()> {
        self.get_ref_mut(name).ok_or(()).map(|r| *r = value)
    }
}