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
100 changes: 77 additions & 23 deletions bauble/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,27 @@ pub type Source = ariadne::Source<String>;
#[derive(Clone, Default)]
struct DefaultUses(IndexMap<TypePathElem, TypePath>);

/// Assets can be either top level or local.
#[derive(Clone, Copy, Debug)]
pub enum AssetKind {
// TODO: update this in stage 2
/// The first asset in a file, if it has the same as the file.
///
/// These assets share the same path as the file and are generally intended to be globally
/// visible and referencable.
TopLevel,
/// All assets that aren't considered top level. These are generally intended to only be
/// referenced from the same file (although this isn't enforced yet).
Local,
}

/// A type containing multiple references generally derived from a path.
#[derive(Default, Clone, Debug)]
pub struct PathReference {
/// The type referenced by a path.
pub ty: Option<TypeId>,
/// The asset (and its path) referenced by the path.
pub asset: Option<(TypeId, TypePath)>,
pub asset: Option<(TypeId, TypePath, AssetKind)>,
/// If the reference references a module.
pub module: Option<TypePath>,
}
Expand All @@ -33,10 +47,10 @@ impl PathReference {
}

/// Constructs a path reference referencing the 'any' type.
pub fn any(path: TypePath) -> Self {
pub fn any(path: TypePath, kind: AssetKind) -> Self {
Self {
ty: Some(TypeRegistry::any_type()),
asset: Some((TypeRegistry::any_type(), path.clone())),
asset: Some((TypeRegistry::any_type(), path.clone(), kind)),
module: Some(path.clone()),
}
}
Expand Down Expand Up @@ -202,7 +216,7 @@ impl BaubleContextBuilder {
#[derive(Default, Clone, Debug)]
struct InnerReference {
ty: Option<TypeId>,
asset: Option<TypeId>,
asset: Option<(TypeId, AssetKind)>,
redirect: Option<TypePath>,
}

Expand Down Expand Up @@ -244,7 +258,10 @@ impl CtxNode {
fn reference(&self, root: &Self) -> PathReference {
let mut this = PathReference {
ty: self.reference.ty,
asset: self.reference.asset.map(|ty| (ty, self.path.clone())),
asset: self
.reference
.asset
.map(|(ty, kind)| (ty, self.path.clone(), kind)),
module: (!self.children.is_empty()).then(|| self.path.clone()),
};

Expand Down Expand Up @@ -381,22 +398,31 @@ impl CtxNode {
&& self.source.is_none()
}

/// Clears all assets in the module this node references (if any).
/// Clears all assets in the file this node references (if any).
///
/// Does not recurse into other files, but will clear inline submodules in the current file (if
/// that feature is added).
fn clear_child_assets(&mut self) {
fn clear_file_assets(&mut self) {
self.children.retain(|_, node| {
if node.source.is_some() {
return true;
// `AssetKind::TopLevel` will be from other files and we don't want to clear those.
node.reference
.asset
.take_if(|(_, kind)| matches!(kind, AssetKind::Local));

// Avoid clearing assets from other files, but recurse into inline submodules (if that
// feature is added).
if node.source.is_none() {
node.clear_file_assets();
}

node.clear_child_assets();

node.reference.asset = None;

!node.is_empty()
});

// Top level assets match the path of their file so this will be from this file if it is
// top level.
self.reference
.asset
.take_if(|(_, kind)| matches!(kind, AssetKind::TopLevel));
}

/// Builds all path elements as modules
Expand Down Expand Up @@ -428,13 +454,15 @@ impl CtxNode {
node.reference.ty = Some(id);
}

fn build_asset(&mut self, path: TypePath<&str>, ty: TypeId) {
fn build_asset(&mut self, path: TypePath<&str>, ty: TypeId, kind: AssetKind) -> Result<(), ()> {
let node = self.build_nodes(path);
if node.reference.asset.is_some() {
panic!("Multiple types with the same path");
// Multiple assets with the same path
return Err(());
}

node.reference.asset = Some(ty);
node.reference.asset = Some((ty, kind));
Ok(())
}
}

Expand Down Expand Up @@ -503,11 +531,31 @@ impl BaubleContext {
/// With this method you can expose assets that aren't in bauble.
///
/// Returns ID of internal Ref type for `ty`.
pub fn register_asset(&mut self, path: TypePath<&str>, ty: TypeId) -> TypeId {
///
/// Returns an error if an asset was already registered at this path. This can occur due to
/// simplification of the top level asset path to match the current file. E.g. the top-level
/// asset in `a::1` will conflict with the path of object `1` in file `a`.
//
// TODO: in stage 2 we might be able to adjust this so that local object paths are
// distinguished from top level objects, such that they don't clash.
pub fn register_asset(
&mut self,
path: TypePath<&str>,
ty: TypeId,
kind: AssetKind,
) -> Result<TypeId, crate::CustomError> {
let ref_ty = self.registry.get_or_register_asset_ref(ty);
self.root_node.build_type(ref_ty, &self.registry);
self.root_node.build_asset(path, ref_ty);
ref_ty
self.root_node
.build_asset(path, ref_ty, kind)
.map_err(|()| {
crate::CustomError::new(format!(
"'{path}' refers to an existing asset in another file. This can be \n\
caused by special cased path simplification for the first object in a \n\
file.",
))
})?;
Ok(ref_ty)
}

fn file(&self, file: FileId) -> (TypePath<&str>, &Source) {
Expand Down Expand Up @@ -575,7 +623,7 @@ impl BaubleContext {
// Need a partial borrow here.
let (path, _) = &self.files[file.0];
if let Some(node) = self.root_node.node_at_mut(path.borrow()) {
node.clear_child_assets();
node.clear_file_assets();
}
}

Expand Down Expand Up @@ -731,18 +779,24 @@ impl BaubleContext {
}

/// Get all the assets starting from `path`, with an optional maximum depth of `max_depth`.
///
/// Inline objects are not registered as assets, they are exclusively visible in the list of
/// objects returned when loading/reloading files.
pub fn assets(
&self,
path: TypePath<&str>,
max_depth: Option<usize>,
) -> impl Iterator<Item = TypePath<&str>> {
) -> impl Iterator<Item = (TypePath<&str>, AssetKind)> {
self.root_node
.node_at(path)
.map(|node| node.iter_all_children(max_depth))
.into_iter()
.flatten()
.filter(|node| node.reference(&self.root_node).asset.is_some())
.map(|node| node.path.borrow())
.filter_map(|node| {
node.reference(&self.root_node)
.asset
.map(|(_, _, kind)| (node.path.borrow(), kind))
})
}

/// Get all the types starting from `path`, with an optional maximum depth of `max_depth`.
Expand Down
4 changes: 2 additions & 2 deletions bauble/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub mod types;
pub use bauble_macros::Bauble;

pub use builtin::Ref;
pub use context::{BaubleContext, BaubleContextBuilder, FileId, PathReference, Source};
pub use context::{AssetKind, BaubleContext, BaubleContextBuilder, FileId, PathReference, Source};
pub use error::{BaubleError, BaubleErrors, CustomError, Level};
pub use spanned::{Span, SpanExt, Spanned};
pub use traits::{
Expand Down Expand Up @@ -110,7 +110,7 @@ pub mod private {
let mut error_msg = errors.try_to_string(&ctx.read().unwrap()).unwrap();
for (path, re_source) in re_path_sources {
use std::fmt::Write;
writeln!(&mut error_msg, "In file \"{path}\": {re_source}").unwrap();
writeln!(&mut error_msg, "In file \"{path}\":\n{re_source}").unwrap();
}
panic!("Error re-converting: \n{error_msg}");
}
Expand Down
12 changes: 9 additions & 3 deletions bauble/src/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,13 +681,19 @@ pub fn parser<'a>() -> impl Parser<'a, ParserSource<'a>, ParseValues, Extra<'a>>
.collect::<Vec<_>>(),
)
.validate(|(uses, values), _, emitter| {
values.into_iter().fold(
values.into_iter().enumerate().fold(
ParseValues {
uses,
values: IndexMap::default(),
},
|mut values, (ident, type_path, value)| {
let binding = Binding { type_path, value };
|mut values, (i, (ident, type_path, value))| {
let is_first = i == 0;

let binding = Binding {
type_path,
value,
is_first,
};

if values.values.contains_key(&ident) {
emitter.emit(Rich::custom(
Expand Down
1 change: 1 addition & 0 deletions bauble/src/parse/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ impl SpannedValue for ParseVal {
pub struct Binding {
pub type_path: Option<Path>,
pub value: ParseVal,
pub is_first: bool,
}

#[derive(Debug)]
Expand Down
20 changes: 16 additions & 4 deletions bauble/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ impl Display for TypeSystemError<'_> {
}

impl TypeRegistry {
/// The path of the builtin in generic `Ref<_>` type without the generics.
pub const REF_TYPE_PATH: &str = "Ref";

/// This is not performant and should not be exposed API, it is used for tests.
#[doc(hidden)]
pub fn find_rust_type(&self, ty: TypeId) -> Option<std::any::TypeId> {
Expand Down Expand Up @@ -429,8 +432,12 @@ impl TypeRegistry {
this.asset_refs.insert(inner, id);
let ty = Type {
meta: TypeMeta {
path: TypePath::new(format!("Ref<{}>", this.key_type(inner).meta.path))
.expect("Invariant"),
path: TypePath::new(format!(
"{}<{}>",
Self::REF_TYPE_PATH,
this.key_type(inner).meta.path
))
.expect("Invariant"),
traits: this.key_type(inner).meta.traits.clone(),
..Default::default()
},
Expand Down Expand Up @@ -586,11 +593,16 @@ impl TypeRegistry {
for (name, value) in additonal.into_objects() {
objects.push(crate::Object {
object_path: file.join(&name),
top_level: false,
value,
})
}

objects.push(crate::Object { object_path, value })
objects.push(crate::Object {
object_path,
top_level: false,
value,
})
}

// Check that instantiated objects match after being serialized to bauble text and
Expand Down Expand Up @@ -721,7 +733,7 @@ impl TypeRegistry {
}
}

/// Register `T` if it is not registerted already, then get the trait ID for `T`.
/// Register `T` if it is not registered already, then get the trait ID for `T`.
pub fn get_or_register_trait<T: ?Sized + BaubleTrait>(&mut self) -> TraitId {
let rust_id = std::any::TypeId::of::<T>();
if let Some(id) = self.type_from_rust.get(&rust_id) {
Expand Down
4 changes: 4 additions & 0 deletions bauble/src/types/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,15 @@ impl<S: AsRef<str>> TypePath<S> {
}

/// Split the start segment with the remaining segments.
///
/// Returns `None` if self is empty.
pub fn get_start(&self) -> Option<(TypePathElem<&str>, TypePath<&str>)> {
self.borrow().split_start()
}

/// Split the end segment with the remaining segments.
///
/// Returns `None` if self is empty.
pub fn get_end(&self) -> Option<(TypePath<&str>, TypePathElem<&str>)> {
self.borrow().split_end()
}
Expand Down
Loading
Loading