chiark / gitweb /
bundles: Rework loading (wip)
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 9 May 2021 14:49:53 +0000 (15:49 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 9 May 2021 14:50:27 +0000 (15:50 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/bundles.rs
src/global.rs

index 5bab100400d4431871b9ceccf93a2374d0f3b7db..35adbd5f8b56c81092de19fe15ea93e0d6e6e865 100644 (file)
@@ -38,7 +38,6 @@ pub struct InstanceBundles {
 #[derive(Debug,Clone,Serialize,Deserialize)]
 pub enum State {
   Uploading,
-  BadBundle(BadBundle),
   Loaded(Loaded),
 }
 
@@ -68,6 +67,11 @@ display_as_debug!{LoadError}
 
 //---------- private definitions ----------
 
+#[derive(Debug,Clone,Serialize,Deserialize)]
+struct Parsed {
+  pub meta: BundleMeta,
+}
+
 const BUNDLES_MAX: Index = Index(64);
 
 #[derive(Debug,Clone,Serialize,Deserialize)]
@@ -78,12 +82,6 @@ struct Note {
 
 pub type BadBundle = String;
 
-#[derive(Error,Debug)]
-enum IncorporateError {
-  #[error("NotBundle({0})")] NotBundle(#[from] NotBundle),
-  #[error("{0}")] IE(#[from] IE),
-}
-
 use LoadError as LE;
 
 //---------- straightformward impls ----------
@@ -115,6 +113,13 @@ impl From<&'static str> for NotBundle {
   }
 }
 
+impl From<LoadError> for MgmtError {
+  fn from(le: LoadError) -> MgmtError { match le {
+    LE::BadBundle(why) => ME::BadBundle(why),
+    LE::IE(ie) => ME::from(ie),
+  } }
+}
+
 //---------- pathname handling (including Id leafname) ----------
 
 pub fn b_dir(instance: &InstanceName) -> String {
@@ -266,65 +271,60 @@ impl<R> ZipArchive<R> where R: Read + io::Seek {
   }
 }
 
-#[throws(IE)]
-fn load_bundle(ib: &mut InstanceBundles, ig: &mut Instance,
-               id: Id, path: &str) {
+pub trait BufReadSeek: BufRead + io::Seek { }
+impl<T> BufReadSeek for T where T: BufRead + io::Seek { }
+
+#[throws(LoadError)]
+fn parse_bundle(id: Id, file: &mut dyn BufReadSeek) -> Parsed {
+  match id.kind { Kind::Zip => () }
+  let mut za = ZipArchive::new(file)?;
+
+  let meta = {
+    let mut mf = za.by_name_caseless("otter.toml")?;
+    let mut meta = String::new();
+    mf.read_to_string(&mut meta).map_err(
+      |e| LE::BadBundle(format!("access toml zip member: {}", e)))?;
+    let meta = meta.parse().map_err(
+      |e| LE::BadBundle(format!("parse zip member as toml: {}", e)))?;
+    let meta = toml_de::from_value(&meta).map_err(
+      |e| LE::BadBundle(format!("interpret zip member metadata: {}", e)))?;
+    meta
+  };
+
+  // todo: do actual things, eg libraries and specs
+
+  Parsed { meta }
+}
+
+//---------- scanning/incorporating/uploading ----------
+
+#[throws(LoadError)]
+fn load_bundle(ib: &mut InstanceBundles, _ig: &mut Instance,
+               id: Id, fpath: &str) {
+  let file = File::open(fpath)
+    .with_context(|| fpath.to_owned()).context("open zipfile")
+    .map_err(IE::from)?;
+  let mut file = BufReader::new(file);
+
+  let Parsed { meta } = parse_bundle(id, &mut file)?;
+  
   let iu: usize = id.index.into();
 
-  match ib.bundles.get(iu) {
-    None => ib.bundles.resize_with(iu+1, default),
-    Some(None) => { },
-    Some(Some(Note { kind:_, state: State::Uploading })) => { },
-    Some(Some(ref note)) => throw!(IE::DuplicateBundle {
+  if ib.bundles.get(iu).is_none() { ib.bundles.resize_with(iu+1, default); }
+  let slot = &mut ib.bundles[iu];
+  match slot {
+    None => { },
+    Some(Note { kind:_, state: State::Uploading }) => { },
+    Some(ref note) => throw!(IE::DuplicateBundle {
       index: id.index,
       kinds: [note.kind, id.kind],
     })
   };
-  let slot = &mut ib.bundles[iu];
-
-  #[throws(LoadError)]
-  fn inner(_ig: &mut Instance, id: Id, path: &str) -> Loaded {
-    match id.kind { Kind::Zip => () }
-    let za = File::open(path)
-      .with_context(|| path.to_owned()).context("open zipfile")
-      .map_err(IE::from)?;
-    let mut za = ZipArchive::new(za)?;
-
-    let meta = {
-      let mut mf = za.by_name_caseless("otter.toml")?;
-      let mut meta = String::new();
-      mf.read_to_string(&mut meta).map_err(
-        |e| LE::BadBundle(format!("access toml zip member: {}", e)))?;
-      let meta = meta.parse().map_err(
-        |e| LE::BadBundle(format!("parse zip member as toml: {}", e)))?;
-      let meta = toml_de::from_value(&meta).map_err(
-        |e| LE::BadBundle(format!("interpret zip member metadata: {}", e)))?;
-      meta
-    };
-
-    Loaded { meta }
-    // todo: do actual things, eg libraries and specs
-  }
-
-  let state = match inner(ig,id,path) {
-    Ok(loaded)                     => State::Loaded(loaded),
-    Err(LoadError::BadBundle(bad)) => State::BadBundle(bad),
-    Err(LoadError::IE(ie))         => throw!(ie),
-  };
 
+  let state = State::Loaded(Loaded { meta });
   *slot = Some(Note { kind: id.kind, state });
 }
 
-//---------- scanning/incorporating/uploading ----------
-
-#[throws(IncorporateError)]
-fn incorporate_bundle(ib: &mut InstanceBundles, ig: &mut Instance,
-                      fpath: &str) {
-  let fleaf = fpath.rsplitn(2, '/').next().unwrap();
-  let id = fleaf.parse()?;
-  load_bundle(ib, ig, id, fpath)?;
-}
-
 impl InstanceBundles {
   pub fn new() -> Self { InstanceBundles{ bundles: default() } }
 
@@ -354,7 +354,7 @@ impl InstanceBundles {
   }
 
   #[throws(IE)]
-  pub fn load_game_bundles(ig: &mut Instance) -> Self {
+  pub fn reload_game_bundles(ig: &mut Instance) -> Self {
     let bd = b_dir(&ig.name);
     let mo = glob::MatchOptions {
       require_literal_leading_dot: true,
@@ -368,12 +368,24 @@ impl InstanceBundles {
       let fpath = fpath.context("bundle glob")?;
       let fpath = fpath
         .to_str().ok_or_else(|| anyhow!("glob unicode conversion"))?;
-      match incorporate_bundle(&mut ib, ig, fpath) {
-        Ok(()) => { },
-        Err(IncorporateError::NotBundle(why)) => {
+
+      let fleaf = fpath.rsplitn(2, '/').next().unwrap();
+      let id: Id = match fleaf.parse() {
+        Ok(y) => y,
+        Err(NotBundle(why)) => {
           debug!("bundle file {:?} skippping {}", &fpath, why);
+          // xxx delete?
+          continue;
+        },
+      };
+
+      match load_bundle(&mut ib, ig, id, fpath) {
+        Ok(()) => { },
+        Err(LE::BadBundle(why)) => {
+          debug!("bundle file {:?} bad {}", &fpath, why);
+          continue;
         }
-        Err(IncorporateError::IE(ie)) => throw!(ie),
+        Err(LE::IE(ie)) => throw!(ie),
       }
     }
     debug!("loaded bundles {} {:?}", &ig.name, ib);
@@ -438,6 +450,8 @@ impl InstanceBundles {
     file.flush()
       .with_context(|| tmp.clone()).context("flush").map_err(IE::from)?;
     if hash.as_slice() != &expected.0[..] { throw!(ME::UploadCorrupted) }
+    file.rewind().context("rewind"). map_err(IE::from)?;
+
     load_bundle(self, ig, id, &tmp)?;
     self.updated(ig);
     match self.bundles.get(usize::from(id.index)) {
@@ -446,9 +460,6 @@ impl InstanceBundles {
           .with_context(||install.clone())
           .context("install").map_err(IE::from)?;
       }
-      Some(Some(Note { state: State::BadBundle(ref bad), .. })) => {
-        throw!(ME::BadBundle(bad.clone()))
-      }
       ref x => panic!("unexpected {:?}", x),
     };
   }
index 415a01ada84305675eed9347daa8db5a8acb6348..c24ac09e85e4a24396bf10b6e6a820ab04f13ceb 100644 (file)
@@ -1166,7 +1166,7 @@ impl InstanceGuard<'_> {
       asset_url_key,
     };
 
-    let b = InstanceBundles::load_game_bundles(&mut g)?;
+    let b = InstanceBundles::reload_game_bundles(&mut g)?;
     let b = Mutex::new(b);
 
     let c = InstanceContainer {