chiark / gitweb /
new UI operations arrangements
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 27 Sep 2020 22:45:32 +0000 (23:45 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 27 Sep 2020 22:45:32 +0000 (23:45 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/api.rs
src/cmdlistener.rs
src/gamestate.rs
src/global.rs
src/imports.rs
src/pieces.rs
src/shapelib.rs
src/spec.rs
src/updates.rs

index 13bf4c7d3f07ddaccbfb4e8366e8c1b7dc02caeb..653d7141d0c36971547afb1b5225e5177796903c 100644 (file)
@@ -265,11 +265,59 @@ impl ApiPieceOp for ApiPieceMove {
   }
 }
 
+const DEFKEY_FLIP : UoKey = 'f';
+
+#[derive(Debug,Serialize,Deserialize)]
+struct ApiPieceUo (UoKey);
+#[post("/_/api/k", format="json", data="<form>")]
+#[throws(OE)]
+fn api_uo(form : Json<ApiPiece<ApiPieceUo>>) -> impl response::Responder<'static> {
+  api_piece_op(form)
+}
+impl ApiPieceOp for ApiPieceUo {
+  #[throws(ApiPieceOpError)]
+  fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
+        p: &dyn Piece, lens: &dyn Lens)
+        -> (PieceUpdateOp<()>, Vec<LogEntry>) {
+    let def_key = self.0;
+
+    '_normal_global_ops__not_loop: loop {
+      let pc = gs.pieces.byid_mut(piece)?;
+      let pl = gs.players.byid(player)?;
+      let _: Impossible = match def_key {
+
+        DEFKEY_FLIP => {
+          let nfaces = p.nfaces();
+          pc.face = (RawFaceId::from(pc.face) % nfaces).into();
+          return (PieceUpdateOp::Modify(()),
+                  vec![ LogEntry { html: Html(format!(
+                    "{} flipped {}",
+                    &htmlescape::encode_minimal(&pl.nick),
+                    p.describe_pri(&lens.log_pri(piece, pc)).0
+                  )) }])
+        },
+
+        _ => break,
+      };
+    }
+
+    '_abnormal_global_ops__notloop: loop {
+      let _: Impossible = match def_key {
+
+        _ => break,
+      };
+    }
+
+    p.ui_operation(gs, player, piece, def_key, lens)?
+  }
+}
+
 pub fn mount(rocket_instance: Rocket) -> Rocket {
   rocket_instance.mount("/", routes![
     api_grab,
     api_ungrab,
     api_raise,
     api_move,
+    api_uo,
   ])
 }
index 99e59c62028907fc7b64b5006b8add7d10081840..95d93170cffc44c987262ad9fe4009ddff82bd7f 100644 (file)
@@ -265,7 +265,10 @@ fn execute_game_insn(cs: &CommandStream,
       let mut pos = pos.unwrap_or(DEFAULT_POS_START);
       for i in 0..count {
         let p = info.load()?;
-        let face = p.resolve_spec_face(face)?;
+        let face = face.unwrap_or_default();
+        if p.nfaces() <= face.into() {
+          throw!(SpecError::FaceNotFound);
+        }
         let z = ZCoord(gs.max_z.0 + (i + 1) as f64);
         let pc = PieceState {
           held: None,
index 2caf94ad1c84ed80f4a4a289ecd654d7875e527f..e4ee921483efeebf49d16a2641aa86ae9b173ad9 100644 (file)
@@ -90,43 +90,26 @@ pub trait Outline : Send + Debug {
 
 pub type UoKey = char;
 
+#[derive(Debug,Clone,Serialize,Deserialize)]
 pub struct UoDescription {
   pub def_key: UoKey,
   pub opname: String,
   pub desc: Html,
 }
 
-impl UoDescription {
-  pub fn new_flip() -> UoDescription { UoDescription {
-    def_key: 'f'.into(),
-    opname: "flip".to_string(),
-    desc: Html::lit("flip"),
-  } }
-}
-
-#[throws(ApiPieceOpError)]
-pub fn ui_operation_flip(gs: &mut GameState, player: PlayerId,
-                         piece: PieceId, lens: &dyn Lens,
-                         def_key: UoKey, nfaces: RawFaceId)
-                         -> PieceUpdateFromOp {
-  let pl = gs.players.byid(player)?;
-  let pc = gs.pieces.byid_mut(piece)?;
-  pc.face = (pc.face.into::<RawFaceId>() % nfaces).into();
-  (PieceUpdateOp::Modify(()), LogEntry { html: Html(format!(
-    "{} flipped {}",
-    &htmlescape::encode_minimal(&pl.nick),
-    pc.describe_pri(&lens.log_pri(piece, pc)).0))})
-}
-
 #[typetag::serde]
 pub trait Piece : Outline + Send + Debug {
-  fn resolve_spec_face(&self, face : Option<FaceId>)
-                       -> Result<FaceId,SpecError>;
-  fn ui_operations<'p>(&'p self) -> Result<Option<
-      Box<dyn ExactSizeIterator<Item=&UoDescription> +'p
-          >>, IE>;
-  fn ui_operation(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
-                  def_key: char, lens: &dyn Lens) -> PieceUpdateResult;
+  fn nfaces(&self) -> RawFaceId;
+
+  #[throws(InternalError)]
+  fn add_ui_operations(&self, _upd: &mut Vec<UoDescription>) { }
+
+  fn ui_operation(&self,
+                  _gs: &mut GameState, _player: PlayerId, _piece: PieceId,
+                  _def_key: char, _lens: &dyn Lens)
+                  -> PieceUpdateResult {
+    throw!(OE::BadOperation)
+  }
 
   // #[throws] doesn't work here for some reason
   fn svg_piece(&self, f: &mut Html, pri: &PieceRenderInstructions) -> IR;
@@ -243,6 +226,7 @@ impl PieceState {
       svg        : p.make_defs(pri)?,
       z          : self.zlevel.z,
       zg         : self.zlevel.zg,
+      uos        : p.ui_operations()?,
     }
   }
 }
@@ -250,6 +234,7 @@ impl PieceState {
 pub trait PieceExt {
   fn make_defs(&self, pri : &PieceRenderInstructions) -> Result<Html,IE>;
   fn describe_pri(&self, pri : &PieceRenderInstructions) -> Html;
+  fn ui_operations(&self) -> Result<Vec<UoDescription>, IE>;
 }
 
 impl<T> PieceExt for T where T: Piece + ?Sized {
@@ -276,6 +261,20 @@ impl<T> PieceExt for T where T: Piece + ?Sized {
   fn describe_pri(&self, pri : &PieceRenderInstructions) -> Html {
     self.describe_html(Some(pri.face))
   }
+
+  #[throws(InternalError)]
+  fn ui_operations(&self) -> Vec<UoDescription> {
+    let mut out = vec![];
+    if self.nfaces() > 1 {
+      out.push(UoDescription {
+        def_key: 'f'.into(),
+        opname: "flip".to_string(),
+        desc: Html::lit("flip"),
+      })
+    }
+    self.add_ui_operations(&mut out)?;
+    out
+  }
 }
 
 // ========== ad-hoc and temporary ==========
index cee4af1f24374be643c642dbf1df07e35c793e2d..469b0d49f2969594281ecb426478180375ca249b 100644 (file)
@@ -1051,7 +1051,6 @@ pub fn client_expire_old_clients() {
   }
   for gref in expire.drain(..) {
     struct Now(HashSet<ClientId>);
-    enum Impossible { }
     impl ClientIterator for Now {
       type Ret = Impossible;
       fn old(&mut self, client: ClientId)
index fa6d610b94365de0745fd4928aff70b8e3aa9ba2..9cdcaed4331a13a27de894099d925eb4c2e0f694 100644 (file)
@@ -115,6 +115,7 @@ pub use crate::shapelib;
 pub use nix::unistd::Uid;
 
 pub fn default<T:Default>() -> T { Default::default() }
+pub enum Impossible { }
 
 pub type E = anyhow::Error;
 pub type AE = anyhow::Error;
index 77528f6f93f2df7837e96a650509dc6665fdd188..3c09b729d9fb248119939ec4e0ad3a154627c24f 100644 (file)
@@ -136,31 +136,7 @@ impl Piece for SimpleShape {
       format!("a {}", self.desc.0)
     })
   }
-  #[throws(SpecError)]
-  fn resolve_spec_face(&self, face: Option<FaceId>) -> FaceId {
-    let face = face.unwrap_or_default();
-    self.colours.get(face).ok_or(SpecError::FaceNotFound)?;
-    face
-  }
-
-  #[throws(IE)]
-  fn ui_operations(&self) -> Box<dyn ExactSizeIterator<Item=&UoDescription>> {
-    if self.colours.len() > 1 {
-      Box::new(iter::once(UoDescription::new_flip()))
-    } else {
-      Box::new(iter::empty())
-    }
-  }
-
-  #[throws(ApiPieceOpError)]
-  fn ui_operation(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
-                  def_key: UoKey, lens: &dyn Lens) -> PieceUpdateFromOp {
-    if let Some(got) =
-      ui_operation_flip(gs,player,piece,def_key,lens,
-                        self.colours().into())? { return got }
-    throw!(OE::BadOperation)
-  }
-  
+  fn nfaces(&self) -> RawFaceId { self.colours.len().try_into().unwrap() }
 
   fn itemname(&self) -> &str { &self.itemname }
 }
index d80b06df629ca535b2f6ed007b1f007bfdc54a19..075e472daac2b0021eba3ac6b69e9f67044867df 100644 (file)
@@ -158,12 +158,7 @@ impl Outline for Item { delegate! { to self.outline {
 
 #[typetag::serde(name="Lib")]
 impl Piece for Item {
-  #[throws(SpecError)]
-  fn resolve_spec_face(&self, face: Option<FaceId>) -> FaceId {
-    let face = face.unwrap_or_default();
-    self.faces.get(face).ok_or(SpecError::FaceNotFound)?;
-    face
-  }
+  fn nfaces(&self) -> RawFaceId { self.faces.len().try_into().unwrap() }
 
   #[throws(IE)]
   fn svg_piece(&self, f: &mut Html, pri: &PieceRenderInstructions) {
index 332c3cb33e17840dd62a9d416faa0280a38b3ac2..e9359080effebcd4595b9e9d7fb56a7600c03ff5 100644 (file)
@@ -31,6 +31,7 @@ pub type RawFaceId = u8;
 define_index_type! {
   #[derive(Default)]
   pub struct FaceId = RawFaceId;
+  IMPL_RAW_CONVERSIONS = true;
 }
 
 #[derive(Serialize,Deserialize)]
index 359d6bed78297996ad2bcc591e8255b43f12c806..a46e8456fc9b29b895f0fdeb95938fadf24a6c9c 100644 (file)
@@ -59,6 +59,7 @@ pub struct PreparedPieceState {
   pub held : Option<PlayerId>,
   pub z : ZCoord,
   pub zg : Generation,
+  pub uos: Vec<UoDescription>,
 }
 
 // ---------- piece updates ----------