Skip to content

Commit

Permalink
Validate exported 3MF files
Browse files Browse the repository at this point in the history
  • Loading branch information
hannobraun committed Apr 14, 2022
1 parent 6439f7b commit f49030c
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 2 deletions.
62 changes: 62 additions & 0 deletions tools/export-validator/src/ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//! Minimal foreign function interface for lib3mf
//!
//! See these header files for more information:
//! - https://github.com/3MFConsortium/lib3mf/blob/master/Autogenerated/Bindings/C/lib3mf.h
//! - https://github.com/3MFConsortium/lib3mf/blob/master/Autogenerated/Bindings/C/lib3mf_types.h
use std::{ffi::c_void, os::raw::c_char};

pub type Model = Handle;
pub type Object = Handle;
pub type Reader = Handle;

pub type ObjectIterator = Handle;
pub type ResourceIterator = Handle;

pub type Handle = *const c_void;
pub type Result = i32;

extern "C" {
pub fn lib3mf_createmodel(pModel: *mut Model) -> Result;

pub fn lib3mf_model_queryreader(
pModel: Model,
pReaderClass: *const c_char,
pReaderInstance: *mut Reader,
) -> Result;

pub fn lib3mf_reader_setstrictmodeactive(
pReader: Reader,
bStrictModeActive: bool,
) -> Result;

pub fn lib3mf_reader_readfromfile(
pReader: Reader,
pFilename: *const c_char,
) -> Result;

pub fn lib3mf_reader_getwarningcount(
pReader: Reader,
pCount: *mut u32,
) -> Result;

pub fn lib3mf_model_getobjects(
pModel: Model,
pResourceIterator: *mut ObjectIterator,
) -> Result;

pub fn lib3mf_resourceiterator_movenext(
pResourceIterator: ResourceIterator,
pHasNext: *mut bool,
) -> Result;

pub fn lib3mf_objectiterator_getcurrentobject(
pObjectIterator: ObjectIterator,
pResource: *mut Object,
) -> Result;

pub fn lib3mf_object_isvalid(
pObject: Object,
pIsValid: &mut bool,
) -> Result;
}
118 changes: 116 additions & 2 deletions tools/export-validator/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::{fs, process::Command};
mod ffi;

use anyhow::{anyhow, bail};
use std::{ffi::CString, fs, process::Command, ptr};

use anyhow::{anyhow, bail, Context as _};

fn main() -> anyhow::Result<()> {
for model in fs::read_dir("models")? {
Expand All @@ -24,6 +26,118 @@ fn main() -> anyhow::Result<()> {
{exit_status}"
);
}

// Presumably we're using the library in the way it's intended, so this
// might be sound?
unsafe {
validate_model(&export_file).with_context(|| {
format!("Could not validate model `{model}`")
})?;
}
}

Ok(())
}

unsafe fn validate_model(file: &str) -> anyhow::Result<()> {
let mut model = ptr::null();

let result = ffi::lib3mf_createmodel(&mut model);
if result != 0 {
bail!("Failed to create model; error code: {result}");
}

let mut reader = ptr::null();
let reader_class = CString::new("3mf")?;

let result = ffi::lib3mf_model_queryreader(
model,
reader_class.as_ptr(),
&mut reader,
);
if result != 0 {
bail!("Failed to query reader; error code: {result}");
}

let result = ffi::lib3mf_reader_setstrictmodeactive(reader, true);
if result != 0 {
bail!("Failed to set strict mode; error code: {result}");
}

let path = CString::new(file)?;

let result = ffi::lib3mf_reader_readfromfile(reader, path.as_ptr());
if result != 0 {
bail!("Failed to read model; error code: {result}");
}

let mut num_warnings = 0;

let result = ffi::lib3mf_reader_getwarningcount(reader, &mut num_warnings);
if result != 0 {
bail!("Failed to get number of warnings; error code: {result}");
}

if num_warnings > 0 {
bail!(
"Warnings while reading model; number of warnings: {num_warnings}"
);
}

let mut object_iterator = ptr::null();

let result = ffi::lib3mf_model_getobjects(model, &mut object_iterator);
if result != 0 {
bail!("Failed to get object iterator; error code: {result}");
}

loop {
let mut has_next = false;

let result = ffi::lib3mf_resourceiterator_movenext(
object_iterator,
&mut has_next,
);
if result != 0 {
bail!(
"Failed to move iterator to next object; error code: {result}"
);
}

if !has_next {
break;
}

let mut object = ptr::null();

let result = ffi::lib3mf_objectiterator_getcurrentobject(
object_iterator,
&mut object,
);
if result != 0 {
bail!("Failed to get object; error code: {result}");
}

let mut is_valid = false;

let result = ffi::lib3mf_object_isvalid(object, &mut is_valid);
if result != 0 {
bail!(
"Failed to determine if object is valid; error code: {result}"
);
}

if !is_valid {
// Yes, this error message is a bit sparse. If anyone is interested
// in expanding this program, you're welcome to do that.
//
// However, the point here is to fail the CI build if something is
// wrong. Once you know *that* the file is wrong, you can use an
// existing validator to get more information. This is probably more
// productive than expending effort to turn this into the perfect
// validator.
bail!("Object is not valid");
}
}

Ok(())
Expand Down

0 comments on commit f49030c

Please sign in to comment.