@@ -34,17 +34,8 @@ pub struct Selector(&'static str);
3434/// A `Command` consists of a [`Selector`], that indicates what the command is,
3535/// and an optional argument, that can be used to pass arbitrary data.
3636///
37- ///
38- /// # One-shot and reusable `Commands`
39- ///
40- /// Commands come in two varieties, 'reusable' and 'one-shot'.
41- ///
42- /// Regular commands are created with [`Command::new`], and their argument
43- /// objects may be accessed repeatedly, via [`Command::get_object`].
44- ///
45- /// One-shot commands are intended for cases where an object should only be
46- /// used once; an example would be if you have some resource that cannot be
47- /// cloned, and you wish to send it to another widget.
37+ /// If the payload can't or shouldn't be cloned,
38+ /// wrapping it with [`SingleUse`] allows you to `take` the object.
4839///
4940/// # Examples
5041/// ```
@@ -59,31 +50,50 @@ pub struct Selector(&'static str);
5950///
6051/// [`Command::new`]: #method.new
6152/// [`Command::get_object`]: #method.get_object
53+ /// [`SingleUse`]: struct.SingleUse.html
6254/// [`Selector`]: struct.Selector.html
6355#[ derive( Debug , Clone ) ]
6456pub struct Command {
6557 /// The command's `Selector`.
6658 pub selector : Selector ,
67- object : Option < Arg > ,
59+ object : Option < Arc < dyn Any > > ,
6860}
6961
70- #[ derive( Debug , Clone ) ]
71- enum Arg {
72- Reusable ( Arc < dyn Any > ) ,
73- OneShot ( Arc < Mutex < Option < Box < dyn Any > > > > ) ,
74- }
62+ /// A wrapper type for [`Command`] arguments that should only be used once.
63+ ///
64+ /// This is useful if you have some resource that cannot be
65+ /// cloned, and you wish to send it to another widget.
66+ ///
67+ /// # Examples
68+ /// ```
69+ /// use druid::{Command, Selector, SingleUse};
70+ ///
71+ /// struct CantClone(u8);
72+ ///
73+ /// let selector = Selector::new("use-once");
74+ /// let num = CantClone(42);
75+ /// let command = Command::new(selector, SingleUse::new(num));
76+ ///
77+ /// let object: &SingleUse<CantClone> = command.get_object().unwrap();
78+ /// if let Some(num) = object.take() {
79+ /// // now you own the data
80+ /// assert_eq!(num.0, 42);
81+ /// }
82+ ///
83+ /// // subsequent calls will return `None`
84+ /// assert!(object.take().is_none());
85+ /// ```
86+ ///
87+ /// [`Command`]: struct.Command.html
88+ pub struct SingleUse < T > ( Mutex < Option < T > > ) ;
7589
7690/// Errors that can occur when attempting to retrieve the a command's argument.
7791#[ derive( Debug , Clone , PartialEq ) ]
7892pub enum ArgumentError {
7993 /// The command did not have an argument.
8094 NoArgument ,
81- /// The argument was expected to be reusable and wasn't, or vice-versa.
82- WrongVariant ,
8395 /// The argument could not be downcast to the specified type.
8496 IncorrectType ,
85- /// The one-shot argument has already been taken.
86- Consumed ,
8797}
8898
8999/// The target of a command.
@@ -231,67 +241,34 @@ impl Command {
231241 pub fn new ( selector : Selector , arg : impl Any ) -> Self {
232242 Command {
233243 selector,
234- object : Some ( Arg :: Reusable ( Arc :: new ( arg) ) ) ,
235- }
236- }
237-
238- /// Create a new 'one-shot' `Command`.
239- ///
240- /// Unlike those created with `Command::new`, one-shot commands cannot
241- /// be reused; their argument is consumed when it is accessed, via
242- /// [`take_object`].
243- ///
244- /// [`take_object`]: #method.take_object
245- pub fn one_shot ( selector : Selector , arg : impl Any ) -> Self {
246- Command {
247- selector,
248- object : Some ( Arg :: OneShot ( Arc :: new ( Mutex :: new ( Some ( Box :: new ( arg) ) ) ) ) ) ,
244+ object : Some ( Arc :: new ( arg) ) ,
249245 }
250246 }
251247
252248 /// Used to create a command from the types sent via an `ExtEventSink`.
253249 pub ( crate ) fn from_ext ( selector : Selector , object : Option < Box < dyn Any + Send > > ) -> Self {
254250 let object: Option < Box < dyn Any > > = object. map ( |obj| obj as Box < dyn Any > ) ;
255- let object = object. map ( |o| Arg :: Reusable ( o. into ( ) ) ) ;
251+ let object = object. map ( |o| o. into ( ) ) ;
256252 Command { selector, object }
257253 }
258254
259255 /// Return a reference to this `Command`'s object, if it has one.
260- ///
261- /// This only works for 'reusable' commands; it does not work for commands
262- /// created with [`one_shot`].
263- ///
264- /// [`one_shot`]: #method.one_shot
265256 pub fn get_object < T : Any > ( & self ) -> Result < & T , ArgumentError > {
266257 match self . object . as_ref ( ) {
267- Some ( Arg :: Reusable ( o) ) => o. downcast_ref ( ) . ok_or ( ArgumentError :: IncorrectType ) ,
268- Some ( Arg :: OneShot ( _) ) => Err ( ArgumentError :: WrongVariant ) ,
258+ Some ( o) => o. downcast_ref ( ) . ok_or ( ArgumentError :: IncorrectType ) ,
269259 None => Err ( ArgumentError :: NoArgument ) ,
270260 }
271261 }
262+ }
272263
273- /// Attempt to take the object of a [`one-shot`] command.
274- ///
275- /// [`one-shot`]: #method.one_shot
276- pub fn take_object < T : Any > ( & self ) -> Result < Box < T > , ArgumentError > {
277- match self . object . as_ref ( ) {
278- Some ( Arg :: Reusable ( _) ) => Err ( ArgumentError :: WrongVariant ) ,
279- Some ( Arg :: OneShot ( inner) ) => {
280- let obj = inner
281- . lock ( )
282- . unwrap ( )
283- . take ( )
284- . ok_or ( ArgumentError :: Consumed ) ?;
285- match obj. downcast :: < T > ( ) {
286- Ok ( obj) => Ok ( obj) ,
287- Err ( obj) => {
288- inner. lock ( ) . unwrap ( ) . replace ( obj) ;
289- Err ( ArgumentError :: IncorrectType )
290- }
291- }
292- }
293- None => Err ( ArgumentError :: NoArgument ) ,
294- }
264+ impl < T : Any > SingleUse < T > {
265+ pub fn new ( data : T ) -> Self {
266+ SingleUse ( Mutex :: new ( Some ( data) ) )
267+ }
268+
269+ /// Takes the value, leaving a None in its place.
270+ pub fn take ( & self ) -> Option < T > {
271+ self . 0 . lock ( ) . unwrap ( ) . take ( )
295272 }
296273}
297274
@@ -315,12 +292,6 @@ impl std::fmt::Display for ArgumentError {
315292 match self {
316293 ArgumentError :: NoArgument => write ! ( f, "Command has no argument" ) ,
317294 ArgumentError :: IncorrectType => write ! ( f, "Downcast failed: wrong concrete type" ) ,
318- ArgumentError :: Consumed => write ! ( f, "One-shot command arguemnt already consumed" ) ,
319- ArgumentError :: WrongVariant => write ! (
320- f,
321- "Incorrect access method for argument type; \
322- check Command::one_shot docs for more detail."
323- ) ,
324295 }
325296 }
326297}
0 commit comments