diff --git a/bauble/src/context.rs b/bauble/src/context.rs index 90f7b09..486141e 100644 --- a/bauble/src/context.rs +++ b/bauble/src/context.rs @@ -469,14 +469,28 @@ impl BaubleContext { /// * `path` describes the bauble "module" that the file corresponds to. That is it say, what /// prefix path is necessary inside of bauble to reference the context of this file. /// * `source` is the string of Bauble text to be parsed. + /// + /// If a file with this path already exists, its content will be overwritten. pub fn register_file(&mut self, path: TypePath<&str>, source: impl Into) -> FileId { - let node = self.root_node.build_nodes(path); - let id = FileId(self.files.len()); - node.source = Some(id); - self.files - .push((path.to_owned(), ariadne::Source::from(source.into()))); + let existing_file_id = self + .root_node + .node_at(path.borrow()) + .and_then(|node| node.source); + match existing_file_id { + Some(id) => { + *self.file_mut(id).1 = Source::from(source.into()); + id + } + None => { + let node = self.root_node.build_nodes(path); + let id = FileId(self.files.len()); + node.source = Some(id); + self.files + .push((path.to_owned(), ariadne::Source::from(source.into()))); - id + id + } + } } /// Iterates all registered files. @@ -520,19 +534,8 @@ impl BaubleContext { ) -> (Vec, BaubleErrors) { let ids = paths .into_iter() - .map(|(path, source)| { - let file_id = self - .root_node - .node_at(path.borrow()) - .and_then(|node| node.source); - match file_id { - Some(id) => { - *self.file_mut(id).1 = Source::from(source.into()); - id - } - None => self.register_file(path.borrow(), source), - } - }) + .map(|(path, source)| self.register_file(path.borrow(), source)) + // Need to collect to avoid conflicts of borrowing `self`. .collect::>(); self.reload_files(ids) diff --git a/bauble/src/lib.rs b/bauble/src/lib.rs index e9ffda4..4c103ce 100644 --- a/bauble/src/lib.rs +++ b/bauble/src/lib.rs @@ -49,7 +49,15 @@ macro_rules! bauble_test { ($ctx_static:ident [$($ty:ty),* $(,)?] [$($source:literal),* $(,)?] [$($test_value:expr),* $(,)?]) => { static $ctx_static: std::sync::OnceLock> = std::sync::OnceLock::new(); { - let file_path = $crate::path::TypePath::new("test").unwrap(); + let file_sources = [$($source),*]; + let file_paths = if file_sources.len() == 1 { + vec![$crate::path::TypePath::new(String::from("test")).unwrap()] + } else { + (0..file_sources.len()).map(|i| { + $crate::path::TypePath::new(format!("test{i}")).unwrap() + }).collect() + }; + let ctx = $ctx_static.get_or_init(|| { let mut ctx = $crate::BaubleContextBuilder::new(); $(ctx.register_type::<$ty, _>();)* @@ -57,9 +65,9 @@ macro_rules! bauble_test { let mut ctx = ctx.build(); ctx.type_registry().validate(true).expect("Invalid type registry"); - $( - ctx.register_file(file_path, format!("\n{}\n", $source)); - )* + for (path, source) in file_paths.iter().zip(file_sources) { + ctx.register_file(path.borrow(), format!("\n{}\n", source)); + } std::sync::RwLock::new(ctx) }); @@ -73,15 +81,35 @@ macro_rules! bauble_test { } // Test round-trip of objects through source format - let re_source = $crate::display_formatted(objects.as_slice(), ctx.read().unwrap().type_registry(), &$crate::DisplayConfig { - ..$crate::DisplayConfig::default() - }); + let mut file_objects = ::std::collections::HashMap::new(); + for object in &objects { + use $crate::SpannedValue; + file_objects + .entry(object.value.span().file()) + .or_insert(Vec::new()) + .push(object.clone()); + } - let (re_objects, errors) = ctx.write().unwrap().reload_paths([(file_path, re_source.as_str())]); + let re_path_sources = file_objects.iter().map(|(file_id, objects)| { + let ctx = ctx.read().unwrap(); + let re_source = bauble::display_formatted( + objects.as_slice(), + ctx.type_registry(), + &$crate::DisplayConfig { + ..$crate::DisplayConfig::default() + }, + ); + (ctx.get_file_path(*file_id).to_owned(), re_source) + }) + .collect::>(); + + let (re_objects, errors) = ctx.write().unwrap().reload_paths(re_path_sources.iter().map(|(p, s)| (p.borrow(), s))); if !errors.is_empty() { $crate::print_errors(Err::<(), _>(errors), &ctx.read().unwrap()); - eprintln!("{re_source}"); + for (path, re_source) in re_path_sources { + eprintln!("In file \"{path}\": {re_source}"); + } panic!("Error re-converting"); } diff --git a/bauble/src/value/display.rs b/bauble/src/value/display.rs index edd6ad0..b4ffc4b 100644 --- a/bauble/src/value/display.rs +++ b/bauble/src/value/display.rs @@ -688,13 +688,13 @@ where types: w.ctx(), }; let mut w = w.with_ctx(&ctx); - let mut iter = written.into_iter(); + let mut objects_to_write = written.into_iter(); - if let Some(o) = iter.next() { + if let Some(o) = objects_to_write.next() { o.indented_display(w.reborrow()); } - for o in iter { + for o in objects_to_write { w.write("\n\n"); o.indented_display(w.reborrow()); } diff --git a/bauble/tests/integration.rs b/bauble/tests/integration.rs index f1f0e11..cd65ef5 100644 --- a/bauble/tests/integration.rs +++ b/bauble/tests/integration.rs @@ -431,22 +431,22 @@ pub fn ref_explicit_type_multiple_files() { [Test] [ "test = integration::Test{ x: -5, y: 5 }", - "r: Ref = $test::test" + "r: Ref = $test0::test" ] [ Test { x: -5, y: 5 }, - Ref::::from_path(TypePath::new_unchecked("test::test").to_owned()), + Ref::::from_path(TypePath::new_unchecked("test0::test").to_owned()), ] ); bauble::bauble_test!( [Test] [ - "r: Ref = $test::test", + "r: Ref = $test1::test", "test = integration::Test{ x: -5, y: 5 }" ] [ - Ref::::from_path(TypePath::new_unchecked("test::test").to_owned()), + Ref::::from_path(TypePath::new_unchecked("test1::test").to_owned()), Test { x: -5, y: 5 }, ] ); @@ -458,22 +458,22 @@ pub fn ref_implicit_type_multiple_files() { [Test] [ "test = integration::Test{ x: -5, y: 5 }", - "r = $test::test" + "r = $test0::test" ] [ Test { x: -5, y: 5 }, - Ref::::from_path(TypePath::new_unchecked("test::test").to_owned()), + Ref::::from_path(TypePath::new_unchecked("test0::test").to_owned()), ] ); bauble::bauble_test!( [Test] [ - "r = $test::test", + "r = $test1::test", "test = integration::Test{ x: -5, y: 5 }" ] [ - Ref::::from_path(TypePath::new_unchecked("test::test").to_owned()), + Ref::::from_path(TypePath::new_unchecked("test1::test").to_owned()), Test { x: -5, y: 5 }, ] );