chiark / gitweb /
much error reorg
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 2 Sep 2020 23:48:49 +0000 (00:48 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 2 Sep 2020 23:48:49 +0000 (00:48 +0100)
src/api.rs
src/commands.rs
src/error.rs
src/gamestate.rs
src/global.rs
src/http.rs
src/pieces.rs
src/session.rs
src/spec.rs
src/updates.rs

index a3224a9c2d81b2f16d3851175288ba6df74b6f70..5ab15b7c7951a3dfb02238bc7f4ccfcdc648c5e7 100644 (file)
@@ -11,12 +11,19 @@ struct ApiPiece<O : ApiPieceOp> {
   op : O,
 }
 trait ApiPieceOp : Debug {
-  #[throws(GameError)]
+  #[throws(ApiPieceOpError)]
   fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
         lens: &dyn Lens /* used for LogEntry and PieceId but not Pos */)
         -> (PieceUpdateOp<()>, Vec<LogEntry>);
 }
 
+#[derive(Error,Debug)]
+enum ApiPieceOpError {
+  ReportViaResponse(#[from] OnlineError),
+  ReportViaUpdate(#[from] PieceOpError),
+}
+display_as_debug!(ApiPieceOpError);
+
 pub trait Lens : Debug {
   fn pieceid2visible(&self, piece: PieceId) -> VisiblePieceId;
   fn log_pri(&self, piece: PieceId, pc: &PieceState)
@@ -82,24 +89,15 @@ fn api_piece_op<O: ApiPieceOp>(form : Json<ApiPiece<O>>)
 
     eprintln!("Q_GEN={:?} U_GEN={:?}", u_gen, q_gen);
 
-    if u_gen > q_gen { throw!(GameError::Conflict) }
+    if u_gen > q_gen { throw!(PieceOpError::Conflict) }
     if pc.held != None && pc.held != Some(player) {
-      throw!(GameError::PieceHeld)
+      throw!(OnlineError::PieceHeld)
     };
     let (update, logents) = form.op.op(gs,player,piece,&lens)?;
-    Ok((update, logents))
+    Ok::<_,ApiPieceOpError>((update, logents))
   })() {
-    Err(err) => {
-      let err : GameError = err;
-      if let GameError::InternalErrorSVG(svg) = err { throw!(svg) }
-      eprintln!("API {:?} => {:?}", &form, &err);
-      // Restating the state of this piece (with a new generation)
-      // will forcibly synchronise the client which made the failing
-      // request.
-      let mut buf = PrepareUpdatesBuffer::new(g, None, None);
-      buf.piece_update(piece, PieceUpdateOp::Modify(()), &lens);
-      throw!(err);
-    },
+    Err(err) => err.report(&mut ig, piece, player, client, &lens)?,
+
     Ok((update, logents)) => {
       let mut buf = PrepareUpdatesBuffer::new(g, Some((client, form.cseq)),
                                               Some(1 + logents.len()));
@@ -113,6 +111,38 @@ fn api_piece_op<O: ApiPieceOp>(form : Json<ApiPiece<O>>)
   ""
 }
 
+impl ApiPieceOpError {
+  pub fn report(self, ig: &mut InstanceGuard,
+                piece: PieceId, player: PlayerId, client: ClientId,
+                lens: &dyn Lens) -> Result<(),OE> {
+    use ApiPieceOpError::*;
+
+    match self {
+      ReportViaUpdate(poe) => {
+        let gen = ig.gs.gen;
+        ig.updates.get_mut(player)
+          .ok_or(OE::NoPlayer)?
+          .push(Arc::new(PreparedUpdate {
+            gen,
+            us : vec![ PreparedUpdateEntry::Error(
+              Some(client),
+              ErrorSignaledViaUpdate::PieceOpError(piece, poe),
+            )],
+          }));
+
+        let mut buf = PrepareUpdatesBuffer::new(ig, None, None);
+        buf.piece_update(piece, PieceUpdateOp::Modify(()), lens);
+      },
+      
+      ReportViaResponse(err) => {
+        eprintln!("API ERROR => {:?}", &err);
+        Err(err)?;
+      },
+    }
+    Ok(())
+  }
+}
+
 #[derive(Debug,Serialize,Deserialize)]
 struct ApiPieceGrab {
 }
@@ -123,14 +153,14 @@ fn api_grab(form : Json<ApiPiece<ApiPieceGrab>>)
   api_piece_op(form)
 }
 impl ApiPieceOp for ApiPieceGrab {
-  #[throws(GameError)]
+  #[throws(ApiPieceOpError)]
   fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
         lens: &dyn Lens)
         -> (PieceUpdateOp<()>, Vec<LogEntry>) {
     let pl = gs.players.byid(player).unwrap();
     let pc = gs.pieces.byid_mut(piece).unwrap();
 
-    if pc.held.is_some() { throw!(GameError::PieceHeld) }
+    if pc.held.is_some() { throw!(OnlineError::PieceHeld) }
     pc.held = Some(player);
     
     let update = PieceUpdateOp::Modify(());
@@ -155,14 +185,14 @@ fn api_ungrab(form : Json<ApiPiece<ApiPieceUngrab>>)
   api_piece_op(form)
 }
 impl ApiPieceOp for ApiPieceUngrab {
-  #[throws(GameError)]
+  #[throws(ApiPieceOpError)]
   fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
         lens: &dyn Lens)
         -> (PieceUpdateOp<()>, Vec<LogEntry>) {
     let pl = gs.players.byid(player).unwrap();
     let pc = gs.pieces.byid_mut(piece).unwrap();
 
-    if pc.held != Some(player) { throw!(GameError::PieceHeld) }
+    if pc.held != Some(player) { throw!(OnlineError::PieceHeld) }
     pc.held = None;
     
     let update = PieceUpdateOp::Modify(());
@@ -188,7 +218,7 @@ fn api_raise(form : Json<ApiPiece<ApiPieceRaise>>)
   api_piece_op(form)
 }
 impl ApiPieceOp for ApiPieceRaise {
-  #[throws(GameError)]
+  #[throws(ApiPieceOpError)]
   fn op(&self, gs: &mut GameState, _: PlayerId, piece: PieceId,
         _: &dyn Lens)
         -> (PieceUpdateOp<()>, Vec<LogEntry>) {
@@ -207,13 +237,13 @@ fn api_move(form : Json<ApiPiece<ApiPieceMove>>) -> impl response::Responder<'st
   api_piece_op(form)
 }
 impl ApiPieceOp for ApiPieceMove {
-  #[throws(GameError)]
+  #[throws(ApiPieceOpError)]
   fn op(&self, gs: &mut GameState, _: PlayerId, piece: PieceId,
         _lens: &dyn Lens)
         -> (PieceUpdateOp<()>, Vec<LogEntry>) {
     let pc = gs.pieces.byid_mut(piece).unwrap();
     if let (_,true) = self.0.clamped(gs.table_size) {
-      throw!(GameError::PosOffTable);
+      Err(ApiPieceOpError::ReportViaUpdate(PieceOpError::PosOffTable))?;
     }
     pc.pos = self.0;
     let update = PieceUpdateOp::Move(self.0);
index 1936bb81febf4f20eeaf5f57dcf0024d977da68a..2fbc49e0c68e43ad32482cefdc34972b93ef4ac8 100644 (file)
@@ -82,9 +82,8 @@ pub enum MgmtError {
   PlayerNotFound,
   PieceNotFound,
   LimitExceeded,
-  SVGProcessingFailed(#[from] SVGProcessingError),
-  GameError(#[from] GameError),
   ServerFailure(String),
+  BadSpec(#[from] SpecError),
 }
 impl Display for MgmtError {
   #[throws(fmt::Error)]
@@ -97,8 +96,8 @@ impl Display for MgmtError {
   }
 }
 
-impl From<ServerFailure> for MgmtError {
-  fn from(e: ServerFailure) -> MgmtError {
+impl From<InternalError> for MgmtError {
+  fn from(e: InternalError) -> MgmtError {
     MgmtError::ServerFailure(format!("ServerFailure {}\n", &e))
   }
 }
index 8aa36586ccdbdaa4d1a860f31ff25519ccd8a033..a860909ed30980081e3f8239ac39c814023e48a0 100644 (file)
@@ -1,59 +1,67 @@
 
 use crate::imports::*;
 
-#[derive(Error,Clone,Debug,Serialize,Deserialize)]
-#[error("operation error {:?}",self)]
-pub enum GameError {
-  Conflict,
-  PieceGone,
-  PieceHeld,
-  FaceNotFound,
-  PosOffTable,
-  InternalErrorSVG(#[from] SVGProcessingError),
-}
-
 #[derive(Error,Debug)]
 pub enum OnlineError {
   #[error("Game in process of being destroyed")]
   GameBeingDestroyed,
-  #[error("Game corrupted by previous crash - consult administrator")]
-  GameCorrupted,
   #[error("client session not recognised (terminated by server?)")]
   NoClient,
   #[error("player not part of game (removed?)")]
   NoPlayer,
-  #[error("game operation error")]
-  GameError(#[from] GameError),
   #[error("invalid Z coordinate")]
   InvalidZCoord,
-  #[error("JSON~ serialisation error: {0:?}")]
-  JSONSerializeFailed(#[from] serde_json::error::Error),
-  #[error("SVG processing/generation error {0:?}")]
-  SVGProcessingFailed(#[from] SVGProcessingError),
-  #[error("Server operational problems: {0:?}")]
-  ServerFailure(#[from] ServerFailure),
+  #[error("improper piece hold status for op (client should have known)")]
+  PieceHeld,
+  #[error("Server operational problems - consult administrator: {0:?}")]
+  ServerFailure(#[from] InternalError),
+  #[error("JSON deserialisation error: {0:?}")]
+  BadJSON(serde_json::Error),
 }
 from_instance_lock_error!{OnlineError}
 
 #[derive(Error,Debug)]
-pub enum ServerFailure {
+pub enum InternalError {
+  #[error("Game corrupted by previous crash")]
+  GameCorrupted,
   #[error("Server MessagePack encoding error {0}")]
   MessagePackEncodeFail(#[from] rmp_serde::encode::Error),
   #[error("Server MessagePack decoding error (game load failed) {0}")]
   MessagePackDecodeFail(#[from] rmp_serde::decode::Error),
   #[error("Server internal logic error {0}")]
   InternalLogicError(String),
+  #[error("SVG processing/generation error {0:?}")]
+  SVGProcessingFailed(#[from] SVGProcessingError),
+  #[error("String formatting error {0}")]
+  StringFormatting(#[from] fmt::Error),
+  #[error("JSON deserialisation error: {0:?}")]
+  JSONEncode(serde_json::Error),
   #[error("Server error {0:?}")]
   Anyhow(#[from] anyhow::Error),
 }
 
+impl From<InternalError> for SpecError {
+  fn from(ie: InternalError) -> SpecError {
+    SpecError::InternalError(format!("{:?}",ie))
+  }
+}
+
 #[derive(Error,Debug,Serialize,Copy,Clone)]
 pub enum ErrorSignaledViaUpdate {
-  RenderingError,
+  InternalError,
   PlayerRemoved,
+  PieceOpError(PieceId, PieceOpError),
 }
 display_as_debug!{ErrorSignaledViaUpdate}
 
+#[derive(Error,Debug,Serialize,Copy,Clone)]
+pub enum PieceOpError {
+  Gone,
+  Conflict,
+  PosOffTable,
+}
+display_as_debug!{PieceOpError}
+
 pub type StartupError = anyhow::Error;
 
 pub use OnlineError::{NoClient,NoPlayer};
@@ -70,8 +78,8 @@ macro_rules! from_instance_lock_error {
       fn from(e: InstanceLockError) -> $into {
         use InstanceLockError::*;
         match e {
-          GameCorrupted      => $into::GameCorrupted,
           GameBeingDestroyed => $into::GameBeingDestroyed,
+          GameCorrupted      => InternalError::GameCorrupted.into(),
         }
       }
     }
@@ -120,16 +128,17 @@ impl<T> IdForById for T where T : AccessId {
 }
 
 impl IdForById for PieceId {
-  type Error = GameError;
-  const ERROR : GameError = GameError::PieceGone;
+  type Error = PieceOpError;
+  const ERROR : PieceOpError = PieceOpError::Gone;
 }
 
 #[macro_export]
 macro_rules! display_as_debug {
   {$x:ty} => {
-    impl Display for $x {
-      fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        <Self as Debug>::fmt(self, f)
+    impl std::fmt::Display for $x {
+      #[throws(std::fmt::Error)]
+      fn fmt(&self, f: &mut std::fmt::Formatter) {
+        <Self as Debug>::fmt(self, f)?
       }
     }
   }
@@ -145,3 +154,9 @@ macro_rules! error_from_losedetails {
   }
 }
 pub use crate::error_from_losedetails;
+
+impl From<SVGProcessingError> for SpecError {
+  fn from(se: SVGProcessingError) -> SpecError {
+    InternalError::SVGProcessingFailed(se).into()
+  }
+}
index f860430a6c45d74d6419ebc009217ca614638cf8..1b3b0cbedfff9d183bbaa1d98c1b58ccb944c017 100644 (file)
@@ -70,20 +70,21 @@ pub struct LogEntry {
 
 // ---------- piece trait, and rendering ----------
 
+type IE = InternalError;
+type IR = Result<(),IE>;
 type SE = SVGProcessingError;
-type SR = Result<(),SE>;
 
 #[typetag::serde]
 pub trait Piece : Send + Debug {
   // #[throws] doesn't work here for some reason
-  fn svg_piece(&self, f: &mut String, pri: &PieceRenderInstructions) -> SR;
+  fn svg_piece(&self, f: &mut String, pri: &PieceRenderInstructions) -> IR;
 
-  #[throws(SE)]
+  #[throws(IE)]
   fn surround_path(&self, pri : &PieceRenderInstructions) -> String;
 
-  fn svg_x_defs(&self, f: &mut String, pri : &PieceRenderInstructions) -> SR;
+  fn svg_x_defs(&self, f: &mut String, pri : &PieceRenderInstructions) -> IR;
 
-  #[throws(SE)]
+  #[throws(IE)]
   fn thresh_dragraise(&self, pri : &PieceRenderInstructions)
                       -> Option<Coord>;
 
@@ -103,9 +104,9 @@ pub struct PieceRenderInstructions {
 
 #[typetag::serde(tag="type")]
 pub trait PieceSpec : Debug {
-  fn load(&self) -> Result<Box<dyn Piece>,SE>;
+  fn load(&self) -> Result<Box<dyn Piece>,SpecError>;
   fn resolve_spec_face(&self, face : Option<FaceId>)
-                       -> Result<FaceId,GameError>;
+                       -> Result<FaceId,SpecError>;
 }
 
 // ========== implementations ==========
@@ -172,7 +173,7 @@ impl ClampTable for Pos {
 // ---------- game state - rendering etc. ----------
 
 impl PieceState {
-  #[throws(SE)]
+  #[throws(IE)]
   pub fn make_defs(&self, pri : &PieceRenderInstructions) -> String {
     let pr = self;
     let mut defs = String::new();
@@ -193,7 +194,7 @@ impl PieceState {
     defs
   }
 
-  #[throws(SE)]
+  #[throws(IE)]
   pub fn prep_piecestate(&self, pri : &PieceRenderInstructions)
                          -> PreparedPieceState {
     PreparedPieceState {
index 5501b3cc3984de2a8561c11d31ed65d3c9fd6f3d..0123ace693d516488bb145cda535a0df36fbf89a 100644 (file)
@@ -252,7 +252,7 @@ impl Instance {
       .clone()
   }
 
-  #[throws(ServerFailure)]
+  #[throws(InternalError)]
   pub fn destroy_game(mut g: InstanceGuard) {
     let a_savefile = savefilename(&g.name, "a-", "");
 
@@ -327,7 +327,7 @@ impl InstanceGuard<'_> {
   //  #[throws(ServerFailure)]
   //  https://github.com/withoutboats/fehler/issues/62
   pub fn player_remove(&mut self, oldplayer: PlayerId)
-                       -> Result<Option<PlayerState>,ServerFailure> {
+                       -> Result<Option<PlayerState>,InternalError> {
     // We have to filter this player out of everything
     // Then save
     // Then send updates
@@ -408,6 +408,7 @@ impl InstanceGuard<'_> {
         updates. push(PreparedUpdate {
           gen: self.c.g.gs.gen,
           us : vec![ PreparedUpdateEntry::Error(
+            None,
             ErrorSignaledViaUpdate::PlayerRemoved
           )],
         });
@@ -571,7 +572,7 @@ fn savefilename_parse(leaf: &[u8]) -> SavefilenameParseResult {
 }
 
 impl InstanceGuard<'_> {
-  #[throws(ServerFailure)]
+  #[throws(InternalError)]
   fn save_something(
     &self, prefix: &str,
     w: fn(s: &Self, w: &mut BufWriter<fs::File>)
@@ -594,7 +595,7 @@ impl InstanceGuard<'_> {
     eprintln!("saved to {}", &out);
   }
 
-  #[throws(ServerFailure)]
+  #[throws(InternalError)]
   pub fn save_game_now(&mut self) {
     self.save_something("g-", |s,w| {
       rmp_serde::encode::write_named(w, &s.c.g.gs)
@@ -602,7 +603,7 @@ impl InstanceGuard<'_> {
     self.c.game_dirty = false;
   }
 
-  #[throws(ServerFailure)]
+  #[throws(InternalError)]
   fn save_access_now(&mut self) {
     self.save_something("a-", |s,w| {
       let tokens_players : Vec<(&str, PlayerId)> = {
@@ -620,7 +621,7 @@ impl InstanceGuard<'_> {
     })?;
   }
 
-  #[throws(ServerFailure)]
+  #[throws(InternalError)]
   fn load_something<T:DeserializeOwned>(name: &InstanceName, prefix: &str)
                                         -> T {
     let inp = savefilename(name, prefix, "");
@@ -653,7 +654,7 @@ impl InstanceGuard<'_> {
     let mut access_load : InstanceSaveAccesses<String>
       = Self::load_something(&name, "a-")
       .or_else(|e| {
-        if let ServerFailure::Anyhow(ae) = &e {
+        if let InternalError::Anyhow(ae) = &e {
           if let Some(ioe) = ae.downcast_ref::<io::Error>() {
             if ioe.kind() == io::ErrorKind::NotFound {
               return Ok(Default::default())
index baaad11ffc8e374a169eabef8f456590815a3911..494bae62ef82cb46de4155b0bd1b088eb96068f4 100644 (file)
@@ -14,11 +14,10 @@ impl<'r> Responder<'r> for OnlineError {
     use rocket::http::Status;
     use OnlineError::*;
     let status = match self {
-      GameCorrupted | JSONSerializeFailed(_) | SVGProcessingFailed(_)
-        | ServerFailure(_)
-        => Status::InternalServerError,
+      ServerFailure(_) => Status::InternalServerError,
       NoClient | NoPlayer | GameBeingDestroyed => Status::NotFound,
-      InvalidZCoord | OnlineError::GameError(_) => Status::BadRequest,
+      InvalidZCoord | BadJSON(_) | OnlineError::PieceHeld
+        => Status::BadRequest,
     };
     let mut resp = Responder::respond_to(msg,req).unwrap();
     resp.set_status(status);
index e0a41ecddcf121742446d5d4e1351a307b244a39..ea8df9b66fb44dc3e01315c2e3284562fe6d9f3c 100644 (file)
@@ -22,14 +22,17 @@ pub enum SVGProcessingError {
   BadNumber,
   WriteFail,
   NegativeDragraise,
-  ImproperSizeSpec,
-  UnsupportedColourSpec,
 }
 
 display_as_debug!{SVGProcessingError}
 error_from_losedetails!{SVGProcessingError,WriteFail,fmt::Error}
 error_from_losedetails!{SVGProcessingError,BadNumber,std::num::ParseFloatError}
 
+impl From<SVGProcessingError> for MgmtError {
+  fn from(se: SVGProcessingError) -> MgmtError { se.into() }
+}
+
+type IE = InternalError;
 type SE = SVGProcessingError;
 
 #[throws(SE)]
@@ -83,22 +86,22 @@ eprintln!("rescaled by {}: {} as {}",scale,&input,&out);
 
 #[typetag::serde]
 impl Piece for SimpleShape {
-  #[throws(SE)]
+  #[throws(IE)]
   fn svg_piece(&self, f: &mut String, pri: &PieceRenderInstructions) {
     write!(f, r##"<path fill="{}" d="{}"/>"##,
            self.colours[pri.face],
            &self.path)?;
   }
-  #[throws(SE)]
+  #[throws(IE)]
   fn surround_path(&self, _pri : &PieceRenderInstructions) -> String {
     self.scaled_path.clone()
   }
-  #[throws(SE)]
+  #[throws(IE)]
   fn thresh_dragraise(&self, _pri : &PieceRenderInstructions)
                       -> Option<Coord> {
     Some(self.approx_dia / 2)
   }
-  #[throws(SE)]
+  #[throws(IE)]
   fn svg_x_defs(&self, _f: &mut String, _pri : &PieceRenderInstructions) {
   }
   fn describe_html(&self, face : Option<FaceId>) -> String {
@@ -113,30 +116,30 @@ impl Piece for SimpleShape {
 impl SimpleShape {
   fn new_from_path(desc: String, path: String, approx_dia: Coord,
                    faces: &IndexVec<FaceId,ColourSpec>)
-                   -> Result<Box<dyn Piece>,SE> {
+                   -> Result<Box<dyn Piece>,SpecError> {
     let scaled_path = svg_rescale_path(&path, SELECT_SCALE)?;
     let colours = faces
       .iter()
       .map(|s| s.try_into())
-      .collect::<Result<_,SE>>()?;
+      .collect::<Result<_,SpecError>>()?;
     Ok(Box::new(SimpleShape {
       scaled_path, desc, approx_dia, path, colours,
     }))
   }
 }
 
-#[throws(GameError)]
+#[throws(SpecError)]
 fn simple_resolve_spec_face(faces: &IndexSlice<FaceId,[ColourSpec]>,
                             face: Option<FaceId>)
                             -> FaceId {
   let face = face.unwrap_or_default();
-  faces.get(face).ok_or(GameError::FaceNotFound)?;
+  faces.get(face).ok_or(SpecError::FaceNotFound)?;
   face
 }
 
 #[typetag::serde]
 impl PieceSpec for piece_specs::Disc {
-  #[throws(SE)]
+  #[throws(SpecError)]
   fn load(&self) -> Box<dyn Piece> {
     let unit_path =
       "M 0 1  a 1 1 0 1 0 0 -2 \
@@ -146,7 +149,7 @@ impl PieceSpec for piece_specs::Disc {
     SimpleShape::new_from_path("circle".to_owned(), path, self.diam,
                                &self.faces)?
   }
-  #[throws(GameError)]
+  #[throws(SpecError)]
   fn resolve_spec_face(&self, face: Option<FaceId>) -> FaceId {
     simple_resolve_spec_face(&self.faces, face)?
   }
@@ -154,19 +157,19 @@ impl PieceSpec for piece_specs::Disc {
 
 #[typetag::serde]
 impl PieceSpec for piece_specs::Square {
-  #[throws(SE)]
+  #[throws(SpecError)]
   fn load(&self) -> Box<dyn Piece> {
     let (x, y) = match *self.size.as_slice() {
       [s,] => (s,s),
       [x, y] => (x,y),
-      _ => throw!(SE::ImproperSizeSpec),
+      _ => throw!(SpecError::ImproperSizeSpec),
     };
     let path = format!("M {} {} h {} v {} h {} z",
                        -(x as f64)*0.5, -(y as f64)*0.5, x, y, -x);
     SimpleShape::new_from_path("square".to_owned(), path, (x+y+1)/2,
                                &self.faces)?
   }
-  #[throws(GameError)]
+  #[throws(SpecError)]
   fn resolve_spec_face(&self, face: Option<FaceId>) -> FaceId {
     simple_resolve_spec_face(&self.faces, face)?
   }
index fdaaa615899e952536b33f615fc19d6cb87d3ac3..a65924c8bbe4e78393c75442a35ea64650fc1bb2 100644 (file)
@@ -105,7 +105,8 @@ fn session(form : Json<SessionForm>) -> Result<Template,OE> {
       let for_piece = SessionPieceContext {
         id: pri.id,
         pos : pr.pos,
-        info : serde_json::to_string(&for_info)?,
+        info : serde_json::to_string(&for_info)
+          .map_err(|e| InternalError::JSONEncode(e))?,
       };
       uses.push(for_piece);
     }
@@ -121,7 +122,7 @@ fn session(form : Json<SessionForm>) -> Result<Template,OE> {
       nick : pl.nick.clone(),
       load : serde_json::to_string(&DataLoad {
         players : load_players,
-      })?,
+      }).map_err(|e| InternalError::JSONEncode(e))?,
     };
     eprintln!("SRC {:?}", &src);
     src
index 178682ce5f6aa05dc19a1a05a57565ac98e43da1..a2e5e34975b6a2b8153082f3cc1d857fc485d135 100644 (file)
@@ -6,6 +6,8 @@ use index_vec::{define_index_type,IndexVec};
 use crate::gamestate::PieceSpec;
 use std::fmt::Debug;
 use implementation::PlayerAccessSpec;
+use thiserror::Error;
+use crate::error::display_as_debug;
 
 //---------- common types ----------
 
@@ -27,6 +29,15 @@ define_index_type! {
 #[repr(transparent)]
 pub struct ColourSpec(String);
 
+#[derive(Error,Clone,Serialize,Deserialize,Debug)]
+pub enum SpecError {
+  ImproperSizeSpec,
+  UnsupportedColourSpec,
+  FaceNotFound,
+  InternalError(String),
+}
+display_as_debug!{SpecError}
+
 //---------- Table TOML file ----------
 
 #[derive(Debug,Serialize,Deserialize)]
@@ -91,7 +102,6 @@ mod implementation {
   use super::*;
   use crate::imports::*;
   type Insn = crate::commands::MgmtGameInstruction;
-  type SE = SVGProcessingError;
 
   #[typetag::serde(tag="access")]
   pub trait PlayerAccessSpec : Debug {
@@ -125,8 +135,8 @@ mod implementation {
   }
 
   impl TryFrom<&ColourSpec> for Colour {
-    type Error = SE;
-    #[throws(SE)]
+    type Error = SpecError;
+    #[throws(SpecError)]
     fn try_from(spec: &ColourSpec) -> Colour {
       lazy_static! {
         static ref RE: Regex = Regex::new(concat!(
@@ -138,7 +148,7 @@ mod implementation {
       }
       let s = &spec.0;
       if !RE.is_match(s) {
-        throw!(SVGProcessingError::UnsupportedColourSpec);
+        throw!(SpecError::UnsupportedColourSpec);
       }
       spec.0.clone()
     }
index 4b04ef996cf1db0082058a0ea7000830cff81bc1..5328a01f0d1c0d154adde69b63f063f2d8b18855 100644 (file)
@@ -45,7 +45,7 @@ pub enum PreparedUpdateEntry {
   },
   SetTableSize(Pos),
   Log (Arc<LogEntry>),
-  Error (ErrorSignaledViaUpdate),
+  Error (Option<ClientId> /* none: all */, ErrorSignaledViaUpdate),
 }
 
 #[derive(Debug,Serialize)]
@@ -132,7 +132,7 @@ impl PreparedUpdateEntry {
         logent.html.as_bytes().len() * 3
       },
       SetTableSize(_) |
-      Error(_) => {
+      Error(_,_) => {
         100
       },
     }
@@ -209,7 +209,7 @@ impl<'r> PrepareUpdatesBuffer<'r> {
     }
   }
 
-  #[throws(SVGProcessingError)]
+  #[throws(InternalError)]
   fn piece_update_fallible(&mut self, piece: PieceId,
                            update: PieceUpdateOp<()>,
                            lens: &dyn Lens) -> PreparedUpdateEntry {
@@ -234,13 +234,13 @@ impl<'r> PrepareUpdatesBuffer<'r> {
           |_|{
             let mut ns = pc.prep_piecestate(&pri_for_all)?;
             lens.massage_prep_piecestate(&mut ns);
-            <Result<_,SVGProcessingError>>::Ok(ns)
+            <Result<_,InternalError>>::Ok(ns)
           },
         )?;
 
         (update, pri_for_all.id)
       },
-      Err(GameError::PieceGone) => {
+      Err(PieceOpError::Gone) => {
         (PieceUpdateOp::Delete(), lens.pieceid2visible(piece))
       }
       Err(e) => {
@@ -265,7 +265,8 @@ impl<'r> PrepareUpdatesBuffer<'r> {
       .unwrap_or_else(|e| {
         eprintln!("piece update error! piece={:?} lens={:?} error={:?}",
                   piece, &lens, &e);
-        PreparedUpdateEntry::Error(ErrorSignaledViaUpdate::RenderingError)
+        PreparedUpdateEntry::Error(None,
+                                   ErrorSignaledViaUpdate::InternalError)
       });
     self.us.push(update);
   }
@@ -323,7 +324,8 @@ impl PreparedUpdate {
         &PreparedUpdateEntry::SetTableSize(size) => {
           TransmitUpdateEntry::SetTableSize(size)
         },
-        &PreparedUpdateEntry::Error(e) => {
+        &PreparedUpdateEntry::Error(c, e) => {
+          if let Some(c) = c { if c != dest { continue } }
           TransmitUpdateEntry::Error(e)
         }
       };