Skip to content

feat: add global instances to LAD format #283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 12, 2025
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
1 change: 1 addition & 0 deletions crates/ladfile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ repository = "https://github.com/makspll/bevy_mod_scripting"
homepage = "https://github.com/makspll/bevy_mod_scripting"
keywords = ["bevy", "gamedev", "scripting", "format", "json"]
categories = ["game-development", "parser-implementations"]
include = ["readme.md", "/src", "/test_assets"]
readme = "readme.md"

[dependencies]
Expand Down
243 changes: 1 addition & 242 deletions crates/ladfile/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,245 +9,4 @@ A file format specifying the available exported:
For a `bevy` game engine project.

## Example

```json
{
"version": "0.1.0",
"types": {
"ladfile::test::EnumType": {
"identifier": "EnumType",
"crate": "ladfile",
"path": "ladfile::test::EnumType",
"layout": [
{
"kind": "Unit",
"name": "Unit"
},
{
"kind": "Struct",
"name": "Struct",
"fields": [
{
"name": "field",
"type": "usize"
}
]
},
{
"kind": "TupleStruct",
"name": "TupleStruct",
"fields": [
{
"type": "usize"
},
{
"type": "alloc::string::String"
}
]
}
]
},
"ladfile::test::StructType<usize>": {
"identifier": "StructType",
"crate": "ladfile",
"path": "ladfile::test::StructType<usize>",
"generics": [
{
"type_id": "usize",
"name": "T"
}
],
"documentation": " I am a struct",
"layout": {
"kind": "Struct",
"name": "StructType",
"fields": [
{
"name": "field",
"type": "usize"
},
{
"name": "field2",
"type": "usize"
}
]
}
},
"ladfile::test::TupleStructType": {
"identifier": "TupleStructType",
"crate": "ladfile",
"path": "ladfile::test::TupleStructType",
"documentation": " I am a tuple test type",
"layout": {
"kind": "TupleStruct",
"name": "TupleStructType",
"fields": [
{
"type": "usize"
},
{
"type": "alloc::string::String"
}
]
}
},
"ladfile::test::UnitType": {
"identifier": "UnitType",
"crate": "ladfile",
"path": "ladfile::test::UnitType",
"documentation": " I am a unit test type",
"layout": {
"kind": "Struct",
"name": "UnitType"
}
}
},
"functions": {
"::hello_world": {
"identifier": "hello_world",
"arguments": [
{
"kind": {
"primitive": "usize"
},
"name": "arg1"
}
],
"return_type": "usize"
},
"ladfile::test::StructType<usize>::hello_world": {
"identifier": "hello_world",
"arguments": [
{
"kind": {
"primitive": "reflectReference"
},
"name": "ref_"
},
{
"kind": {
"tuple": [
{
"primitive": "usize"
},
{
"primitive": "string"
}
]
},
"name": "tuple"
},
{
"kind": {
"option": {
"vec": {
"ref": "ladfile::test::EnumType"
}
}
},
"name": "option_vec_ref_wrapper"
}
],
"return_type": "usize"
}
},
"primitives": {
"TypeId(0x0b36ea25c1cf517efce182c726ea2190)": {
"kind": "pathBuf",
"documentation": "A heap allocated file path"
},
"TypeId(0x1c306727557831f62320b5841ddc7eb3)": {
"kind": "dynamicFunction",
"documentation": "A callable dynamic function"
},
"TypeId(0x7adbf8cf2ed263727e95f06e821c8654)": {
"kind": "osString",
"documentation": "A heap allocated OS string"
},
"TypeId(0x7f945ad2d333d63863e3b6f35dfc0c5d)": {
"kind": "dynamicFunctionMut",
"documentation": "A stateful and callable dynamic function"
},
"TypeId(0xb98b1b7157a6417863eb502cd6cb5d6d)": {
"kind": "str",
"documentation": "A static string slice"
},
"alloc::string::String": {
"kind": "string",
"documentation": "A heap allocated string"
},
"bevy_mod_scripting_core::bindings::function::script_function::FunctionCallContext": {
"kind": "functionCallContext",
"documentation": "Function call context, if accepted by a function, means the function can access the world in arbitrary ways."
},
"bevy_mod_scripting_core::bindings::reference::ReflectReference": {
"kind": "reflectReference",
"documentation": "A reference to a reflectable type"
},
"bool": {
"kind": "bool",
"documentation": "A boolean value"
},
"char": {
"kind": "char",
"documentation": "An 8-bit character"
},
"f32": {
"kind": "f32",
"documentation": "A 32-bit floating point number"
},
"f64": {
"kind": "f64",
"documentation": "A 64-bit floating point number"
},
"i128": {
"kind": "i128",
"documentation": "A signed 128-bit integer"
},
"i16": {
"kind": "i16",
"documentation": "A signed 16-bit integer"
},
"i32": {
"kind": "i32",
"documentation": "A signed 32-bit integer"
},
"i64": {
"kind": "i64",
"documentation": "A signed 64-bit integer"
},
"i8": {
"kind": "i8",
"documentation": "A signed 8-bit integer"
},
"isize": {
"kind": "isize",
"documentation": "A signed pointer-sized integer"
},
"u128": {
"kind": "u128",
"documentation": "An unsigned 128-bit integer"
},
"u16": {
"kind": "u16",
"documentation": "An unsigned 16-bit integer"
},
"u32": {
"kind": "u32",
"documentation": "An unsigned 32-bit integer"
},
"u64": {
"kind": "u64",
"documentation": "An unsigned 64-bit integer"
},
"u8": {
"kind": "u8",
"documentation": "An unsigned 8-bit integer"
},
"usize": {
"kind": "usize",
"documentation": "An unsigned pointer-sized integer"
}
}
}
```
See an example of a `LAD` file [here](./test_assets/test.lad.json)
54 changes: 48 additions & 6 deletions crates/ladfile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@ use bevy_reflect::{
use indexmap::IndexMap;
use std::{any::TypeId, borrow::Cow, collections::HashMap, ffi::OsString, path::PathBuf};

const LAD_VERSION: &str = "0.1.0";
/// The current version of the LAD_VERSION format supported by this library.
/// Earlier versions are not guaranteed to be supported.
const LAD_VERSION: &str = env!("CARGO_PKG_VERSION");

#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
/// A Language Agnostic Declaration (LAD) file.
pub struct LadFile {
/// The version of the LAD file format used.
pub version: Cow<'static, str>,

/// The global instances defined in the LAD file.
pub globals: IndexMap<Cow<'static, str>, LadInstance>,

/// The types defined in the LAD file.
pub types: IndexMap<LadTypeId, LadType>,

Expand All @@ -38,6 +43,7 @@ impl LadFile {
pub fn new() -> Self {
Self {
version: LAD_VERSION.into(),
globals: IndexMap::new(),
types: IndexMap::new(),
functions: IndexMap::new(),
primitives: IndexMap::new(),
Expand All @@ -51,6 +57,19 @@ impl Default for LadFile {
}
}

/// A LAD global instance
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct LadInstance {
/// The type of the instance
pub type_id: LadTypeId,

/// whether the instance is static or not
///
/// static instances do not support method call syntax on them. I.e. only functions without a self parameter can be called on them.
/// They also do not support field access syntax.
pub is_static: bool,
}

#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
Expand Down Expand Up @@ -432,6 +451,24 @@ impl<'t> LadFileBuilder<'t> {
self
}

/// Add a global instance to the LAD file.
///
/// Requires the type to be registered via [`Self::add_type`] or [`Self::add_type_info`] first to provide rich type information.
///
/// If `is_static` is true, the instance will be treated as a static instance
/// and hence not support method call syntax or method calls (i.e. only functions without a self parameter can be called on them).
pub fn add_instance<T: 'static>(
&mut self,
key: impl Into<Cow<'static, str>>,
is_static: bool,
) -> &mut Self {
let type_id = self.lad_id_from_type_id(TypeId::of::<T>());
self.file
.globals
.insert(key.into(), LadInstance { type_id, is_static });
self
}

/// Add a type definition to the LAD file.
///
/// Equivalent to calling [`Self::add_type_info`] with `T::type_info()`.
Expand Down Expand Up @@ -726,7 +763,7 @@ mod test {
const BLESS_TEST_FILE: bool = false;

/// normalize line endings etc..
fn normalize_file_for_os(file: &mut String) {
fn normalize_file(file: &mut String) {
*file = file.replace("\r\n", "\n");
}

Expand Down Expand Up @@ -794,7 +831,7 @@ mod test {
.get_function_info("hello_world".into(), GlobalNamespace::into_namespace())
.with_arg_names(&["arg1"]);

let lad_file = LadFileBuilder::new(&type_registry)
let mut lad_file = LadFileBuilder::new(&type_registry)
.set_sorted(true)
.add_function_info(function_info)
.add_function_info(global_function_info)
Expand All @@ -803,10 +840,15 @@ mod test {
.add_type::<UnitType>()
.add_type::<TupleStructType>()
.add_type_info(EnumType::type_info())
.add_instance::<StructType<usize>>("my_static_instance", true)
.add_instance::<UnitType>("my_non_static_instance", false)
.build();

// normalize the version so we don't have to update it every time
lad_file.version = "{{version}}".into();
let mut serialized = serialize_lad_file(&lad_file, true).unwrap();

normalize_file_for_os(&mut serialized);
normalize_file(&mut serialized);

if BLESS_TEST_FILE {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
Expand All @@ -818,7 +860,7 @@ mod test {
}

let mut expected = include_str!("../test_assets/test.lad.json").to_owned();
normalize_file_for_os(&mut expected);
normalize_file(&mut expected);

assert_eq!(
serialized.trim(),
Expand All @@ -833,6 +875,6 @@ mod test {
fn test_asset_deserializes_correctly() {
let asset = include_str!("../test_assets/test.lad.json");
let deserialized = parse_lad_file(asset).unwrap();
assert_eq!(deserialized.version, LAD_VERSION);
assert_eq!(deserialized.version, "{{version}}");
}
}
12 changes: 11 additions & 1 deletion crates/ladfile/test_assets/test.lad.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
{
"version": "0.1.0",
"version": "{{version}}",
"globals": {
"my_static_instance": {
"type_id": "ladfile::test::StructType<usize>",
"is_static": true
},
"my_non_static_instance": {
"type_id": "ladfile::test::UnitType",
"is_static": false
}
},
"types": {
"ladfile::test::EnumType": {
"identifier": "EnumType",
Expand Down
Loading