From 7b8335a704bee3033b48e8ed396a8e481f750b30 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Fri, 28 Jul 2023 12:08:42 -0500 Subject: [PATCH 01/17] Bump typedefs version in vscode settings --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e19a802..75095c1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,7 @@ "luau-lsp.types.roblox": false, "luau-lsp.require.mode": "relativeToFile", "luau-lsp.require.directoryAliases": { - "@lune/": "~/.lune/.typedefs/0.7.4/" + "@lune/": "~/.lune/.typedefs/0.7.5/" }, // Luau - ignore type defs file in docs dir and dev scripts we use "luau-lsp.ignoreGlobs": [ From eafb566e91b36e508423e0de009f8d1fe8090d26 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Fri, 4 Aug 2023 13:32:07 -0500 Subject: [PATCH 02/17] Update dependencies --- Cargo.lock | 19 +++++++++---------- Cargo.toml | 8 ++++---- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f707e2..7cf815a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -873,12 +873,12 @@ dependencies = [ [[package]] name = "hyper-tungstenite" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226df6fd0aece319a325419d770aa9d947defa60463f142cd82b329121f906a3" +checksum = "7cc7dcb1ab67cd336f468a12491765672e61a3b6b148634dbfe2fe8acd3fe7d9" dependencies = [ "hyper", - "pin-project", + "pin-project-lite", "tokio", "tokio-tungstenite", "tungstenite", @@ -1092,9 +1092,9 @@ dependencies = [ [[package]] name = "lz4_flex" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8c72594ac26bfd34f2d99dfced2edfaddfe8a476e3ff2ca0eb293d925c4f83" +checksum = "3ea9b256699eda7b0387ffbc776dd625e28bde3918446381781245b7a50349d8" dependencies = [ "twox-hash", ] @@ -2187,9 +2187,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c" +checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2" dependencies = [ "futures-util", "log", @@ -2283,9 +2283,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" +checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649" dependencies = [ "byteorder 1.4.3", "bytes", @@ -2299,7 +2299,6 @@ dependencies = [ "thiserror", "url", "utf-8", - "webpki", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2bb9309..163c716 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,10 +70,10 @@ thiserror = "1.0" async-trait = "0.1" dialoguer = "0.10" dunce = "1.0" -lz4_flex = "0.10" +lz4_flex = "0.11" pin-project = "1.0" os_str_bytes = "6.4" -urlencoding = "2.1.2" +urlencoding = "2.1" ### RUNTIME @@ -97,11 +97,11 @@ toml = { version = "0.7", features = ["preserve_order"] } ### NET hyper = { version = "0.14", features = ["full"] } -hyper-tungstenite = { version = "0.10" } +hyper-tungstenite = { version = "0.11" } reqwest = { version = "0.11", default-features = false, features = [ "rustls-tls", ] } -tokio-tungstenite = { version = "0.19", features = ["rustls-tls-webpki-roots"] } +tokio-tungstenite = { version = "0.20", features = ["rustls-tls-webpki-roots"] } ### CLI From 31e625aa719997027ee78a46bcbb26d04dfc954c Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Fri, 4 Aug 2023 13:58:54 -0500 Subject: [PATCH 03/17] Separate userdata impl for Instance out into new module for maintainability --- src/roblox/instance/base.rs | 356 +++++++++++++++++++++++++++++++++++ src/roblox/instance/mod.rs | 363 ++---------------------------------- 2 files changed, 369 insertions(+), 350 deletions(-) create mode 100644 src/roblox/instance/base.rs diff --git a/src/roblox/instance/base.rs b/src/roblox/instance/base.rs new file mode 100644 index 0000000..b927a60 --- /dev/null +++ b/src/roblox/instance/base.rs @@ -0,0 +1,356 @@ +use mlua::prelude::*; + +use rbx_dom_weak::{ + types::{Variant as DomValue, VariantType as DomType}, + Instance as DomInstance, +}; + +use crate::roblox::{ + datatypes::{ + attributes::{ensure_valid_attribute_name, ensure_valid_attribute_value}, + conversion::{DomValueToLua, LuaToDomValue}, + types::EnumItem, + userdata_impl_eq, userdata_impl_to_string, + }, + shared::instance::{class_is_a, find_property_info}, +}; + +use super::{data_model, Instance}; + +pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(m: &mut M) { + m.add_meta_method(LuaMetaMethod::ToString, |lua, this, ()| { + ensure_not_destroyed(this)?; + userdata_impl_to_string(lua, this, ()) + }); + m.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); + m.add_meta_method(LuaMetaMethod::Index, instance_property_get); + m.add_meta_method_mut(LuaMetaMethod::NewIndex, instance_property_set); + m.add_method("Clone", |lua, this, ()| { + ensure_not_destroyed(this)?; + this.clone_instance().into_lua(lua) + }); + m.add_method_mut("Destroy", |_, this, ()| { + this.destroy(); + Ok(()) + }); + m.add_method_mut("ClearAllChildren", |_, this, ()| { + this.clear_all_children(); + Ok(()) + }); + m.add_method("GetChildren", |lua, this, ()| { + ensure_not_destroyed(this)?; + this.get_children().into_lua(lua) + }); + m.add_method("GetDescendants", |lua, this, ()| { + ensure_not_destroyed(this)?; + this.get_descendants().into_lua(lua) + }); + m.add_method("GetFullName", |lua, this, ()| { + ensure_not_destroyed(this)?; + this.get_full_name().into_lua(lua) + }); + m.add_method("FindFirstAncestor", |lua, this, name: String| { + ensure_not_destroyed(this)?; + this.find_ancestor(|child| child.name == name).into_lua(lua) + }); + m.add_method( + "FindFirstAncestorOfClass", + |lua, this, class_name: String| { + ensure_not_destroyed(this)?; + this.find_ancestor(|child| child.class == class_name) + .into_lua(lua) + }, + ); + m.add_method( + "FindFirstAncestorWhichIsA", + |lua, this, class_name: String| { + ensure_not_destroyed(this)?; + this.find_ancestor(|child| class_is_a(&child.class, &class_name).unwrap_or(false)) + .into_lua(lua) + }, + ); + m.add_method( + "FindFirstChild", + |lua, this, (name, recursive): (String, Option)| { + ensure_not_destroyed(this)?; + let predicate = |child: &DomInstance| child.name == name; + if matches!(recursive, Some(true)) { + this.find_descendant(predicate).into_lua(lua) + } else { + this.find_child(predicate).into_lua(lua) + } + }, + ); + m.add_method( + "FindFirstChildOfClass", + |lua, this, (class_name, recursive): (String, Option)| { + ensure_not_destroyed(this)?; + let predicate = |child: &DomInstance| child.class == class_name; + if matches!(recursive, Some(true)) { + this.find_descendant(predicate).into_lua(lua) + } else { + this.find_child(predicate).into_lua(lua) + } + }, + ); + m.add_method( + "FindFirstChildWhichIsA", + |lua, this, (class_name, recursive): (String, Option)| { + ensure_not_destroyed(this)?; + let predicate = + |child: &DomInstance| class_is_a(&child.class, &class_name).unwrap_or(false); + if matches!(recursive, Some(true)) { + this.find_descendant(predicate).into_lua(lua) + } else { + this.find_child(predicate).into_lua(lua) + } + }, + ); + m.add_method("IsA", |_, this, class_name: String| { + ensure_not_destroyed(this)?; + Ok(class_is_a(&this.class_name, class_name).unwrap_or(false)) + }); + m.add_method( + "IsAncestorOf", + |_, this, instance: LuaUserDataRef| { + ensure_not_destroyed(this)?; + Ok(instance + .find_ancestor(|ancestor| ancestor.referent() == this.dom_ref) + .is_some()) + }, + ); + m.add_method( + "IsDescendantOf", + |_, this, instance: LuaUserDataRef| { + ensure_not_destroyed(this)?; + Ok(this + .find_ancestor(|ancestor| ancestor.referent() == instance.dom_ref) + .is_some()) + }, + ); + m.add_method("GetAttribute", |lua, this, name: String| { + ensure_not_destroyed(this)?; + match this.get_attribute(name) { + Some(attribute) => Ok(LuaValue::dom_value_to_lua(lua, &attribute)?), + None => Ok(LuaValue::Nil), + } + }); + m.add_method("GetAttributes", |lua, this, ()| { + ensure_not_destroyed(this)?; + let attributes = this.get_attributes(); + let tab = lua.create_table_with_capacity(0, attributes.len())?; + for (key, value) in attributes.into_iter() { + tab.set(key, LuaValue::dom_value_to_lua(lua, &value)?)?; + } + Ok(tab) + }); + m.add_method( + "SetAttribute", + |lua, this, (attribute_name, lua_value): (String, LuaValue)| { + ensure_not_destroyed(this)?; + ensure_valid_attribute_name(&attribute_name)?; + match lua_value.lua_to_dom_value(lua, None) { + Ok(dom_value) => { + ensure_valid_attribute_value(&dom_value)?; + this.set_attribute(attribute_name, dom_value); + Ok(()) + } + Err(e) => Err(e.into()), + } + }, + ); + m.add_method("GetTags", |_, this, ()| { + ensure_not_destroyed(this)?; + Ok(this.get_tags()) + }); + m.add_method("HasTag", |_, this, tag: String| { + ensure_not_destroyed(this)?; + Ok(this.has_tag(tag)) + }); + m.add_method("AddTag", |_, this, tag: String| { + ensure_not_destroyed(this)?; + this.add_tag(tag); + Ok(()) + }); + m.add_method("RemoveTag", |_, this, tag: String| { + ensure_not_destroyed(this)?; + this.remove_tag(tag); + Ok(()) + }); +} + +fn ensure_not_destroyed(inst: &Instance) -> LuaResult<()> { + if inst.is_destroyed() { + Err(LuaError::RuntimeError( + "Instance has been destroyed".to_string(), + )) + } else { + Ok(()) + } +} + +/* + Gets a property value for an instance. + + Getting a value does the following: + + 1. Check if it is a special property like "ClassName", "Name" or "Parent" + 2. Check if a property exists for the wanted name + 2a. Get an existing instance property OR + 2b. Get a property from a known default value + 3. Get a current child of the instance + 4. No valid property or instance found, throw error +*/ +fn instance_property_get<'lua>( + lua: &'lua Lua, + this: &Instance, + prop_name: String, +) -> LuaResult> { + ensure_not_destroyed(this)?; + + match prop_name.as_str() { + "ClassName" => return this.get_class_name().into_lua(lua), + "Name" => { + return this.get_name().into_lua(lua); + } + "Parent" => { + return this.get_parent().into_lua(lua); + } + _ => {} + } + + if let Some(info) = find_property_info(&this.class_name, &prop_name) { + if let Some(prop) = this.get_property(&prop_name) { + if let DomValue::Enum(enum_value) = prop { + let enum_name = info.enum_name.ok_or_else(|| { + LuaError::RuntimeError(format!( + "Failed to get property '{}' - encountered unknown enum", + prop_name + )) + })?; + EnumItem::from_enum_name_and_value(&enum_name, enum_value.to_u32()) + .ok_or_else(|| { + LuaError::RuntimeError(format!( + "Failed to get property '{}' - Enum.{} does not contain numeric value {}", + prop_name, enum_name, enum_value.to_u32() + )) + })? + .into_lua(lua) + } else { + Ok(LuaValue::dom_value_to_lua(lua, &prop)?) + } + } else if let (Some(enum_name), Some(enum_value)) = (info.enum_name, info.enum_default) { + EnumItem::from_enum_name_and_value(&enum_name, enum_value) + .ok_or_else(|| { + LuaError::RuntimeError(format!( + "Failed to get property '{}' - Enum.{} does not contain numeric value {}", + prop_name, enum_name, enum_value + )) + })? + .into_lua(lua) + } else if let Some(prop_default) = info.value_default { + Ok(LuaValue::dom_value_to_lua(lua, prop_default)?) + } else if info.value_type.is_some() { + if info.value_type == Some(DomType::Ref) { + Ok(LuaValue::Nil) + } else { + Err(LuaError::RuntimeError(format!( + "Failed to get property '{}' - missing default value", + prop_name + ))) + } + } else { + Err(LuaError::RuntimeError(format!( + "Failed to get property '{}' - malformed property info", + prop_name + ))) + } + } else if let Some(inst) = this.find_child(|inst| inst.name == prop_name) { + Ok(LuaValue::UserData(lua.create_userdata(inst)?)) + } else { + Err(LuaError::RuntimeError(format!( + "{} is not a valid member of {}", + prop_name, this + ))) + } +} + +/* + Sets a property value for an instance. + + Setting a value does the following: + + 1. Check if it is a special property like "ClassName", "Name" or "Parent" + 2. Check if a property exists for the wanted name + 2a. Set a strict enum from a given EnumItem OR + 2b. Set a normal property from a given value +*/ +fn instance_property_set<'lua>( + lua: &'lua Lua, + this: &mut Instance, + (prop_name, prop_value): (String, LuaValue<'lua>), +) -> LuaResult<()> { + ensure_not_destroyed(this)?; + + match prop_name.as_str() { + "ClassName" => { + return Err(LuaError::RuntimeError( + "Failed to set ClassName - property is read-only".to_string(), + )); + } + "Name" => { + let name = String::from_lua(prop_value, lua)?; + this.set_name(name); + return Ok(()); + } + "Parent" => { + if this.get_class_name() == data_model::CLASS_NAME { + return Err(LuaError::RuntimeError( + "Failed to set Parent - DataModel can not be reparented".to_string(), + )); + } + type Parent<'lua> = Option>; + let parent = Parent::from_lua(prop_value, lua)?; + this.set_parent(parent.map(|p| p.clone())); + return Ok(()); + } + _ => {} + } + + let info = match find_property_info(&this.class_name, &prop_name) { + Some(b) => b, + None => { + return Err(LuaError::RuntimeError(format!( + "{} is not a valid member of {}", + prop_name, this + ))) + } + }; + + if let Some(enum_name) = info.enum_name { + match LuaUserDataRef::::from_lua(prop_value, lua) { + Ok(given_enum) if given_enum.parent.desc.name == enum_name => { + this.set_property(prop_name, DomValue::Enum((*given_enum).clone().into())); + Ok(()) + } + Ok(given_enum) => Err(LuaError::RuntimeError(format!( + "Failed to set property '{}' - expected Enum.{}, got Enum.{}", + prop_name, enum_name, given_enum.parent.desc.name + ))), + Err(e) => Err(e), + } + } else if let Some(dom_type) = info.value_type { + match prop_value.lua_to_dom_value(lua, Some(dom_type)) { + Ok(dom_value) => { + this.set_property(prop_name, dom_value); + Ok(()) + } + Err(e) => Err(e.into()), + } + } else { + Err(LuaError::RuntimeError(format!( + "Failed to set property '{}' - malformed property info", + prop_name + ))) + } +} diff --git a/src/roblox/instance/mod.rs b/src/roblox/instance/mod.rs index 4aec36d..512f69f 100644 --- a/src/roblox/instance/mod.rs +++ b/src/roblox/instance/mod.rs @@ -8,22 +8,13 @@ use std::{ use mlua::prelude::*; use once_cell::sync::Lazy; use rbx_dom_weak::{ - types::{ - Attributes as DomAttributes, Ref as DomRef, Variant as DomValue, VariantType as DomType, - }, + types::{Attributes as DomAttributes, Ref as DomRef, Variant as DomValue}, Instance as DomInstance, InstanceBuilder as DomInstanceBuilder, WeakDom, }; -use crate::roblox::{ - datatypes::{ - attributes::{ensure_valid_attribute_name, ensure_valid_attribute_value}, - conversion::{DomValueToLua, LuaToDomValue}, - types::EnumItem, - userdata_impl_eq, userdata_impl_to_string, - }, - shared::instance::{class_exists, class_is_a, find_property_info}, -}; +use crate::roblox::shared::instance::{class_exists, class_is_a}; +pub(crate) mod base; pub(crate) mod data_model; pub(crate) mod workspace; @@ -202,16 +193,6 @@ impl Instance { } } - fn ensure_not_destroyed(&self) -> LuaResult<()> { - if self.is_destroyed() { - Err(LuaError::RuntimeError( - "Instance has been destroyed".to_string(), - )) - } else { - Ok(()) - } - } - fn is_destroyed(&self) -> bool { // NOTE: This property can not be cached since instance references // other than this one may have destroyed this one, and we don't @@ -766,341 +747,23 @@ impl Instance { } } +/* + Here we add inheritance-like behavior for instances by creating + fields that are restricted to specific classnames / base classes + + Note that we should try to be conservative with how many classes + and methods we support here - we should only implement methods that + are necessary for modifying the dom and / or having ergonomic access + to the dom, not try to replicate Roblox engine behavior of instances +*/ impl LuaUserData for Instance { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { - // Here we add inheritance-like behavior for instances by creating - // fields that are restricted to specific classnames / base classes data_model::add_fields(fields); workspace::add_fields(fields); } fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_meta_method(LuaMetaMethod::ToString, |lua, this, ()| { - this.ensure_not_destroyed()?; - userdata_impl_to_string(lua, this, ()) - }); - methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); - /* - Getting a value does the following: - - 1. Check if it is a special property like "ClassName", "Name" or "Parent" - 2. Check if a property exists for the wanted name - 2a. Get an existing instance property OR - 2b. Get a property from a known default value - 3. Get a current child of the instance - 4. No valid property or instance found, throw error - */ - methods.add_meta_method(LuaMetaMethod::Index, |lua, this, prop_name: String| { - this.ensure_not_destroyed()?; - - match prop_name.as_str() { - "ClassName" => return this.get_class_name().into_lua(lua), - "Name" => { - return this.get_name().into_lua(lua); - } - "Parent" => { - return this.get_parent().into_lua(lua); - } - _ => {} - } - - if let Some(info) = find_property_info(&this.class_name, &prop_name) { - if let Some(prop) = this.get_property(&prop_name) { - if let DomValue::Enum(enum_value) = prop { - let enum_name = info.enum_name.ok_or_else(|| { - LuaError::RuntimeError(format!( - "Failed to get property '{}' - encountered unknown enum", - prop_name - )) - })?; - EnumItem::from_enum_name_and_value(&enum_name, enum_value.to_u32()) - .ok_or_else(|| { - LuaError::RuntimeError(format!( - "Failed to get property '{}' - Enum.{} does not contain numeric value {}", - prop_name, enum_name, enum_value.to_u32() - )) - })? - .into_lua(lua) - } else { - Ok(LuaValue::dom_value_to_lua(lua, &prop)?) - } - } else if let (Some(enum_name), Some(enum_value)) = (info.enum_name, info.enum_default) { - EnumItem::from_enum_name_and_value(&enum_name, enum_value) - .ok_or_else(|| { - LuaError::RuntimeError(format!( - "Failed to get property '{}' - Enum.{} does not contain numeric value {}", - prop_name, enum_name, enum_value - )) - })? - .into_lua(lua) - } else if let Some(prop_default) = info.value_default { - Ok(LuaValue::dom_value_to_lua(lua, prop_default)?) - } else if info.value_type.is_some() { - if info.value_type == Some(DomType::Ref) { - Ok(LuaValue::Nil) - } else { - Err(LuaError::RuntimeError(format!( - "Failed to get property '{}' - missing default value", - prop_name - ))) - } - } else { - Err(LuaError::RuntimeError(format!( - "Failed to get property '{}' - malformed property info", - prop_name - ))) - } - } else if let Some(inst) = this.find_child(|inst| inst.name == prop_name) { - Ok(LuaValue::UserData(lua.create_userdata(inst)?)) - } else { - Err(LuaError::RuntimeError(format!( - "{} is not a valid member of {}", - prop_name, this - ))) - } - }); - /* - Setting a value does the following: - - 1. Check if it is a special property like "ClassName", "Name" or "Parent" - 2. Check if a property exists for the wanted name - 2a. Set a strict enum from a given EnumItem OR - 2b. Set a normal property from a given value - */ - methods.add_meta_method_mut( - LuaMetaMethod::NewIndex, - |lua, this, (prop_name, prop_value): (String, LuaValue)| { - this.ensure_not_destroyed()?; - - match prop_name.as_str() { - "ClassName" => { - return Err(LuaError::RuntimeError(format!( - "Failed to set property '{}' - property is read-only", - prop_name - ))); - } - "Name" => { - let name = String::from_lua(prop_value, lua)?; - this.set_name(name); - return Ok(()); - } - "Parent" => { - if this.get_class_name() == data_model::CLASS_NAME { - return Err(LuaError::RuntimeError(format!( - "Failed to set property '{}' - DataModel can not be reparented", - prop_name - ))); - } - type Parent<'lua> = Option>; - let parent = Parent::from_lua(prop_value, lua)?; - this.set_parent(parent.map(|p| p.clone())); - return Ok(()); - } - _ => {} - } - - let info = match find_property_info(&this.class_name, &prop_name) { - Some(b) => b, - None => { - return Err(LuaError::RuntimeError(format!( - "{} is not a valid member of {}", - prop_name, this - ))) - } - }; - - if let Some(enum_name) = info.enum_name { - match LuaUserDataRef::::from_lua(prop_value, lua) { - Ok(given_enum) if given_enum.parent.desc.name == enum_name => { - this.set_property( - prop_name, - DomValue::Enum((*given_enum).clone().into()), - ); - Ok(()) - } - Ok(given_enum) => Err(LuaError::RuntimeError(format!( - "Failed to set property '{}' - expected Enum.{}, got Enum.{}", - prop_name, enum_name, given_enum.parent.desc.name - ))), - Err(e) => Err(e), - } - } else if let Some(dom_type) = info.value_type { - match prop_value.lua_to_dom_value(lua, Some(dom_type)) { - Ok(dom_value) => { - this.set_property(prop_name, dom_value); - Ok(()) - } - Err(e) => Err(e.into()), - } - } else { - Err(LuaError::RuntimeError(format!( - "Failed to set property '{}' - malformed property info", - prop_name - ))) - } - }, - ); - /* - Implementations of base methods on the Instance class - - It should be noted that any methods that deal with events - and/or have functionality that affects instances other - than this instance itself are intentionally left out. - */ - methods.add_method("Clone", |lua, this, ()| { - this.ensure_not_destroyed()?; - this.clone_instance().into_lua(lua) - }); - methods.add_method_mut("Destroy", |_, this, ()| { - this.destroy(); - Ok(()) - }); - methods.add_method_mut("ClearAllChildren", |_, this, ()| { - this.clear_all_children(); - Ok(()) - }); - methods.add_method("GetChildren", |lua, this, ()| { - this.ensure_not_destroyed()?; - this.get_children().into_lua(lua) - }); - methods.add_method("GetDescendants", |lua, this, ()| { - this.ensure_not_destroyed()?; - this.get_descendants().into_lua(lua) - }); - methods.add_method("GetFullName", |lua, this, ()| { - this.ensure_not_destroyed()?; - this.get_full_name().into_lua(lua) - }); - methods.add_method("FindFirstAncestor", |lua, this, name: String| { - this.ensure_not_destroyed()?; - this.find_ancestor(|child| child.name == name).into_lua(lua) - }); - methods.add_method( - "FindFirstAncestorOfClass", - |lua, this, class_name: String| { - this.ensure_not_destroyed()?; - this.find_ancestor(|child| child.class == class_name) - .into_lua(lua) - }, - ); - methods.add_method( - "FindFirstAncestorWhichIsA", - |lua, this, class_name: String| { - this.ensure_not_destroyed()?; - this.find_ancestor(|child| class_is_a(&child.class, &class_name).unwrap_or(false)) - .into_lua(lua) - }, - ); - methods.add_method( - "FindFirstChild", - |lua, this, (name, recursive): (String, Option)| { - this.ensure_not_destroyed()?; - let predicate = |child: &DomInstance| child.name == name; - if matches!(recursive, Some(true)) { - this.find_descendant(predicate).into_lua(lua) - } else { - this.find_child(predicate).into_lua(lua) - } - }, - ); - methods.add_method( - "FindFirstChildOfClass", - |lua, this, (class_name, recursive): (String, Option)| { - this.ensure_not_destroyed()?; - let predicate = |child: &DomInstance| child.class == class_name; - if matches!(recursive, Some(true)) { - this.find_descendant(predicate).into_lua(lua) - } else { - this.find_child(predicate).into_lua(lua) - } - }, - ); - methods.add_method( - "FindFirstChildWhichIsA", - |lua, this, (class_name, recursive): (String, Option)| { - this.ensure_not_destroyed()?; - let predicate = - |child: &DomInstance| class_is_a(&child.class, &class_name).unwrap_or(false); - if matches!(recursive, Some(true)) { - this.find_descendant(predicate).into_lua(lua) - } else { - this.find_child(predicate).into_lua(lua) - } - }, - ); - methods.add_method("IsA", |_, this, class_name: String| { - this.ensure_not_destroyed()?; - Ok(class_is_a(&this.class_name, class_name).unwrap_or(false)) - }); - methods.add_method( - "IsAncestorOf", - |_, this, instance: LuaUserDataRef| { - this.ensure_not_destroyed()?; - Ok(instance - .find_ancestor(|ancestor| ancestor.referent() == this.dom_ref) - .is_some()) - }, - ); - methods.add_method( - "IsDescendantOf", - |_, this, instance: LuaUserDataRef| { - this.ensure_not_destroyed()?; - Ok(this - .find_ancestor(|ancestor| ancestor.referent() == instance.dom_ref) - .is_some()) - }, - ); - methods.add_method("GetAttribute", |lua, this, name: String| { - this.ensure_not_destroyed()?; - match this.get_attribute(name) { - Some(attribute) => Ok(LuaValue::dom_value_to_lua(lua, &attribute)?), - None => Ok(LuaValue::Nil), - } - }); - methods.add_method("GetAttributes", |lua, this, ()| { - this.ensure_not_destroyed()?; - let attributes = this.get_attributes(); - let tab = lua.create_table_with_capacity(0, attributes.len())?; - for (key, value) in attributes.into_iter() { - tab.set(key, LuaValue::dom_value_to_lua(lua, &value)?)?; - } - Ok(tab) - }); - methods.add_method( - "SetAttribute", - |lua, this, (attribute_name, lua_value): (String, LuaValue)| { - this.ensure_not_destroyed()?; - ensure_valid_attribute_name(&attribute_name)?; - match lua_value.lua_to_dom_value(lua, None) { - Ok(dom_value) => { - ensure_valid_attribute_value(&dom_value)?; - this.set_attribute(attribute_name, dom_value); - Ok(()) - } - Err(e) => Err(e.into()), - } - }, - ); - methods.add_method("GetTags", |_, this, ()| { - this.ensure_not_destroyed()?; - Ok(this.get_tags()) - }); - methods.add_method("HasTag", |_, this, tag: String| { - this.ensure_not_destroyed()?; - Ok(this.has_tag(tag)) - }); - methods.add_method("AddTag", |_, this, tag: String| { - this.ensure_not_destroyed()?; - this.add_tag(tag); - Ok(()) - }); - methods.add_method("RemoveTag", |_, this, tag: String| { - this.ensure_not_destroyed()?; - this.remove_tag(tag); - Ok(()) - }); - // Here we add inheritance-like behavior for instances by creating - // methods that are restricted to specific classnames / base classes + base::add_methods(methods); data_model::add_methods(methods); } } From 0869b16ba64c417805abadb86dd6e1bbd7a3780f Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Fri, 4 Aug 2023 14:05:44 -0500 Subject: [PATCH 04/17] Use plain mutex for global dom instead of rwlock --- src/roblox/instance/mod.rs | 118 +++++++++++-------------------------- 1 file changed, 33 insertions(+), 85 deletions(-) diff --git a/src/roblox/instance/mod.rs b/src/roblox/instance/mod.rs index 512f69f..ab111ef 100644 --- a/src/roblox/instance/mod.rs +++ b/src/roblox/instance/mod.rs @@ -2,7 +2,7 @@ use std::{ collections::{BTreeMap, VecDeque}, fmt, hash::{Hash, Hasher}, - sync::RwLock, + sync::Mutex, }; use mlua::prelude::*; @@ -21,8 +21,8 @@ pub(crate) mod workspace; const PROPERTY_NAME_ATTRIBUTES: &str = "Attributes"; const PROPERTY_NAME_TAGS: &str = "Tags"; -static INTERNAL_DOM: Lazy> = - Lazy::new(|| RwLock::new(WeakDom::new(DomInstanceBuilder::new("ROOT")))); +static INTERNAL_DOM: Lazy> = + Lazy::new(|| Mutex::new(WeakDom::new(DomInstanceBuilder::new("ROOT")))); #[derive(Debug, Clone)] pub struct Instance { @@ -38,9 +38,7 @@ impl Instance { or if the given dom object ref points to the dom root. */ pub(crate) fn new(dom_ref: DomRef) -> Self { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let instance = dom .get_by_ref(dom_ref) @@ -62,9 +60,7 @@ impl Instance { Panics if the given dom object ref points to the dom root. */ pub(crate) fn new_opt(dom_ref: DomRef) -> Option { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); if let Some(instance) = dom.get_by_ref(dom_ref) { if instance.referent() == dom.root_ref() { @@ -86,9 +82,7 @@ impl Instance { An orphaned instance is an instance at the root of a weak dom. */ pub(crate) fn new_orphaned(class_name: impl AsRef) -> Self { - let mut dom = INTERNAL_DOM - .try_write() - .expect("Failed to get write access to document"); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let class_name = class_name.as_ref(); @@ -113,9 +107,7 @@ impl Instance { */ pub fn from_external_dom(external_dom: &mut WeakDom, external_dom_ref: DomRef) -> Self { { - let mut dom = INTERNAL_DOM - .try_write() - .expect("Failed to get write access to document"); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let dom_root = dom.root_ref(); external_dom.transfer(external_dom_ref, &mut dom, dom_root); @@ -131,9 +123,7 @@ impl Instance { root of the weak dom, and return its referent. */ pub fn clone_into_external_dom(self, external_dom: &mut WeakDom) -> DomRef { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let cloned = dom.clone_into_external(self.dom_ref, external_dom); external_dom.transfer_within(cloned, external_dom.root_ref()); @@ -155,9 +145,7 @@ impl Instance { // NOTE: We create a new scope here to avoid deadlocking since // our clone implementation must have exclusive write access let new_ref = { - let mut dom = INTERNAL_DOM - .try_write() - .expect("Failed to get write access to document"); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); dom.clone_within(self.dom_ref) }; @@ -184,9 +172,7 @@ impl Instance { if self.is_destroyed() { false } else { - let mut dom = INTERNAL_DOM - .try_write() - .expect("Failed to get write access to document"); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); dom.destroy(self.dom_ref); true @@ -197,9 +183,7 @@ impl Instance { // NOTE: This property can not be cached since instance references // other than this one may have destroyed this one, and we don't // keep track of all current instance reference structs - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); dom.get_by_ref(self.dom_ref).is_none() } @@ -212,9 +196,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn clear_all_children(&mut self) { - let mut dom = INTERNAL_DOM - .try_write() - .expect("Failed to get write access to document"); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let instance = dom .get_by_ref(self.dom_ref) @@ -258,9 +240,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn get_name(&self) -> String { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); dom.get_by_ref(self.dom_ref) .expect("Failed to find instance in document") @@ -276,9 +256,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn set_name(&self, name: impl Into) { - let mut dom = INTERNAL_DOM - .try_write() - .expect("Failed to get write access to document"); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); dom.get_by_ref_mut(self.dom_ref) .expect("Failed to find instance in document") @@ -293,9 +271,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn get_parent(&self) -> Option { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let parent_ref = dom .get_by_ref(self.dom_ref) @@ -321,9 +297,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn set_parent(&self, parent: Option) { - let mut dom = INTERNAL_DOM - .try_write() - .expect("Failed to get write access to target document"); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let parent_ref = parent .map(|parent| parent.dom_ref) @@ -337,8 +311,8 @@ impl Instance { */ pub fn get_property(&self, name: impl AsRef) -> Option { INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document") + .lock() + .expect("Failed to lock document") .get_by_ref(self.dom_ref) .expect("Failed to find instance in document") .properties @@ -354,8 +328,8 @@ impl Instance { */ pub fn set_property(&self, name: impl AsRef, value: DomValue) { INTERNAL_DOM - .try_write() - .expect("Failed to get read access to document") + .lock() + .expect("Failed to lock document") .get_by_ref_mut(self.dom_ref) .expect("Failed to find instance in document") .properties @@ -370,9 +344,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn get_attribute(&self, name: impl AsRef) -> Option { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let inst = dom .get_by_ref(self.dom_ref) .expect("Failed to find instance in document"); @@ -393,9 +365,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn get_attributes(&self) -> BTreeMap { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let inst = dom .get_by_ref(self.dom_ref) .expect("Failed to find instance in document"); @@ -416,9 +386,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn set_attribute(&self, name: impl AsRef, value: DomValue) { - let mut dom = INTERNAL_DOM - .try_write() - .expect("Failed to get write access to document"); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let inst = dom .get_by_ref_mut(self.dom_ref) .expect("Failed to find instance in document"); @@ -450,9 +418,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn add_tag(&self, name: impl AsRef) { - let mut dom = INTERNAL_DOM - .try_write() - .expect("Failed to get write access to document"); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let inst = dom .get_by_ref_mut(self.dom_ref) .expect("Failed to find instance in document"); @@ -474,9 +440,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn get_tags(&self) -> Vec { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let inst = dom .get_by_ref(self.dom_ref) .expect("Failed to find instance in document"); @@ -495,9 +459,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn has_tag(&self, name: impl AsRef) -> bool { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let inst = dom .get_by_ref(self.dom_ref) .expect("Failed to find instance in document"); @@ -517,9 +479,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn remove_tag(&self, name: impl AsRef) { - let mut dom = INTERNAL_DOM - .try_write() - .expect("Failed to get write access to document"); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let inst = dom .get_by_ref_mut(self.dom_ref) .expect("Failed to find instance in document"); @@ -545,9 +505,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn get_children(&self) -> Vec { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let children = dom .get_by_ref(self.dom_ref) @@ -569,9 +527,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn get_descendants(&self) -> Vec { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let mut descendants = Vec::new(); let mut queue = VecDeque::from_iter( @@ -604,9 +560,7 @@ impl Instance { on the Roblox Developer Hub */ pub fn get_full_name(&self) -> String { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let dom_root = dom.root_ref(); let mut parts = Vec::new(); @@ -637,9 +591,7 @@ impl Instance { where F: Fn(&DomInstance) -> bool, { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let children = dom .get_by_ref(self.dom_ref) @@ -672,9 +624,7 @@ impl Instance { where F: Fn(&DomInstance) -> bool, { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let mut ancestor_ref = dom .get_by_ref(self.dom_ref) @@ -704,9 +654,7 @@ impl Instance { where F: Fn(&DomInstance) -> bool, { - let dom = INTERNAL_DOM - .try_read() - .expect("Failed to get read access to document"); + let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let mut queue = VecDeque::from_iter( dom.get_by_ref(self.dom_ref) From 82e5844dad99412e9168df3e559c7c17bc000aa4 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Fri, 4 Aug 2023 14:34:58 -0500 Subject: [PATCH 05/17] Fix remaining deadlock cases for instances --- src/roblox/instance/mod.rs | 53 ++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/roblox/instance/mod.rs b/src/roblox/instance/mod.rs index ab111ef..9f99267 100644 --- a/src/roblox/instance/mod.rs +++ b/src/roblox/instance/mod.rs @@ -36,6 +36,9 @@ impl Instance { Panics if the instance does not exist in the internal dom, or if the given dom object ref points to the dom root. + + **WARNING:** Creating a new instance requires locking the internal dom, + any existing lock must first be released to prevent any deadlocking. */ pub(crate) fn new(dom_ref: DomRef) -> Self { let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); @@ -58,6 +61,9 @@ impl Instance { Creates a new `Instance` from a dom object ref, if the instance exists. Panics if the given dom object ref points to the dom root. + + **WARNING:** Creating a new instance requires locking the internal dom, + any existing lock must first be released to prevent any deadlocking. */ pub(crate) fn new_opt(dom_ref: DomRef) -> Option { let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); @@ -80,6 +86,9 @@ impl Instance { Creates a new orphaned `Instance` with a given class name. An orphaned instance is an instance at the root of a weak dom. + + **WARNING:** Creating a new instance requires locking the internal dom, + any existing lock must first be released to prevent any deadlocking. */ pub(crate) fn new_orphaned(class_name: impl AsRef) -> Self { let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); @@ -106,13 +115,12 @@ impl Instance { Panics if the given dom ref is the root dom ref of the external weak dom. */ pub fn from_external_dom(external_dom: &mut WeakDom, external_dom_ref: DomRef) -> Self { - { - let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); - let dom_root = dom.root_ref(); + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); + let dom_root = dom.root_ref(); - external_dom.transfer(external_dom_ref, &mut dom, dom_root); - } + external_dom.transfer(external_dom_ref, &mut dom, dom_root); + drop(dom); // Self::new needs mutex handle, drop it first Self::new(external_dom_ref) } @@ -142,13 +150,9 @@ impl Instance { on the Roblox Developer Hub */ pub fn clone_instance(&self) -> Instance { - // NOTE: We create a new scope here to avoid deadlocking since - // our clone implementation must have exclusive write access - let new_ref = { - let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); - - dom.clone_within(self.dom_ref) - }; + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); + let new_ref = dom.clone_within(self.dom_ref); + drop(dom); // Self::new needs mutex handle, drop it first let new_inst = Self::new(new_ref); new_inst.set_parent(None); @@ -281,6 +285,7 @@ impl Instance { if parent_ref == dom.root_ref() { None } else { + drop(dom); // Self::new needs mutex handle, drop it first Some(Self::new(parent_ref)) } } @@ -513,6 +518,7 @@ impl Instance { .children() .to_vec(); + drop(dom); // Self::new needs mutex handle, drop it first children.into_iter().map(Self::new).collect() } @@ -544,6 +550,7 @@ impl Instance { } } + drop(dom); // Self::new needs mutex handle, drop it first descendants.into_iter().map(Self::new).collect() } @@ -599,17 +606,16 @@ impl Instance { .children() .to_vec(); - children.into_iter().find_map(|child_ref| { - if let Some(child_inst) = dom.get_by_ref(child_ref) { - if predicate(child_inst) { - Some(Self::new(child_ref)) - } else { - None - } + let found_ref = children.into_iter().find(|child_ref| { + if let Some(child_inst) = dom.get_by_ref(*child_ref) { + predicate(child_inst) } else { - None + false } - }) + }); + + drop(dom); // Self::new needs mutex handle, drop it first + found_ref.map(Self::new) } /** @@ -633,6 +639,7 @@ impl Instance { while let Some(ancestor) = dom.get_by_ref(ancestor_ref) { if predicate(ancestor) { + drop(dom); // Self::new needs mutex handle, drop it first return Some(Self::new(ancestor_ref)); } else { ancestor_ref = ancestor.parent(); @@ -667,7 +674,9 @@ impl Instance { .and_then(|queue_ref| dom.get_by_ref(*queue_ref)) { if predicate(queue_item) { - return Some(Self::new(queue_item.referent())); + let queue_ref = queue_item.referent(); + drop(dom); // Self::new needs mutex handle, drop it first + return Some(Self::new(queue_ref)); } else { queue.extend(queue_item.children()) } From 924832dede15ecd75b1b6c540d6e8f83aa2ce32d Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Fri, 4 Aug 2023 14:35:32 -0500 Subject: [PATCH 06/17] Run tests using multiple threads --- .justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.justfile b/.justfile index 04e1760..33d8a26 100644 --- a/.justfile +++ b/.justfile @@ -8,4 +8,4 @@ run-file FILE_NAME: # Run tests for the Lune library test: - cargo test --lib -- --test-threads 1 + cargo test --lib From 7a2f31f8dbdadbf294d6bee0e12594ed3a4406b0 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Fri, 4 Aug 2023 14:58:04 -0500 Subject: [PATCH 07/17] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51430e2..270e19b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Fixed instances and `roblox` built-in library APIs erroring when used asynchronously/concurrently. + ## `0.7.5` - July 22nd, 2023 ### Added From df8570b16eb30378b927277fefc0ec6ac38611af Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Fri, 4 Aug 2023 15:36:50 -0500 Subject: [PATCH 08/17] Run tests in multiple threads in CI --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4dbf2ad..b871d69 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -31,4 +31,4 @@ jobs: run: cargo clippy - name: Test - run: cargo test --package lune -- --test-threads 1 + run: cargo test --lib From 9ff142e6e2907e02b7e1506487c1ad94199e4486 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Fri, 4 Aug 2023 16:58:18 -0500 Subject: [PATCH 09/17] Fix require on files with multiple extensions --- CHANGELOG.md | 1 + src/lune/importer/require.rs | 43 +++++++++++++++++-------- src/tests.rs | 1 + tests/require/tests/multi.ext.file.luau | 4 +++ tests/require/tests/multi_ext.luau | 1 + 5 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 tests/require/tests/multi.ext.file.luau create mode 100644 tests/require/tests/multi_ext.luau diff --git a/CHANGELOG.md b/CHANGELOG.md index 270e19b..54179d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed not being able to require files with multiple extensions, eg. `module.spec.luau` was not require-able using `require("module.spec")` - Fixed instances and `roblox` built-in library APIs erroring when used asynchronously/concurrently. ## `0.7.5` - July 22nd, 2023 diff --git a/src/lune/importer/require.rs b/src/lune/importer/require.rs index 4211a22..20048c6 100644 --- a/src/lune/importer/require.rs +++ b/src/lune/importer/require.rs @@ -6,7 +6,6 @@ use std::{ sync::Arc, }; -use dunce::canonicalize; use mlua::{prelude::*, Compiler as LuaCompiler}; use tokio::fs; use tokio::sync::Mutex as AsyncMutex; @@ -29,6 +28,19 @@ return yield() type RequireWakersVec<'lua> = Vec>>>; +fn append_extension_and_canonicalize( + path: impl Into, + ext: &'static str, +) -> Result { + let mut new = path.into(); + match new.extension() { + // FUTURE: There's probably a better way to do this than converting to a lossy string + Some(e) => new.set_extension(format!("{}.{ext}", e.to_string_lossy())), + None => new.set_extension(ext), + }; + dunce::canonicalize(new) +} + #[derive(Debug, Clone, Default)] struct RequireContext<'lua> { // NOTE: We need to use arc here so that mlua clones @@ -128,25 +140,28 @@ impl<'lua> RequireContext<'lua> { .join(&require_path); // Try to normalize and resolve relative path segments such as './' and '../' let file_path = match ( - canonicalize(path_relative_to_pwd.with_extension("luau")), - canonicalize(path_relative_to_pwd.with_extension("lua")), + append_extension_and_canonicalize(&path_relative_to_pwd, "luau"), + append_extension_and_canonicalize(&path_relative_to_pwd, "lua"), ) { (Ok(luau), _) => luau, (_, Ok(lua)) => lua, // If we did not find a luau/lua file at the wanted path, // we should also look for "init" files in directories - _ => match ( - canonicalize(path_relative_to_pwd.join("init").with_extension("luau")), - canonicalize(path_relative_to_pwd.join("init").with_extension("lua")), - ) { - (Ok(luau), _) => luau, - (_, Ok(lua)) => lua, - _ => { - return Err(LuaError::RuntimeError(format!( - "File does not exist at path '{require_path}'" - ))) + _ => { + let init_dir_path = path_relative_to_pwd.join("init"); + match ( + append_extension_and_canonicalize(&init_dir_path, "luau"), + append_extension_and_canonicalize(&init_dir_path, "lua"), + ) { + (Ok(luau), _) => luau, + (_, Ok(lua)) => lua, + _ => { + return Err(LuaError::RuntimeError(format!( + "File does not exist at path '{require_path}'" + ))); + } } - }, + } }; let absolute = file_path.to_string_lossy().to_string(); let relative = absolute.trim_start_matches(&self.pwd).to_string(); diff --git a/src/tests.rs b/src/tests.rs index b80f5bd..96a840c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -69,6 +69,7 @@ create_tests! { require_children: "require/tests/children", require_init: "require/tests/init", require_invalid: "require/tests/invalid", + require_multi_ext: "require/tests/multi_ext", require_nested: "require/tests/nested", require_parents: "require/tests/parents", require_siblings: "require/tests/siblings", diff --git a/tests/require/tests/multi.ext.file.luau b/tests/require/tests/multi.ext.file.luau new file mode 100644 index 0000000..cb3159b --- /dev/null +++ b/tests/require/tests/multi.ext.file.luau @@ -0,0 +1,4 @@ +return { + Foo = "Bar", + Hello = "World", +} diff --git a/tests/require/tests/multi_ext.luau b/tests/require/tests/multi_ext.luau new file mode 100644 index 0000000..ec20042 --- /dev/null +++ b/tests/require/tests/multi_ext.luau @@ -0,0 +1 @@ +require("multi.ext.file") From b4c3cc77ac313bf1b70b6c56652cc249c8465741 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sat, 5 Aug 2023 12:24:19 -0500 Subject: [PATCH 10/17] Use custom serialization options in serde builtin --- src/lune/lua/serde/encode_decode.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/lune/lua/serde/encode_decode.rs b/src/lune/lua/serde/encode_decode.rs index e753847..77711e3 100644 --- a/src/lune/lua/serde/encode_decode.rs +++ b/src/lune/lua/serde/encode_decode.rs @@ -4,6 +4,15 @@ use serde_json::Value as JsonValue; use serde_yaml::Value as YamlValue; use toml::Value as TomlValue; +const LUA_SERIALIZE_OPTIONS: LuaSerializeOptions = LuaSerializeOptions::new() + .set_array_metatable(false) + .serialize_none_to_null(false) + .serialize_unit_to_null(false); + +const LUA_DESERIALIZE_OPTIONS: LuaDeserializeOptions = LuaDeserializeOptions::new() + .deny_recursive_tables(false) + .deny_unsupported_types(true); + #[derive(Debug, Clone, Copy)] pub enum EncodeDecodeFormat { Json, @@ -50,22 +59,25 @@ impl EncodeDecodeConfig { ) -> LuaResult> { let bytes = match self.format { EncodeDecodeFormat::Json => { + let serialized: JsonValue = lua.from_value_with(value, LUA_DESERIALIZE_OPTIONS)?; if self.pretty { - serde_json::to_vec_pretty(&value).map_err(LuaError::external)? + serde_json::to_vec_pretty(&serialized).map_err(LuaError::external)? } else { - serde_json::to_vec(&value).map_err(LuaError::external)? + serde_json::to_vec(&serialized).map_err(LuaError::external)? } } EncodeDecodeFormat::Yaml => { + let serialized: YamlValue = lua.from_value_with(value, LUA_DESERIALIZE_OPTIONS)?; let mut writer = Vec::with_capacity(128); - serde_yaml::to_writer(&mut writer, &value).map_err(LuaError::external)?; + serde_yaml::to_writer(&mut writer, &serialized).map_err(LuaError::external)?; writer } EncodeDecodeFormat::Toml => { + let serialized: TomlValue = lua.from_value_with(value, LUA_DESERIALIZE_OPTIONS)?; let s = if self.pretty { - toml::to_string_pretty(&value).map_err(LuaError::external)? + toml::to_string_pretty(&serialized).map_err(LuaError::external)? } else { - toml::to_string(&value).map_err(LuaError::external)? + toml::to_string(&serialized).map_err(LuaError::external)? }; s.as_bytes().to_vec() } @@ -82,16 +94,16 @@ impl EncodeDecodeConfig { match self.format { EncodeDecodeFormat::Json => { let value: JsonValue = serde_json::from_slice(bytes).map_err(LuaError::external)?; - lua.to_value(&value) + lua.to_value_with(&value, LUA_SERIALIZE_OPTIONS) } EncodeDecodeFormat::Yaml => { let value: YamlValue = serde_yaml::from_slice(bytes).map_err(LuaError::external)?; - lua.to_value(&value) + lua.to_value_with(&value, LUA_SERIALIZE_OPTIONS) } EncodeDecodeFormat::Toml => { if let Ok(s) = string.to_str() { let value: TomlValue = toml::from_str(s).map_err(LuaError::external)?; - lua.to_value(&value) + lua.to_value_with(&value, LUA_SERIALIZE_OPTIONS) } else { Err(LuaError::RuntimeError( "TOML must be valid utf-8".to_string(), From cacaa97b6e142b9038d072ad4ab990b902f75d74 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sat, 5 Aug 2023 12:49:03 -0500 Subject: [PATCH 11/17] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54179d7..c19a216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Fixed not being able to require files with multiple extensions, eg. `module.spec.luau` was not require-able using `require("module.spec")` +- Fixed `serde.decode` deserializing `null` values as `userdata` instead of `nil`. +- Fixed not being able to require files with multiple extensions, eg. `module.spec.luau` was not require-able using `require("module.spec")`. - Fixed instances and `roblox` built-in library APIs erroring when used asynchronously/concurrently. ## `0.7.5` - July 22nd, 2023 From 483713e635183dcac1156997963bf63d00f91624 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sat, 5 Aug 2023 17:12:25 -0500 Subject: [PATCH 12/17] Use mlua trait for mapping into lua errors --- src/lune/builtins/fs.rs | 20 ++++++++------------ src/lune/builtins/net.rs | 8 +++----- src/lune/builtins/roblox.rs | 8 ++++---- src/lune/builtins/stdio.rs | 2 +- src/lune/importer/require.rs | 2 +- src/lune/lua/net/client.rs | 6 +++--- src/lune/lua/net/response.rs | 4 ++-- src/lune/lua/net/server.rs | 4 ++-- src/lune/lua/net/websocket.rs | 10 +++++----- src/lune/lua/process/mod.rs | 8 ++------ src/lune/lua/serde/compress_decompress.rs | 6 +++--- src/lune/lua/serde/encode_decode.rs | 16 ++++++++-------- src/lune/lua/stdio/formatting.rs | 6 +++--- 13 files changed, 45 insertions(+), 55 deletions(-) diff --git a/src/lune/builtins/fs.rs b/src/lune/builtins/fs.rs index 8c4a081..71fc131 100644 --- a/src/lune/builtins/fs.rs +++ b/src/lune/builtins/fs.rs @@ -26,14 +26,14 @@ pub fn create(lua: &'static Lua) -> LuaResult { } async fn fs_read_file(lua: &'static Lua, path: String) -> LuaResult { - let bytes = fs::read(&path).await.map_err(LuaError::external)?; + let bytes = fs::read(&path).await.into_lua_err()?; lua.create_string(bytes) } async fn fs_read_dir(_: &'static Lua, path: String) -> LuaResult> { let mut dir_strings = Vec::new(); - let mut dir = fs::read_dir(&path).await.map_err(LuaError::external)?; - while let Some(dir_entry) = dir.next_entry().await.map_err(LuaError::external)? { + let mut dir = fs::read_dir(&path).await.into_lua_err()?; + while let Some(dir_entry) = dir.next_entry().await.into_lua_err()? { if let Some(dir_path_str) = dir_entry.path().to_str() { dir_strings.push(dir_path_str.to_owned()); } else { @@ -63,21 +63,19 @@ async fn fs_write_file( _: &'static Lua, (path, contents): (String, LuaString<'_>), ) -> LuaResult<()> { - fs::write(&path, &contents.as_bytes()) - .await - .map_err(LuaError::external) + fs::write(&path, &contents.as_bytes()).await.into_lua_err() } async fn fs_write_dir(_: &'static Lua, path: String) -> LuaResult<()> { - fs::create_dir_all(&path).await.map_err(LuaError::external) + fs::create_dir_all(&path).await.into_lua_err() } async fn fs_remove_file(_: &'static Lua, path: String) -> LuaResult<()> { - fs::remove_file(&path).await.map_err(LuaError::external) + fs::remove_file(&path).await.into_lua_err() } async fn fs_remove_dir(_: &'static Lua, path: String) -> LuaResult<()> { - fs::remove_dir_all(&path).await.map_err(LuaError::external) + fs::remove_dir_all(&path).await.into_lua_err() } async fn fs_metadata(_: &'static Lua, path: String) -> LuaResult { @@ -122,9 +120,7 @@ async fn fs_move( path_to.display() ))); } - fs::rename(path_from, path_to) - .await - .map_err(LuaError::external)?; + fs::rename(path_from, path_to).await.into_lua_err()?; Ok(()) } diff --git a/src/lune/builtins/net.rs b/src/lune/builtins/net.rs index 358d5ce..8bb7707 100644 --- a/src/lune/builtins/net.rs +++ b/src/lune/builtins/net.rs @@ -73,7 +73,7 @@ async fn net_request<'a>(lua: &'static Lua, config: RequestConfig<'a>) -> LuaRes .body(config.body.unwrap_or_default()) .send() .await - .map_err(LuaError::external)?; + .into_lua_err()?; // Extract status, headers let res_status = res.status().as_u16(); let res_status_text = res.status().canonical_reason(); @@ -88,7 +88,7 @@ async fn net_request<'a>(lua: &'static Lua, config: RequestConfig<'a>) -> LuaRes }) .collect::>(); // Read response bytes - let mut res_bytes = res.bytes().await.map_err(LuaError::external)?.to_vec(); + let mut res_bytes = res.bytes().await.into_lua_err()?.to_vec(); // Check for extra options, decompression if config.options.decompress { // NOTE: Header names are guaranteed to be lowercase because of the above @@ -120,9 +120,7 @@ async fn net_request<'a>(lua: &'static Lua, config: RequestConfig<'a>) -> LuaRes } async fn net_socket<'a>(lua: &'static Lua, url: String) -> LuaResult { - let (ws, _) = tokio_tungstenite::connect_async(url) - .await - .map_err(LuaError::external)?; + let (ws, _) = tokio_tungstenite::connect_async(url).await.into_lua_err()?; NetWebSocket::new(ws).into_lua_table(lua) } diff --git a/src/lune/builtins/roblox.rs b/src/lune/builtins/roblox.rs index 7c19386..2141d5c 100644 --- a/src/lune/builtins/roblox.rs +++ b/src/lune/builtins/roblox.rs @@ -41,7 +41,7 @@ async fn deserialize_place<'lua>( let data_model = doc.into_data_model_instance()?; Ok::<_, DocumentError>(data_model) }); - fut.await.map_err(LuaError::external)??.into_lua(lua) + fut.await.into_lua_err()??.into_lua(lua) } async fn deserialize_model<'lua>( @@ -54,7 +54,7 @@ async fn deserialize_model<'lua>( let instance_array = doc.into_instance_array()?; Ok::<_, DocumentError>(instance_array) }); - fut.await.map_err(LuaError::external)??.into_lua(lua) + fut.await.into_lua_err()??.into_lua(lua) } async fn serialize_place<'lua>( @@ -70,7 +70,7 @@ async fn serialize_place<'lua>( })?; Ok::<_, DocumentError>(bytes) }); - let bytes = fut.await.map_err(LuaError::external)??; + let bytes = fut.await.into_lua_err()??; lua.create_string(bytes) } @@ -87,7 +87,7 @@ async fn serialize_model<'lua>( })?; Ok::<_, DocumentError>(bytes) }); - let bytes = fut.await.map_err(LuaError::external)??; + let bytes = fut.await.into_lua_err()??; lua.create_string(bytes) } diff --git a/src/lune/builtins/stdio.rs b/src/lune/builtins/stdio.rs index 48b12c7..acd3727 100644 --- a/src/lune/builtins/stdio.rs +++ b/src/lune/builtins/stdio.rs @@ -43,7 +43,7 @@ pub fn create(lua: &'static Lua) -> LuaResult { .with_async_function("prompt", |_, options: PromptOptions| async move { task::spawn_blocking(move || prompt(options)) .await - .map_err(LuaError::external)? + .into_lua_err()? })? .build_readonly() } diff --git a/src/lune/importer/require.rs b/src/lune/importer/require.rs index 20048c6..51588e4 100644 --- a/src/lune/importer/require.rs +++ b/src/lune/importer/require.rs @@ -202,7 +202,7 @@ async fn load_file<'lua>( } // Try to read the wanted file, note that we use bytes instead of reading // to a string since lua scripts are not necessarily valid utf-8 strings - let contents = fs::read(&absolute_path).await.map_err(LuaError::external)?; + let contents = fs::read(&absolute_path).await.into_lua_err()?; // Use a name without extensions for loading the chunk, some // other code assumes the require path is without extensions let path_relative_no_extension = relative_path diff --git a/src/lune/lua/net/client.rs b/src/lune/lua/net/client.rs index 6381f63..2120c58 100644 --- a/src/lune/lua/net/client.rs +++ b/src/lune/lua/net/client.rs @@ -23,8 +23,8 @@ impl NetClientBuilder { { let mut map = HeaderMap::new(); for (key, val) in headers { - let hkey = HeaderName::from_str(key.as_ref()).map_err(LuaError::external)?; - let hval = HeaderValue::from_bytes(val.as_ref()).map_err(LuaError::external)?; + let hkey = HeaderName::from_str(key.as_ref()).into_lua_err()?; + let hval = HeaderValue::from_bytes(val.as_ref()).into_lua_err()?; map.insert(hkey, hval); } self.builder = self.builder.default_headers(map); @@ -32,7 +32,7 @@ impl NetClientBuilder { } pub fn build(self) -> LuaResult { - let client = self.builder.build().map_err(LuaError::external)?; + let client = self.builder.build().into_lua_err()?; Ok(NetClient(client)) } } diff --git a/src/lune/lua/net/response.rs b/src/lune/lua/net/response.rs index 2e94e24..fa2e748 100644 --- a/src/lune/lua/net/response.rs +++ b/src/lune/lua/net/response.rs @@ -24,7 +24,7 @@ impl NetServeResponse { .status(200) .header("Content-Type", "text/plain") .body(Body::from(self.body.unwrap())) - .map_err(LuaError::external)?, + .into_lua_err()?, NetServeResponseKind::Table => { let mut response = Response::builder(); for (key, value) in self.headers { @@ -33,7 +33,7 @@ impl NetServeResponse { response .status(self.status) .body(Body::from(self.body.unwrap_or_default())) - .map_err(LuaError::external)? + .into_lua_err()? } }) } diff --git a/src/lune/lua/net/server.rs b/src/lune/lua/net/server.rs index 236c2ae..d3428df 100644 --- a/src/lune/lua/net/server.rs +++ b/src/lune/lua/net/server.rs @@ -57,7 +57,7 @@ impl Service> for NetServiceInner { task::spawn_local(async move { // Create our new full websocket object, then // schedule our handler to get called asap - let ws = ws.await.map_err(LuaError::external)?; + let ws = ws.await.into_lua_err()?; let sock = NetWebSocket::new(ws).into_lua_table(lua)?; let sched = lua .app_data_ref::<&TaskScheduler>() @@ -77,7 +77,7 @@ impl Service> for NetServiceInner { let (parts, body) = req.into_parts(); Box::pin(async move { // Convert request body into bytes, extract handler - let bytes = to_bytes(body).await.map_err(LuaError::external)?; + let bytes = to_bytes(body).await.into_lua_err()?; let handler: LuaFunction = lua.registry_value(&key)?; // Create a readonly table for the request query params let query_params = TableBuilder::new(lua)? diff --git a/src/lune/lua/net/websocket.rs b/src/lune/lua/net/websocket.rs index 2f613a9..1057b1b 100644 --- a/src/lune/lua/net/websocket.rs +++ b/src/lune/lua/net/websocket.rs @@ -167,10 +167,10 @@ where reason: "".into(), }))) .await - .map_err(LuaError::external)?; + .into_lua_err()?; let res = ws.close(); - res.await.map_err(LuaError::external) + res.await.into_lua_err() } async fn send<'lua, T>( @@ -187,11 +187,11 @@ where let msg = if matches!(as_binary, Some(true)) { WsMessage::Binary(string.as_bytes().to_vec()) } else { - let s = string.to_str().map_err(LuaError::external)?; + let s = string.to_str().into_lua_err()?; WsMessage::Text(s.to_string()) }; let mut ws = socket.write_stream.lock().await; - ws.send(msg).await.map_err(LuaError::external) + ws.send(msg).await.into_lua_err() } async fn next<'lua, T>( @@ -202,7 +202,7 @@ where T: AsyncRead + AsyncWrite + Unpin, { let mut ws = socket.read_stream.lock().await; - let item = ws.next().await.transpose().map_err(LuaError::external); + let item = ws.next().await.transpose().into_lua_err(); let msg = match item { Ok(Some(WsMessage::Close(msg))) => { if let Some(msg) = &msg { diff --git a/src/lune/lua/process/mod.rs b/src/lune/lua/process/mod.rs index c4274bd..b74cd00 100644 --- a/src/lune/lua/process/mod.rs +++ b/src/lune/lua/process/mod.rs @@ -24,9 +24,7 @@ pub async fn pipe_and_inherit_child_process_stdio( let mut stdout = io::stdout(); let mut tee = AsyncTeeWriter::new(&mut stdout); - io::copy(&mut child_stdout, &mut tee) - .await - .map_err(LuaError::external)?; + io::copy(&mut child_stdout, &mut tee).await.into_lua_err()?; Ok::<_, LuaError>(tee.into_vec()) }); @@ -35,9 +33,7 @@ pub async fn pipe_and_inherit_child_process_stdio( let mut stderr = io::stderr(); let mut tee = AsyncTeeWriter::new(&mut stderr); - io::copy(&mut child_stderr, &mut tee) - .await - .map_err(LuaError::external)?; + io::copy(&mut child_stderr, &mut tee).await.into_lua_err()?; Ok::<_, LuaError>(tee.into_vec()) }); diff --git a/src/lune/lua/serde/compress_decompress.rs b/src/lune/lua/serde/compress_decompress.rs index 528847b..85e7ac5 100644 --- a/src/lune/lua/serde/compress_decompress.rs +++ b/src/lune/lua/serde/compress_decompress.rs @@ -102,7 +102,7 @@ pub async fn compress<'lua>( let source = source.as_ref().to_vec(); return task::spawn_blocking(move || compress_prepend_size(&source)) .await - .map_err(LuaError::external); + .into_lua_err(); } let mut bytes = Vec::new(); @@ -135,8 +135,8 @@ pub async fn decompress<'lua>( let source = source.as_ref().to_vec(); return task::spawn_blocking(move || decompress_size_prepended(&source)) .await - .map_err(LuaError::external)? - .map_err(LuaError::external); + .into_lua_err()? + .into_lua_err(); } let mut bytes = Vec::new(); diff --git a/src/lune/lua/serde/encode_decode.rs b/src/lune/lua/serde/encode_decode.rs index 77711e3..edbcc1b 100644 --- a/src/lune/lua/serde/encode_decode.rs +++ b/src/lune/lua/serde/encode_decode.rs @@ -61,23 +61,23 @@ impl EncodeDecodeConfig { EncodeDecodeFormat::Json => { let serialized: JsonValue = lua.from_value_with(value, LUA_DESERIALIZE_OPTIONS)?; if self.pretty { - serde_json::to_vec_pretty(&serialized).map_err(LuaError::external)? + serde_json::to_vec_pretty(&serialized).into_lua_err()? } else { - serde_json::to_vec(&serialized).map_err(LuaError::external)? + serde_json::to_vec(&serialized).into_lua_err()? } } EncodeDecodeFormat::Yaml => { let serialized: YamlValue = lua.from_value_with(value, LUA_DESERIALIZE_OPTIONS)?; let mut writer = Vec::with_capacity(128); - serde_yaml::to_writer(&mut writer, &serialized).map_err(LuaError::external)?; + serde_yaml::to_writer(&mut writer, &serialized).into_lua_err()?; writer } EncodeDecodeFormat::Toml => { let serialized: TomlValue = lua.from_value_with(value, LUA_DESERIALIZE_OPTIONS)?; let s = if self.pretty { - toml::to_string_pretty(&serialized).map_err(LuaError::external)? + toml::to_string_pretty(&serialized).into_lua_err()? } else { - toml::to_string(&serialized).map_err(LuaError::external)? + toml::to_string(&serialized).into_lua_err()? }; s.as_bytes().to_vec() } @@ -93,16 +93,16 @@ impl EncodeDecodeConfig { let bytes = string.as_bytes(); match self.format { EncodeDecodeFormat::Json => { - let value: JsonValue = serde_json::from_slice(bytes).map_err(LuaError::external)?; + let value: JsonValue = serde_json::from_slice(bytes).into_lua_err()?; lua.to_value_with(&value, LUA_SERIALIZE_OPTIONS) } EncodeDecodeFormat::Yaml => { - let value: YamlValue = serde_yaml::from_slice(bytes).map_err(LuaError::external)?; + let value: YamlValue = serde_yaml::from_slice(bytes).into_lua_err()?; lua.to_value_with(&value, LUA_SERIALIZE_OPTIONS) } EncodeDecodeFormat::Toml => { if let Ok(s) = string.to_str() { - let value: TomlValue = toml::from_str(s).map_err(LuaError::external)?; + let value: TomlValue = toml::from_str(s).into_lua_err()?; lua.to_value_with(&value, LUA_SERIALIZE_OPTIONS) } else { Err(LuaError::RuntimeError( diff --git a/src/lune/lua/stdio/formatting.rs b/src/lune/lua/stdio/formatting.rs index d51a83e..8437299 100644 --- a/src/lune/lua/stdio/formatting.rs +++ b/src/lune/lua/stdio/formatting.rs @@ -197,12 +197,12 @@ pub fn pretty_format_multi_value(multi: &LuaMultiValue) -> LuaResult { for value in multi { counter += 1; if let LuaValue::String(s) = value { - write!(buffer, "{}", s.to_string_lossy()).map_err(LuaError::external)?; + write!(buffer, "{}", s.to_string_lossy()).into_lua_err()?; } else { - pretty_format_value(&mut buffer, value, 0).map_err(LuaError::external)?; + pretty_format_value(&mut buffer, value, 0).into_lua_err()?; } if counter < multi.len() { - write!(&mut buffer, " ").map_err(LuaError::external)?; + write!(&mut buffer, " ").into_lua_err()?; } } Ok(buffer) From 55a2338a3e4eb21dff79f3f3ded71fe3dd2c6a41 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sat, 5 Aug 2023 17:51:56 -0500 Subject: [PATCH 13/17] Use global lua compiler instead of recreating on load --- src/lune/importer/require.rs | 5 ++--- src/lune/lua/create.rs | 18 +++++++++++++++++- src/lune/mod.rs | 4 +--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/lune/importer/require.rs b/src/lune/importer/require.rs index 51588e4..113c593 100644 --- a/src/lune/importer/require.rs +++ b/src/lune/importer/require.rs @@ -6,7 +6,7 @@ use std::{ sync::Arc, }; -use mlua::{prelude::*, Compiler as LuaCompiler}; +use mlua::prelude::*; use tokio::fs; use tokio::sync::Mutex as AsyncMutex; @@ -209,9 +209,8 @@ async fn load_file<'lua>( .trim_end_matches(".lua") .trim_end_matches(".luau"); // Load the file into a thread - let compiled_func = LuaCompiler::default().compile(&contents); let loaded_func = lua - .load(compiled_func) + .load(contents) .set_name(path_relative_no_extension) .into_function()?; let loaded_thread = lua.create_thread(loaded_func)?; diff --git a/src/lune/lua/create.rs b/src/lune/lua/create.rs index 4ab8dde..bc5067c 100644 --- a/src/lune/lua/create.rs +++ b/src/lune/lua/create.rs @@ -1,4 +1,4 @@ -use mlua::prelude::*; +use mlua::{prelude::*, Compiler as LuaCompiler}; /* - Level 0 is the call to info @@ -76,14 +76,27 @@ end */ pub fn create() -> LuaResult<&'static Lua> { let lua = Lua::new().into_static(); + + // Enable jit and set global compiler options + lua.set_compiler( + LuaCompiler::default() + .set_coverage_level(0) + .set_debug_level(1) + .set_optimization_level(1), + ); + + // Extract some global tables that we will extract + // built-in functions from and store in the registry let globals = &lua.globals(); let debug: LuaTable = globals.raw_get("debug")?; let table: LuaTable = globals.raw_get("table")?; let string: LuaTable = globals.raw_get("string")?; let coroutine: LuaTable = globals.get("coroutine")?; + // Create a _G table that is separate from our built-in globals let global_table = lua.create_table()?; globals.set("_G", global_table)?; + // Store original lua global functions in the registry so we can use // them later without passing them around and dealing with lifetimes lua.set_named_registry_value("print", globals.get::<_, LuaFunction>("print")?)?; @@ -109,6 +122,7 @@ pub fn create() -> LuaResult<&'static Lua> { "tab.setmeta", globals.get::<_, LuaFunction>("setmetatable")?, )?; + // Create a trace function that can be called to obtain a full stack trace from // lua, this is not possible to do from rust when using our manual scheduler let dbg_trace_env = lua.create_table_with_capacity(0, 1)?; @@ -122,6 +136,7 @@ pub fn create() -> LuaResult<&'static Lua> { .set_environment(dbg_trace_env) .into_function()?; lua.set_named_registry_value("dbg.trace", dbg_trace_fn)?; + // Modify the _VERSION global to also contain the current version of Lune let luau_version_full = globals .get::<_, LuaString>("_VERSION") @@ -142,6 +157,7 @@ pub fn create() -> LuaResult<&'static Lua> { luau = luau_version, ))?, )?; + // All done Ok(lua) } diff --git a/src/lune/mod.rs b/src/lune/mod.rs index 09b3842..e0eeec6 100644 --- a/src/lune/mod.rs +++ b/src/lune/mod.rs @@ -2,7 +2,6 @@ use std::process::ExitCode; use lua::task::{TaskScheduler, TaskSchedulerResumeExt, TaskSchedulerScheduleExt}; use mlua::prelude::*; -use mlua::Compiler as LuaCompiler; use tokio::task::LocalSet; pub mod builtins; @@ -67,7 +66,6 @@ impl Lune { ) -> Result { // Create our special lune-flavored Lua object with extra registry values let lua = lua::create_lune_lua()?; - let script = LuaCompiler::default().compile(script_contents); // Create our task scheduler and all globals // NOTE: Some globals require the task scheduler to exist on startup let sched = TaskScheduler::new(lua)?.into_static(); @@ -75,7 +73,7 @@ impl Lune { importer::create(lua, self.args.clone())?; // Create the main thread and schedule it let main_chunk = lua - .load(script) + .load(script_contents.as_ref()) .set_name(script_name.as_ref()) .into_function()?; let main_thread = lua.create_thread(main_chunk)?; From fea97972a27448ae3d8b72417310046783ac4aae Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sat, 5 Aug 2023 17:53:32 -0500 Subject: [PATCH 14/17] Enable jit --- Cargo.toml | 6 +++++- src/lune/lua/create.rs | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 163c716..ac79a8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,11 @@ urlencoding = "2.1" ### RUNTIME -mlua = { version = "0.9.0-beta.3", features = ["luau", "serialize"] } +mlua = { version = "0.9.0-beta.3", features = [ + "luau", + "luau-jit", + "serialize", +] } tokio = { version = "1.24", features = ["full"] } ### SERDE diff --git a/src/lune/lua/create.rs b/src/lune/lua/create.rs index bc5067c..dc22ec8 100644 --- a/src/lune/lua/create.rs +++ b/src/lune/lua/create.rs @@ -78,6 +78,7 @@ pub fn create() -> LuaResult<&'static Lua> { let lua = Lua::new().into_static(); // Enable jit and set global compiler options + lua.enable_jit(true); lua.set_compiler( LuaCompiler::default() .set_coverage_level(0) From 2867ccb9b8a231abfb248aa780246da1c0a05c93 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sat, 5 Aug 2023 17:55:04 -0500 Subject: [PATCH 15/17] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c19a216..64e1fea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changed + +- Enabled Luau JIT backend for potential performance improvements 🚀
+ If you run into any strange behavior please open an issue! + ### Fixed - Fixed `serde.decode` deserializing `null` values as `userdata` instead of `nil`. From 9f662f21f1fdb5cf3580d365b4b4f352fa7c9ad3 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 7 Aug 2023 16:18:21 -0500 Subject: [PATCH 16/17] Bump rbx_cookie dependency --- Cargo.lock | 94 ++++-------------------------------------------------- Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cf815a..7f90ed1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,15 +41,6 @@ dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" version = "0.3.2" @@ -148,17 +139,6 @@ dependencies = [ "syn 2.0.27", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -311,21 +291,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "clap" version = "4.3.19" @@ -346,7 +311,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.10.0", + "strsim", ] [[package]] @@ -545,19 +510,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.10.0" @@ -778,15 +730,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.2" @@ -946,7 +889,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi", "rustix", "windows-sys 0.48.0", ] @@ -1034,12 +977,12 @@ dependencies = [ "anyhow", "async-compression", "async-trait", - "clap 4.3.19", + "clap", "console", "dialoguer", "directories", "dunce", - "env_logger 0.10.0", + "env_logger", "futures-util", "glam", "hyper", @@ -1174,7 +1117,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi", "libc", ] @@ -1408,15 +1351,13 @@ dependencies = [ [[package]] name = "rbx_cookie" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d909d60469944842c9d204fa44afc90d9cb1e1f3f30a29bd1def490edd525a96" +checksum = "6716fd418196130f1dd19947608b94bda942d096b514be9b441409d262bf5e59" dependencies = [ "byteorder 0.5.3", - "clap 2.34.0", "cookie", "dirs", - "env_logger 0.9.3", "log", "plist", "winapi", @@ -1973,12 +1914,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" @@ -2035,15 +1970,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.44" @@ -2385,12 +2311,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index ac79a8d..e1d9ee5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,7 +125,7 @@ regex = { optional = true, version = "1.7", default-features = false, features = glam = { optional = true, version = "0.24" } rand = { optional = true, version = "0.8" } -rbx_cookie = { optional = true, version = "0.1.2" } +rbx_cookie = { optional = true, version = "0.1.3", default-features = false } rbx_binary = { optional = true, git = "https://github.com/rojo-rbx/rbx-dom", rev = "e7a813d569c3f8a54be8a8873c33f8976c37b8b1" } rbx_dom_weak = { optional = true, git = "https://github.com/rojo-rbx/rbx-dom", rev = "e7a813d569c3f8a54be8a8873c33f8976c37b8b1" } From 036c171eb3f57ccf7fb7fced0e887d51edea9e63 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 7 Aug 2023 16:20:53 -0500 Subject: [PATCH 17/17] Update dependencies --- CHANGELOG.md | 1 + Cargo.lock | 136 +++++++++++++++++++++++++++------------------------ 2 files changed, 74 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64e1fea..869c785 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Update to Luau version `0.588` - Enabled Luau JIT backend for potential performance improvements 🚀
If you run into any strange behavior please open an issue! diff --git a/Cargo.lock b/Cargo.lock index 7f90ed1..b5f2529 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,7 +136,7 @@ checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -281,9 +281,12 @@ checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -323,7 +326,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -422,6 +425,12 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +[[package]] +name = "deranged" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" + [[package]] name = "dialoguer" version = "0.10.4" @@ -540,9 +549,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -613,7 +622,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -941,9 +950,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lock_api" @@ -963,9 +972,9 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "luau0-src" -version = "0.5.11+luau583" +version = "0.6.0+luau588" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7183d0a3eaa387403dd4ac7b449536af82c1545b7a48247af1652ca66926d5" +checksum = "4c628f5525cc62a89a2d478b2ee619c77b35da55c8e3231752f3b8fe528a6c49" dependencies = [ "cc", ] @@ -1076,9 +1085,9 @@ dependencies = [ [[package]] name = "mlua" -version = "0.9.0-rc.1" +version = "0.9.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5369212118d0f115c9adbe7f7905e36fb3ef2994266039c51fc3e96374d705d" +checksum = "01a6500a9fb74b519a85ac206cd57f9f91b270ce39d6cb12ab06a8ed29c3563d" dependencies = [ "bstr", "erased-serde", @@ -1092,9 +1101,9 @@ dependencies = [ [[package]] name = "mlua-sys" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3daecc55a656cae8e54fc599701ab597b67cdde68f780cac8c1c49b9cfff2f5" +checksum = "aa5b61f6c943d77dd6ab5f670865670f65b978400127c8bf31c2df7d6e76289a" dependencies = [ "cc", "cfg-if", @@ -1197,29 +1206,29 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] name = "pin-project-lite" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c" [[package]] name = "pin-utils" @@ -1244,7 +1253,7 @@ dependencies = [ "line-wrap", "quick-xml", "serde", - "time 0.3.23", + "time 0.3.25", ] [[package]] @@ -1270,21 +1279,21 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332cd62e95873ea4f41f3dfd6bbbfc5b52aec892d7e8d534197c4720a0bbbab2" +checksum = "46b2164ebdb1dfeec5e337be164292351e11daf63a05174c6776b2f47460f0c9" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a10adb8d151bb1280afb8bed41ae5db26be1b056964947133c7525b0bf39c0b0" +checksum = "097bf8b99121dfb8c75eed54dfbdbdb1d53e372c53d2353e8a15aad2a479249d" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -1298,9 +1307,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -1469,9 +1478,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ "aho-corasick", "memchr", @@ -1481,9 +1490,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.3" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" dependencies = [ "aho-corasick", "memchr", @@ -1607,9 +1616,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.4" +version = "0.38.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +checksum = "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399" dependencies = [ "bitflags 2.3.3", "errno", @@ -1620,13 +1629,13 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.5" +version = "0.21.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" +checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" dependencies = [ "log", "ring", - "rustls-webpki 0.101.1", + "rustls-webpki 0.101.2", "sct", ] @@ -1651,9 +1660,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.1" +version = "0.101.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e" +checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" dependencies = [ "ring", "untrusted", @@ -1704,9 +1713,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.174" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] @@ -1723,20 +1732,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.174" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5c3a298c7f978e53536f95a63bdc4c4a64550582f31a0359a9afda6aede62e" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] name = "serde_json" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "indexmap 2.0.0", "itoa", @@ -1939,9 +1948,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.27" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -1950,9 +1959,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.7.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" +checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" dependencies = [ "cfg-if", "fastrand", @@ -1987,7 +1996,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -2007,14 +2016,15 @@ dependencies = [ [[package]] name = "time" -version = "0.3.23" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" dependencies = [ + "deranged", "itoa", "serde", "time-core", - "time-macros 0.2.10", + "time-macros 0.2.11", ] [[package]] @@ -2035,9 +2045,9 @@ dependencies = [ [[package]] name = "time-macros" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" dependencies = [ "time-core", ] @@ -2098,7 +2108,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -2359,7 +2369,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", "wasm-bindgen-shared", ] @@ -2393,7 +2403,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2607,9 +2617,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.5.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" +checksum = "acaaa1190073b2b101e15083c38ee8ec891b5e05cbee516521e94ec008f61e64" dependencies = [ "memchr", ]