diff --git a/Cargo.toml b/Cargo.toml index 89ef8c1950..54557c47da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,7 +88,7 @@ members = [ "crates/bevy_mod_scripting_functions", "crates/xtask", "crates/script_integration_test_harness", - "crates/bevy_mod_scripting_derive", + "crates/bevy_mod_scripting_derive", "crates/ladfile", ] resolver = "2" exclude = ["crates/bevy_api_gen", "crates/macro_tests"] diff --git a/crates/bevy_mod_scripting_core/Cargo.toml b/crates/bevy_mod_scripting_core/Cargo.toml index f657f88b68..d1b5c250f1 100644 --- a/crates/bevy_mod_scripting_core/Cargo.toml +++ b/crates/bevy_mod_scripting_core/Cargo.toml @@ -34,6 +34,7 @@ bevy = { workspace = true, default-features = false, features = [ "bevy_asset", "reflect_functions", ] } + thiserror = "1.0.31" parking_lot = "0.12.1" dashmap = "6" @@ -43,6 +44,7 @@ derivative = "2.2" profiling = { workspace = true } bevy_mod_scripting_derive = { workspace = true } + [dev-dependencies] test_utils = { workspace = true } tokio = { version = "1", features = ["rt", "macros"] } diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/into_ref.rs b/crates/bevy_mod_scripting_core/src/bindings/function/into_ref.rs index cda4eb4640..e39b98c246 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/into_ref.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/into_ref.rs @@ -30,11 +30,6 @@ pub trait IntoScriptRef { /// a utility for matching types by their [`std::any::TypeId`] macro_rules! match_by_type { (match $on:ident {$($id:ident : $ty:ty => $conv:expr),*}) => { - $( - #[allow(unused_variables)] - let $id = std::any::TypeId::of::<$ty>(); - )* - match $on { $( $id if $id == std::any::TypeId::of::<$ty>() => {$conv}, diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/mod.rs b/crates/bevy_mod_scripting_core/src/bindings/function/mod.rs index 6c5e08e121..d659932ea2 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/mod.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/mod.rs @@ -69,7 +69,7 @@ mod test { assert_eq!(test_fn.info.arg_info[1].name, Some("_arg1".into())); assert_eq!( - test_fn.info.return_info.as_ref().unwrap().type_id, + test_fn.info.return_info.type_id, std::any::TypeId::of::<()>() ); } diff --git a/crates/bevy_mod_scripting_core/src/docgen/info.rs b/crates/bevy_mod_scripting_core/src/docgen/info.rs index b22b460770..4fa182b900 100644 --- a/crates/bevy_mod_scripting_core/src/docgen/info.rs +++ b/crates/bevy_mod_scripting_core/src/docgen/info.rs @@ -23,7 +23,7 @@ pub struct FunctionInfo { /// Information about the arguments of the function. pub arg_info: Vec, /// Information about the return value of the function. - pub return_info: Option, + pub return_info: FunctionReturnInfo, /// Documentation for the function. pub docs: Option>, } @@ -41,7 +41,7 @@ impl FunctionInfo { name: Cow::Borrowed(""), namespace: Namespace::Global, arg_info: Vec::new(), - return_info: None, + return_info: FunctionReturnInfo::default(), docs: None, } } @@ -52,7 +52,7 @@ impl FunctionInfo { name, namespace, arg_info: Vec::new(), - return_info: None, + return_info: FunctionReturnInfo::default(), docs: None, } } @@ -69,7 +69,7 @@ impl FunctionInfo { /// Add a return value to the function info. pub fn add_return(mut self) -> Self { - self.return_info = Some(FunctionReturnInfo::new_for::()); + self.return_info = FunctionReturnInfo::new_for::(); self } @@ -136,6 +136,12 @@ pub struct FunctionReturnInfo { pub type_id: TypeId, } +impl Default for FunctionReturnInfo { + fn default() -> Self { + Self::new_for::<()>() + } +} + impl FunctionReturnInfo { /// Create a new function return info for a specific type. pub fn new_for() -> Self { @@ -169,7 +175,10 @@ bevy::utils::all_tuples!(impl_documentable, 0, 13, T); #[cfg(test)] mod test { - use crate::bindings::function::from::{Mut, Ref, Val}; + use crate::{ + bindings::function::from::{Mut, Ref, Val}, + docgen::typed_through::UntypedWrapperKind, + }; use super::*; @@ -183,7 +192,7 @@ mod test { assert_eq!(info.name, "test_fn"); assert_eq!(info.namespace, Namespace::Global); assert_eq!(info.arg_info.len(), 2); - assert_eq!(info.return_info.unwrap().type_id, TypeId::of::()); + assert_eq!(info.return_info.type_id, TypeId::of::()); assert_eq!(info.arg_info[0].type_id, TypeId::of::()); assert_eq!(info.arg_info[1].type_id, TypeId::of::()); @@ -211,33 +220,33 @@ mod test { assert_eq!(info.name, "test_fn"); assert_eq!(info.namespace, Namespace::Global); assert_eq!(info.arg_info.len(), 2); - assert_eq!(info.return_info.unwrap().type_id, TypeId::of::>()); + assert_eq!(info.return_info.type_id, TypeId::of::>()); assert_eq!(info.arg_info[0].type_id, TypeId::of::>()); assert_eq!(info.arg_info[1].type_id, TypeId::of::>()); - match info.arg_info[0].type_info.as_ref().unwrap() { - ThroughTypeInfo::UntypedWrapper { + match &info.arg_info[0].type_info { + Some(ThroughTypeInfo::UntypedWrapper { through_type, wrapper_type_id, - wrapper_name, - } => { + wrapper_kind, + }) => { assert_eq!(through_type.type_id(), TypeId::of::()); assert_eq!(*wrapper_type_id, TypeId::of::>()); - assert_eq!(*wrapper_name, "Ref"); + assert_eq!(*wrapper_kind, UntypedWrapperKind::Ref); } _ => panic!("Expected UntypedWrapper"), } - match info.arg_info[1].type_info.as_ref().unwrap() { - ThroughTypeInfo::UntypedWrapper { + match &info.arg_info[1].type_info { + Some(ThroughTypeInfo::UntypedWrapper { through_type, wrapper_type_id, - wrapper_name, - } => { + wrapper_kind, + }) => { assert_eq!(through_type.type_id(), TypeId::of::()); assert_eq!(*wrapper_type_id, TypeId::of::>()); - assert_eq!(*wrapper_name, "Mut"); + assert_eq!(*wrapper_kind, UntypedWrapperKind::Mut); } _ => panic!("Expected UntypedWrapper"), } diff --git a/crates/bevy_mod_scripting_core/src/docgen/typed_through.rs b/crates/bevy_mod_scripting_core/src/docgen/typed_through.rs index 3efb4f1155..e6fefdd88f 100644 --- a/crates/bevy_mod_scripting_core/src/docgen/typed_through.rs +++ b/crates/bevy_mod_scripting_core/src/docgen/typed_through.rs @@ -37,7 +37,7 @@ pub enum ThroughTypeInfo { /// The type id of the wrapper type. wrapper_type_id: TypeId, /// The name of the wrapper type. - wrapper_name: &'static str, + wrapper_kind: UntypedWrapperKind, }, /// A wrapper around a through typed type, which itself is also a `Typed` type. TypedWrapper(TypedWrapperKind), @@ -45,6 +45,17 @@ pub enum ThroughTypeInfo { TypeInfo(&'static TypeInfo), } +#[derive(Debug, Clone, PartialEq, Eq)] +/// The kind of untyped wrapper. +pub enum UntypedWrapperKind { + /// A reference wrapper. + Ref, + /// A mutable reference wrapper. + Mut, + /// A value wrapper. + Val, +} + /// The kind of typed wrapper. #[derive(Debug, Clone)] pub enum TypedWrapperKind { @@ -73,7 +84,7 @@ impl TypedThrough for Ref<'_, T> { ThroughTypeInfo::UntypedWrapper { through_type: T::type_info(), wrapper_type_id: TypeId::of::>(), - wrapper_name: "Ref", + wrapper_kind: UntypedWrapperKind::Ref, } } } @@ -83,7 +94,7 @@ impl TypedThrough for Mut<'_, T> { ThroughTypeInfo::UntypedWrapper { through_type: T::type_info(), wrapper_type_id: TypeId::of::>(), - wrapper_name: "Mut", + wrapper_kind: UntypedWrapperKind::Mut, } } } @@ -93,7 +104,7 @@ impl TypedThrough for Val { ThroughTypeInfo::UntypedWrapper { through_type: T::type_info(), wrapper_type_id: TypeId::of::>(), - wrapper_name: "Val", + wrapper_kind: UntypedWrapperKind::Val, } } } diff --git a/crates/ladfile/.gitignore b/crates/ladfile/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/crates/ladfile/.gitignore @@ -0,0 +1 @@ +/target diff --git a/crates/ladfile/Cargo.toml b/crates/ladfile/Cargo.toml new file mode 100644 index 0000000000..ff909b559b --- /dev/null +++ b/crates/ladfile/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "ladfile" +version = "0.1.0" +edition = "2021" +authors = ["Maksymilian Mozolewski "] +license = "MIT OR Apache-2.0" +description = "Language Agnostic Declaration (LAD) file format for the bevy_mod_scripting crate" +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"] +readme = "readme.md" + +[dependencies] +bevy_mod_scripting_core = { workspace = true } +# I don't think bevy has a top level feature for this :C +bevy_reflect = { version = "0.15.1", features = ["documentation"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +indexmap = { version = "2.7", features = ["serde"] } + +[lints] +workspace = true diff --git a/crates/ladfile/readme.md b/crates/ladfile/readme.md new file mode 100644 index 0000000000..ed86ada64b --- /dev/null +++ b/crates/ladfile/readme.md @@ -0,0 +1,253 @@ +# Language Agnostic Declaration file + +A file format specifying the available exported: +- functions +- types +- primitives +- documentation + +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": { + "identifier": "StructType", + "crate": "ladfile", + "path": "ladfile::test::StructType", + "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::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" + } + } +} +``` diff --git a/crates/ladfile/src/lib.rs b/crates/ladfile/src/lib.rs new file mode 100644 index 0000000000..964b06ca72 --- /dev/null +++ b/crates/ladfile/src/lib.rs @@ -0,0 +1,838 @@ +//! Parsing definitions for the LAD (Language Agnostic Decleration) file format. + +use bevy_mod_scripting_core::{ + bindings::{function::script_function::FunctionCallContext, ReflectReference}, + docgen::{ + info::FunctionInfo, + typed_through::{ThroughTypeInfo, TypedWrapperKind, UntypedWrapperKind}, + }, + match_by_type, +}; +use bevy_reflect::{ + func::{DynamicFunction, DynamicFunctionMut}, + NamedField, Reflect, TypeInfo, TypeRegistry, Typed, UnnamedField, +}; +use indexmap::IndexMap; +use std::{any::TypeId, borrow::Cow, collections::HashMap, ffi::OsString, path::PathBuf}; + +const LAD_VERSION: &str = "0.1.0"; + +#[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 types defined in the LAD file. + pub types: IndexMap, + + /// The functions defined in the LAD file. + pub functions: IndexMap, + + /// A mapping from type ids to primitive types + pub primitives: IndexMap, +} + +impl LadFile { + /// Create a new empty LAD file. + pub fn new() -> Self { + Self { + version: LAD_VERSION.into(), + types: IndexMap::new(), + functions: IndexMap::new(), + primitives: IndexMap::new(), + } + } +} + +impl Default for LadFile { + fn default() -> Self { + Self::new() + } +} + +#[derive( + Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, +)] +/// A unique identifier for a function in a LAD file. +/// Only unique within the LAD file. +pub struct LadFunctionId(String); + +impl LadFunctionId { + /// Create a new LAD function id with a string. + pub fn new_string_id(function_id: String) -> Self { + LadFunctionId(function_id) + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +/// A function definition used in a LAD file. +pub struct LadFunction { + /// The identifier or name of the function. + pub identifier: Cow<'static, str>, + /// The argument information for the function. + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub arguments: Vec, + /// The return type of the function. + pub return_type: LadTypeId, + /// The documentation describing the function. + #[serde(skip_serializing_if = "Option::is_none", default)] + pub documentation: Option>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +/// An argument definition used in a LAD file. +pub struct LadArgument { + /// The kind and type of argument + pub kind: LadArgumentKind, + /// The name of the argument + #[serde(skip_serializing_if = "Option::is_none", default)] + pub name: Option>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +/// The kind of an argument in a LAD file. +#[serde(rename_all = "camelCase")] +pub enum LadArgumentKind { + /// a `Ref` wrapped argument + Ref(LadTypeId), + /// a `Mut` wrapped argument + Mut(LadTypeId), + /// a `Val` wrapped argument + Val(LadTypeId), + /// an `Option` wrapped argument + Option(Box), + /// a `Vec` + Vec(Box), + /// a `HashMap` + HashMap(Box, Box), + /// A `InteropResult` + InteropResult(Box), + /// A tuple of arguments + Tuple(Vec), + /// An array + Array(Box, usize), + /// A primitive type, implementing `IntoScript` and `FromScript` natively in BMS. + Primitive(LadBMSPrimitiveKind), + /// An arbitrary type which is either unsupported, doesn't contain type information, or is generally unknown. + /// + /// This will be the variant used for external primitives as well. + Unknown(LadTypeId), +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +/// A BMS primitive definition +pub struct LadBMSPrimitiveType { + /// The kind of primitive + pub kind: LadBMSPrimitiveKind, + /// The documentation describing the primitive + pub documentation: Cow<'static, str>, +} + +/// A primitive type kind in the LAD file format. +/// +/// The docstrings on variants corresponding to Reflect types, are used to generate documentation for these primitives. +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Reflect)] +#[serde(rename_all = "camelCase")] +#[allow(missing_docs)] +pub enum LadBMSPrimitiveKind { + Bool, + Isize, + I8, + I16, + I32, + I64, + I128, + Usize, + U8, + U16, + U32, + U64, + U128, + F32, + F64, + Char, + Str, + String, + OsString, + PathBuf, + FunctionCallContext, + DynamicFunction, + DynamicFunctionMut, + ReflectReference, +} + +impl LadBMSPrimitiveKind { + /// Get the corresponding type id for a primitive kind. + pub fn lad_type_id(self) -> LadTypeId { + match self { + LadBMSPrimitiveKind::Bool => LadTypeId::new_string_id("bool".into()), + LadBMSPrimitiveKind::Isize => LadTypeId::new_string_id("isize".into()), + LadBMSPrimitiveKind::I8 => LadTypeId::new_string_id("i8".into()), + LadBMSPrimitiveKind::I16 => LadTypeId::new_string_id("i16".into()), + LadBMSPrimitiveKind::I32 => LadTypeId::new_string_id("i32".into()), + LadBMSPrimitiveKind::I64 => LadTypeId::new_string_id("i64".into()), + LadBMSPrimitiveKind::I128 => LadTypeId::new_string_id("i128".into()), + LadBMSPrimitiveKind::Usize => LadTypeId::new_string_id("usize".into()), + LadBMSPrimitiveKind::U8 => LadTypeId::new_string_id("u8".into()), + LadBMSPrimitiveKind::U16 => LadTypeId::new_string_id("u16".into()), + LadBMSPrimitiveKind::U32 => LadTypeId::new_string_id("u32".into()), + LadBMSPrimitiveKind::U64 => LadTypeId::new_string_id("u64".into()), + LadBMSPrimitiveKind::U128 => LadTypeId::new_string_id("u128".into()), + LadBMSPrimitiveKind::F32 => LadTypeId::new_string_id("f32".into()), + LadBMSPrimitiveKind::F64 => LadTypeId::new_string_id("f64".into()), + LadBMSPrimitiveKind::Char => LadTypeId::new_string_id("char".into()), + LadBMSPrimitiveKind::Str => LadTypeId::new_string_id("str".into()), + LadBMSPrimitiveKind::String => LadTypeId::new_string_id("String".into()), + LadBMSPrimitiveKind::OsString => LadTypeId::new_string_id("OsString".into()), + LadBMSPrimitiveKind::PathBuf => LadTypeId::new_string_id("PathBuf".into()), + LadBMSPrimitiveKind::FunctionCallContext => { + LadTypeId::new_string_id("FunctionCallContext".into()) + } + LadBMSPrimitiveKind::DynamicFunction => { + LadTypeId::new_string_id("DynamicFunction".into()) + } + LadBMSPrimitiveKind::DynamicFunctionMut => { + LadTypeId::new_string_id("DynamicFunctionMut".into()) + } + LadBMSPrimitiveKind::ReflectReference => { + LadTypeId::new_string_id("ReflectReference".into()) + } + } + } + + /// We can assume that the types here will be either primitives + /// or reflect types, as the rest will be covered by typed wrappers + /// so just check + fn from_type_id(type_id: TypeId) -> Option { + match_by_type!(match type_id { + i: bool => return Some(LadBMSPrimitiveKind::Bool), + i: isize => return Some(LadBMSPrimitiveKind::Isize), + i: i8 => return Some(LadBMSPrimitiveKind::I8), + i: i16 => return Some(LadBMSPrimitiveKind::I16), + i: i32 => return Some(LadBMSPrimitiveKind::I32), + i: i64 => return Some(LadBMSPrimitiveKind::I64), + i: i128 => return Some(LadBMSPrimitiveKind::I128), + i: usize => return Some(LadBMSPrimitiveKind::Usize), + i: u8 => return Some(LadBMSPrimitiveKind::U8), + i: u16 => return Some(LadBMSPrimitiveKind::U16), + i: u32 => return Some(LadBMSPrimitiveKind::U32), + i: u64 => return Some(LadBMSPrimitiveKind::U64), + i: u128 => return Some(LadBMSPrimitiveKind::U128), + i: f32 => return Some(LadBMSPrimitiveKind::F32), + i: f64 => return Some(LadBMSPrimitiveKind::F64), + i: char => return Some(LadBMSPrimitiveKind::Char), + i: &'static str => return Some(LadBMSPrimitiveKind::Str), + i: str => return Some(LadBMSPrimitiveKind::Str), + i: String => return Some(LadBMSPrimitiveKind::String), + i: OsString => return Some(LadBMSPrimitiveKind::OsString), + i: PathBuf => return Some(LadBMSPrimitiveKind::PathBuf), + i: FunctionCallContext => return Some(LadBMSPrimitiveKind::FunctionCallContext), + i: DynamicFunction => return Some(LadBMSPrimitiveKind::DynamicFunction), + i: DynamicFunctionMut => return Some(LadBMSPrimitiveKind::DynamicFunctionMut), + i: ReflectReference => return Some(LadBMSPrimitiveKind::ReflectReference) + }); + + None + } +} + +#[derive( + Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, +)] +/// A unique identifier for a type in a LAD file. +/// +/// Only guaranteed to be unique within the LAD file. +/// It *might* be unique across LAD files, but this is not guaranteed and depends on the type itself. +pub struct LadTypeId(Cow<'static, str>); + +impl LadTypeId { + /// Create a new LAD type id with a specific index. + pub fn new_string_id(type_id: Cow<'static, str>) -> Self { + LadTypeId(type_id) + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +/// A type definition used in a LAD file. +pub struct LadType { + /// The identifier or name of the type. + pub identifier: String, + + /// The source crate of the type + #[serde(rename = "crate", skip_serializing_if = "Option::is_none", default)] + pub crate_: Option, + + /// The full path of the type + pub path: String, + + /// The generic parameters of the type. + /// + /// Generics are always monomorphized in the LAD file format. + /// Meaning that they are always assigned a concrete type. + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub generics: Vec, + + /// The documentation describing the type. + #[serde(skip_serializing_if = "Option::is_none", default)] + pub documentation: Option, + + /// Functions which are "associated" with this type. + /// I.e. those which are either methods or static functions of this type. + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub associated_functions: Vec, + + /// The layout or kind of the type. + pub layout: LadTypeLayout, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +/// Description of a type layout in a LAD file. +pub enum LadTypeLayout { + /// A type with hidden layout + Opaque, + /// A type with at least one variant + MonoVariant(LadVariant), + /// A type with multiple variants + Enum(Vec), +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(tag = "kind")] +/// A variant definition used in a LAD file. +pub enum LadVariant { + /// A tuple struct variant i.e. a struct with unnamed fields. + TupleStruct { + /// The name of the variant. + /// + /// For types which are not Enums, this will simply be the name of the type or its path if no identifier is present. + name: Cow<'static, str>, + + /// The fields of the tuple struct variant. + #[serde(skip_serializing_if = "Vec::is_empty", default)] + fields: Vec, + }, + /// A struct variant i.e. a struct with named fields. + Struct { + /// The name of the variant. + /// + /// For types which are not Enums, this will simply be the name of the type or its path if no identifier is present. + name: Cow<'static, str>, + + /// The fields of the struct variant. + #[serde(skip_serializing_if = "Vec::is_empty", default)] + fields: Vec, + }, + /// A unit variant i.e. a type with no fields + Unit { + /// The name of the variant. + /// + /// For types which are not Enums, this will simply be the name of the type or its path if no identifier is present. + name: Cow<'static, str>, + }, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +/// A field definition used in a LAD file. +pub struct LadField { + #[serde(rename = "type")] + type_: LadTypeId, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +/// A named field definition used in a LAD file. +pub struct LadNamedField { + name: String, + #[serde(rename = "type")] + type_: LadTypeId, +} + +/// A generic type definition used in a LAD file. +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct LadGeneric { + /// The id of the type assigned to this generic. + pub type_id: LadTypeId, + /// The name of the generic + pub name: String, +} + +/// A builder for constructing LAD files. +/// This should be your preferred way of constructing LAD files. +pub struct LadFileBuilder<'t> { + file: LadFile, + type_id_mapping: HashMap, + type_registry: &'t TypeRegistry, + sorted: bool, +} + +impl<'t> LadFileBuilder<'t> { + /// Create a new LAD file builder loaded with primitives. + pub fn new(type_registry: &'t TypeRegistry) -> Self { + let mut builder = Self { + file: LadFile::new(), + type_id_mapping: HashMap::new(), + type_registry, + sorted: false, + }; + + builder + .add_bms_primitive::("A boolean value") + .add_bms_primitive::("A signed pointer-sized integer") + .add_bms_primitive::("A signed 8-bit integer") + .add_bms_primitive::("A signed 16-bit integer") + .add_bms_primitive::("A signed 32-bit integer") + .add_bms_primitive::("A signed 64-bit integer") + .add_bms_primitive::("A signed 128-bit integer") + .add_bms_primitive::("An unsigned pointer-sized integer") + .add_bms_primitive::("An unsigned 8-bit integer") + .add_bms_primitive::("An unsigned 16-bit integer") + .add_bms_primitive::("An unsigned 32-bit integer") + .add_bms_primitive::("An unsigned 64-bit integer") + .add_bms_primitive::("An unsigned 128-bit integer") + .add_bms_primitive::("A 32-bit floating point number") + .add_bms_primitive::("A 64-bit floating point number") + .add_bms_primitive::("An 8-bit character") + .add_bms_primitive::<&'static str>("A string slice") + .add_bms_primitive::("A heap allocated string") + .add_bms_primitive::("A heap allocated OS string") + .add_bms_primitive::("A heap allocated file path") + .add_bms_primitive::("Function call context, if accepted by a function, means the function can access the world in arbitrary ways.") + .add_bms_primitive::("A callable dynamic function") + .add_bms_primitive::("A stateful and callable dynamic function") + .add_bms_primitive::("A reference to a reflectable type"); + + builder + } + + /// Set whether the LAD file should be sorted at build time. + pub fn set_sorted(&mut self, sorted: bool) -> &mut Self { + self.sorted = sorted; + self + } + + /// Add a BMS primitive to the LAD file. + /// Will do nothing if the type is not a BMS primitive. + pub fn add_bms_primitive( + &mut self, + docs: impl Into>, + ) -> &mut Self { + let type_id = self.lad_id_from_type_id(TypeId::of::()); + let kind = match LadBMSPrimitiveKind::from_type_id(TypeId::of::()) { + Some(primitive) => primitive, + None => return self, + }; + self.file.primitives.insert( + type_id, + LadBMSPrimitiveType { + kind, + documentation: docs.into(), + }, + ); + self + } + + /// Add a type definition to the LAD file. + /// + /// Equivalent to calling [`Self::add_type_info`] with `T::type_info()`. + pub fn add_type(&mut self) -> &mut Self { + self.add_type_info(T::type_info()); + self + } + + /// Add a type definition to the LAD file. + /// Will overwrite any existing type definitions with the same type id. + pub fn add_type_info(&mut self, type_info: &TypeInfo) -> &mut Self { + let type_id = self.lad_id_from_type_id(type_info.type_id()); + let lad_type = LadType { + identifier: type_info + .type_path_table() + .ident() + .unwrap_or_default() + .to_string(), + generics: type_info + .generics() + .iter() + .map(|param| LadGeneric { + type_id: self.lad_id_from_type_id(param.type_id()), + name: param.name().to_string(), + }) + .collect(), + documentation: type_info.docs().map(|s| s.to_string()), + associated_functions: Vec::new(), + crate_: type_info + .type_path_table() + .crate_name() + .map(|s| s.to_owned()), + path: type_info.type_path_table().path().to_owned(), + layout: self.lad_layout_from_type_info(type_info), + }; + self.file.types.insert(type_id, lad_type); + self + } + + /// Add a function definition to the LAD file. + /// Will overwrite any existing function definitions with the same function id. + pub fn add_function_info(&mut self, function_info: FunctionInfo) -> &mut Self { + let function_id = self.lad_function_id_from_info(&function_info); + let lad_function = LadFunction { + identifier: function_info.name, + arguments: function_info + .arg_info + .into_iter() + .map(|arg| { + let kind = match &arg.type_info { + Some(through_type) => { + self.lad_argument_type_from_through_type(through_type) + } + None => LadArgumentKind::Unknown(self.lad_id_from_type_id(arg.type_id)), + }; + LadArgument { + kind, + name: arg.name, + } + }) + .collect(), + return_type: self.lad_id_from_type_id(function_info.return_info.type_id), + documentation: function_info.docs, + }; + self.file.functions.insert(function_id, lad_function); + self + } + + /// Build the finalized and optimized LAD file. + pub fn build(&mut self) -> LadFile { + let mut file = std::mem::replace(&mut self.file, LadFile::new()); + if self.sorted { + file.types.sort_keys(); + file.functions.sort_keys(); + file.primitives.sort_keys(); + } + + file + } + + fn variant_identifier_for_non_enum(type_info: &TypeInfo) -> Cow<'static, str> { + type_info + .type_path_table() + .ident() + .unwrap_or_else(|| type_info.type_path_table().path()) + .into() + } + + fn struct_variant_from_named_fields<'a, I: Iterator>( + &mut self, + name: Cow<'static, str>, + fields: I, + ) -> LadVariant { + LadVariant::Struct { + name, + fields: fields + .map(|field| LadNamedField { + name: field.name().to_string(), + type_: self.lad_id_from_type_id(field.type_id()), + }) + .collect(), + } + } + + fn tuple_struct_variant_from_fields<'a, I: Iterator>( + &mut self, + name: Cow<'static, str>, + fields: I, + ) -> LadVariant { + LadVariant::TupleStruct { + name, + fields: fields + .map(|field| LadField { + type_: self.lad_id_from_type_id(field.type_id()), + }) + .collect(), + } + } + + fn lad_layout_from_type_info(&mut self, type_info: &TypeInfo) -> LadTypeLayout { + match type_info { + TypeInfo::Struct(struct_info) => { + let fields = (0..struct_info.field_len()).filter_map(|i| struct_info.field_at(i)); + + LadTypeLayout::MonoVariant(self.struct_variant_from_named_fields( + Self::variant_identifier_for_non_enum(type_info), + fields, + )) + } + TypeInfo::TupleStruct(tuple_struct_info) => { + let fields = (0..tuple_struct_info.field_len()) + .filter_map(|i| tuple_struct_info.field_at(i)); + + LadTypeLayout::MonoVariant(self.tuple_struct_variant_from_fields( + Self::variant_identifier_for_non_enum(type_info), + fields, + )) + } + TypeInfo::Enum(enum_info) => { + let mut variants = Vec::new(); + for i in 0..enum_info.variant_len() { + if let Some(variant) = enum_info.variant_at(i) { + let variant_name = variant.name(); + let variant = match variant { + bevy_reflect::VariantInfo::Struct(struct_variant_info) => { + let fields = (0..struct_variant_info.field_len()) + .filter_map(|i| struct_variant_info.field_at(i)); + + self.struct_variant_from_named_fields(variant_name.into(), fields) + } + bevy_reflect::VariantInfo::Tuple(tuple_variant_info) => { + let fields = (0..tuple_variant_info.field_len()) + .filter_map(|i| tuple_variant_info.field_at(i)); + + self.tuple_struct_variant_from_fields(variant_name.into(), fields) + } + bevy_reflect::VariantInfo::Unit(_) => LadVariant::Unit { + name: variant_name.into(), + }, + }; + variants.push(variant); + } + } + LadTypeLayout::Enum(variants) + } + _ => LadTypeLayout::Opaque, + } + } + + fn lad_id_from_type_id(&mut self, type_id: TypeId) -> LadTypeId { + if let Some(lad_id) = self.type_id_mapping.get(&type_id) { + return lad_id.clone(); + } + + let new_id = match LadBMSPrimitiveKind::from_type_id(type_id) { + Some(primitive) => primitive.lad_type_id(), + None => { + if let Some(info) = self.type_registry.get_type_info(type_id) { + LadTypeId::new_string_id(info.type_path_table().path().into()) + } else { + LadTypeId::new_string_id(format!("{type_id:?}").into()) + } + } + }; + + self.type_id_mapping.insert(type_id, new_id.clone()); + new_id + } + + fn lad_function_id_from_info(&mut self, function_info: &FunctionInfo) -> LadFunctionId { + let namespace_string = match function_info.namespace { + bevy_mod_scripting_core::bindings::function::namespace::Namespace::Global => { + "".to_string() + } + bevy_mod_scripting_core::bindings::function::namespace::Namespace::OnType(type_id) => { + self.lad_id_from_type_id(type_id).0.to_string() + } + }; + + LadFunctionId::new_string_id(format!("{}::{}", namespace_string, function_info.name)) + } + + fn lad_argument_type_from_through_type( + &mut self, + through_type: &ThroughTypeInfo, + ) -> LadArgumentKind { + match through_type { + ThroughTypeInfo::UntypedWrapper { + through_type, + wrapper_kind, + .. + } => match wrapper_kind { + UntypedWrapperKind::Ref => { + LadArgumentKind::Ref(self.lad_id_from_type_id(through_type.type_id())) + } + UntypedWrapperKind::Mut => { + LadArgumentKind::Mut(self.lad_id_from_type_id(through_type.type_id())) + } + UntypedWrapperKind::Val => { + LadArgumentKind::Val(self.lad_id_from_type_id(through_type.type_id())) + } + }, + ThroughTypeInfo::TypedWrapper(typed_wrapper_kind) => match typed_wrapper_kind { + TypedWrapperKind::Vec(through_type_info) => LadArgumentKind::Vec(Box::new( + self.lad_argument_type_from_through_type(through_type_info), + )), + TypedWrapperKind::HashMap(through_type_info, through_type_info1) => { + LadArgumentKind::HashMap( + Box::new(self.lad_argument_type_from_through_type(through_type_info)), + Box::new(self.lad_argument_type_from_through_type(through_type_info1)), + ) + } + TypedWrapperKind::Array(through_type_info, size) => LadArgumentKind::Array( + Box::new(self.lad_argument_type_from_through_type(through_type_info)), + *size, + ), + TypedWrapperKind::Option(through_type_info) => LadArgumentKind::Option(Box::new( + self.lad_argument_type_from_through_type(through_type_info), + )), + TypedWrapperKind::InteropResult(through_type_info) => { + LadArgumentKind::InteropResult(Box::new( + self.lad_argument_type_from_through_type(through_type_info), + )) + } + TypedWrapperKind::Tuple(through_type_infos) => LadArgumentKind::Tuple( + through_type_infos + .iter() + .map(|through_type_info| { + self.lad_argument_type_from_through_type(through_type_info) + }) + .collect(), + ), + }, + ThroughTypeInfo::TypeInfo(type_info) => { + match LadBMSPrimitiveKind::from_type_id(type_info.type_id()) { + Some(primitive) => LadArgumentKind::Primitive(primitive), + None => LadArgumentKind::Unknown(self.lad_id_from_type_id(type_info.type_id())), + } + } + } + } +} + +/// Parses a toml string into a LAD file. +pub fn parse_lad_file(toml: &str) -> Result { + serde_json::from_str(toml) +} + +/// Serializes a LAD file into a toml file. +pub fn serialize_lad_file(lad_file: &LadFile, pretty: bool) -> Result { + if pretty { + serde_json::to_string_pretty(lad_file) + } else { + serde_json::to_string(lad_file) + } +} + +#[cfg(test)] +mod test { + use bevy_mod_scripting_core::{ + bindings::function::{ + from::Ref, + namespace::{GlobalNamespace, IntoNamespace}, + }, + docgen::info::GetFunctionInfo, + }; + use bevy_reflect::Typed; + + use super::*; + + /// Set to true to put output into test_assets. + const BLESS_TEST_FILE: bool = false; + + /// normalize line endings etc.. + fn normalize_file_for_os(file: &mut String) { + *file = file.replace("\r\n", "\n"); + } + + #[test] + fn test_empty_lad_file_serializes_correctly() { + let lad_file = LadFile::new(); + let serialized = serialize_lad_file(&lad_file, false).unwrap(); + let deserialized = parse_lad_file(&serialized).unwrap(); + assert_eq!(lad_file, deserialized); + assert_eq!(deserialized.version, LAD_VERSION); + } + + #[test] + fn test_serializes_as_expected() { + let mut type_registry = TypeRegistry::default(); + + #[derive(Reflect)] + /// I am a struct + struct StructType { + /// hello from field + field: usize, + /// hello from field 2 + field2: T, + } + + #[derive(Reflect)] + /// I am a unit test type + struct UnitType; + + #[derive(Reflect)] + /// I am a tuple test type + struct TupleStructType(pub usize, #[doc = "hello"] pub String); + + #[derive(Reflect)] + enum EnumType { + /// hello from variant + Unit, + /// hello from variant 2 + Struct { + /// hello from field + field: usize, + }, + /// hello from variant 3 + TupleStruct(usize, #[doc = "asd"] String), + } + + type_registry.register::>(); + type_registry.register::(); + type_registry.register::(); + type_registry.register::(); + + let function = |_: ReflectReference, _: usize| 2usize; + let function_info = function + .get_function_info("hello_world".into(), StructType::::into_namespace()) + .with_docs("hello docs"); + + let function_with_complex_args = + |_: ReflectReference, _: (usize, String), _: Option>>| 2usize; + let function_with_complex_args_info = function_with_complex_args + .get_function_info("hello_world".into(), StructType::::into_namespace()) + .with_arg_names(&["ref_", "tuple", "option_vec_ref_wrapper"]); + + let global_function = |_: usize| 2usize; + let global_function_info = global_function + .get_function_info("hello_world".into(), GlobalNamespace::into_namespace()) + .with_arg_names(&["arg1"]); + + let lad_file = LadFileBuilder::new(&type_registry) + .set_sorted(true) + .add_function_info(function_info) + .add_function_info(global_function_info) + .add_function_info(function_with_complex_args_info) + .add_type::>() + .add_type::() + .add_type::() + .add_type_info(EnumType::type_info()) + .build(); + let mut serialized = serialize_lad_file(&lad_file, true).unwrap(); + + normalize_file_for_os(&mut serialized); + + if BLESS_TEST_FILE { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let path_to_test_assets = std::path::Path::new(&manifest_dir).join("test_assets"); + + println!("Blessing test file at {:?}", path_to_test_assets); + std::fs::write(path_to_test_assets.join("test.lad.json"), &serialized).unwrap(); + return; + } + + let mut expected = include_str!("../test_assets/test.lad.json").to_owned(); + normalize_file_for_os(&mut expected); + + assert_eq!( + serialized.trim(), + expected.trim(), + "Expected:---\n {}\n---\nGot: ---\n{}\n---", + expected, + serialized + ); + } + + #[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); + } +} diff --git a/crates/ladfile/test_assets/test.lad.json b/crates/ladfile/test_assets/test.lad.json new file mode 100644 index 0000000000..8a9a6ab71e --- /dev/null +++ b/crates/ladfile/test_assets/test.lad.json @@ -0,0 +1,239 @@ +{ + "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": "String" + } + ] + } + ] + }, + "ladfile::test::StructType": { + "identifier": "StructType", + "crate": "ladfile", + "path": "ladfile::test::StructType", + "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": "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::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": { + "DynamicFunction": { + "kind": "dynamicFunction", + "documentation": "A callable dynamic function" + }, + "DynamicFunctionMut": { + "kind": "dynamicFunctionMut", + "documentation": "A stateful and callable dynamic function" + }, + "FunctionCallContext": { + "kind": "functionCallContext", + "documentation": "Function call context, if accepted by a function, means the function can access the world in arbitrary ways." + }, + "OsString": { + "kind": "osString", + "documentation": "A heap allocated OS string" + }, + "PathBuf": { + "kind": "pathBuf", + "documentation": "A heap allocated file path" + }, + "ReflectReference": { + "kind": "reflectReference", + "documentation": "A reference to a reflectable type" + }, + "String": { + "kind": "string", + "documentation": "A heap allocated string" + }, + "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" + }, + "str": { + "kind": "str", + "documentation": "A string slice" + }, + "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" + } + } +} \ No newline at end of file diff --git a/release-plz.toml b/release-plz.toml index 116b184194..bc0497d441 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -3,6 +3,15 @@ dependencies_update = false publish_timeout = "30m" git_release_enable = false git_tag_enable = false +git_release_body = """ +{{ changelog }} +{% if remote.contributors %} +### Contributors +{% for contributor in remote.contributors %} +* @{{ contributor.username }} +{% endfor %} +{% endif %} +""" [changelog] commit_parsers = [ @@ -27,15 +36,7 @@ git_release_enable = true git_tag_enable = true git_tag_name = "v{{ version }}" git_release_name = "v{{ version }}" -git_release_body = """ -{{ changelog }} -{% if remote.contributors %} -### Contributors -{% for contributor in remote.contributors %} -* @{{ contributor.username }} -{% endfor %} -{% endif %} -""" + changelog_include = [ "bevy_mod_scripting_lua", "bevy_mod_scripting_core", @@ -68,3 +69,11 @@ version_group = "main" [[package]] name = "bevy_mod_scripting_functions" version_group = "main" + +[[package]] +name = "ladfile" +git_release_enable = true +git_release_latest = false +git_tag_enable = true +git_tag_name = "v{{ version }}-ladfile" +git_release_name = "v{{ version }}-ladfile" diff --git a/tealr_doc_gen_config.json b/tealr_doc_gen_config.json deleted file mode 100644 index 819078d063..0000000000 --- a/tealr_doc_gen_config.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "doc_template": "Builtin", - "page_root": "", - "store_in": "CoreBevyAPI", - "name": "CoreBevyAPI", - "is_global": true, - "type_def_files": { - "runner": "Builtin", - "templates": { - "teal": { - "extension": ".d.tl", - "template": "Teal" - } - } - } -} \ No newline at end of file diff --git a/tlconfig.lua b/tlconfig.lua deleted file mode 100644 index 53c5e8adc8..0000000000 --- a/tlconfig.lua +++ /dev/null @@ -1,5 +0,0 @@ -return { - include_dir = { - "bevy_mod_scripting/", - } -} \ No newline at end of file