Created
May 17, 2019 23:29
-
-
Save rust-play/b528cef8e9e01b38d9c36e4ab04a122a to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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