Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions bauble/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@ impl BaubleErrors {
ctx,
);
}

/// Writes errors and attempts to convert them to a string.
pub fn try_to_string(&self, ctx: &BaubleContext) -> Result<String, std::string::FromUtf8Error> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this can ever fail, but I wanted to keep any potential panicking out the main bauble API. So this returns a result.

let mut buf = Vec::new();
self.write_errors(ctx, &mut buf);
String::from_utf8(buf)
}
}

impl<B: BaubleError> From<B> for BaubleErrors {
Expand All @@ -228,12 +235,3 @@ impl<B: BaubleError> From<Vec<B>> for BaubleErrors {
)
}
}

/// Will print the errors of `res` in case of an error, otherwise, return the ok value of `res` of type `T`.
pub fn print_errors<T>(res: Result<T, impl Into<BaubleErrors>>, ctx: &BaubleContext) -> Option<T> {
res.map_err(|errors| {
let errors: BaubleErrors = errors.into();
errors.print_errors(ctx)
})
.ok()
}
48 changes: 32 additions & 16 deletions bauble/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub use bauble_macros::Bauble;

pub use builtin::Ref;
pub use context::{BaubleContext, BaubleContextBuilder, FileId, PathReference, Source};
pub use error::{BaubleError, BaubleErrors, CustomError, Level, print_errors};
pub use error::{BaubleError, BaubleErrors, CustomError, Level};
pub use spanned::{Span, SpanExt, Spanned};
pub use traits::{
Bauble, BaubleAllocator, DefaultAllocator, ToRustError, ToRustErrorKind, VariantKind,
Expand Down Expand Up @@ -72,8 +72,8 @@ pub mod private {
let (objects, errors) = ctx.write().unwrap().load_all();

if !errors.is_empty() {
crate::print_errors(Err::<(), _>(errors), &ctx.read().unwrap());
panic!("Error converting");
let error_msg = errors.try_to_string(&ctx.read().unwrap()).unwrap();
panic!("Error converting: \n{error_msg}");
}

// Test round-trip of objects through source format
Expand Down Expand Up @@ -107,11 +107,12 @@ pub mod private {
.reload_paths(re_path_sources.iter().map(|(p, s)| (p.borrow(), s)));

if !errors.is_empty() {
crate::print_errors(Err::<(), _>(errors), &ctx.read().unwrap());
let mut error_msg = errors.try_to_string(&ctx.read().unwrap()).unwrap();
for (path, re_source) in re_path_sources {
eprintln!("In file \"{path}\": {re_source}");
use std::fmt::Write;
writeln!(&mut error_msg, "In file \"{path}\": {re_source}").unwrap();
}
panic!("Error re-converting");
panic!("Error re-converting: \n{error_msg}");
}

assert_eq!(objects, re_objects);
Expand All @@ -121,6 +122,30 @@ pub mod private {
compare_objects(objects, ctx);
compare_objects(re_objects, ctx);
}

/// Converts bauble object to the type of the `expected_value` and then asserts that they are
/// the same.
///
/// Panics if converting object fails.
pub fn expected_from_bauble<T>(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't reuse this in bauble/tests/integration.rs because this takes the RwLock which I believe needs to be unlocked when calling from_bauble for certain tests (so we can't just take reference to BaubleContext here)

expected_value: T,
value: crate::Object,
ctx: &std::sync::RwLock<crate::BaubleContext>,
) where
T: PartialEq + for<'a> crate::Bauble<'a> + std::fmt::Debug,
{
match <T as crate::Bauble>::from_bauble(value.value, &crate::DefaultAllocator) {
Ok(read_value) => {
assert_eq!(read_value, expected_value);
}
Err(error) => {
let error_msg = crate::BaubleErrors::from(error)
.try_to_string(&ctx.read().unwrap())
.unwrap();
panic!("Error converting object to rust value: \n{error_msg}");
}
}
}
}

// TODO(@docs)
Expand All @@ -147,16 +172,7 @@ macro_rules! bauble_test {

$(
let value = objects.next().expect("Not as many objects as test expr in bauble test?");
// Infer type for `read_value` to be the same as `test_value`.
let [test_value, read_value] = [
$test_value,
$crate::print_errors(
$crate::Bauble::from_bauble(value.value, &$crate::DefaultAllocator),
&ctx.read().unwrap()
).unwrap(),
];

assert_eq!(read_value, test_value);
$crate::private::expected_from_bauble($test_value, value, &ctx);
)*

if objects.next().is_some() {
Expand Down
2 changes: 1 addition & 1 deletion bauble/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ impl TypeRegistry {
let (loaded_objects, errors) = ctx.load_all();

if !errors.is_empty() {
crate::print_errors(Err::<(), _>(errors), &ctx);
errors.print_errors(&ctx);

return Err(TypeSystemError::InstantiableErrors);
}
Expand Down
40 changes: 24 additions & 16 deletions bauble/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ fn expected_value_fn<T: for<'a> Bauble<'a> + PartialEq + std::fmt::Debug>(
) -> Box<dyn Fn(Object, &BaubleContext)> {
Box::new(move |object, ctx| {
let result = T::from_bauble(object.value, &bauble::DefaultAllocator);
let read_value = bauble::print_errors(result, ctx).unwrap();
assert_eq!(&read_value, &expected_value);
match result {
Ok(read_value) => assert_eq!(read_value, expected_value),
Err(error) => {
let errors = bauble::BaubleErrors::from(error);
let error_msg = errors.try_to_string(ctx).unwrap();
panic!("Error converting object to rust value: \n{error_msg}");
}
}
})
}

Expand Down Expand Up @@ -62,6 +68,10 @@ fn make_ctx(with_ctx_builder: &dyn Fn(&mut bauble::BaubleContextBuilder)) -> bau
ctx
}

fn panic_errors(ctx: &bauble::BaubleContext, errors: bauble::BaubleErrors) -> ! {
panic!("{}", errors.try_to_string(ctx).unwrap());
}

fn test_load(with_ctx_builder: &dyn Fn(&mut bauble::BaubleContextBuilder), files: &[&TestFile]) {
let mut ctx = make_ctx(with_ctx_builder);

Expand All @@ -72,8 +82,7 @@ fn test_load(with_ctx_builder: &dyn Fn(&mut bauble::BaubleContextBuilder), files

let (objects, errors) = ctx.load_all();
if !errors.is_empty() {
bauble::print_errors(Err::<(), _>(errors), &ctx);
panic!("Error converting");
panic_errors(&ctx, errors);
}
compare_objects(objects, files, &ctx);
}
Expand All @@ -92,21 +101,19 @@ fn test_reload(

let (objects, errors) = ctx.load_all();
if !errors.is_empty() {
bauble::print_errors(Err::<(), _>(errors), &ctx);
panic!("Error converting");
panic_errors(&ctx, errors);
}
compare_objects(objects, start, &ctx);

// Test reloading with new content and new files that are nested as submodules.
let (objects, errors) = ctx.reload_paths(new.iter().map(|f| (f.path.borrow(), &f.content)));
if !errors.is_empty() {
bauble::print_errors(Err::<(), _>(errors), &ctx);
panic!("Error converting reload");
panic_errors(&ctx, errors);
}
compare_objects(objects, new, &ctx);
}

/// Doesn't fail test when some files have errors as long as they had no expected values.
/// Doesn't fail test when some files have errors as long as all expected values are loaded.
///
/// Expects at least one error.
fn test_load_partial(
Expand All @@ -124,7 +131,7 @@ fn test_load_partial(
if errors.is_empty() {
panic!("At least one error is expected");
} else {
bauble::print_errors(Err::<(), _>(errors), &ctx);
errors.print_errors(&ctx);
}
compare_objects(objects, files, &ctx);
}
Expand Down Expand Up @@ -170,7 +177,7 @@ fn new_nested_reload_paths() {
}

#[test]
#[should_panic]
#[should_panic = "This identifier was already used"]
fn duplicate_objects() {
let a = &test_file!(
"a",
Expand All @@ -190,7 +197,7 @@ fn duplicate_objects() {
// NOTE: This currently fails because `b::test` isn't allowed by itself but if we add support for
// that we still want this case to fail.
#[test]
#[should_panic]
#[should_panic = "found ':' expected identifier, or '*'"]
fn duplicate_objects_across_files() {
let a = &test_file!(
"a",
Expand Down Expand Up @@ -318,8 +325,8 @@ fn same_file_references() {
}

#[test]
#[should_panic] // TODO: see todo in `register_assets` about registering assets in the correct
// order.
// TODO: see todo in `register_assets` about registering assets in the correct order.
#[should_panic = "Expected this path to refer to an asset"]
fn same_file_references_reverse() {
let a = &test_file!(
"a",
Expand Down Expand Up @@ -357,7 +364,8 @@ fn same_file_references_reverse_full() {
}

#[test]
#[should_panic] // TODO: see todo in `register_assets` where `add_use` is called.
// TODO: see todo in `register_assets` where `add_use` is called.
#[should_panic = "Expected this path to refer to a valid reference"]
fn reference_with_use() {
let a = &test_file!(
"a",
Expand Down Expand Up @@ -480,7 +488,7 @@ pub fn ref_implicit_type_multiple_files() {
}

#[test]
#[should_panic]
#[should_panic = "Expected this path to refer to a type"]
pub fn ref_explicit_type_incorrect() {
#[derive(Bauble, PartialEq, Eq, Debug)]
struct Incorrect(u32);
Expand Down
Loading