Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created May 17, 2019 23:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rust-play/b528cef8e9e01b38d9c36e4ab04a122a to your computer and use it in GitHub Desktop.
Save rust-play/b528cef8e9e01b38d9c36e4ab04a122a to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
/// Demonstration of a way to do property lookup of standardized collections
/// of properties. This approach could be extended to do things like fetch
/// a list of the supported keys.
/// Utility trait to aggregate lookups
pub trait TraitGlue<T>
where
T: ?Sized,
{
fn get_sub_property(&self, key: &str) -> Result<String, ()>;
}
/// Module for defining "Variant A" properties.
/// In some applications of this technique, this module could be created
/// using macros.
mod variant_a {
/// The trait to implement if you want to support the properties
/// in this variant.
pub trait Trait {
fn get_foo(&self) -> String;
fn get_bar(&self) -> String;
}
/// This is effectively the "path" of the properties in this variant.
pub const VARIANT_ID: &str = "A";
// Each variant gets their own implementation of `TraitGlue`.
impl<T: Trait> super::TraitGlue<Trait> for T {
fn get_sub_property(&self, key: &str) -> Result<String, ()> {
match key {
"foo" => Ok(self.get_foo()),
"bar" => Ok(self.get_bar()),
_ => Err(()),
}
}
}
}
/// Module for defining "Variant B" properties.
/// This is just like "Variant A", but with different properties.
mod variant_b {
/// The trait to implement if you want to support the properties
/// in this variant.
pub trait Trait {
fn get_widget(&self) -> String;
fn get_fuzz(&self) -> String;
}
/// This is effectively the "path" of the properties in this variant.
pub const VARIANT_ID: &str = "B";
// Each variant gets their own implementation of `TraitGlue`.
impl<T: Trait> super::TraitGlue<Trait> for T {
fn get_sub_property(&self, key: &str) -> Result<String, ()> {
match key {
"widget" => Ok(self.get_widget()),
"fuzz" => Ok(self.get_fuzz()),
_ => Err(()),
}
}
}
}
/// This is a trait that will be our general-purpose property interface. It is
/// intended to be used as a trait object.
///
/// This trait is implemented automatically by the `impl_super_trait!` macro,
/// defined below.
pub trait SuperTrait {
fn get_property(&self, key: &str) -> Result<String, ()>;
}
/// This macro implements `SuperTrait::get_property()` for the specified
/// struct and given variant modules. The syntax is:
/// ```
/// impl_super_trait!(<STRUCT-NAME> { <VARIANT-MODULE>, ... });
/// ```
macro_rules! impl_super_trait {
($backing_struct:ident { $( $T:ident ),* }) => {
impl SuperTrait for $backing_struct {
fn get_property(&self, key: &str) -> Result<String,()> {
match key.split_at(key.find("/").unwrap_or(0)) {
$( ($T::VARIANT_ID,subkey) =>
TraitGlue::<$T::Trait>::get_sub_property(self, &subkey[1..]),
)*
_ => Err(()),
}
}
}
};
// Handle dangling comma case.
($backing_struct:ident {$( $t:ident ),+ ,}) => {
impl_super_trait!($backing_struct { $( $t ),* });
};
}
///////////////////////////////////////////////////////////////////////////////
// USAGE EXAMPLE //////////////////////////////////////////////////////////////
// Our data backing struct. Super simple for the sake of clarity.
pub struct MyDataBacking();
impl variant_a::Trait for MyDataBacking {
fn get_foo(&self) -> String {
return "THIS IS FOO".to_string();
}
fn get_bar(&self) -> String {
return "THIS IS BAR".to_string();
}
}
impl variant_b::Trait for MyDataBacking {
fn get_widget(&self) -> String {
return "THIS IS WIDGET".to_string();
}
fn get_fuzz(&self) -> String {
return "THIS IS FUZZ".to_string();
}
}
// This macro call builds our `SuperTrait` implementation. Notice
// that the macro arguments specify the name of the struct as
// well as a "list" of implemented variants (identified by module).
// If a variant trait is implemented but not listed here it will be
// not be accessable via `SuperTrait::get_property()`.
impl_super_trait!( MyDataBacking {
variant_a,
variant_b,
});
///////////////////////////////////////////////////////////////////////////////
// TEST CASE //////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
fn main() {
let backing = MyDataBacking();
let keys = [
"A/foo", "A/bar", "B/widget", "B/fuzz", "C/blah", "A/widget", "crabs",
];
for key in &keys {
let value = backing.get_property(key);
println!(r#" "{}": {:?} "#, key, value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment