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