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
//! 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 } }
};
}