chiark / gitweb /
sip multilib
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 23 Nov 2020 01:41:26 +0000 (01:41 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 23 Nov 2020 01:41:26 +0000 (01:41 +0000)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/cmdlistener.rs
src/gamestate.rs
src/pieces.rs
src/shapelib.rs
src/spec.rs

index 35a956dca7adea9428264276f29c0d589aa66173..20acfe2f4ff876a19c579619162fad3b3e3cb8e2 100644 (file)
@@ -16,6 +16,7 @@ use pwd::Passwd;
 pub use crate::from_instance_lock_error;
 pub use std::os::unix::net::UnixStream;
 
+type SE = SpecError;
 type CSE = anyhow::Error;
 
 use MgmtCommand::*;
@@ -483,15 +484,31 @@ fn execute_game_insn<'cs, 'igr, 'ig : 'igr>(
       let (ig_g, modperm, _) = cs.check_acl_modify_pieces(ag, ig)?;
       let ig = &mut **ig_g;
       let gs = &mut ig.gs;
-      let count = count.unwrap_or(1);
-      if count > CREATE_PIECES_MAX { throw!(ME::LimitExceeded) }
+      let implicit : u32 = info.count()
+        .try_into().map_err(
+          |_| SE::InternalError(format!("implicit item count out of range"))
+        )?;
+      let count : Box<dyn ExactSizeIterator<Item=u32>> = match count {
+        Some(explicit) if implicit == 1 => {
+          Box::new((0..explicit).map(|_| 0))
+        },
+        Some(explicit) if implicit != explicit => {
+          throw!(SpecError::InconsistentPieceCount)
+        },
+        None | Some(_) => {
+          Box::new(0..implicit)
+        },
+      };
+
+      let count_len = count.len();
+      if count.len() > CREATE_PIECES_MAX as usize { throw!(ME::LimitExceeded) }
       let posd = posd.unwrap_or(DEFAULT_POS_DELTA);
 
-      let mut updates = Vec::with_capacity(count as usize);
+      let mut updates = Vec::with_capacity(count_len);
       let mut pos = pos.unwrap_or(DEFAULT_POS_START);
       let mut z = gs.max_z.clone_mut();
-      for _ in 0..count {
-        let p = info.load()?;
+      for piece_i in count {
+        let p = info.load(piece_i as usize)?;
         let face = face.unwrap_or_default();
         if p.nfaces() <= face.into() {
           throw!(SpecError::FaceNotFound);
@@ -517,7 +534,7 @@ fn execute_game_insn<'cs, 'igr, 'ig : 'igr>(
 
       (U{ pcs: updates,
           log: vec![ LogEntry {
-            html: Html(format!("{} added {} pieces", &who.0, count)),
+            html: Html(format!("{} added {} pieces", &who.0, count_len)),
           }],
           raw: None },
        Fine, ig_g)
index 65848b8be04f93cc99e8c4f9e32fb3373d18f493..b9395a4b00200ff749dd228b68dd7f75d0df257e 100644 (file)
@@ -141,7 +141,8 @@ pub struct PieceRenderInstructions {
 
 #[typetag::serde(tag="type")]
 pub trait PieceSpec : Debug {
-  fn load(&self) -> Result<Box<dyn Piece>,SpecError>;
+  fn count(&self) -> usize { 1 }
+  fn load(&self, i: usize) -> Result<Box<dyn Piece>,SpecError>;
 }
 
 // ========== implementations ==========
index ec97620f06bac426d7429eb021fad66776cac97c..fdf4323008715f966ec1c6c6d18648f60b0d0196 100644 (file)
@@ -157,7 +157,7 @@ impl SimpleShape {
 #[typetag::serde]
 impl PieceSpec for piece_specs::Disc {
   #[throws(SpecError)]
-  fn load(&self) -> Box<dyn Piece> {
+  fn load(&self, _: usize) -> Box<dyn Piece> {
     let outline = Box::new(shapelib::Circle { diam: self.diam as f64 });
     let path = svg_circle_path(self.diam as f64)?;
     let itemname = self.itemname.clone()
@@ -170,7 +170,7 @@ impl PieceSpec for piece_specs::Disc {
 #[typetag::serde]
 impl PieceSpec for piece_specs::Square {
   #[throws(SpecError)]
-  fn load(&self) -> Box<dyn Piece> {
+  fn load(&self, _: usize) -> Box<dyn Piece> {
     let xy = match *self.size.as_slice() {
       [s,]  => PosC([s,s]),
       [x,y] => PosC([x,y]),
index 0daae466f80930b924ce3283c4a650398a9416e0..d15ad41d119ee48d3504aff584610a252fd7b358 100644 (file)
@@ -122,6 +122,12 @@ pub struct ItemSpec {
   pub item: String,
 }
 
+#[derive(Debug,Clone,Serialize,Deserialize)]
+pub struct MultiSpec {
+  pub lib: String,
+  pub items: Vec<String>,
+}
+
 define_index_type!{ pub struct DescId = u8; }
 define_index_type!{ pub struct SvgId = u8; }
 
@@ -271,11 +277,23 @@ impl Contents {
 
 #[typetag::serde(name="Lib")]
 impl PieceSpec for ItemSpec {
-  fn load(&self) -> Result<Box<dyn Piece>,SpecError> {
+  fn load(&self, _: usize) -> Result<Box<dyn Piece>,SpecError> {
     self.load()
   }
 }
 
+#[typetag::serde(name="LibList")]
+impl PieceSpec for MultiSpec {
+  fn count(&self) -> usize { self.items.len() }
+  fn load(&self, i: usize) -> Result<Box<dyn Piece>,SpecError> {
+    let item = self.items.get(i).ok_or_else(
+      || SE::InternalError(format!("item {:?} from {:?}", i, &self))
+    )?.clone();
+    let lib = self.lib.clone();
+    ItemSpec { lib, item }.load()
+  }
+}
+
 #[throws(LibraryLoadError)]
 fn resolve_inherit<'r>(depth: u8, groups: &toml::value::Table,
                        group_name: &str, group: &'r toml::Value)
index 212be8d910083b010e9bbab67e1d84c668be43f8..98e2a5e0efc54fa6d97f4e6acc3f1782f97a35fc 100644 (file)
@@ -57,6 +57,7 @@ pub enum SpecError {
   LibraryItemNotFound,
   AclInvalidAccountGlob,
   AclEntryOverlappingAllowDeny,
+  InconsistentPieceCount,
 }
 display_as_debug!{SpecError}