Skip to content

Commit

Permalink
feat: add global instances to LAD format (#283)
Browse files Browse the repository at this point in the history
* feat: add global instances to LAD format

* link to test data in readme
  • Loading branch information
makspll authored Feb 12, 2025
1 parent e6d0199 commit 2da36d5
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 249 deletions.
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

0 comments on commit 2da36d5

Please sign in to comment.