diff --git a/src/lib.rs b/src/lib.rs index 12463b0..a1832c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,7 @@ pub type IOBuffer = buffer::DataBuffer; #[derive(Debug)] pub enum Error { IO(io::Error), + Write(writer::Error), Parse(nom::ErrorKind), Unknown, } @@ -46,6 +47,13 @@ impl From for io::Error { } } +impl From for Error { + fn from(e: writer::Error) -> Error { + Error::Write(e) + } +} + + /// Helper function that implements the actual importing routine. fn import_impl(file_path: &Path, parse: F) -> Result where @@ -121,7 +129,7 @@ pub fn export(data: model::Vtk, file_path: &Path) -> Result<(), Error> { use writer::WriteVtk; let mut file = ::std::fs::File::create(file_path)?; - file.write_all(Vec::::new().write_vtk(data).as_slice())?; + file.write_all(Vec::::new().write_vtk(data)?.as_slice())?; Ok(()) } @@ -131,7 +139,7 @@ pub fn export_le(data: model::Vtk, file_path: &Path) -> Result<(), Error> { use writer::WriteVtk; let mut file = ::std::fs::File::create(file_path)?; - file.write_all(Vec::::new().write_vtk_le(data).as_slice())?; + file.write_all(Vec::::new().write_vtk_le(data)?.as_slice())?; Ok(()) } @@ -141,7 +149,7 @@ pub fn export_be(data: model::Vtk, file_path: &Path) -> Result<(), Error> { use writer::WriteVtk; let mut file = ::std::fs::File::create(file_path)?; - file.write_all(Vec::::new().write_vtk_be(data).as_slice())?; + file.write_all(Vec::::new().write_vtk_be(data)?.as_slice())?; Ok(()) } @@ -172,7 +180,7 @@ pub fn export_ascii(data: model::Vtk, file_path: &Path) -> Result<(), Error> { use writer::WriteVtk; let mut out_str = String::new(); - out_str.write_vtk(data); + out_str.write_vtk(data)?; { let mut file = File::create(file_path)?; file.write_all(out_str.as_bytes())?; diff --git a/src/writer.rs b/src/writer.rs index d5e5342..8289326 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -8,38 +8,142 @@ mod write_vtk_impl { use byteorder::WriteBytesExt; use num_traits::ToPrimitive; - #[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Error; + pub mod error { + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub enum EntryPart { + /// The part of a header with just tags. + Tags, + /// The part of the header with corresponding size info. + Sizes, + /// Tags and sizes together. + Header, + /// The actually data for the entry (this can be binary or ASCII). + /// If applicable, this enum will report any IO errors when writing data. + Data(Option), + /// Lookup table name. Only relevant for Scalars. + LookupTable, + } + + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub enum AttributeError { + Scalars(EntryPart), + ColorScalars(EntryPart), + LookupTable(EntryPart), + Vectors(EntryPart), + Normals(EntryPart), + TextureCoordinates(EntryPart), + Tensors(EntryPart), + Field(EntryPart), + FieldArray(EntryPart), + } + + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub enum Header { + Version, + Title, + /// Binary or ASCII. + FileType, + } + + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub enum DataSetPart { + /// Tags identifying the data set type. For example UNSTRUCTURED_GRID or POLY_DATA. + Tags, + Points(EntryPart), + Cells(EntryPart), + CellTypes(EntryPart), + Dimensions, + Origin, + Spacing(EntryPart), + XCoordinates(EntryPart), + YCoordinates(EntryPart), + ZCoordinates(EntryPart), + } + + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub enum DataSetError { + FieldDataHeader, + FieldArray(EntryPart), + + PolyData(DataSetPart), + UnstructuredGrid(DataSetPart), + StructuredGrid(DataSetPart), + StructuredPoints(DataSetPart), + RectilinearGrid(DataSetPart), + } + + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub enum Error { + PointDataHeader, + CellDataHeader, + Attribute(AttributeError), + + Header(Header), + DataSet(DataSetError), + NewLine, + + /// Generic formatting error originating from [`std::fmt::Error`]. + FormatError, + /// Generic IO error originating from [`std::io::Error`]. + IOError(std::io::ErrorKind), + } + + /// Extract a raw IO Error from our error if any. This helps annotate the IO error with + /// where it originated from when reported from lower level functions. + impl Into> for Error { + fn into(self) -> Option { + match self { + Error::IOError(err) => Some(err), + _ => None, + } + } + } + + impl From for Error { + fn from(_: std::fmt::Error) -> Error { + Error::FormatError + } + } + + impl From for Error { + fn from(err: std::io::Error) -> Error { + Error::IOError(err.kind()) + } + } + } + + use self::error::*; + pub use self::error::Error; + + /// A typical result of a write operation. + type Result = std::result::Result<(), Error>; pub trait WriteVtkImpl { /// This function is called by the `write!` macro used throughout this module. /// Each writer needs to call the appropriate `write_fmt` in the implementation /// of this method. - fn write_fmt(&mut self, args: Arguments) -> Result<(), Error>; - fn write_file_type(&mut self, err: &str); - fn write_cell_types(&mut self, data: Vec, err: &str); - fn write_u32_vec(&mut self, data: Vec, err: &str); - fn write_buf(&mut self, data: IOBuffer, err: &str); + fn write_fmt(&mut self, args: Arguments) -> Result; + fn write_file_type(&mut self) -> Result; + fn write_cell_types(&mut self, data: Vec) -> Result; + fn write_u32_vec(&mut self, data: Vec) -> Result; + fn write_buf(&mut self, data: IOBuffer) -> Result; fn write_attrib( &mut self, data: Attributes, num_points: usize, num_cells: usize, - ) { - write!(self, "\nPOINT_DATA {}\n", num_points) - .expect("Error writing point data header."); - self.write_attrib_data::(data.point); - - write!(self, "\nCELL_DATA {}\n", num_cells) - .expect("Error writing cell data header."); - self.write_attrib_data::(data.cell); + ) -> Result { + write!(self, "\nPOINT_DATA {}\n", num_points).map_err(|_| Error::PointDataHeader)?; + self.write_attrib_data::(data.point)?; + write!(self, "\nCELL_DATA {}\n", num_cells).map_err(|_| Error::CellDataHeader)?; + self.write_attrib_data::(data.cell) } - fn write_attrib_data(&mut self, data: Vec<(String, Attribute)>) { + fn write_attrib_data(&mut self, data: Vec<(String, Attribute)>) -> Result { for (name, attrib) in data { - write!(self, "\n"); + write!(self, "\n").map_err(|_| Error::NewLine)?; match attrib { Attribute::Scalars { num_comp, @@ -52,23 +156,23 @@ mod write_vtk_impl { name, DataType::from(data.element_type_id()), num_comp - ).expect("Error writing a Scalars attribute header."); + ).map_err(|_| Error::Attribute(AttributeError::Scalars(EntryPart::Header)))?; write!( self, "LOOKUP_TABLE {}\n", lookup_table.unwrap_or(String::from("default")) - ).expect("Error writing a lookup table name for Scalars data."); - self.write_buf::(data, "Error writing Scalars attribute data."); + ).map_err(|_| Error::Attribute(AttributeError::Scalars(EntryPart::LookupTable)))?; + self.write_buf::(data).map_err(|e| Error::Attribute(AttributeError::Scalars(EntryPart::Data(e.into()))))?; } Attribute::ColorScalars { num_comp, data } => { write!(self, "COLOR_SCALARS {} {}\n", name, num_comp) - .expect("Error writing a ColorScalars attribute header."); - self.write_buf::(data, "Error writing ColorScalars attribute data."); + .map_err(|_| Error::Attribute(AttributeError::ColorScalars(EntryPart::Header)))?; + self.write_buf::(data).map_err(|e| Error::Attribute(AttributeError::ColorScalars(EntryPart::Data(e.into()))))?; } Attribute::LookupTable { data } => { write!(self, "LOOKUP_TABLE {} {}\n", name, data.len() / 4) - .expect("Error writing a LookupTable attribute header."); - self.write_buf::(data, "Error writing LookupTable attribute data."); + .map_err(|_| Error::Attribute(AttributeError::LookupTable(EntryPart::Header)))?; + self.write_buf::(data).map_err(|e| Error::Attribute(AttributeError::LookupTable(EntryPart::Data(e.into()))))?; } Attribute::Vectors { data } => { write!( @@ -76,8 +180,8 @@ mod write_vtk_impl { "VECTORS {} {}\n", name, DataType::from(data.element_type_id()) - ).expect("Error writing a Vectors attribute header."); - self.write_buf::(data, "Error writing Vectors attribute data."); + ).map_err(|_| Error::Attribute(AttributeError::Vectors(EntryPart::Header)))?; + self.write_buf::(data).map_err(|e| Error::Attribute(AttributeError::Vectors(EntryPart::Data(e.into()))))?; } Attribute::Normals { data } => { write!( @@ -85,8 +189,8 @@ mod write_vtk_impl { "NORMALS {} {}\n", name, DataType::from(data.element_type_id()) - ).expect("Error writing a Normals attribute header."); - self.write_buf::(data, "Error writing Normals attribute data."); + ).map_err(|_| Error::Attribute(AttributeError::Normals(EntryPart::Header)))?; + self.write_buf::(data).map_err(|e| Error::Attribute(AttributeError::Normals(EntryPart::Data(e.into()))))?; } Attribute::TextureCoordinates { dim, data } => { write!( @@ -95,11 +199,8 @@ mod write_vtk_impl { name, dim, DataType::from(data.element_type_id()) - ).expect("Error writing a TextureCoordinates attribute header."); - self.write_buf::( - data, - "Error writing TextureCoordinates attribute data.", - ); + ).map_err(|_| Error::Attribute(AttributeError::TextureCoordinates(EntryPart::Header)))?; + self.write_buf::(data).map_err(|e| Error::Attribute(AttributeError::TextureCoordinates(EntryPart::Data(e.into()))))?; } Attribute::Tensors { data } => { write!( @@ -107,12 +208,12 @@ mod write_vtk_impl { "TENSORS {} {}\n", name, DataType::from(data.element_type_id()) - ).expect("Error writing a Tensors attribute header."); - self.write_buf::(data, "Error writing Tensors attribute data."); + ).map_err(|_| Error::Attribute(AttributeError::Tensors(EntryPart::Header)))?; + self.write_buf::(data).map_err(|e| Error::Attribute(AttributeError::Tensors(EntryPart::Data(e.into()))))?; } Attribute::Field { data_array } => { write!(self, "FIELD {} {}\n", name, data_array.len()) - .expect("Error writing a Field attribute header."); + .map_err(|_| Error::Attribute(AttributeError::Field(EntryPart::Header)))?; for field in data_array { write!( self, @@ -121,25 +222,23 @@ mod write_vtk_impl { field.num_comp, field.data.len() / field.num_comp as usize, DataType::from(field.data.element_type_id()) - ).expect("Error writing Field attribute array header."); - self.write_buf::( - field.data, - "Error writing Field attribute array data.", - ); + ).map_err(|_| Error::Attribute(AttributeError::FieldArray(EntryPart::Header)))?; + self.write_buf::(field.data).map_err(|e| Error::Attribute(AttributeError::FieldArray(EntryPart::Data(e.into()))))?; } } } } + Ok(()) } - fn write_vtk_impl(&mut self, vtk: Vtk) -> &mut Self { + fn write_vtk_impl(&mut self, vtk: Vtk) -> std::result::Result<&mut Self, Error> { write!(self, "# vtk DataFile Version {}\n", vtk.version) - .expect("Error writing VTK Header."); - write!(self, "{}\n", vtk.title).expect("Error writing VTK Title."); - self.write_file_type("Error writing File Type."); + .map_err(|_| Error::Header(Header::Version))?; + write!(self, "{}\n", vtk.title).map_err(|_| Error::Header(Header::Version))?; + self.write_file_type()?; match vtk.data { DataSet::Field { name, data_array } => { write!(self, "FIELD {} {}\n", name, data_array.len()) - .expect("Error writing Field data header."); + .map_err(|_| Error::DataSet(DataSetError::FieldDataHeader))?; for field in data_array { write!( self, @@ -148,27 +247,25 @@ mod write_vtk_impl { field.num_comp, field.data.len() / field.num_comp as usize, DataType::from(field.data.element_type_id()) - ).expect("Error writing Field array data header."); - self.write_buf::( - field.data, - "Error writing a new line after binary data.", - ); + ).map_err(|_| Error::DataSet(DataSetError::FieldArray(EntryPart::Header)))?; + self.write_buf::(field.data) + .map_err(|e| Error::DataSet(DataSetError::FieldArray(EntryPart::Data(e.into()))))?; } } DataSet::PolyData { points, topo, data } => { - write!(self, "DATASET POLYDATA\n").expect("Error writing PolyData tags."); + write!(self, "DATASET POLYDATA\n").map_err(|_| Error::DataSet(DataSetError::PolyData(DataSetPart::Tags)))?; write!( self, "POINTS {} {}\n", points.len() / 3, DataType::from(points.element_type_id()) - ).expect("Error writing PolyData points."); + ).map_err(|_| Error::DataSet(DataSetError::PolyData(DataSetPart::Points(EntryPart::Header))))?; let num_points = points.len() / 3; - self.write_buf::(points, "Error writing PolyData points."); + self.write_buf::(points).map_err(|e| Error::DataSet(DataSetError::PolyData(DataSetPart::Points(EntryPart::Data(e.into())))))?; - write!(self, "\n"); + write!(self, "\n").map_err(|_| Error::NewLine)?; let mut num_cells = 0; for topo_type in topo { @@ -177,7 +274,8 @@ mod write_vtk_impl { PolyDataTopology::Lines(_) => write!(self, "LINES"), PolyDataTopology::Polygons(_) => write!(self, "POLYGONS"), PolyDataTopology::TriangleStrips(_) => write!(self, "TRIANGLE_STRIPS"), - }.expect("Error writing PolyData topology tag."); + } + .map_err(|_| Error::DataSet(DataSetError::PolyData(DataSetPart::Cells(EntryPart::Tags))))?; let cells = match topo_type { PolyDataTopology::Vertices(c) => c, @@ -187,17 +285,16 @@ mod write_vtk_impl { }; write!(self, " {} {}\n", cells.num_cells, cells.vertices.len()) - .expect("Error writing PolyData topology sizes."); + .map_err(|_| Error::DataSet(DataSetError::PolyData(DataSetPart::Cells(EntryPart::Sizes))))?; self.write_u32_vec::( - cells.vertices, - "Error writing PolyData topology data.", - ); + cells.vertices + ).map_err(|e| Error::DataSet(DataSetError::PolyData(DataSetPart::Cells(EntryPart::Data(e.into())))))?; num_cells += cells.num_cells as usize; } - self.write_attrib::(data, num_points, num_cells); + self.write_attrib::(data, num_points, num_cells)?; } DataSet::UnstructuredGrid { @@ -207,36 +304,35 @@ mod write_vtk_impl { data, } => { write!(self, "DATASET UNSTRUCTURED_GRID\n") - .expect("Error writing UnstructuredGrid tags."); + .map_err(|_| Error::DataSet(DataSetError::UnstructuredGrid(DataSetPart::Tags)))?; write!( self, "POINTS {} {}\n", points.len() / 3, DataType::from(points.element_type_id()) - ).expect("Error writing UnstructuredGrid points."); + ).map_err(|_| Error::DataSet(DataSetError::UnstructuredGrid(DataSetPart::Points(EntryPart::Header))))?; let num_points = points.len() / 3; - self.write_buf::(points, "Error writing UnstructuredGrid points."); + self.write_buf::(points) + .map_err(|e| Error::DataSet(DataSetError::UnstructuredGrid(DataSetPart::Points(EntryPart::Data(e.into())))))?; write!( self, "\nCELLS {} {}\n", cells.num_cells, cells.vertices.len() - ).expect("Error writing UnstructuredGrid cell sizes."); + ).map_err(|_| Error::DataSet(DataSetError::UnstructuredGrid(DataSetPart::Cells(EntryPart::Header))))?; self.write_u32_vec::( cells.vertices, - "Error writing UnstructuredGrid cell data.", - ); + ).map_err(|e| Error::DataSet(DataSetError::UnstructuredGrid(DataSetPart::Cells(EntryPart::Data(e.into())))))?; write!(self, "\nCELL_TYPES {}\n", cell_types.len()) - .expect("Error writing UnstructuredGrid cell types size."); + .map_err(|_| Error::DataSet(DataSetError::UnstructuredGrid(DataSetPart::CellTypes(EntryPart::Header))))?; - let err_cell_types = "Error writing UnstructuredGrid cell types."; - self.write_cell_types::(cell_types, err_cell_types); + self.write_cell_types::(cell_types)?; - self.write_attrib::(data, num_points, cells.num_cells as usize); + self.write_attrib::(data, num_points, cells.num_cells as usize)?; } DataSet::StructuredPoints { @@ -246,44 +342,45 @@ mod write_vtk_impl { data, } => { write!(self, "DATASET STRUCTURED_POINTS\n") - .expect("Error writing StructuredPoints tags."); + .map_err(|_| Error::DataSet(DataSetError::StructuredPoints(DataSetPart::Tags)))?; write!(self, "DIMENSIONS {} {} {}\n", dims[0], dims[1], dims[2]) - .expect("Error writing StructuredPoints dimensions."); + .map_err(|_| Error::DataSet(DataSetError::StructuredPoints(DataSetPart::Dimensions)))?; write!(self, "ORIGIN {} {} {}\n", origin[0], origin[1], origin[2]) - .expect("Error writing StructuredPoints origin."); + .map_err(|_| Error::DataSet(DataSetError::StructuredPoints(DataSetPart::Origin)))?; if vtk.version.major < 2 { write!(self, "ASPECT_RATIO") } else { write!(self, "SPACING") - }.expect("Error writing StructuredPoints spacing."); + }.map_err(|_| Error::DataSet(DataSetError::StructuredPoints(DataSetPart::Spacing(EntryPart::Tags))))?; write!(self, " {} {} {}\n", spacing[0], spacing[1], spacing[2]) - .expect("Error writing StructuredPoints spacing."); + .map_err(|_| Error::DataSet(DataSetError::StructuredPoints(DataSetPart::Spacing(EntryPart::Sizes))))?; let num_points = (dims[0] * dims[1] * dims[2]) as usize; - self.write_attrib::(data, num_points, 0); + self.write_attrib::(data, num_points, 0)?; } DataSet::StructuredGrid { dims, points, data } => { write!(self, "DATASET STRUCTURED_GRID\n") - .expect("Error writing StructuredGrid tags."); + .map_err(|_| Error::DataSet(DataSetError::StructuredGrid(DataSetPart::Tags)))?; write!(self, "DIMENSIONS {} {} {}\n", dims[0], dims[1], dims[2]) - .expect("Error writing StructuredGrid dimensions."); + .map_err(|_| Error::DataSet(DataSetError::StructuredGrid(DataSetPart::Dimensions)))?; write!( self, "POINTS {} {}\n", points.len() / 3, DataType::from(points.element_type_id()) - ).expect("Error writing StructuredGrid points."); + ).map_err(|_| Error::DataSet(DataSetError::StructuredGrid(DataSetPart::Points(EntryPart::Header))))?; let num_points = points.len() / 3; - self.write_buf::(points, "Error writing StructuredGrid points."); + self.write_buf::(points) + .map_err(|e| Error::DataSet(DataSetError::StructuredGrid(DataSetPart::Points(EntryPart::Data(e.into())))))?; assert_eq!((dims[0] * dims[1] * dims[2]) as usize, num_points); - self.write_attrib::(data, num_points, 1); + self.write_attrib::(data, num_points, 1)?; } DataSet::RectilinearGrid { @@ -294,154 +391,159 @@ mod write_vtk_impl { data, } => { write!(self, "DATASET RECTILINEAR_GRID\n") - .expect("Error writing RectilinearGrid tags."); + .map_err(|_| Error::DataSet(DataSetError::RectilinearGrid(DataSetPart::Tags)))?; write!(self, "DIMENSIONS {} {} {}\n", dims[0], dims[1], dims[2]) - .expect("Error writing RectilinearGrid dimensions."); + .map_err(|_| Error::DataSet(DataSetError::RectilinearGrid(DataSetPart::Dimensions)))?; write!( self, "X_COORDINATES {} {}\n", x_coords.len(), DataType::from(x_coords.element_type_id()) - ).expect("Error writing RectilinearGrid x_coordinates."); + ).map_err(|_| Error::DataSet(DataSetError::RectilinearGrid(DataSetPart::XCoordinates(EntryPart::Header))))?; let num_x_coords = x_coords.len(); - self.write_buf::(x_coords, "Error writing RectilinearGrid x_coordinates."); + self.write_buf::(x_coords) + .map_err(|e| Error::DataSet(DataSetError::RectilinearGrid(DataSetPart::XCoordinates(EntryPart::Data(e.into())))))?; write!( self, "Y_COORDINATES {} {}\n", y_coords.len(), DataType::from(y_coords.element_type_id()) - ).expect("Error writing RectilinearGrid y_coordinates."); + ).map_err(|_| Error::DataSet(DataSetError::RectilinearGrid(DataSetPart::YCoordinates(EntryPart::Header))))?; let num_y_coords = y_coords.len(); - self.write_buf::(y_coords, "Error writing RectilinearGrid y_coordinates."); + self.write_buf::(y_coords) + .map_err(|e| Error::DataSet(DataSetError::RectilinearGrid(DataSetPart::YCoordinates(EntryPart::Data(e.into())))))?; write!( self, "Z_COORDINATES {} {}\n", z_coords.len(), DataType::from(z_coords.element_type_id()) - ).expect("Error writing RectilinearGrid z_coordinates."); + ).map_err(|_| Error::DataSet(DataSetError::RectilinearGrid(DataSetPart::ZCoordinates(EntryPart::Header))))?; let num_z_coords = z_coords.len(); - self.write_buf::(z_coords, "Error writing RectilinearGrid z_coordinates."); + self.write_buf::(z_coords) + .map_err(|e| Error::DataSet(DataSetError::RectilinearGrid(DataSetPart::ZCoordinates(EntryPart::Data(e.into())))))?; let num_points = num_x_coords * num_y_coords * num_z_coords; let num_cells = (num_x_coords - 1) * (num_y_coords - 1) * (num_z_coords - 1); - self.write_attrib::(data, num_points, num_cells); + self.write_attrib::(data, num_points, num_cells)?; } } - write!(self, "\n"); - self + write!(self, "\n").map_err(|_| Error::NewLine)?; + Ok(self) } } impl WriteVtkImpl for Vec { - fn write_fmt(&mut self, args: Arguments) -> Result<(), Error> { - match ::std::io::Write::write_fmt(self, args) { - Ok(v) => Ok(v), - Err(_) => Err(Error), - } + fn write_fmt(&mut self, args: Arguments) -> Result { + std::io::Write::write_fmt(self, args)?; + Ok(()) } - fn write_file_type(&mut self, err: &str) { - write!(self, "BINARY\n\n").expect(err); + fn write_file_type(&mut self) -> Result { + write!(self, "BINARY\n\n").map_err(|_| Error::Header(Header::FileType)) } - fn write_cell_types(&mut self, data: Vec, err: &str) { + fn write_cell_types(&mut self, data: Vec) -> Result { + let err_fn = |ek: Option| Error::DataSet(DataSetError::UnstructuredGrid(DataSetPart::CellTypes(EntryPart::Data(ek)))); + let err = |e: std::io::Error| err_fn(Some(e.kind())); for t in data { - self.write_i32::(t.to_i32().expect(err)).expect(err); + self.write_i32::(t.to_i32().ok_or(err_fn(None))?).map_err(err)?; } - write!(self, "\n").expect(err); + write!(self, "\n").map_err(|_| Error::NewLine) } - fn write_u32_vec(&mut self, data: Vec, err: &str) { + fn write_u32_vec(&mut self, data: Vec) -> Result { let buf = IOBuffer::from(data); - self.write_buf::(buf, err); + self.write_buf::(buf) } - fn write_buf(&mut self, buf: IOBuffer, err: &str) { + fn write_buf(&mut self, buf: IOBuffer) -> Result { use std::any::TypeId; - fn write_buf_impl(buf: IOBuffer, writer: &mut W, elem_writer: E, err: &str) + fn write_buf_impl(buf: IOBuffer, writer: &mut W, elem_writer: E) -> Result where W: WriteBytesExt, - E: Fn(&mut W, T) -> ::std::io::Result<()>, + E: Fn(&mut W, T) -> std::io::Result<()>, { for elem in buf.reinterpret_into_vec::() { - elem_writer(writer, elem).expect(err); + elem_writer(writer, elem)?; } + Ok(()) } match buf.element_type_id() { - x if x == TypeId::of::() => write_buf_impl(buf, self, Self::write_u8, err), - x if x == TypeId::of::() => write_buf_impl(buf, self, Self::write_i8, err), + x if x == TypeId::of::() => write_buf_impl(buf, self, Self::write_u8)?, + x if x == TypeId::of::() => write_buf_impl(buf, self, Self::write_i8)?, x if x == TypeId::of::() => { - write_buf_impl(buf, self, Self::write_u16::, err) + write_buf_impl(buf, self, Self::write_u16::)?; } x if x == TypeId::of::() => { - write_buf_impl(buf, self, Self::write_i16::, err) + write_buf_impl(buf, self, Self::write_i16::)?; } x if x == TypeId::of::() => { - write_buf_impl(buf, self, Self::write_u32::, err) + write_buf_impl(buf, self, Self::write_u32::)?; } x if x == TypeId::of::() => { - write_buf_impl(buf, self, Self::write_i32::, err) + write_buf_impl(buf, self, Self::write_i32::)?; } x if x == TypeId::of::() => { - write_buf_impl(buf, self, Self::write_u64::, err) + write_buf_impl(buf, self, Self::write_u64::)?; } x if x == TypeId::of::() => { - write_buf_impl(buf, self, Self::write_i64::, err) + write_buf_impl(buf, self, Self::write_i64::)?; } x if x == TypeId::of::() => { - write_buf_impl(buf, self, Self::write_f32::, err) + write_buf_impl(buf, self, Self::write_f32::)?; } x if x == TypeId::of::() => { - write_buf_impl(buf, self, Self::write_f64::, err) + write_buf_impl(buf, self, Self::write_f64::)?; } _ => {} } - write!(self, "\n").expect("Error writing a new line after binary data."); + write!(self, "\n") } } impl WriteVtkImpl for String { - fn write_fmt(&mut self, args: Arguments) -> Result<(), Error> { - match ::std::fmt::Write::write_fmt(self, args) { - Ok(v) => Ok(v), - Err(_) => Err(Error), - } + fn write_fmt(&mut self, args: Arguments) -> Result { + std::fmt::Write::write_fmt(self, args)?; + Ok(()) } - fn write_file_type(&mut self, err: &str) { - write!(self, "ASCII\n\n").expect(err); + fn write_file_type(&mut self) -> Result { + write!(self, "ASCII\n\n").map_err(|_| Error::Header(Header::FileType)) } - fn write_cell_types(&mut self, data: Vec, err: &str) { + fn write_cell_types(&mut self, data: Vec) -> Result { + let err = Error::DataSet(DataSetError::UnstructuredGrid(DataSetPart::CellTypes(EntryPart::Data(None)))); for t in data { - write!(self, "{}\n", t.to_u8().unwrap()).expect(err); + write!(self, "{}\n", t.to_u8().ok_or(err)?).map_err(|_| err)?; } - write!(self, "\n").expect(err); + write!(self, "\n").map_err(|_| err) } - fn write_u32_vec(&mut self, data: Vec, err: &str) { + fn write_u32_vec(&mut self, data: Vec) -> Result { for i in 0..data.len() { - write!(self, "{}", data[i]).expect(err); + write!(self, "{}", data[i])?; if i < data.len() - 1 { // add an extra space between elements - write!(self, " ").expect(err); + write!(self, " ")?; } } - write!(self, "\n").expect(err); // finish with a new line + write!(self, "\n") // finish with a new line } - fn write_buf(&mut self, data: IOBuffer, err: &str) { - write!(self, "{}\n", data).expect(err); + fn write_buf(&mut self, data: IOBuffer) -> Result { + write!(self, "{}\n", data) } } } +pub use self::write_vtk_impl::Error; + pub trait WriteVtk: write_vtk_impl::WriteVtkImpl { - fn write_vtk(&mut self, vtk: Vtk) -> &mut Self { + fn write_vtk(&mut self, vtk: Vtk) -> Result<&mut Self, Error> { self.write_vtk_impl::(vtk) } - fn write_vtk_le(&mut self, vtk: Vtk) -> &mut Self { + fn write_vtk_le(&mut self, vtk: Vtk) -> Result<&mut Self, Error> { self.write_vtk_impl::(vtk) } - fn write_vtk_be(&mut self, vtk: Vtk) -> &mut Self { + fn write_vtk_be(&mut self, vtk: Vtk) -> Result<&mut Self, Error> { self.write_vtk_impl::(vtk) } } diff --git a/tests/vtkio.rs b/tests/vtkio.rs index 2845d86..735b4a2 100644 --- a/tests/vtkio.rs +++ b/tests/vtkio.rs @@ -7,6 +7,7 @@ use vtkio::model::*; use vtkio::parser::*; use vtkio::writer::*; use vtkio::IOBuffer; +use vtkio::Error; macro_rules! test { ($fn:ident ($in:expr, $($args:expr),*) => ($rem:expr, $out:expr)) => { @@ -49,9 +50,11 @@ macro_rules! test_ignore_rem { }; } +type Result = std::result::Result<(), Error>; + // Test paraview output of a single tet #[test] -fn para_tet_test() { +fn para_tet_test() -> Result { let in1 = include_bytes!("../assets/para_tet.vtk"); let in2 = include_str!("../assets/para_tet_ascii.vtk").as_bytes(); let out1 = Vtk { @@ -83,15 +86,16 @@ fn para_tet_test() { }; test_ignore_rem!(parse_be(in1) => out1); test_ignore_rem!(parse_be(in2) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); + Ok(()) } // Test paraview output of a few tets #[test] -fn para_tets_test() { +fn para_tets_test() -> Result { let in1 = include_bytes!("../assets/para_test.vtk"); let in2 = include_str!("../assets/para_test_ascii.vtk").as_bytes(); let out1 = Vtk { @@ -166,14 +170,15 @@ fn para_tets_test() { }; test_ignore_rem!(parse_be(in1) => out1); test_ignore_rem!(parse_be(in2) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); + Ok(()) } #[test] -fn tet_test() { +fn tet_test() -> Result { let in1 = include_str!("../assets/tet.vtk"); let in2 = include_bytes!("../assets/tet_test.vtk"); let in3 = include_bytes!("../assets/tet_test_binary.vtk"); @@ -195,14 +200,15 @@ fn tet_test() { test!(parse_le(in1) => out1); test_b!(parse_le(in2) => out1); test_b!(parse_be(in3) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); + Ok(()) } #[test] -fn tri_test() { +fn tri_test() -> Result { let in1 = include_str!("../assets/tri.vtk"); let out1 = Vtk { version: Version::new((2, 0)), @@ -217,12 +223,13 @@ fn tri_test() { }, }; test!(parse(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + Ok(()) } #[test] -fn tri_attrib_ascii_test() { +fn tri_attrib_ascii_test() -> Result { let in1 = include_str!("../assets/tri_attrib.vtk"); let out1 = Vtk { version: Version::new((2, 0)), @@ -261,13 +268,14 @@ fn tri_attrib_ascii_test() { }, }; test!(parse(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); // Color scalars are floats only in the ASCII vtk file format. Thus we have another test for // The same file but in binary in tri_attrib_binary_test. + Ok(()) } #[test] -fn tri_attrib_binary_test() { +fn tri_attrib_binary_test() -> Result { let in1 = include_bytes!("../assets/tri_attrib_binary.vtk"); let out1 = Vtk { version: Version::new((4, 2)), @@ -306,13 +314,14 @@ fn tri_attrib_binary_test() { }, }; test_ignore_rem!(parse_be(in1) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); // Color scalars are u8 only in the Binary vtk file format. ASCII style color scalars are // stored as floats and are tested in tri_attrib_ascii_test. + Ok(()) } #[test] -fn square_test() { +fn square_test() -> Result { let in1 = include_str!("../assets/square.vtk"); let out1 = Vtk { version: Version::new((2, 0)), @@ -329,14 +338,15 @@ fn square_test() { }, }; test!(parse(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); + Ok(()) } #[test] -fn cube_test() { +fn cube_test() -> Result { let in1 = include_str!("../assets/cube.vtk"); let out1 = Vtk { version: Version::new((4, 2)), @@ -355,14 +365,15 @@ fn cube_test() { }, }; test!(parse(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); + Ok(()) } #[test] -fn structured_grid_test() { +fn structured_grid_test() -> Result { let in1 = include_str!("../assets/structured_grid.vtk"); let out1 = Vtk { version: Version::new((3, 0)), @@ -414,14 +425,15 @@ fn structured_grid_test() { }, }; test!(parse(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); + Ok(()) } #[test] -fn rectilinear_grid_test() { +fn rectilinear_grid_test() -> Result { let in1 = include_bytes!("../assets/rectilinear_grid.vtk"); let out1 = Vtk { version: Version::new((3, 0)), @@ -447,10 +459,10 @@ fn rectilinear_grid_test() { }, }; test_b!(parse_be(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); // Same thing should work in binary let in1 = include_bytes!("../assets/rectilinear_grid_binary.vtk"); @@ -459,10 +471,11 @@ fn rectilinear_grid_test() { ..out1 }; test_b!(parse_be(in1) => out2); + Ok(()) } #[test] -fn field_test() { +fn field_test() -> Result { let in1 = include_bytes!("../assets/field.vtk"); let out1 = Vtk { version: Version::new((2, 0)), @@ -484,14 +497,15 @@ fn field_test() { }, }; test_b!(parse(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); + Ok(()) } #[test] -fn cube_complex_test() { +fn cube_complex_test() -> Result { let mut topo = vec![PolyDataTopology::Polygons(Cells { num_cells: 6, vertices: vec![ @@ -630,18 +644,19 @@ fn cube_complex_test() { }; test!(parse(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); test_b!(parse(in2) => out2); - test_b!(parse(String::new().write_vtk(out2.clone()).as_bytes()) => out2); - test_b!(parse_le(Vec::::new().write_vtk_le(out2.clone())) => out2); - test_b!(parse_be(Vec::::new().write_vtk_be(out2.clone())) => out2); + test_b!(parse(String::new().write_vtk(out2.clone())?.as_bytes()) => out2); + test_b!(parse_le(Vec::::new().write_vtk_le(out2.clone())?) => out2); + test_b!(parse_be(Vec::::new().write_vtk_be(out2.clone())?) => out2); + Ok(()) } #[test] -fn unstructured_grid_complex_test() { +fn unstructured_grid_complex_test() -> Result { let in1 = include_str!("../assets/unstructured_grid_complex.vtk"); let out1 = Vtk { version: Version::new((2, 0)), @@ -706,14 +721,15 @@ fn unstructured_grid_complex_test() { }, }; test!(parse(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); + Ok(()) } #[test] -fn volume_complex_test() { +fn volume_complex_test() -> Result { let in1 = include_str!("../assets/volume_complex.vtk"); let out1 = Vtk { version: Version::new((2, 0)), @@ -741,14 +757,15 @@ fn volume_complex_test() { }, }; test!(parse(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); + Ok(()) } #[test] -fn dodecagon_test() { +fn dodecagon_test() -> Result { let in1 = include_bytes!("../assets/dodecagon_ascii_simple.vtk"); let out1 = Vtk { version: Version::new((4, 2)), @@ -802,10 +819,11 @@ fn dodecagon_test() { }; test_b!(parse(in1) => out1); - test_b!(parse(String::new().write_vtk(out1.clone()).as_bytes()) => out1); - test_b!(parse(Vec::::new().write_vtk(out1.clone())) => out1); - test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())) => out1); - test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())) => out1); + test_b!(parse(String::new().write_vtk(out1.clone())?.as_bytes()) => out1); + test_b!(parse(Vec::::new().write_vtk(out1.clone())?) => out1); + test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => out1); + test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); + Ok(()) } #[test]