}
}
+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,
])
}
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,
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;
svg : p.make_defs(pri)?,
z : self.zlevel.z,
zg : self.zlevel.zg,
+ uos : p.ui_operations()?,
}
}
}
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 {
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 ==========
}
for gref in expire.drain(..) {
struct Now(HashSet<ClientId>);
- enum Impossible { }
impl ClientIterator for Now {
type Ret = Impossible;
fn old(&mut self, client: ClientId)
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;
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 }
}
#[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) {
define_index_type! {
#[derive(Default)]
pub struct FaceId = RawFaceId;
+ IMPL_RAW_CONVERSIONS = true;
}
#[derive(Serialize,Deserialize)]
pub held : Option<PlayerId>,
pub z : ZCoord,
pub zg : Generation,
+ pub uos: Vec<UoDescription>,
}
// ---------- piece updates ----------