From: Ian Jackson Date: Sun, 9 May 2021 14:49:53 +0000 (+0100) Subject: bundles: Rework loading (wip) X-Git-Tag: otter-0.6.0~367 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=0f29991e33e4bb746580a500344a23c4569a9636;p=otter.git bundles: Rework loading (wip) Signed-off-by: Ian Jackson --- diff --git a/src/bundles.rs b/src/bundles.rs index 5bab1004..35adbd5f 100644 --- a/src/bundles.rs +++ b/src/bundles.rs @@ -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 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 ZipArchive 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 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), }; } diff --git a/src/global.rs b/src/global.rs index 415a01ad..c24ac09e 100644 --- a/src/global.rs +++ b/src/global.rs @@ -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 {