chiark / gitweb /
Introduce OpOutcomeThunk
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 19 Apr 2022 18:58:32 +0000 (19:58 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 28 Apr 2022 22:56:17 +0000 (23:56 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
daemon/api.rs
src/clock.rs
src/deck.rs
src/dice.rs
src/gamestate.rs
src/hand.rs
src/updates.rs

index ccd3b55cff5ac9da49de3717090fa20859c22452..e44bea8ebaa03ee6a2d6b8c67293db96e1dd2157 100644 (file)
@@ -59,13 +59,13 @@ mod op {
  
   pub trait Complex: Core + Debug { 
     #[throws(ApiPieceOpError)]
-    fn op_complex(&self, a: ApiPieceOpArgs) -> UpdateFromOpComplex;
+    fn op_complex(&self, a: ApiPieceOpArgs) -> OpOutcomeThunk;
   }
 
   impl<T> Complex for T where T: Core + Simple {
     #[throws(ApiPieceOpError)]
-    fn op_complex(&self, a: ApiPieceOpArgs) -> UpdateFromOpComplex {
-      (self.op(a)?, None)
+    fn op_complex(&self, a: ApiPieceOpArgs) -> OpOutcomeThunk {
+      (self.op(a)?, None).into()
     }
   }
 }
@@ -142,7 +142,12 @@ fn api_piece_op<O: op::Complex>(form: Json<ApiPiece<O>>)
         to_recalculate: &mut to_recalculate,
       })?;
     Ok::<_,ApiPieceOpError>((update, loose_conflict))
-  })() {
+  })().and_then(|(thunk, loose_conflict)| Ok((
+    match thunk {
+      OpOutcomeThunk::Immediate(r) => r,
+      OpOutcomeThunk::Reborrow(f) => f(g, player, piece)?,
+    }, loose_conflict
+  ))) {
     Err(APOE::Inapplicable(poe)) => {
       PrepareUpdatesBuffer::piece_report_error(
         &mut ig, poe,
@@ -569,13 +574,15 @@ api_route!{
     n: MultigrabQty,
   }
 
-  as:
-  #[throws(ApiPieceOpError)]
-  fn op(&self, mut a: ApiPieceOpArgs) -> PieceUpdate {
-    if ! a.ipc.special.multigrab { throw!(Ia::BadPieceStateForOperation) }
-    let pri = a.pri()?;
-    let y = pri.fully_visible().ok_or(Ia::Occultation)?;
-    a.ipc.show(y).op_multigrab(a, pri, self.n)?
+  impl op::Core as { }
+  impl op::Complex as {
+    #[throws(ApiPieceOpError)]
+    fn op_complex(&self, mut a: ApiPieceOpArgs) -> OpOutcomeThunk {
+      if ! a.ipc.special.multigrab { throw!(Ia::BadPieceStateForOperation) }
+      let pri = a.pri()?;
+      let y = pri.fully_visible().ok_or(Ia::Occultation)?;
+      a.ipc.show(y).op_multigrab(a, pri, self.n)?
+    }
   }
 }
 
@@ -589,7 +596,7 @@ api_route!{
   impl op::Core as { }
   impl op::Complex as {
     #[throws(ApiPieceOpError)]
-    fn op_complex(&self, mut a: ApiPieceOpArgs) -> UpdateFromOpComplex {
+    fn op_complex(&self, mut a: ApiPieceOpArgs) -> OpOutcomeThunk {
       let pri = a.pri()?;
       let ApiPieceOpArgs { ioccults,player,piece,ipc, .. } = a;
       let gs = &mut a.gs;
@@ -615,7 +622,7 @@ api_route!{
               wrc,
               PieceUpdateOp::Modify(()),
               logents,
-            ).into(), None)
+            ).into(), None).into()
           },
 
           _ => break,
index b8c49bc9e2ae977f51663446b6489d24ab3bd47e..256954d9a86ef6788323450d1cfaf10961c15292 100644 (file)
@@ -611,7 +611,7 @@ impl PieceTrait for Clock {
   #[throws(ApiPieceOpError)]
   fn ui_operation(&self, _: ShowUnocculted, args: ApiPieceOpArgs<'_>,
                   opname: &str, _wrc: WhatResponseToClientOp)
-                  -> UpdateFromOpComplex {
+                  -> OpOutcomeThunk {
     let ApiPieceOpArgs { gs,piece,player,ioccults,ipc,ig,.. } = args;
     let gpc = gs.pieces.byid_mut(piece)?;
     let held = gpc.held;
@@ -708,7 +708,7 @@ impl PieceTrait for Clock {
         );
         r
       }
-    }
+    }.into()
   }
 
   #[throws(IE)]
index 006af8d000a93eb5811ec4f807c27f5002e00edd..15328ef56c2696c99dad19126c8248ce37a16408 100644 (file)
@@ -173,7 +173,7 @@ impl PieceTrait for Deck {
   fn ui_operation(&self, vis: ShowUnocculted,
                   a: ApiPieceOpArgs<'_>,
                   opname: &str, wrc: WhatResponseToClientOp)
-                  -> UpdateFromOpComplex {
+                  -> OpOutcomeThunk {
     let ApiPieceOpArgs { gs,player,piece,ipieces,ioccults,to_recalculate,.. } = a;
     let gen = &mut gs.gen;
     let gplayers = &mut gs.players;
@@ -247,7 +247,7 @@ impl PieceTrait for Deck {
     (PieceUpdate {
       wrc, log,
       ops: puos.into(),
-    }, xupdates.into_unprepared(None))
+    }, xupdates.into_unprepared(None)).into()
   }
 
   fn occultation_notify_hook(&self, piece: PieceId) -> UnpreparedUpdates {
index 5026eaadd15c8398331eead82694f607a07cc357..09db332071eefcff8f988c777a11fa0c6ba30b00 100644 (file)
@@ -371,7 +371,7 @@ impl PieceTrait for Die {
   #[throws(ApiPieceOpError)]
   fn ui_operation(&self, _: ShowUnocculted, args: ApiPieceOpArgs<'_>,
                   opname: &str, wrc: WhatResponseToClientOp)
-                  -> UpdateFromOpComplex {
+                  -> OpOutcomeThunk {
     let ApiPieceOpArgs { gs,piece,player,ioccults,ipc,.. } = args;
     let gpc = gs.pieces.byid_mut(piece)?;
     let gpl = gs.players.byid(player)?;
@@ -392,7 +392,7 @@ impl PieceTrait for Die {
           wrc,
           PieceUpdateOp::Modify(()),
           logents,
-        ).into(), None)
+        ).into(), None).into()
 
       },
       _ => throw!(Ia::BadUiOperation)
index 523eb8a8392ea24ab223e742752e70053efaab7e..98e41037b397b9b3520366b41e2e1cd2449ee000 100644 (file)
@@ -199,7 +199,7 @@ pub trait PieceTrait: PieceBaseTrait + Send + Debug + 'static {
 
   fn ui_operation(&self, _: ShowUnocculted, _a: ApiPieceOpArgs<'_>,
                   _opname: &str, _wrc: WhatResponseToClientOp)
-                  -> Result<UpdateFromOpComplex, ApiPieceOpError> {
+                  -> Result<OpOutcomeThunk, ApiPieceOpError> {
     throw!(Ia::BadUiOperation)
   }
 
@@ -244,8 +244,9 @@ pub trait PieceTrait: PieceBaseTrait + Send + Debug + 'static {
     None
   }
 
+  #[throws(ApiPieceOpError)]
   fn op_multigrab(&self, _: ApiPieceOpArgs, _: PieceRenderInstructions,
-                  _: MultigrabQty) -> Result<PieceUpdate,ApiPieceOpError> {
+                  _: MultigrabQty) -> OpOutcomeThunk {
     Err(Ia::BadPieceStateForOperation)?
   }
 
index 23972717d03de38c06b922a9226128c9d9c64ed5..955e632e80d4a971fa12f4771bcabc6570776751 100644 (file)
@@ -229,7 +229,7 @@ impl PieceTrait for Hand {
   #[throws(ApiPieceOpError)]
   fn ui_operation(&self, vis: ShowUnocculted, mut a: ApiPieceOpArgs<'_>,
                   opname: &str, wrc: WhatResponseToClientOp)
-                  -> UpdateFromOpComplex {
+                  -> OpOutcomeThunk {
     if let Some(r) = {
       let gpc = a.gs.pieces.byid_mut(a.piece)?;
       let rot_checked = gpc.occulter_check_unrotated(vis)?;
@@ -238,7 +238,7 @@ impl PieceTrait for Hand {
                  internal_error_bydebug(&(&gpc.pos, &self.shape)))?;
       organise::ui_operation(&mut a, rot_checked, opname, wrc, &rect)?                             
     } {
-      return r;
+      return r.into();
     }
 
     let ApiPieceOpArgs { gs,player,piece,ipieces,ioccults,to_recalculate,.. } = a;
@@ -342,6 +342,7 @@ impl PieceTrait for Hand {
       wrc, log,
       ops: puos.into(),
     }, xupdates.into_unprepared(None))
+      .into()
   }
 
   fn occultation_notify_hook(&self, piece: PieceId) -> UnpreparedUpdates {
index 663ce087e2f19cc99fe5889dd1bf63edaca2e453..cdbd874eee418f41731b6b77c075dd6f3ba8f823 100644 (file)
@@ -165,6 +165,41 @@ pub enum PieceUpdateOp<NS,ZL> {
   SetZLevelQuiet(ZL),
 }
 
+#[derive(From)]
+pub enum OpOutcomeThunk {
+  Immediate(UpdateFromOpComplex),
+  /// Allows a UI operation full mutable access to the whole Instance.
+  ///
+  /// Use with care!  Eg, you might have to call save_game_and_aux_late.r
+  ///
+  /// Rules for adding and removing pieces:
+  ///
+  ///  * Adding a piece: add it to both ipieces and pieces.
+  ///    Call `save_game_and_aux_later`.
+  ///    Aux is always saved first, so if the piece is in pieces,
+  ///    it will be in ipieces on any reload.
+  ///
+  ///  * Deleting a piece: if the deletion as not the result of
+  ///    some kind of merge, and it doesn't matter if only the
+  ///    deletion happens, and not other recent events: just delete it.
+  ///    Call `save_game_and_aux_later`.
+  ///
+  ///  * Deleting a piece, in some kind of more complicated situation
+  ///    where the deletion must be atomic with other operations.
+  ///    Delete the piece *only* from pieces.  Leave it in ipieces.
+  ///    A reload will always restore a `GameState` snapshot.
+  ///    TODO: we should garbage-collect the slot in ipieces.
+  ///
+  /// TODO: Provide `&mut InstanceGuard` to the closure,
+  ///       not `&umut Instance`, since the latter is not sufficient.
+  ///
+  /// TODO: Provide cooked methods for this (taking `ModifyingPieces`)
+  ///
+  /// TODO: Provide a `ModifyingPieces` to the closure.
+  Reborrow(Box<dyn FnOnce(&mut Instance, PlayerId, PieceId)
+                   -> Result<UpdateFromOpComplex, ApiPieceOpError>>),
+}
+
 pub type UpdateFromOpComplex = (
   PieceUpdate,
   UnpreparedUpdates,