diff --git a/crates/engine/src/cmd/file.rs b/crates/engine/src/cmd/file.rs index 545ead2..2fc0087 100644 --- a/crates/engine/src/cmd/file.rs +++ b/crates/engine/src/cmd/file.rs @@ -89,6 +89,12 @@ fn open_sample(s: &mut Session, p: &Value) -> Result { Ok(reply) } +/// A file of at most `max` bytes, through FieldCraft's file edge ([`fieldcraft_io::fs`]: the disk, +/// or the page's files in the browser). +pub(crate) fn read_file(path: &str, max: u64) -> Result> { + Ok(fieldcraft_io::fs::read_capped(Path::new(path), max)?) +} + /// `path` made absolute against the working folder (as given where there is none). pub(crate) fn absolute(path: &str) -> String { std::path::absolute(path).map(|p| p.to_string_lossy().into_owned()).unwrap_or_else(|_| path.to_string()) @@ -119,7 +125,7 @@ fn reopen( stored: &SourceFile, base: Option<&Path>, roots: &Roots, - max: usize, + max: u64, open: impl FnOnce(&[u8]) -> fieldcraft_geometry::Result, warnings: &mut Vec, ) -> (Option, SourceFile) { @@ -132,7 +138,7 @@ fn reopen( return (None, kept); } }; - let bytes = match fieldcraft_geometry::read_file(&checked, max) { + let bytes = match read_file(&checked, max) { Ok(b) => b, Err(e) => { warnings.push(format!("{what} `{path}` was not opened ({e}); the model opens without it")); @@ -173,13 +179,13 @@ impl Session { let mut sources = Geometry::default(); let mut design: Option> = None; if let Some(stored) = &file.geometry.design { - let (opened, source) = reopen("design", stored, base, roots, MAX_DESIGN_BYTES, open_design, &mut warnings); + let (opened, source) = reopen("design", stored, base, roots, MAX_DESIGN_BYTES as u64, open_design, &mut warnings); design = opened.map(Arc::new); sources.design = Some(source); } let mut surfaces = Vec::new(); for stored in &file.geometry.meshes { - let (opened, source) = reopen("mesh", stored, base, roots, tagged_mesh::MAX_FILE_BYTES, tagged_mesh::read_3mf, &mut warnings); + let (opened, source) = reopen("mesh", stored, base, roots, tagged_mesh::MAX_FILE_BYTES as u64, tagged_mesh::read_3mf, &mut warnings); surfaces.extend(opened.into_iter().flatten().map(Arc::new)); sources.meshes.push(source); } @@ -226,8 +232,7 @@ fn load(s: &mut Session, bytes: &[u8], path: Option, saved: bool) -> Res fn open(s: &mut Session, p: &Value) -> Result { let path = absolute(&file_param(s, p, "path", Access::Read)?); - let max = usize::try_from(fieldcraft_io::MAX_FILE_BYTES).unwrap_or(usize::MAX); - let bytes = fieldcraft_geometry::read_file(&path, max)?; + let bytes = read_file(&path, fieldcraft_io::MAX_FILE_BYTES)?; load(s, &bytes, Some(path.clone()), true).map_err(|e| EngineError::Params(format!("{path}: {e}"))) } diff --git a/crates/engine/src/cmd/geometry.rs b/crates/engine/src/cmd/geometry.rs index 9ee9b76..8447df7 100644 --- a/crates/engine/src/cmd/geometry.rs +++ b/crates/engine/src/cmd/geometry.rs @@ -42,7 +42,7 @@ pub const IMPORT_MESH: CommandSpec = CommandSpec { fn open(s: &mut Session, p: &Value) -> Result { let path = super::file_param(s, p, "path", Access::Read)?; - let bytes = fieldcraft_geometry::read_file(&path, MAX_DESIGN_BYTES)?; + let bytes = super::file::read_file(&path, MAX_DESIGN_BYTES as u64)?; let source = SourceFile::of(super::file::absolute(&path), &bytes)?; let design = open_design(&bytes)?; let out = json!({ @@ -101,7 +101,7 @@ fn import_mesh(s: &mut Session, p: &Value) -> Result { if s.sources.meshes.len() >= fieldcraft_io::MAX_MESH_SOURCES { return Err(EngineError::Params(format!("a model imports at most {} meshes", fieldcraft_io::MAX_MESH_SOURCES))); } - let bytes = fieldcraft_geometry::read_file(&path, tagged_mesh::MAX_FILE_BYTES)?; + let bytes = super::file::read_file(&path, tagged_mesh::MAX_FILE_BYTES as u64)?; let source = SourceFile::of(super::file::absolute(&path), &bytes)?; let surfaces = tagged_mesh::read_3mf(&bytes)?; let first = s.surfaces.len(); diff --git a/crates/geometry/src/design.rs b/crates/geometry/src/design.rs index 6d975f2..9ffe3e2 100644 --- a/crates/geometry/src/design.rs +++ b/crates/geometry/src/design.rs @@ -2,9 +2,8 @@ //! with SolidCraft, and hand back its bodies with persistent face names. //! //! The design is rebuilt from its authoritative inputs (sketches, features, parameters); cached -//! display meshes in the file are never used. [`open_design`] takes bytes; [`open_design_file`] -//! reads a path through SolidCraft's file access, which in the browser is the in-memory files the -//! web host loaded. +//! display meshes in the file are never used. [`open_design`] takes the file's bytes (reading +//! files is `fieldcraft_io::fs`'s job, which in the browser holds the page's files). use solidcraft_doc::{Document, Model, naming}; use solidcraft_kernel::{KernelError, guard}; @@ -45,11 +44,6 @@ pub fn open_design(bytes: &[u8]) -> Result { rebuild(&doc) } -/// Read a design file (a regular file of at most [`MAX_DESIGN_BYTES`]) and rebuild it. -pub fn open_design_file(path: &str) -> Result { - open_design(&crate::read_file(path, MAX_DESIGN_BYTES)?) -} - /// Rebuild an in-memory document (the design half of [`open_design`]). pub(crate) fn rebuild(doc: &Document) -> Result { let model = guard("rebuild design", || { @@ -165,8 +159,5 @@ mod tests { assert!(open_design(bytes).is_err(), "{bytes:?}"); } assert!(open_design(&vec![b' '; MAX_DESIGN_BYTES + 1]).is_err()); - for path in ["", "/", "/dev/zero", "/no/such/design.solidcraft"] { - assert!(open_design_file(path).is_err(), "{path}"); - } } } diff --git a/crates/geometry/src/lib.rs b/crates/geometry/src/lib.rs index bca9fc4..4aec077 100644 --- a/crates/geometry/src/lib.rs +++ b/crates/geometry/src/lib.rs @@ -22,7 +22,7 @@ pub mod sample; #[cfg(feature = "scripts")] mod script; -pub use design::{Design, FeatureError, MAX_DESIGN_BYTES, open_design, open_design_file}; +pub use design::{Design, FeatureError, MAX_DESIGN_BYTES, open_design}; pub mod tagged_mesh; #[cfg(feature = "scripts")] pub use script::{MAX_SCRIPT_BYTES, script_design}; @@ -47,14 +47,6 @@ impl From for Error { } } -/// Read a regular file of at most `max_bytes` through SolidCraft's file access (in the browser, -/// the in-memory files the web host loaded). Opening a design or a 3MF package from these bytes -/// ([`open_design`], [`tagged_mesh::read_3mf`]) lets the caller record a hash of exactly what it -/// opened. -pub fn read_file(path: &str, max_bytes: usize) -> Result> { - solidcraft_io::vfs::read(path, max_bytes).map_err(|e| Error::Invalid(format!("cannot read `{path}`: {e}"))) -} - /// SolidCraft's linear modelling tolerance (mm): points closer than this are the same point. const LINEAR_TOLERANCE: f64 = solidcraft_geom::LINEAR_TOL; diff --git a/crates/geometry/src/tagged_mesh.rs b/crates/geometry/src/tagged_mesh.rs index 53b87ca..66e7a8f 100644 --- a/crates/geometry/src/tagged_mesh.rs +++ b/crates/geometry/src/tagged_mesh.rs @@ -344,12 +344,6 @@ fn surface(mut o: Object, k: usize, id: u32, xf: &Xf, unit: f64) -> Result Result> { - read_3mf(&crate::read_file(path, MAX_FILE_BYTES)?) -} - /// The surfaces of a 3MF package, one per build item, in millimetres. pub fn read_3mf(bytes: &[u8]) -> Result> { if bytes.len() > MAX_FILE_BYTES {