1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
//! A 'property' is basically something that exposes both a getter and a setter.
//! Keep in mind that if you just need only either of those, a 'property'
//! wouldn't really be useful in the first place. Also keep in mind that this
//! implementation of properties only support immutable setters. This design is
//! suitable for our application because _Browser Window_ only exposes getters
//! and setters that call C functions that do all the work. There is no memory
//! unsafety caused by any of this.
//!
//! # Usage
//! ```ignore
//! use std::cell::Cell;
//! use std::ffi::OsString;
//!
//! struct MyStruct {
//! oss: Cell<OsString>
//! }
//!
//! prop!{
//! /// Your doc comments go here...
//! pub MyProperty<String, &str>( this: MyStruct ) {
//! get => this.oss.get().into_string().unwrap(),
//! set(val) => this.oss.set( val.into() )
//! }
//! }
//! ```
//! This property is called `MyProperty`, of which the getter returns a `String`
//! and the setter takes a `&str`. Also, the property will be part of
//! `MyStruct`, taking a reference to it in its `get` and `set` implementations
//! called `this` (note that the keyword `self` is taken already and can not be
//! used within the macro). The syntax was chosen to be somewhat Rust-like, but
//! require as little as possible boilerplate.
//!
//! There is one last thing that needs to be done, and that is that the property
//! needs to be added in the implementation of `MyStruct`:
//! ```ignore
//! impl MyStruct {
//! impl_prop!( pub my_property: MyProperty );
//! }
//! ```
//! Keep in mind that the `pub` keywords in both places are options.
//!
//! Then we have it.
//! The property now can be accessed like this:
//! ```ignore
//! let my_struct = MyStruct { Cell::new( OsString::new() ) };
//! let string = my_struct.my_property().get();
//! string.push_str("something");
//! my_struct.my_property().set( string );
//! ```
/// A property is something that has a setter and a getter.
// The setters are immutable.
// This is because they can not be changed from threads other than the GUI
// thread anyway.
pub trait Property<G, S> {
fn get(&self) -> G;
fn set(&self, value: S);
}
#[doc(hidden)]
#[macro_export]
macro_rules! _prop_internal {
( $(#[$meta:meta])*, $vis:tt, $name:ident, $tg:ty, $ts:ty, $this:ident, $stype:ty, $get:expr, $val:ident, $set:expr ) => {
// The struct is basically empty.
$(#[$meta])*
$vis struct $name<'a> {
parent: &'a $stype
}
// And it implements the `Property` trait.
impl<'a> Property<$tg,$ts> for $name<'a> {
fn get( &self ) -> $tg { let $this = &self.parent; $get }
fn set( &self, $val: $ts ) { let $this = &self.parent; $set }
}
}
}
/// A macro to define a so called 'property'.
/// Kind of similar to how C# properties work.
#[doc(hidden)]
#[macro_export]
macro_rules! prop {
( $(#[$metas:meta])* $name:ident<$type:ty>( $this:ident: $stype:ty ) { get => $get:expr, set( $val:ident ) => $set:expr } ) => {
_prop_internal!( $(#[$metas])*, pub, $name, $type, $type, $this, $stype, $get, $val, $set );
};
/*( $(#[$metas:meta])* pub $name:ident<$type:ty>( $this:ident: $stype:ty ) { get => $get:expr, set( $val:ident ) => $set:expr } ) => {
_prop_internal!( $(#[metas])*, pub, $name, $type, $type, $this, $stype, $get, $val, $set );
};
( $(#[$metas:meta])* $name:ident<$tg:ty, $ts:ty>($this:ident: $stype:ty) { get => $get:expr, set( $val:ident ) => $set:expr } ) => {
_prop_internal!( $(#[$metas])*, , $name, $tg, $ts, $this, $stype, $get, $val, $set );
};*/
( $(#[$metas:meta])* pub $name:ident<$tg:ty, $ts:ty>($this:ident: $stype:ty) { get => $get:expr, set( $val:ident ) => $set:expr } ) => {
_prop_internal!( $(#[$metas])*, pub, $name, $tg, $ts, $this, $stype, $get, $val, $set );
};
( $(#[$metas:meta])* pub($vis:tt) $name:ident<$tg:ty, $ts:ty>($this:ident: $stype:ty) { get => $get:expr, set( $val:ident ) => $set:expr } ) => {
_prop_internal!( $(#[$metas])*, pub($vis:tt), $name, $tg, $ts, $this, $stype, $get, $val, $set );
};
}
/// A macro to implement the property for a struct.
#[macro_export]
#[doc(hidden)]
macro_rules! impl_prop {
($name:ident : $property:ident) => {
fn $name<'a>(&'a self) -> $property { $property { parent: self } }
};
(pub $name:ident : $property:ident) => {
pub fn $name<'a>(&'a self) -> $property { $property { parent: self } }
};
}