From 91fd9d92a90042bc3cb1ad16337a772ab26c7c74 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 13:41:11 +0200 Subject: [PATCH 01/68] Set up workspace manifest and move lune crate into crates dir --- Cargo.toml | 163 ++++-------------- crates/lune/Cargo.toml | 139 +++++++++++++++ .../lune/src}/cli/build/base_exe.rs | 0 {src => crates/lune/src}/cli/build/files.rs | 0 {src => crates/lune/src}/cli/build/mod.rs | 0 {src => crates/lune/src}/cli/build/result.rs | 0 {src => crates/lune/src}/cli/build/target.rs | 0 {src => crates/lune/src}/cli/list.rs | 0 {src => crates/lune/src}/cli/mod.rs | 0 {src => crates/lune/src}/cli/repl.rs | 0 {src => crates/lune/src}/cli/run.rs | 0 {src => crates/lune/src}/cli/setup.rs | 0 {src => crates/lune/src}/cli/utils/files.rs | 0 {src => crates/lune/src}/cli/utils/listing.rs | 0 {src => crates/lune/src}/cli/utils/mod.rs | 0 {src => crates/lune/src}/lib.rs | 0 .../lune/src}/lune/builtins/datetime/error.rs | 0 .../lune/src}/lune/builtins/datetime/mod.rs | 0 .../src}/lune/builtins/datetime/values.rs | 0 .../lune/src}/lune/builtins/fs/copy.rs | 0 .../lune/src}/lune/builtins/fs/metadata.rs | 0 .../lune/src}/lune/builtins/fs/mod.rs | 0 .../lune/src}/lune/builtins/fs/options.rs | 0 .../lune/src}/lune/builtins/luau/mod.rs | 0 .../lune/src}/lune/builtins/luau/options.rs | 0 {src => crates/lune/src}/lune/builtins/mod.rs | 0 .../lune/src}/lune/builtins/net/client.rs | 0 .../lune/src}/lune/builtins/net/config.rs | 0 .../lune/src}/lune/builtins/net/mod.rs | 0 .../src}/lune/builtins/net/server/keys.rs | 0 .../lune/src}/lune/builtins/net/server/mod.rs | 0 .../src}/lune/builtins/net/server/request.rs | 0 .../src}/lune/builtins/net/server/response.rs | 0 .../src}/lune/builtins/net/server/service.rs | 0 .../lune/src}/lune/builtins/net/util.rs | 0 .../lune/src}/lune/builtins/net/websocket.rs | 0 .../lune/src}/lune/builtins/process/mod.rs | 0 .../lune/builtins/process/options/kind.rs | 0 .../src}/lune/builtins/process/options/mod.rs | 0 .../lune/builtins/process/options/stdio.rs | 0 .../src}/lune/builtins/process/tee_writer.rs | 0 .../lune/builtins/process/wait_for_child.rs | 0 .../lune/src}/lune/builtins/regex/captures.rs | 0 .../lune/src}/lune/builtins/regex/matches.rs | 0 .../lune/src}/lune/builtins/regex/mod.rs | 0 .../lune/src}/lune/builtins/regex/regex.rs | 0 .../lune/src}/lune/builtins/roblox/mod.rs | 0 .../builtins/serde/compress_decompress.rs | 0 .../src}/lune/builtins/serde/encode_decode.rs | 0 .../lune/src}/lune/builtins/serde/mod.rs | 0 .../lune/src}/lune/builtins/stdio/mod.rs | 0 .../lune/src}/lune/builtins/stdio/prompt.rs | 0 .../lune/src}/lune/builtins/task/mod.rs | 0 {src => crates/lune/src}/lune/error.rs | 0 .../lune/src}/lune/globals/g_table.rs | 0 {src => crates/lune/src}/lune/globals/mod.rs | 0 .../lune/src}/lune/globals/print.rs | 0 .../lune/src}/lune/globals/require/alias.rs | 0 .../lune/src}/lune/globals/require/builtin.rs | 0 .../lune/src}/lune/globals/require/context.rs | 0 .../lune/src}/lune/globals/require/mod.rs | 0 .../lune/src}/lune/globals/require/path.rs | 0 .../lune/src}/lune/globals/version.rs | 0 {src => crates/lune/src}/lune/globals/warn.rs | 0 {src => crates/lune/src}/lune/mod.rs | 0 .../lune/src}/lune/util/formatting.rs | 0 {src => crates/lune/src}/lune/util/luaurc.rs | 0 {src => crates/lune/src}/lune/util/mod.rs | 0 {src => crates/lune/src}/lune/util/paths.rs | 0 .../lune/src}/lune/util/table_builder.rs | 0 {src => crates/lune/src}/lune/util/traits.rs | 0 {src => crates/lune/src}/main.rs | 0 .../lune/src}/roblox/datatypes/attributes.rs | 0 .../lune/src}/roblox/datatypes/conversion.rs | 0 .../lune/src}/roblox/datatypes/extension.rs | 0 .../lune/src}/roblox/datatypes/mod.rs | 0 .../lune/src}/roblox/datatypes/result.rs | 0 .../lune/src}/roblox/datatypes/types/axes.rs | 0 .../roblox/datatypes/types/brick_color.rs | 0 .../src}/roblox/datatypes/types/cframe.rs | 0 .../src}/roblox/datatypes/types/color3.rs | 0 .../roblox/datatypes/types/color_sequence.rs | 0 .../types/color_sequence_keypoint.rs | 0 .../lune/src}/roblox/datatypes/types/enum.rs | 0 .../src}/roblox/datatypes/types/enum_item.rs | 0 .../lune/src}/roblox/datatypes/types/enums.rs | 0 .../lune/src}/roblox/datatypes/types/faces.rs | 0 .../lune/src}/roblox/datatypes/types/font.rs | 0 .../lune/src}/roblox/datatypes/types/mod.rs | 0 .../roblox/datatypes/types/number_range.rs | 0 .../roblox/datatypes/types/number_sequence.rs | 0 .../types/number_sequence_keypoint.rs | 0 .../datatypes/types/physical_properties.rs | 0 .../lune/src}/roblox/datatypes/types/ray.rs | 0 .../lune/src}/roblox/datatypes/types/rect.rs | 0 .../src}/roblox/datatypes/types/region3.rs | 0 .../roblox/datatypes/types/region3int16.rs | 0 .../lune/src}/roblox/datatypes/types/udim.rs | 0 .../lune/src}/roblox/datatypes/types/udim2.rs | 0 .../src}/roblox/datatypes/types/vector2.rs | 0 .../roblox/datatypes/types/vector2int16.rs | 0 .../src}/roblox/datatypes/types/vector3.rs | 0 .../roblox/datatypes/types/vector3int16.rs | 0 .../lune/src}/roblox/datatypes/util.rs | 0 .../lune/src}/roblox/document/error.rs | 0 .../lune/src}/roblox/document/format.rs | 0 .../lune/src}/roblox/document/kind.rs | 0 .../lune/src}/roblox/document/mod.rs | 0 .../src}/roblox/document/postprocessing.rs | 0 {src => crates/lune/src}/roblox/exports.rs | 0 .../lune/src}/roblox/instance/base.rs | 0 .../lune/src}/roblox/instance/data_model.rs | 0 .../lune/src}/roblox/instance/mod.rs | 0 .../lune/src}/roblox/instance/registry.rs | 0 .../lune/src}/roblox/instance/terrain.rs | 0 .../lune/src}/roblox/instance/workspace.rs | 0 {src => crates/lune/src}/roblox/mod.rs | 0 .../lune/src}/roblox/reflection/class.rs | 0 .../lune/src}/roblox/reflection/enums.rs | 0 .../lune/src}/roblox/reflection/mod.rs | 0 .../lune/src}/roblox/reflection/property.rs | 0 .../lune/src}/roblox/reflection/utils.rs | 0 .../lune/src}/roblox/shared/classes.rs | 0 .../lune/src}/roblox/shared/instance.rs | 0 {src => crates/lune/src}/roblox/shared/mod.rs | 0 .../lune/src}/roblox/shared/userdata.rs | 0 .../lune/src}/standalone/metadata.rs | 0 {src => crates/lune/src}/standalone/mod.rs | 0 {src => crates/lune/src}/standalone/tracer.rs | 0 {src => crates/lune/src}/tests.rs | 0 130 files changed, 168 insertions(+), 134 deletions(-) create mode 100644 crates/lune/Cargo.toml rename {src => crates/lune/src}/cli/build/base_exe.rs (100%) rename {src => crates/lune/src}/cli/build/files.rs (100%) rename {src => crates/lune/src}/cli/build/mod.rs (100%) rename {src => crates/lune/src}/cli/build/result.rs (100%) rename {src => crates/lune/src}/cli/build/target.rs (100%) rename {src => crates/lune/src}/cli/list.rs (100%) rename {src => crates/lune/src}/cli/mod.rs (100%) rename {src => crates/lune/src}/cli/repl.rs (100%) rename {src => crates/lune/src}/cli/run.rs (100%) rename {src => crates/lune/src}/cli/setup.rs (100%) rename {src => crates/lune/src}/cli/utils/files.rs (100%) rename {src => crates/lune/src}/cli/utils/listing.rs (100%) rename {src => crates/lune/src}/cli/utils/mod.rs (100%) rename {src => crates/lune/src}/lib.rs (100%) rename {src => crates/lune/src}/lune/builtins/datetime/error.rs (100%) rename {src => crates/lune/src}/lune/builtins/datetime/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/datetime/values.rs (100%) rename {src => crates/lune/src}/lune/builtins/fs/copy.rs (100%) rename {src => crates/lune/src}/lune/builtins/fs/metadata.rs (100%) rename {src => crates/lune/src}/lune/builtins/fs/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/fs/options.rs (100%) rename {src => crates/lune/src}/lune/builtins/luau/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/luau/options.rs (100%) rename {src => crates/lune/src}/lune/builtins/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/net/client.rs (100%) rename {src => crates/lune/src}/lune/builtins/net/config.rs (100%) rename {src => crates/lune/src}/lune/builtins/net/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/net/server/keys.rs (100%) rename {src => crates/lune/src}/lune/builtins/net/server/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/net/server/request.rs (100%) rename {src => crates/lune/src}/lune/builtins/net/server/response.rs (100%) rename {src => crates/lune/src}/lune/builtins/net/server/service.rs (100%) rename {src => crates/lune/src}/lune/builtins/net/util.rs (100%) rename {src => crates/lune/src}/lune/builtins/net/websocket.rs (100%) rename {src => crates/lune/src}/lune/builtins/process/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/process/options/kind.rs (100%) rename {src => crates/lune/src}/lune/builtins/process/options/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/process/options/stdio.rs (100%) rename {src => crates/lune/src}/lune/builtins/process/tee_writer.rs (100%) rename {src => crates/lune/src}/lune/builtins/process/wait_for_child.rs (100%) rename {src => crates/lune/src}/lune/builtins/regex/captures.rs (100%) rename {src => crates/lune/src}/lune/builtins/regex/matches.rs (100%) rename {src => crates/lune/src}/lune/builtins/regex/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/regex/regex.rs (100%) rename {src => crates/lune/src}/lune/builtins/roblox/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/serde/compress_decompress.rs (100%) rename {src => crates/lune/src}/lune/builtins/serde/encode_decode.rs (100%) rename {src => crates/lune/src}/lune/builtins/serde/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/stdio/mod.rs (100%) rename {src => crates/lune/src}/lune/builtins/stdio/prompt.rs (100%) rename {src => crates/lune/src}/lune/builtins/task/mod.rs (100%) rename {src => crates/lune/src}/lune/error.rs (100%) rename {src => crates/lune/src}/lune/globals/g_table.rs (100%) rename {src => crates/lune/src}/lune/globals/mod.rs (100%) rename {src => crates/lune/src}/lune/globals/print.rs (100%) rename {src => crates/lune/src}/lune/globals/require/alias.rs (100%) rename {src => crates/lune/src}/lune/globals/require/builtin.rs (100%) rename {src => crates/lune/src}/lune/globals/require/context.rs (100%) rename {src => crates/lune/src}/lune/globals/require/mod.rs (100%) rename {src => crates/lune/src}/lune/globals/require/path.rs (100%) rename {src => crates/lune/src}/lune/globals/version.rs (100%) rename {src => crates/lune/src}/lune/globals/warn.rs (100%) rename {src => crates/lune/src}/lune/mod.rs (100%) rename {src => crates/lune/src}/lune/util/formatting.rs (100%) rename {src => crates/lune/src}/lune/util/luaurc.rs (100%) rename {src => crates/lune/src}/lune/util/mod.rs (100%) rename {src => crates/lune/src}/lune/util/paths.rs (100%) rename {src => crates/lune/src}/lune/util/table_builder.rs (100%) rename {src => crates/lune/src}/lune/util/traits.rs (100%) rename {src => crates/lune/src}/main.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/attributes.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/conversion.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/extension.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/mod.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/result.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/axes.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/brick_color.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/cframe.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/color3.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/color_sequence.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/color_sequence_keypoint.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/enum.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/enum_item.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/enums.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/faces.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/font.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/mod.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/number_range.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/number_sequence.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/number_sequence_keypoint.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/physical_properties.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/ray.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/rect.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/region3.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/region3int16.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/udim.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/udim2.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/vector2.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/vector2int16.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/vector3.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/types/vector3int16.rs (100%) rename {src => crates/lune/src}/roblox/datatypes/util.rs (100%) rename {src => crates/lune/src}/roblox/document/error.rs (100%) rename {src => crates/lune/src}/roblox/document/format.rs (100%) rename {src => crates/lune/src}/roblox/document/kind.rs (100%) rename {src => crates/lune/src}/roblox/document/mod.rs (100%) rename {src => crates/lune/src}/roblox/document/postprocessing.rs (100%) rename {src => crates/lune/src}/roblox/exports.rs (100%) rename {src => crates/lune/src}/roblox/instance/base.rs (100%) rename {src => crates/lune/src}/roblox/instance/data_model.rs (100%) rename {src => crates/lune/src}/roblox/instance/mod.rs (100%) rename {src => crates/lune/src}/roblox/instance/registry.rs (100%) rename {src => crates/lune/src}/roblox/instance/terrain.rs (100%) rename {src => crates/lune/src}/roblox/instance/workspace.rs (100%) rename {src => crates/lune/src}/roblox/mod.rs (100%) rename {src => crates/lune/src}/roblox/reflection/class.rs (100%) rename {src => crates/lune/src}/roblox/reflection/enums.rs (100%) rename {src => crates/lune/src}/roblox/reflection/mod.rs (100%) rename {src => crates/lune/src}/roblox/reflection/property.rs (100%) rename {src => crates/lune/src}/roblox/reflection/utils.rs (100%) rename {src => crates/lune/src}/roblox/shared/classes.rs (100%) rename {src => crates/lune/src}/roblox/shared/instance.rs (100%) rename {src => crates/lune/src}/roblox/shared/mod.rs (100%) rename {src => crates/lune/src}/roblox/shared/userdata.rs (100%) rename {src => crates/lune/src}/standalone/metadata.rs (100%) rename {src => crates/lune/src}/standalone/mod.rs (100%) rename {src => crates/lune/src}/standalone/tracer.rs (100%) rename {src => crates/lune/src}/tests.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 81990827..9d431bb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,42 +1,7 @@ -[package] -name = "lune" -version = "0.8.3" -edition = "2021" -license = "MPL-2.0" -repository = "https://github.com/lune-org/lune" -description = "A standalone Luau runtime" -readme = "README.md" -keywords = ["cli", "lua", "luau", "runtime"] -categories = ["command-line-interface"] - -[[bin]] -name = "lune" -path = "src/main.rs" - -[lib] -name = "lune" -path = "src/lib.rs" - -[features] -default = ["cli", "roblox"] -cli = [ - "dep:anyhow", - "dep:env_logger", - "dep:clap", - "dep:include_dir", - "dep:rustyline", - "dep:zip_next", -] -roblox = [ - "dep:glam", - "dep:rand", - "dep:rbx_cookie", - "dep:rbx_binary", - "dep:rbx_dom_weak", - "dep:rbx_reflection", - "dep:rbx_reflection_database", - "dep:rbx_xml", -] +[workspace] +resolver = "2" +default-members = ["crates/lune"] +members = ["crates/lune"] # Profile for building the release binary, with the following options set: # @@ -53,99 +18,29 @@ opt-level = "z" strip = true lto = true -# All of the dependencies for Lune. +# Lints for all crates in the workspace # -# Dependencies are categorized as following: -# -# 1. General dependencies with no specific features set -# 2. Large / core dependencies that have many different crates and / or features set -# 3. Dependencies for specific features of Lune, eg. the CLI or massive Roblox builtin library -# -[dependencies] -console = "0.15" -directories = "5.0" -futures-util = "0.3" -once_cell = "1.17" -thiserror = "1.0" -async-trait = "0.1" -dialoguer = "0.11" -dunce = "1.0" -lz4_flex = "0.11" -path-clean = "1.0" -pathdiff = "0.2" -pin-project = "1.0" -urlencoding = "2.1" -bstr = "1.9" -regex = "1.10" -self_cell = "1.0" - -### RUNTIME - -blocking = "1.5" -tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } -tokio = { version = "1.24", features = ["full", "tracing"] } -os_str_bytes = { version = "7.0", features = ["conversions"] } - -mlua-luau-scheduler = { version = "0.0.2" } -mlua = { version = "0.9.7", features = [ - "luau", - "luau-jit", - "async", - "serialize", -] } - -### SERDE - -async-compression = { version = "0.4", features = [ - "tokio", - "brotli", - "deflate", - "gzip", - "zlib", -] } -serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0", features = ["preserve_order"] } -serde_yaml = "0.9" -toml = { version = "0.8", features = ["preserve_order"] } - -### NET - -hyper = { version = "1.1", features = ["full"] } -hyper-util = { version = "0.1", features = ["full"] } -http = "1.0" -http-body-util = { version = "0.1" } -hyper-tungstenite = { version = "0.13" } - -reqwest = { version = "0.11", default-features = false, features = [ - "rustls-tls", -] } - -tokio-tungstenite = { version = "0.21", features = ["rustls-tls-webpki-roots"] } - -### DATETIME -chrono = "=0.4.34" # NOTE: 0.4.35 does not compile with chrono_lc -chrono_lc = "0.1" - -### CLI - -anyhow = { optional = true, version = "1.0" } -env_logger = { optional = true, version = "0.11" } -itertools = "0.12" -clap = { optional = true, version = "4.1", features = ["derive"] } -include_dir = { optional = true, version = "0.7", features = ["glob"] } -rustyline = { optional = true, version = "14.0" } -zip_next = { optional = true, version = "1.1" } - -### ROBLOX - -glam = { optional = true, version = "0.27" } -rand = { optional = true, version = "0.8" } - -rbx_cookie = { optional = true, version = "0.1.4", default-features = false } - -rbx_binary = { optional = true, version = "0.7.3" } -rbx_dom_weak = { optional = true, version = "2.6.0" } -rbx_reflection = { optional = true, version = "4.4.0" } -rbx_reflection_database = { optional = true, version = "0.2.9" } -rbx_xml = { optional = true, version = "0.13.2" } +# 1. Error on all lints by default, then make cargo + clippy pedantic lints just warn +# 2. Selectively allow some lints that are _too_ pedantic, such as: +# - Casts between number types +# - Module naming conventions +# - Imports and multiple dependency versions +[workspace.lints.clippy] +all = { level = "deny", priority = -3 } +cargo = { level = "warn", priority = -2 } +pedantic = { level = "warn", priority = -1 } + +cast_lossless = { level = "allow", priority = 1 } +cast_possible_truncation = { level = "allow", priority = 1 } +cast_possible_wrap = { level = "allow", priority = 1 } +cast_precision_loss = { level = "allow", priority = 1 } +cast_sign_loss = { level = "allow", priority = 1 } + +unreadable_literal = { level = "allow", priority = 1 } +unnested_or_patterns = { level = "allow", priority = 1 } + +multiple_crate_versions = { level = "allow", priority = 1 } +module_inception = { level = "allow", priority = 1 } +module_name_repetitions = { level = "allow", priority = 1 } +needless_pass_by_value = { level = "allow", priority = 1 } +wildcard_imports = { level = "allow", priority = 1 } diff --git a/crates/lune/Cargo.toml b/crates/lune/Cargo.toml new file mode 100644 index 00000000..d8bf5915 --- /dev/null +++ b/crates/lune/Cargo.toml @@ -0,0 +1,139 @@ +[package] +name = "lune" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" +repository = "https://github.com/lune-org/lune" +description = "A standalone Luau runtime" +readme = "README.md" +keywords = ["cli", "lua", "luau", "runtime"] +categories = ["command-line-interface"] + +[[bin]] +name = "lune" +path = "src/main.rs" + +[lib] +name = "lune" +path = "src/lib.rs" + +[features] +default = ["cli", "roblox"] +cli = [ + "dep:anyhow", + "dep:env_logger", + "dep:clap", + "dep:include_dir", + "dep:rustyline", + "dep:zip_next", +] +roblox = [ + "dep:glam", + "dep:rand", + "dep:rbx_cookie", + "dep:rbx_binary", + "dep:rbx_dom_weak", + "dep:rbx_reflection", + "dep:rbx_reflection_database", + "dep:rbx_xml", +] + +[lints] +workspace = true + +# All of the dependencies for Lune. +# +# Dependencies are categorized as following: +# +# 1. General dependencies with no specific features set +# 2. Large / core dependencies that have many different crates and / or features set +# 3. Dependencies for specific features of Lune, eg. the CLI or massive Roblox builtin library +# +[dependencies] +console = "0.15" +directories = "5.0" +futures-util = "0.3" +once_cell = "1.17" +thiserror = "1.0" +async-trait = "0.1" +dialoguer = "0.11" +dunce = "1.0" +lz4_flex = "0.11" +path-clean = "1.0" +pathdiff = "0.2" +pin-project = "1.0" +urlencoding = "2.1" +bstr = "1.9" +regex = "1.10" +self_cell = "1.0" + +### RUNTIME + +blocking = "1.5" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tokio = { version = "1.24", features = ["full", "tracing"] } +os_str_bytes = { version = "7.0", features = ["conversions"] } + +mlua-luau-scheduler = { version = "0.0.2" } +mlua = { version = "0.9.7", features = [ + "luau", + "luau-jit", + "async", + "serialize", +] } + +### SERDE + +async-compression = { version = "0.4", features = [ + "tokio", + "brotli", + "deflate", + "gzip", + "zlib", +] } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", features = ["preserve_order"] } +serde_yaml = "0.9" +toml = { version = "0.8", features = ["preserve_order"] } + +### NET + +hyper = { version = "1.1", features = ["full"] } +hyper-util = { version = "0.1", features = ["full"] } +http = "1.0" +http-body-util = { version = "0.1" } +hyper-tungstenite = { version = "0.13" } + +reqwest = { version = "0.11", default-features = false, features = [ + "rustls-tls", +] } + +tokio-tungstenite = { version = "0.21", features = ["rustls-tls-webpki-roots"] } + +### DATETIME +chrono = "=0.4.34" # NOTE: 0.4.35 does not compile with chrono_lc +chrono_lc = "0.1" + +### CLI + +anyhow = { optional = true, version = "1.0" } +env_logger = { optional = true, version = "0.11" } +itertools = "0.12" +clap = { optional = true, version = "4.1", features = ["derive"] } +include_dir = { optional = true, version = "0.7", features = ["glob"] } +rustyline = { optional = true, version = "14.0" } +zip_next = { optional = true, version = "1.1" } + +### ROBLOX + +glam = { optional = true, version = "0.27" } +rand = { optional = true, version = "0.8" } + +rbx_cookie = { optional = true, version = "0.1.4", default-features = false } + +rbx_binary = { optional = true, version = "0.7.3" } +rbx_dom_weak = { optional = true, version = "2.6.0" } +rbx_reflection = { optional = true, version = "4.4.0" } +rbx_reflection_database = { optional = true, version = "0.2.9" } +rbx_xml = { optional = true, version = "0.13.2" } diff --git a/src/cli/build/base_exe.rs b/crates/lune/src/cli/build/base_exe.rs similarity index 100% rename from src/cli/build/base_exe.rs rename to crates/lune/src/cli/build/base_exe.rs diff --git a/src/cli/build/files.rs b/crates/lune/src/cli/build/files.rs similarity index 100% rename from src/cli/build/files.rs rename to crates/lune/src/cli/build/files.rs diff --git a/src/cli/build/mod.rs b/crates/lune/src/cli/build/mod.rs similarity index 100% rename from src/cli/build/mod.rs rename to crates/lune/src/cli/build/mod.rs diff --git a/src/cli/build/result.rs b/crates/lune/src/cli/build/result.rs similarity index 100% rename from src/cli/build/result.rs rename to crates/lune/src/cli/build/result.rs diff --git a/src/cli/build/target.rs b/crates/lune/src/cli/build/target.rs similarity index 100% rename from src/cli/build/target.rs rename to crates/lune/src/cli/build/target.rs diff --git a/src/cli/list.rs b/crates/lune/src/cli/list.rs similarity index 100% rename from src/cli/list.rs rename to crates/lune/src/cli/list.rs diff --git a/src/cli/mod.rs b/crates/lune/src/cli/mod.rs similarity index 100% rename from src/cli/mod.rs rename to crates/lune/src/cli/mod.rs diff --git a/src/cli/repl.rs b/crates/lune/src/cli/repl.rs similarity index 100% rename from src/cli/repl.rs rename to crates/lune/src/cli/repl.rs diff --git a/src/cli/run.rs b/crates/lune/src/cli/run.rs similarity index 100% rename from src/cli/run.rs rename to crates/lune/src/cli/run.rs diff --git a/src/cli/setup.rs b/crates/lune/src/cli/setup.rs similarity index 100% rename from src/cli/setup.rs rename to crates/lune/src/cli/setup.rs diff --git a/src/cli/utils/files.rs b/crates/lune/src/cli/utils/files.rs similarity index 100% rename from src/cli/utils/files.rs rename to crates/lune/src/cli/utils/files.rs diff --git a/src/cli/utils/listing.rs b/crates/lune/src/cli/utils/listing.rs similarity index 100% rename from src/cli/utils/listing.rs rename to crates/lune/src/cli/utils/listing.rs diff --git a/src/cli/utils/mod.rs b/crates/lune/src/cli/utils/mod.rs similarity index 100% rename from src/cli/utils/mod.rs rename to crates/lune/src/cli/utils/mod.rs diff --git a/src/lib.rs b/crates/lune/src/lib.rs similarity index 100% rename from src/lib.rs rename to crates/lune/src/lib.rs diff --git a/src/lune/builtins/datetime/error.rs b/crates/lune/src/lune/builtins/datetime/error.rs similarity index 100% rename from src/lune/builtins/datetime/error.rs rename to crates/lune/src/lune/builtins/datetime/error.rs diff --git a/src/lune/builtins/datetime/mod.rs b/crates/lune/src/lune/builtins/datetime/mod.rs similarity index 100% rename from src/lune/builtins/datetime/mod.rs rename to crates/lune/src/lune/builtins/datetime/mod.rs diff --git a/src/lune/builtins/datetime/values.rs b/crates/lune/src/lune/builtins/datetime/values.rs similarity index 100% rename from src/lune/builtins/datetime/values.rs rename to crates/lune/src/lune/builtins/datetime/values.rs diff --git a/src/lune/builtins/fs/copy.rs b/crates/lune/src/lune/builtins/fs/copy.rs similarity index 100% rename from src/lune/builtins/fs/copy.rs rename to crates/lune/src/lune/builtins/fs/copy.rs diff --git a/src/lune/builtins/fs/metadata.rs b/crates/lune/src/lune/builtins/fs/metadata.rs similarity index 100% rename from src/lune/builtins/fs/metadata.rs rename to crates/lune/src/lune/builtins/fs/metadata.rs diff --git a/src/lune/builtins/fs/mod.rs b/crates/lune/src/lune/builtins/fs/mod.rs similarity index 100% rename from src/lune/builtins/fs/mod.rs rename to crates/lune/src/lune/builtins/fs/mod.rs diff --git a/src/lune/builtins/fs/options.rs b/crates/lune/src/lune/builtins/fs/options.rs similarity index 100% rename from src/lune/builtins/fs/options.rs rename to crates/lune/src/lune/builtins/fs/options.rs diff --git a/src/lune/builtins/luau/mod.rs b/crates/lune/src/lune/builtins/luau/mod.rs similarity index 100% rename from src/lune/builtins/luau/mod.rs rename to crates/lune/src/lune/builtins/luau/mod.rs diff --git a/src/lune/builtins/luau/options.rs b/crates/lune/src/lune/builtins/luau/options.rs similarity index 100% rename from src/lune/builtins/luau/options.rs rename to crates/lune/src/lune/builtins/luau/options.rs diff --git a/src/lune/builtins/mod.rs b/crates/lune/src/lune/builtins/mod.rs similarity index 100% rename from src/lune/builtins/mod.rs rename to crates/lune/src/lune/builtins/mod.rs diff --git a/src/lune/builtins/net/client.rs b/crates/lune/src/lune/builtins/net/client.rs similarity index 100% rename from src/lune/builtins/net/client.rs rename to crates/lune/src/lune/builtins/net/client.rs diff --git a/src/lune/builtins/net/config.rs b/crates/lune/src/lune/builtins/net/config.rs similarity index 100% rename from src/lune/builtins/net/config.rs rename to crates/lune/src/lune/builtins/net/config.rs diff --git a/src/lune/builtins/net/mod.rs b/crates/lune/src/lune/builtins/net/mod.rs similarity index 100% rename from src/lune/builtins/net/mod.rs rename to crates/lune/src/lune/builtins/net/mod.rs diff --git a/src/lune/builtins/net/server/keys.rs b/crates/lune/src/lune/builtins/net/server/keys.rs similarity index 100% rename from src/lune/builtins/net/server/keys.rs rename to crates/lune/src/lune/builtins/net/server/keys.rs diff --git a/src/lune/builtins/net/server/mod.rs b/crates/lune/src/lune/builtins/net/server/mod.rs similarity index 100% rename from src/lune/builtins/net/server/mod.rs rename to crates/lune/src/lune/builtins/net/server/mod.rs diff --git a/src/lune/builtins/net/server/request.rs b/crates/lune/src/lune/builtins/net/server/request.rs similarity index 100% rename from src/lune/builtins/net/server/request.rs rename to crates/lune/src/lune/builtins/net/server/request.rs diff --git a/src/lune/builtins/net/server/response.rs b/crates/lune/src/lune/builtins/net/server/response.rs similarity index 100% rename from src/lune/builtins/net/server/response.rs rename to crates/lune/src/lune/builtins/net/server/response.rs diff --git a/src/lune/builtins/net/server/service.rs b/crates/lune/src/lune/builtins/net/server/service.rs similarity index 100% rename from src/lune/builtins/net/server/service.rs rename to crates/lune/src/lune/builtins/net/server/service.rs diff --git a/src/lune/builtins/net/util.rs b/crates/lune/src/lune/builtins/net/util.rs similarity index 100% rename from src/lune/builtins/net/util.rs rename to crates/lune/src/lune/builtins/net/util.rs diff --git a/src/lune/builtins/net/websocket.rs b/crates/lune/src/lune/builtins/net/websocket.rs similarity index 100% rename from src/lune/builtins/net/websocket.rs rename to crates/lune/src/lune/builtins/net/websocket.rs diff --git a/src/lune/builtins/process/mod.rs b/crates/lune/src/lune/builtins/process/mod.rs similarity index 100% rename from src/lune/builtins/process/mod.rs rename to crates/lune/src/lune/builtins/process/mod.rs diff --git a/src/lune/builtins/process/options/kind.rs b/crates/lune/src/lune/builtins/process/options/kind.rs similarity index 100% rename from src/lune/builtins/process/options/kind.rs rename to crates/lune/src/lune/builtins/process/options/kind.rs diff --git a/src/lune/builtins/process/options/mod.rs b/crates/lune/src/lune/builtins/process/options/mod.rs similarity index 100% rename from src/lune/builtins/process/options/mod.rs rename to crates/lune/src/lune/builtins/process/options/mod.rs diff --git a/src/lune/builtins/process/options/stdio.rs b/crates/lune/src/lune/builtins/process/options/stdio.rs similarity index 100% rename from src/lune/builtins/process/options/stdio.rs rename to crates/lune/src/lune/builtins/process/options/stdio.rs diff --git a/src/lune/builtins/process/tee_writer.rs b/crates/lune/src/lune/builtins/process/tee_writer.rs similarity index 100% rename from src/lune/builtins/process/tee_writer.rs rename to crates/lune/src/lune/builtins/process/tee_writer.rs diff --git a/src/lune/builtins/process/wait_for_child.rs b/crates/lune/src/lune/builtins/process/wait_for_child.rs similarity index 100% rename from src/lune/builtins/process/wait_for_child.rs rename to crates/lune/src/lune/builtins/process/wait_for_child.rs diff --git a/src/lune/builtins/regex/captures.rs b/crates/lune/src/lune/builtins/regex/captures.rs similarity index 100% rename from src/lune/builtins/regex/captures.rs rename to crates/lune/src/lune/builtins/regex/captures.rs diff --git a/src/lune/builtins/regex/matches.rs b/crates/lune/src/lune/builtins/regex/matches.rs similarity index 100% rename from src/lune/builtins/regex/matches.rs rename to crates/lune/src/lune/builtins/regex/matches.rs diff --git a/src/lune/builtins/regex/mod.rs b/crates/lune/src/lune/builtins/regex/mod.rs similarity index 100% rename from src/lune/builtins/regex/mod.rs rename to crates/lune/src/lune/builtins/regex/mod.rs diff --git a/src/lune/builtins/regex/regex.rs b/crates/lune/src/lune/builtins/regex/regex.rs similarity index 100% rename from src/lune/builtins/regex/regex.rs rename to crates/lune/src/lune/builtins/regex/regex.rs diff --git a/src/lune/builtins/roblox/mod.rs b/crates/lune/src/lune/builtins/roblox/mod.rs similarity index 100% rename from src/lune/builtins/roblox/mod.rs rename to crates/lune/src/lune/builtins/roblox/mod.rs diff --git a/src/lune/builtins/serde/compress_decompress.rs b/crates/lune/src/lune/builtins/serde/compress_decompress.rs similarity index 100% rename from src/lune/builtins/serde/compress_decompress.rs rename to crates/lune/src/lune/builtins/serde/compress_decompress.rs diff --git a/src/lune/builtins/serde/encode_decode.rs b/crates/lune/src/lune/builtins/serde/encode_decode.rs similarity index 100% rename from src/lune/builtins/serde/encode_decode.rs rename to crates/lune/src/lune/builtins/serde/encode_decode.rs diff --git a/src/lune/builtins/serde/mod.rs b/crates/lune/src/lune/builtins/serde/mod.rs similarity index 100% rename from src/lune/builtins/serde/mod.rs rename to crates/lune/src/lune/builtins/serde/mod.rs diff --git a/src/lune/builtins/stdio/mod.rs b/crates/lune/src/lune/builtins/stdio/mod.rs similarity index 100% rename from src/lune/builtins/stdio/mod.rs rename to crates/lune/src/lune/builtins/stdio/mod.rs diff --git a/src/lune/builtins/stdio/prompt.rs b/crates/lune/src/lune/builtins/stdio/prompt.rs similarity index 100% rename from src/lune/builtins/stdio/prompt.rs rename to crates/lune/src/lune/builtins/stdio/prompt.rs diff --git a/src/lune/builtins/task/mod.rs b/crates/lune/src/lune/builtins/task/mod.rs similarity index 100% rename from src/lune/builtins/task/mod.rs rename to crates/lune/src/lune/builtins/task/mod.rs diff --git a/src/lune/error.rs b/crates/lune/src/lune/error.rs similarity index 100% rename from src/lune/error.rs rename to crates/lune/src/lune/error.rs diff --git a/src/lune/globals/g_table.rs b/crates/lune/src/lune/globals/g_table.rs similarity index 100% rename from src/lune/globals/g_table.rs rename to crates/lune/src/lune/globals/g_table.rs diff --git a/src/lune/globals/mod.rs b/crates/lune/src/lune/globals/mod.rs similarity index 100% rename from src/lune/globals/mod.rs rename to crates/lune/src/lune/globals/mod.rs diff --git a/src/lune/globals/print.rs b/crates/lune/src/lune/globals/print.rs similarity index 100% rename from src/lune/globals/print.rs rename to crates/lune/src/lune/globals/print.rs diff --git a/src/lune/globals/require/alias.rs b/crates/lune/src/lune/globals/require/alias.rs similarity index 100% rename from src/lune/globals/require/alias.rs rename to crates/lune/src/lune/globals/require/alias.rs diff --git a/src/lune/globals/require/builtin.rs b/crates/lune/src/lune/globals/require/builtin.rs similarity index 100% rename from src/lune/globals/require/builtin.rs rename to crates/lune/src/lune/globals/require/builtin.rs diff --git a/src/lune/globals/require/context.rs b/crates/lune/src/lune/globals/require/context.rs similarity index 100% rename from src/lune/globals/require/context.rs rename to crates/lune/src/lune/globals/require/context.rs diff --git a/src/lune/globals/require/mod.rs b/crates/lune/src/lune/globals/require/mod.rs similarity index 100% rename from src/lune/globals/require/mod.rs rename to crates/lune/src/lune/globals/require/mod.rs diff --git a/src/lune/globals/require/path.rs b/crates/lune/src/lune/globals/require/path.rs similarity index 100% rename from src/lune/globals/require/path.rs rename to crates/lune/src/lune/globals/require/path.rs diff --git a/src/lune/globals/version.rs b/crates/lune/src/lune/globals/version.rs similarity index 100% rename from src/lune/globals/version.rs rename to crates/lune/src/lune/globals/version.rs diff --git a/src/lune/globals/warn.rs b/crates/lune/src/lune/globals/warn.rs similarity index 100% rename from src/lune/globals/warn.rs rename to crates/lune/src/lune/globals/warn.rs diff --git a/src/lune/mod.rs b/crates/lune/src/lune/mod.rs similarity index 100% rename from src/lune/mod.rs rename to crates/lune/src/lune/mod.rs diff --git a/src/lune/util/formatting.rs b/crates/lune/src/lune/util/formatting.rs similarity index 100% rename from src/lune/util/formatting.rs rename to crates/lune/src/lune/util/formatting.rs diff --git a/src/lune/util/luaurc.rs b/crates/lune/src/lune/util/luaurc.rs similarity index 100% rename from src/lune/util/luaurc.rs rename to crates/lune/src/lune/util/luaurc.rs diff --git a/src/lune/util/mod.rs b/crates/lune/src/lune/util/mod.rs similarity index 100% rename from src/lune/util/mod.rs rename to crates/lune/src/lune/util/mod.rs diff --git a/src/lune/util/paths.rs b/crates/lune/src/lune/util/paths.rs similarity index 100% rename from src/lune/util/paths.rs rename to crates/lune/src/lune/util/paths.rs diff --git a/src/lune/util/table_builder.rs b/crates/lune/src/lune/util/table_builder.rs similarity index 100% rename from src/lune/util/table_builder.rs rename to crates/lune/src/lune/util/table_builder.rs diff --git a/src/lune/util/traits.rs b/crates/lune/src/lune/util/traits.rs similarity index 100% rename from src/lune/util/traits.rs rename to crates/lune/src/lune/util/traits.rs diff --git a/src/main.rs b/crates/lune/src/main.rs similarity index 100% rename from src/main.rs rename to crates/lune/src/main.rs diff --git a/src/roblox/datatypes/attributes.rs b/crates/lune/src/roblox/datatypes/attributes.rs similarity index 100% rename from src/roblox/datatypes/attributes.rs rename to crates/lune/src/roblox/datatypes/attributes.rs diff --git a/src/roblox/datatypes/conversion.rs b/crates/lune/src/roblox/datatypes/conversion.rs similarity index 100% rename from src/roblox/datatypes/conversion.rs rename to crates/lune/src/roblox/datatypes/conversion.rs diff --git a/src/roblox/datatypes/extension.rs b/crates/lune/src/roblox/datatypes/extension.rs similarity index 100% rename from src/roblox/datatypes/extension.rs rename to crates/lune/src/roblox/datatypes/extension.rs diff --git a/src/roblox/datatypes/mod.rs b/crates/lune/src/roblox/datatypes/mod.rs similarity index 100% rename from src/roblox/datatypes/mod.rs rename to crates/lune/src/roblox/datatypes/mod.rs diff --git a/src/roblox/datatypes/result.rs b/crates/lune/src/roblox/datatypes/result.rs similarity index 100% rename from src/roblox/datatypes/result.rs rename to crates/lune/src/roblox/datatypes/result.rs diff --git a/src/roblox/datatypes/types/axes.rs b/crates/lune/src/roblox/datatypes/types/axes.rs similarity index 100% rename from src/roblox/datatypes/types/axes.rs rename to crates/lune/src/roblox/datatypes/types/axes.rs diff --git a/src/roblox/datatypes/types/brick_color.rs b/crates/lune/src/roblox/datatypes/types/brick_color.rs similarity index 100% rename from src/roblox/datatypes/types/brick_color.rs rename to crates/lune/src/roblox/datatypes/types/brick_color.rs diff --git a/src/roblox/datatypes/types/cframe.rs b/crates/lune/src/roblox/datatypes/types/cframe.rs similarity index 100% rename from src/roblox/datatypes/types/cframe.rs rename to crates/lune/src/roblox/datatypes/types/cframe.rs diff --git a/src/roblox/datatypes/types/color3.rs b/crates/lune/src/roblox/datatypes/types/color3.rs similarity index 100% rename from src/roblox/datatypes/types/color3.rs rename to crates/lune/src/roblox/datatypes/types/color3.rs diff --git a/src/roblox/datatypes/types/color_sequence.rs b/crates/lune/src/roblox/datatypes/types/color_sequence.rs similarity index 100% rename from src/roblox/datatypes/types/color_sequence.rs rename to crates/lune/src/roblox/datatypes/types/color_sequence.rs diff --git a/src/roblox/datatypes/types/color_sequence_keypoint.rs b/crates/lune/src/roblox/datatypes/types/color_sequence_keypoint.rs similarity index 100% rename from src/roblox/datatypes/types/color_sequence_keypoint.rs rename to crates/lune/src/roblox/datatypes/types/color_sequence_keypoint.rs diff --git a/src/roblox/datatypes/types/enum.rs b/crates/lune/src/roblox/datatypes/types/enum.rs similarity index 100% rename from src/roblox/datatypes/types/enum.rs rename to crates/lune/src/roblox/datatypes/types/enum.rs diff --git a/src/roblox/datatypes/types/enum_item.rs b/crates/lune/src/roblox/datatypes/types/enum_item.rs similarity index 100% rename from src/roblox/datatypes/types/enum_item.rs rename to crates/lune/src/roblox/datatypes/types/enum_item.rs diff --git a/src/roblox/datatypes/types/enums.rs b/crates/lune/src/roblox/datatypes/types/enums.rs similarity index 100% rename from src/roblox/datatypes/types/enums.rs rename to crates/lune/src/roblox/datatypes/types/enums.rs diff --git a/src/roblox/datatypes/types/faces.rs b/crates/lune/src/roblox/datatypes/types/faces.rs similarity index 100% rename from src/roblox/datatypes/types/faces.rs rename to crates/lune/src/roblox/datatypes/types/faces.rs diff --git a/src/roblox/datatypes/types/font.rs b/crates/lune/src/roblox/datatypes/types/font.rs similarity index 100% rename from src/roblox/datatypes/types/font.rs rename to crates/lune/src/roblox/datatypes/types/font.rs diff --git a/src/roblox/datatypes/types/mod.rs b/crates/lune/src/roblox/datatypes/types/mod.rs similarity index 100% rename from src/roblox/datatypes/types/mod.rs rename to crates/lune/src/roblox/datatypes/types/mod.rs diff --git a/src/roblox/datatypes/types/number_range.rs b/crates/lune/src/roblox/datatypes/types/number_range.rs similarity index 100% rename from src/roblox/datatypes/types/number_range.rs rename to crates/lune/src/roblox/datatypes/types/number_range.rs diff --git a/src/roblox/datatypes/types/number_sequence.rs b/crates/lune/src/roblox/datatypes/types/number_sequence.rs similarity index 100% rename from src/roblox/datatypes/types/number_sequence.rs rename to crates/lune/src/roblox/datatypes/types/number_sequence.rs diff --git a/src/roblox/datatypes/types/number_sequence_keypoint.rs b/crates/lune/src/roblox/datatypes/types/number_sequence_keypoint.rs similarity index 100% rename from src/roblox/datatypes/types/number_sequence_keypoint.rs rename to crates/lune/src/roblox/datatypes/types/number_sequence_keypoint.rs diff --git a/src/roblox/datatypes/types/physical_properties.rs b/crates/lune/src/roblox/datatypes/types/physical_properties.rs similarity index 100% rename from src/roblox/datatypes/types/physical_properties.rs rename to crates/lune/src/roblox/datatypes/types/physical_properties.rs diff --git a/src/roblox/datatypes/types/ray.rs b/crates/lune/src/roblox/datatypes/types/ray.rs similarity index 100% rename from src/roblox/datatypes/types/ray.rs rename to crates/lune/src/roblox/datatypes/types/ray.rs diff --git a/src/roblox/datatypes/types/rect.rs b/crates/lune/src/roblox/datatypes/types/rect.rs similarity index 100% rename from src/roblox/datatypes/types/rect.rs rename to crates/lune/src/roblox/datatypes/types/rect.rs diff --git a/src/roblox/datatypes/types/region3.rs b/crates/lune/src/roblox/datatypes/types/region3.rs similarity index 100% rename from src/roblox/datatypes/types/region3.rs rename to crates/lune/src/roblox/datatypes/types/region3.rs diff --git a/src/roblox/datatypes/types/region3int16.rs b/crates/lune/src/roblox/datatypes/types/region3int16.rs similarity index 100% rename from src/roblox/datatypes/types/region3int16.rs rename to crates/lune/src/roblox/datatypes/types/region3int16.rs diff --git a/src/roblox/datatypes/types/udim.rs b/crates/lune/src/roblox/datatypes/types/udim.rs similarity index 100% rename from src/roblox/datatypes/types/udim.rs rename to crates/lune/src/roblox/datatypes/types/udim.rs diff --git a/src/roblox/datatypes/types/udim2.rs b/crates/lune/src/roblox/datatypes/types/udim2.rs similarity index 100% rename from src/roblox/datatypes/types/udim2.rs rename to crates/lune/src/roblox/datatypes/types/udim2.rs diff --git a/src/roblox/datatypes/types/vector2.rs b/crates/lune/src/roblox/datatypes/types/vector2.rs similarity index 100% rename from src/roblox/datatypes/types/vector2.rs rename to crates/lune/src/roblox/datatypes/types/vector2.rs diff --git a/src/roblox/datatypes/types/vector2int16.rs b/crates/lune/src/roblox/datatypes/types/vector2int16.rs similarity index 100% rename from src/roblox/datatypes/types/vector2int16.rs rename to crates/lune/src/roblox/datatypes/types/vector2int16.rs diff --git a/src/roblox/datatypes/types/vector3.rs b/crates/lune/src/roblox/datatypes/types/vector3.rs similarity index 100% rename from src/roblox/datatypes/types/vector3.rs rename to crates/lune/src/roblox/datatypes/types/vector3.rs diff --git a/src/roblox/datatypes/types/vector3int16.rs b/crates/lune/src/roblox/datatypes/types/vector3int16.rs similarity index 100% rename from src/roblox/datatypes/types/vector3int16.rs rename to crates/lune/src/roblox/datatypes/types/vector3int16.rs diff --git a/src/roblox/datatypes/util.rs b/crates/lune/src/roblox/datatypes/util.rs similarity index 100% rename from src/roblox/datatypes/util.rs rename to crates/lune/src/roblox/datatypes/util.rs diff --git a/src/roblox/document/error.rs b/crates/lune/src/roblox/document/error.rs similarity index 100% rename from src/roblox/document/error.rs rename to crates/lune/src/roblox/document/error.rs diff --git a/src/roblox/document/format.rs b/crates/lune/src/roblox/document/format.rs similarity index 100% rename from src/roblox/document/format.rs rename to crates/lune/src/roblox/document/format.rs diff --git a/src/roblox/document/kind.rs b/crates/lune/src/roblox/document/kind.rs similarity index 100% rename from src/roblox/document/kind.rs rename to crates/lune/src/roblox/document/kind.rs diff --git a/src/roblox/document/mod.rs b/crates/lune/src/roblox/document/mod.rs similarity index 100% rename from src/roblox/document/mod.rs rename to crates/lune/src/roblox/document/mod.rs diff --git a/src/roblox/document/postprocessing.rs b/crates/lune/src/roblox/document/postprocessing.rs similarity index 100% rename from src/roblox/document/postprocessing.rs rename to crates/lune/src/roblox/document/postprocessing.rs diff --git a/src/roblox/exports.rs b/crates/lune/src/roblox/exports.rs similarity index 100% rename from src/roblox/exports.rs rename to crates/lune/src/roblox/exports.rs diff --git a/src/roblox/instance/base.rs b/crates/lune/src/roblox/instance/base.rs similarity index 100% rename from src/roblox/instance/base.rs rename to crates/lune/src/roblox/instance/base.rs diff --git a/src/roblox/instance/data_model.rs b/crates/lune/src/roblox/instance/data_model.rs similarity index 100% rename from src/roblox/instance/data_model.rs rename to crates/lune/src/roblox/instance/data_model.rs diff --git a/src/roblox/instance/mod.rs b/crates/lune/src/roblox/instance/mod.rs similarity index 100% rename from src/roblox/instance/mod.rs rename to crates/lune/src/roblox/instance/mod.rs diff --git a/src/roblox/instance/registry.rs b/crates/lune/src/roblox/instance/registry.rs similarity index 100% rename from src/roblox/instance/registry.rs rename to crates/lune/src/roblox/instance/registry.rs diff --git a/src/roblox/instance/terrain.rs b/crates/lune/src/roblox/instance/terrain.rs similarity index 100% rename from src/roblox/instance/terrain.rs rename to crates/lune/src/roblox/instance/terrain.rs diff --git a/src/roblox/instance/workspace.rs b/crates/lune/src/roblox/instance/workspace.rs similarity index 100% rename from src/roblox/instance/workspace.rs rename to crates/lune/src/roblox/instance/workspace.rs diff --git a/src/roblox/mod.rs b/crates/lune/src/roblox/mod.rs similarity index 100% rename from src/roblox/mod.rs rename to crates/lune/src/roblox/mod.rs diff --git a/src/roblox/reflection/class.rs b/crates/lune/src/roblox/reflection/class.rs similarity index 100% rename from src/roblox/reflection/class.rs rename to crates/lune/src/roblox/reflection/class.rs diff --git a/src/roblox/reflection/enums.rs b/crates/lune/src/roblox/reflection/enums.rs similarity index 100% rename from src/roblox/reflection/enums.rs rename to crates/lune/src/roblox/reflection/enums.rs diff --git a/src/roblox/reflection/mod.rs b/crates/lune/src/roblox/reflection/mod.rs similarity index 100% rename from src/roblox/reflection/mod.rs rename to crates/lune/src/roblox/reflection/mod.rs diff --git a/src/roblox/reflection/property.rs b/crates/lune/src/roblox/reflection/property.rs similarity index 100% rename from src/roblox/reflection/property.rs rename to crates/lune/src/roblox/reflection/property.rs diff --git a/src/roblox/reflection/utils.rs b/crates/lune/src/roblox/reflection/utils.rs similarity index 100% rename from src/roblox/reflection/utils.rs rename to crates/lune/src/roblox/reflection/utils.rs diff --git a/src/roblox/shared/classes.rs b/crates/lune/src/roblox/shared/classes.rs similarity index 100% rename from src/roblox/shared/classes.rs rename to crates/lune/src/roblox/shared/classes.rs diff --git a/src/roblox/shared/instance.rs b/crates/lune/src/roblox/shared/instance.rs similarity index 100% rename from src/roblox/shared/instance.rs rename to crates/lune/src/roblox/shared/instance.rs diff --git a/src/roblox/shared/mod.rs b/crates/lune/src/roblox/shared/mod.rs similarity index 100% rename from src/roblox/shared/mod.rs rename to crates/lune/src/roblox/shared/mod.rs diff --git a/src/roblox/shared/userdata.rs b/crates/lune/src/roblox/shared/userdata.rs similarity index 100% rename from src/roblox/shared/userdata.rs rename to crates/lune/src/roblox/shared/userdata.rs diff --git a/src/standalone/metadata.rs b/crates/lune/src/standalone/metadata.rs similarity index 100% rename from src/standalone/metadata.rs rename to crates/lune/src/standalone/metadata.rs diff --git a/src/standalone/mod.rs b/crates/lune/src/standalone/mod.rs similarity index 100% rename from src/standalone/mod.rs rename to crates/lune/src/standalone/mod.rs diff --git a/src/standalone/tracer.rs b/crates/lune/src/standalone/tracer.rs similarity index 100% rename from src/standalone/tracer.rs rename to crates/lune/src/standalone/tracer.rs diff --git a/src/tests.rs b/crates/lune/src/tests.rs similarity index 100% rename from src/tests.rs rename to crates/lune/src/tests.rs From 99a8fa3c4cfcd117a404ff509287bdc5d55803a0 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 14:38:38 +0200 Subject: [PATCH 02/68] Remove automated cargo publish workflow --- .github/workflows/publish.yaml | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 .github/workflows/publish.yaml diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml deleted file mode 100644 index 13fc2be0..00000000 --- a/.github/workflows/publish.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: Publish - -on: - push: - branches: - - "main" - workflow_dispatch: - -jobs: - publish: - name: Publish - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - - name: Publish to crates.io - uses: katyo/publish-crates@v2 - with: - registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} - ignore-unpublished-changes: true From e53d247c95e86d1070a6b8475045539b1ad368af Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 14:52:27 +0200 Subject: [PATCH 03/68] Add all crates for standard library to workspace --- Cargo.lock | 56 +++++++++++++++++++++++++++++ Cargo.toml | 15 +++++++- crates/lune-std-datetime/Cargo.toml | 11 ++++++ crates/lune-std-datetime/src/lib.rs | 1 + crates/lune-std-fs/Cargo.toml | 11 ++++++ crates/lune-std-fs/src/lib.rs | 1 + crates/lune-std-luau/Cargo.toml | 11 ++++++ crates/lune-std-luau/src/lib.rs | 1 + crates/lune-std-net/Cargo.toml | 11 ++++++ crates/lune-std-net/src/lib.rs | 1 + crates/lune-std-process/Cargo.toml | 11 ++++++ crates/lune-std-process/src/lib.rs | 1 + crates/lune-std-regex/Cargo.toml | 11 ++++++ crates/lune-std-regex/src/lib.rs | 1 + crates/lune-std-roblox/Cargo.toml | 11 ++++++ crates/lune-std-roblox/src/lib.rs | 1 + crates/lune-std-serde/Cargo.toml | 11 ++++++ crates/lune-std-serde/src/lib.rs | 1 + crates/lune-std-stdio/Cargo.toml | 11 ++++++ crates/lune-std-stdio/src/lib.rs | 1 + crates/lune-std-task/Cargo.toml | 11 ++++++ crates/lune-std-task/src/lib.rs | 1 + crates/lune-std/Cargo.toml | 48 +++++++++++++++++++++++++ crates/lune-std/src/lib.rs | 1 + 24 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 crates/lune-std-datetime/Cargo.toml create mode 100644 crates/lune-std-datetime/src/lib.rs create mode 100644 crates/lune-std-fs/Cargo.toml create mode 100644 crates/lune-std-fs/src/lib.rs create mode 100644 crates/lune-std-luau/Cargo.toml create mode 100644 crates/lune-std-luau/src/lib.rs create mode 100644 crates/lune-std-net/Cargo.toml create mode 100644 crates/lune-std-net/src/lib.rs create mode 100644 crates/lune-std-process/Cargo.toml create mode 100644 crates/lune-std-process/src/lib.rs create mode 100644 crates/lune-std-regex/Cargo.toml create mode 100644 crates/lune-std-regex/src/lib.rs create mode 100644 crates/lune-std-roblox/Cargo.toml create mode 100644 crates/lune-std-roblox/src/lib.rs create mode 100644 crates/lune-std-serde/Cargo.toml create mode 100644 crates/lune-std-serde/src/lib.rs create mode 100644 crates/lune-std-stdio/Cargo.toml create mode 100644 crates/lune-std-stdio/src/lib.rs create mode 100644 crates/lune-std-task/Cargo.toml create mode 100644 crates/lune-std-task/src/lib.rs create mode 100644 crates/lune-std/Cargo.toml create mode 100644 crates/lune-std/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2bc27556..690e16bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1537,6 +1537,62 @@ dependencies = [ "zip_next", ] +[[package]] +name = "lune-std" +version = "0.8.3" +dependencies = [ + "lune-std-datetime", + "lune-std-fs", + "lune-std-luau", + "lune-std-net", + "lune-std-process", + "lune-std-regex", + "lune-std-roblox", + "lune-std-serde", + "lune-std-stdio", + "lune-std-task", +] + +[[package]] +name = "lune-std-datetime" +version = "0.8.3" + +[[package]] +name = "lune-std-fs" +version = "0.8.3" + +[[package]] +name = "lune-std-luau" +version = "0.8.3" + +[[package]] +name = "lune-std-net" +version = "0.8.3" + +[[package]] +name = "lune-std-process" +version = "0.8.3" + +[[package]] +name = "lune-std-regex" +version = "0.8.3" + +[[package]] +name = "lune-std-roblox" +version = "0.8.3" + +[[package]] +name = "lune-std-serde" +version = "0.8.3" + +[[package]] +name = "lune-std-stdio" +version = "0.8.3" + +[[package]] +name = "lune-std-task" +version = "0.8.3" + [[package]] name = "lz4" version = "1.24.0" diff --git a/Cargo.toml b/Cargo.toml index 9d431bb8..b5df8cb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,20 @@ [workspace] resolver = "2" default-members = ["crates/lune"] -members = ["crates/lune"] +members = [ + "crates/lune", + "crates/lune-std", + "crates/lune-std-datetime", + "crates/lune-std-fs", + "crates/lune-std-luau", + "crates/lune-std-net", + "crates/lune-std-process", + "crates/lune-std-regex", + "crates/lune-std-roblox", + "crates/lune-std-serde", + "crates/lune-std-stdio", + "crates/lune-std-task", +] # Profile for building the release binary, with the following options set: # diff --git a/crates/lune-std-datetime/Cargo.toml b/crates/lune-std-datetime/Cargo.toml new file mode 100644 index 00000000..cc1f85e0 --- /dev/null +++ b/crates/lune-std-datetime/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-std-datetime" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-std-datetime/src/lib.rs b/crates/lune-std-datetime/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std-datetime/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] diff --git a/crates/lune-std-fs/Cargo.toml b/crates/lune-std-fs/Cargo.toml new file mode 100644 index 00000000..17a0996e --- /dev/null +++ b/crates/lune-std-fs/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-std-fs" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-std-fs/src/lib.rs b/crates/lune-std-fs/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std-fs/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] diff --git a/crates/lune-std-luau/Cargo.toml b/crates/lune-std-luau/Cargo.toml new file mode 100644 index 00000000..fe51e61d --- /dev/null +++ b/crates/lune-std-luau/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-std-luau" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-std-luau/src/lib.rs b/crates/lune-std-luau/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std-luau/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] diff --git a/crates/lune-std-net/Cargo.toml b/crates/lune-std-net/Cargo.toml new file mode 100644 index 00000000..6409e9db --- /dev/null +++ b/crates/lune-std-net/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-std-net" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-std-net/src/lib.rs b/crates/lune-std-net/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std-net/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] diff --git a/crates/lune-std-process/Cargo.toml b/crates/lune-std-process/Cargo.toml new file mode 100644 index 00000000..0c743bb7 --- /dev/null +++ b/crates/lune-std-process/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-std-process" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-std-process/src/lib.rs b/crates/lune-std-process/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std-process/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] diff --git a/crates/lune-std-regex/Cargo.toml b/crates/lune-std-regex/Cargo.toml new file mode 100644 index 00000000..294e70d6 --- /dev/null +++ b/crates/lune-std-regex/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-std-regex" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-std-regex/src/lib.rs b/crates/lune-std-regex/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std-regex/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] diff --git a/crates/lune-std-roblox/Cargo.toml b/crates/lune-std-roblox/Cargo.toml new file mode 100644 index 00000000..d66ce67a --- /dev/null +++ b/crates/lune-std-roblox/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-std-roblox" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-std-roblox/src/lib.rs b/crates/lune-std-roblox/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std-roblox/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] diff --git a/crates/lune-std-serde/Cargo.toml b/crates/lune-std-serde/Cargo.toml new file mode 100644 index 00000000..d83ea105 --- /dev/null +++ b/crates/lune-std-serde/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-std-serde" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-std-serde/src/lib.rs b/crates/lune-std-serde/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std-serde/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] diff --git a/crates/lune-std-stdio/Cargo.toml b/crates/lune-std-stdio/Cargo.toml new file mode 100644 index 00000000..a018bd64 --- /dev/null +++ b/crates/lune-std-stdio/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-std-stdio" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-std-stdio/src/lib.rs b/crates/lune-std-stdio/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std-stdio/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] diff --git a/crates/lune-std-task/Cargo.toml b/crates/lune-std-task/Cargo.toml new file mode 100644 index 00000000..ee737ca6 --- /dev/null +++ b/crates/lune-std-task/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-std-task" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-std-task/src/lib.rs b/crates/lune-std-task/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std-task/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] diff --git a/crates/lune-std/Cargo.toml b/crates/lune-std/Cargo.toml new file mode 100644 index 00000000..b4a3dbd1 --- /dev/null +++ b/crates/lune-std/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "lune-std" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true + +[features] +default = [ + "datetime", + "fs", + "luau", + "net", + "process", + "regex", + "roblox", + "serde", + "stdio", + "task", +] + +datetime = ["dep:lune-std-datetime"] +fs = ["dep:lune-std-fs"] +luau = ["dep:lune-std-luau"] +net = ["dep:lune-std-net"] +process = ["dep:lune-std-process"] +regex = ["dep:lune-std-regex"] +roblox = ["dep:lune-std-roblox"] +serde = ["dep:lune-std-serde"] +stdio = ["dep:lune-std-stdio"] +task = ["dep:lune-std-task"] + +[dependencies] +lune-std-datetime = { optional = true, version = "0.8.3", path = "../lune-std-datetime" } +lune-std-fs = { optional = true, version = "0.8.3", path = "../lune-std-fs" } +lune-std-luau = { optional = true, version = "0.8.3", path = "../lune-std-luau" } +lune-std-net = { optional = true, version = "0.8.3", path = "../lune-std-net" } +lune-std-process = { optional = true, version = "0.8.3", path = "../lune-std-process" } +lune-std-regex = { optional = true, version = "0.8.3", path = "../lune-std-regex" } +lune-std-roblox = { optional = true, version = "0.8.3", path = "../lune-std-roblox" } +lune-std-serde = { optional = true, version = "0.8.3", path = "../lune-std-serde" } +lune-std-stdio = { optional = true, version = "0.8.3", path = "../lune-std-stdio" } +lune-std-task = { optional = true, version = "0.8.3", path = "../lune-std-task" } diff --git a/crates/lune-std/src/lib.rs b/crates/lune-std/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-std/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] From 5c5ea5b4cbbaeac5d510bf7570f5da25358a4637 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 14:58:36 +0200 Subject: [PATCH 04/68] Add utils crate --- Cargo.lock | 4 ++++ Cargo.toml | 1 + crates/lune-utils/Cargo.toml | 11 +++++++++++ crates/lune-utils/src/lib.rs | 1 + 4 files changed, 17 insertions(+) create mode 100644 crates/lune-utils/Cargo.toml create mode 100644 crates/lune-utils/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 690e16bb..ade73a23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1593,6 +1593,10 @@ version = "0.8.3" name = "lune-std-task" version = "0.8.3" +[[package]] +name = "lune-utils" +version = "0.8.3" + [[package]] name = "lz4" version = "1.24.0" diff --git a/Cargo.toml b/Cargo.toml index b5df8cb4..9ec1fc04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "crates/lune-std-serde", "crates/lune-std-stdio", "crates/lune-std-task", + "crates/lune-utils", ] # Profile for building the release binary, with the following options set: diff --git a/crates/lune-utils/Cargo.toml b/crates/lune-utils/Cargo.toml new file mode 100644 index 00000000..c38eac71 --- /dev/null +++ b/crates/lune-utils/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lune-utils" +version = "0.8.3" +edition = "2021" +license = "MPL-2.0" + +[lib] +path = "src/lib.rs" + +[lints] +workspace = true diff --git a/crates/lune-utils/src/lib.rs b/crates/lune-utils/src/lib.rs new file mode 100644 index 00000000..2e802e78 --- /dev/null +++ b/crates/lune-utils/src/lib.rs @@ -0,0 +1 @@ +#![allow(clippy::cargo_common_metadata)] From 13309baffa7ea0f4ee3e652425387adfd4f3c573 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 15:09:22 +0200 Subject: [PATCH 05/68] Add table builder to utils crate --- Cargo.lock | 3 + crates/lune-utils/Cargo.toml | 3 + crates/lune-utils/src/lib.rs | 4 + crates/lune-utils/src/table_builder.rs | 143 +++++++++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 crates/lune-utils/src/table_builder.rs diff --git a/Cargo.lock b/Cargo.lock index ade73a23..6991ec1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1596,6 +1596,9 @@ version = "0.8.3" [[package]] name = "lune-utils" version = "0.8.3" +dependencies = [ + "mlua", +] [[package]] name = "lz4" diff --git a/crates/lune-utils/Cargo.toml b/crates/lune-utils/Cargo.toml index c38eac71..33dc192d 100644 --- a/crates/lune-utils/Cargo.toml +++ b/crates/lune-utils/Cargo.toml @@ -9,3 +9,6 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = { version = "0.9.7", features = ["async"] } diff --git a/crates/lune-utils/src/lib.rs b/crates/lune-utils/src/lib.rs index 2e802e78..ff27b800 100644 --- a/crates/lune-utils/src/lib.rs +++ b/crates/lune-utils/src/lib.rs @@ -1 +1,5 @@ #![allow(clippy::cargo_common_metadata)] + +mod table_builder; + +pub use self::table_builder::TableBuilder; diff --git a/crates/lune-utils/src/table_builder.rs b/crates/lune-utils/src/table_builder.rs new file mode 100644 index 00000000..1678902a --- /dev/null +++ b/crates/lune-utils/src/table_builder.rs @@ -0,0 +1,143 @@ +#![allow(clippy::missing_errors_doc)] + +use std::future::Future; + +use mlua::prelude::*; + +/** + Utility struct for building Lua tables. +*/ +pub struct TableBuilder<'lua> { + lua: &'lua Lua, + tab: LuaTable<'lua>, +} + +impl<'lua> TableBuilder<'lua> { + /** + Creates a new table builder. + */ + pub fn new(lua: &'lua Lua) -> LuaResult { + let tab = lua.create_table()?; + Ok(Self { lua, tab }) + } + + /** + Adds a new key-value pair to the table. + + This will overwrite any value that already exists. + */ + pub fn with_value(self, key: K, value: V) -> LuaResult + where + K: IntoLua<'lua>, + V: IntoLua<'lua>, + { + self.tab.raw_set(key, value)?; + Ok(self) + } + + /** + Adds multiple key-value pairs to the table. + + This will overwrite any values that already exist. + */ + pub fn with_values(self, values: Vec<(K, V)>) -> LuaResult + where + K: IntoLua<'lua>, + V: IntoLua<'lua>, + { + for (key, value) in values { + self.tab.raw_set(key, value)?; + } + Ok(self) + } + + /** + Adds a new key-value pair to the sequential (array) section of the table. + + This will not overwrite any value that already exists, + instead adding the value to the end of the array. + */ + pub fn with_sequential_value(self, value: V) -> LuaResult + where + V: IntoLua<'lua>, + { + self.tab.raw_push(value)?; + Ok(self) + } + + /** + Adds multiple values to the sequential (array) section of the table. + + This will not overwrite any values that already exist, + instead adding the values to the end of the array. + */ + pub fn with_sequential_values(self, values: Vec) -> LuaResult + where + V: IntoLua<'lua>, + { + for value in values { + self.tab.raw_push(value)?; + } + Ok(self) + } + + /** + Adds a new key-value pair to the table, with a function value. + + This will overwrite any value that already exists. + */ + pub fn with_function(self, key: K, func: F) -> LuaResult + where + K: IntoLua<'lua>, + A: FromLuaMulti<'lua>, + R: IntoLuaMulti<'lua>, + F: Fn(&'lua Lua, A) -> LuaResult + 'static, + { + let f = self.lua.create_function(func)?; + self.with_value(key, LuaValue::Function(f)) + } + + /** + Adds a new key-value pair to the table, with an async function value. + + This will overwrite any value that already exists. + */ + pub fn with_async_function(self, key: K, func: F) -> LuaResult + where + K: IntoLua<'lua>, + A: FromLuaMulti<'lua>, + R: IntoLuaMulti<'lua>, + F: Fn(&'lua Lua, A) -> FR + 'static, + FR: Future> + 'lua, + { + let f = self.lua.create_async_function(func)?; + self.with_value(key, LuaValue::Function(f)) + } + + /** + Adds a metatable to the table. + + This will overwrite any metatable that already exists. + */ + pub fn with_metatable(self, table: LuaTable) -> LuaResult { + self.tab.set_metatable(Some(table)); + Ok(self) + } + + /** + Builds the table as a read-only table. + + This will prevent any *direct* modifications to the table. + */ + pub fn build_readonly(self) -> LuaResult> { + self.tab.set_readonly(true); + Ok(self.tab) + } + + /** + Builds the table. + */ + pub fn build(self) -> LuaResult> { + Ok(self.tab) + } +} From 34e116501314e77152faf0deab75a5f709fab807 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 15:14:37 +0200 Subject: [PATCH 06/68] Allow unnecessary wraps and similar names lints --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9ec1fc04..52123735 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,8 +50,10 @@ cast_possible_wrap = { level = "allow", priority = 1 } cast_precision_loss = { level = "allow", priority = 1 } cast_sign_loss = { level = "allow", priority = 1 } -unreadable_literal = { level = "allow", priority = 1 } +similar_names = { level = "allow", priority = 1 } +unnecessary_wraps = { level = "allow", priority = 1 } unnested_or_patterns = { level = "allow", priority = 1 } +unreadable_literal = { level = "allow", priority = 1 } multiple_crate_versions = { level = "allow", priority = 1 } module_inception = { level = "allow", priority = 1 } From a5e349c54aa30a8b425e9f5b46b77dc3b9c73fac Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 15:32:32 +0200 Subject: [PATCH 07/68] Port task library to new crate --- Cargo.lock | 25 ++++++++++++- crates/lune-std-task/Cargo.toml | 8 ++++ crates/lune-std-task/src/lib.rs | 65 +++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 6991ec1f..94fddce9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1507,7 +1507,7 @@ dependencies = [ "itertools", "lz4_flex", "mlua", - "mlua-luau-scheduler", + "mlua-luau-scheduler 0.0.2", "once_cell", "os_str_bytes", "path-clean", @@ -1592,6 +1592,12 @@ version = "0.8.3" [[package]] name = "lune-std-task" version = "0.8.3" +dependencies = [ + "lune-utils", + "mlua", + "mlua-luau-scheduler 0.0.1", + "tokio", +] [[package]] name = "lune-utils" @@ -1698,6 +1704,23 @@ dependencies = [ "serde-value", ] +[[package]] +name = "mlua-luau-scheduler" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7ba4c0a49d3549cbb152c72cb447a0e733de8f1b70bb276a010e13addbd72b" +dependencies = [ + "async-executor", + "blocking", + "concurrent-queue", + "derive_more", + "event-listener 4.0.3", + "futures-lite", + "mlua", + "rustc-hash", + "tracing", +] + [[package]] name = "mlua-luau-scheduler" version = "0.0.2" diff --git a/crates/lune-std-task/Cargo.toml b/crates/lune-std-task/Cargo.toml index ee737ca6..127a6649 100644 --- a/crates/lune-std-task/Cargo.toml +++ b/crates/lune-std-task/Cargo.toml @@ -9,3 +9,11 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = "0.9.7" +mlua-luau-scheduler = "0.0.1" + +tokio = { version = "1", features = ["time"] } + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-task/src/lib.rs b/crates/lune-std-task/src/lib.rs index 2e802e78..9909281f 100644 --- a/crates/lune-std-task/src/lib.rs +++ b/crates/lune-std-task/src/lib.rs @@ -1 +1,66 @@ #![allow(clippy::cargo_common_metadata)] + +use std::time::Duration; + +use mlua::prelude::*; +use mlua_luau_scheduler::Functions; + +use tokio::time::{sleep, Instant}; + +use lune_utils::TableBuilder; + +/** + Creates the `task` standard library module. + + # Errors + + Errors when out of memory, or if default Lua globals are missing. +*/ +pub fn module(lua: &Lua) -> LuaResult> { + let fns = Functions::new(lua)?; + + // Create wait & delay functions + let task_wait = lua.create_async_function(wait)?; + let task_delay_env = TableBuilder::new(lua)? + .with_value("select", lua.globals().get::<_, LuaFunction>("select")?)? + .with_value("spawn", fns.spawn.clone())? + .with_value("defer", fns.defer.clone())? + .with_value("wait", task_wait.clone())? + .build_readonly()?; + let task_delay = lua + .load(DELAY_IMPL_LUA) + .set_name("task.delay") + .set_environment(task_delay_env) + .into_function()?; + + // Overwrite resume & wrap functions on the coroutine global + // with ones that are compatible with our scheduler + let co = lua.globals().get::<_, LuaTable>("coroutine")?; + co.set("resume", fns.resume.clone())?; + co.set("wrap", fns.wrap.clone())?; + + TableBuilder::new(lua)? + .with_value("cancel", fns.cancel)? + .with_value("defer", fns.defer)? + .with_value("delay", task_delay)? + .with_value("spawn", fns.spawn)? + .with_value("wait", task_wait)? + .build_readonly() +} + +const DELAY_IMPL_LUA: &str = r" +return defer(function(...) + wait(select(1, ...)) + spawn(select(2, ...)) +end, ...) +"; + +async fn wait(_: &Lua, secs: Option) -> LuaResult { + let duration = Duration::from_secs_f64(secs.unwrap_or_default()); + + let before = Instant::now(); + sleep(duration).await; + let after = Instant::now(); + + Ok((after - before).as_secs_f64()) +} From ac3721f7d05839d17fedd754a1561dd65908b56c Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 19:03:28 +0200 Subject: [PATCH 08/68] Migrate stdio builtin to new crate --- Cargo.lock | 7 + crates/lune-std-stdio/Cargo.toml | 9 ++ crates/lune-std-stdio/src/lib.rs | 80 ++++++++++ crates/lune-std-stdio/src/prompt.rs | 227 ++++++++++++++++++++++++++++ crates/lune-std-task/Cargo.toml | 2 +- crates/lune-std-task/src/lib.rs | 2 +- 6 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 crates/lune-std-stdio/src/prompt.rs diff --git a/Cargo.lock b/Cargo.lock index 94fddce9..8e585e0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1588,6 +1588,13 @@ version = "0.8.3" [[package]] name = "lune-std-stdio" version = "0.8.3" +dependencies = [ + "dialoguer", + "lune-utils", + "mlua", + "mlua-luau-scheduler 0.0.1", + "tokio", +] [[package]] name = "lune-std-task" diff --git a/crates/lune-std-stdio/Cargo.toml b/crates/lune-std-stdio/Cargo.toml index a018bd64..c51398fa 100644 --- a/crates/lune-std-stdio/Cargo.toml +++ b/crates/lune-std-stdio/Cargo.toml @@ -9,3 +9,12 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +dialoguer = "0.11" +mlua = "0.9.7" +mlua-luau-scheduler = "0.0.1" + +tokio = { version = "1", default-features = false } + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-stdio/src/lib.rs b/crates/lune-std-stdio/src/lib.rs index 2e802e78..b1f9d7ad 100644 --- a/crates/lune-std-stdio/src/lib.rs +++ b/crates/lune-std-stdio/src/lib.rs @@ -1 +1,81 @@ #![allow(clippy::cargo_common_metadata)] + +use mlua::prelude::*; +use mlua_luau_scheduler::LuaSpawnExt; + +use tokio::io::{stderr, stdin, stdout, AsyncReadExt, AsyncWriteExt}; + +use lune_utils::TableBuilder; + +mod prompt; + +use self::prompt::{prompt, PromptOptions, PromptResult}; + +/** + Creates the `stdio` standard library module. + + # Errors + + Errors when out of memory. +*/ +pub fn module(lua: &Lua) -> LuaResult { + TableBuilder::new(lua)? + .with_function("color", stdio_color)? + .with_function("style", stdio_style)? + .with_function("format", stdio_format)? + .with_async_function("write", stdio_write)? + .with_async_function("ewrite", stdio_ewrite)? + .with_async_function("readToEnd", stdio_read_to_end)? + .with_async_function("prompt", stdio_prompt)? + .build_readonly() +} + +fn stdio_color(_: &Lua, _color: String) -> LuaResult { + // TODO: Migrate from old crate + unimplemented!() +} + +fn stdio_style(_: &Lua, _color: String) -> LuaResult { + // TODO: Migrate from old crate + unimplemented!() +} + +fn stdio_format(_: &Lua, _args: LuaMultiValue) -> LuaResult { + // TODO: Migrate from old crate + unimplemented!() +} + +async fn stdio_write(_: &Lua, s: LuaString<'_>) -> LuaResult<()> { + let mut stdout = stdout(); + stdout.write_all(s.as_bytes()).await?; + stdout.flush().await?; + Ok(()) +} + +async fn stdio_ewrite(_: &Lua, s: LuaString<'_>) -> LuaResult<()> { + let mut stderr = stderr(); + stderr.write_all(s.as_bytes()).await?; + stderr.flush().await?; + Ok(()) +} + +/* + FUTURE: Figure out how to expose some kind of "readLine" function using a buffered reader. + + This is a bit tricky since we would want to be able to use **both** readLine and readToEnd + in the same script, doing something like readLine, readLine, readToEnd from lua, and + having that capture the first two lines and then read the rest of the input. +*/ + +async fn stdio_read_to_end(lua: &Lua, (): ()) -> LuaResult { + let mut input = Vec::new(); + let mut stdin = stdin(); + stdin.read_to_end(&mut input).await?; + lua.create_string(&input) +} + +async fn stdio_prompt(lua: &Lua, options: PromptOptions) -> LuaResult { + lua.spawn_blocking(move || prompt(options)) + .await + .into_lua_err() +} diff --git a/crates/lune-std-stdio/src/prompt.rs b/crates/lune-std-stdio/src/prompt.rs new file mode 100644 index 00000000..e1fcc021 --- /dev/null +++ b/crates/lune-std-stdio/src/prompt.rs @@ -0,0 +1,227 @@ +use std::{fmt, str::FromStr}; + +use dialoguer::{theme::ColorfulTheme, Confirm, Input, MultiSelect, Select}; +use mlua::prelude::*; + +#[derive(Debug, Clone, Copy)] +pub enum PromptKind { + Text, + Confirm, + Select, + MultiSelect, +} + +impl PromptKind { + const ALL: [PromptKind; 4] = [Self::Text, Self::Confirm, Self::Select, Self::MultiSelect]; +} + +impl Default for PromptKind { + fn default() -> Self { + Self::Text + } +} + +impl FromStr for PromptKind { + type Err = (); + fn from_str(s: &str) -> Result { + match s.trim().to_ascii_lowercase().as_str() { + "text" => Ok(Self::Text), + "confirm" => Ok(Self::Confirm), + "select" => Ok(Self::Select), + "multiselect" => Ok(Self::MultiSelect), + _ => Err(()), + } + } +} + +impl fmt::Display for PromptKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Text => "Text", + Self::Confirm => "Confirm", + Self::Select => "Select", + Self::MultiSelect => "MultiSelect", + } + ) + } +} + +impl<'lua> FromLua<'lua> for PromptKind { + fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult { + if let LuaValue::Nil = value { + Ok(Self::default()) + } else if let LuaValue::String(s) = value { + let s = s.to_str()?; + s.parse().map_err(|()| LuaError::FromLuaConversionError { + from: "string", + to: "PromptKind", + message: Some(format!( + "Invalid prompt kind '{s}', valid kinds are:\n{}", + PromptKind::ALL + .iter() + .map(ToString::to_string) + .collect::>() + .join(", ") + )), + }) + } else { + Err(LuaError::FromLuaConversionError { + from: "nil", + to: "PromptKind", + message: None, + }) + } + } +} + +pub struct PromptOptions { + pub kind: PromptKind, + pub text: Option, + pub default_string: Option, + pub default_bool: Option, + pub options: Option>, +} + +impl<'lua> FromLuaMulti<'lua> for PromptOptions { + fn from_lua_multi(mut values: LuaMultiValue<'lua>, lua: &'lua Lua) -> LuaResult { + // Argument #1 - prompt kind (optional) + let kind = values + .pop_front() + .map(|value| PromptKind::from_lua(value, lua)) + .transpose()? + .unwrap_or_default(); + // Argument #2 - prompt text (optional) + let text = values + .pop_front() + .map(|text| String::from_lua(text, lua)) + .transpose()?; + // Argument #3 - default value / options, + // this is different per each prompt kind + let (default_bool, default_string, options) = match values.pop_front() { + None => (None, None, None), + Some(options) => match options { + LuaValue::Nil => (None, None, None), + LuaValue::Boolean(b) => (Some(b), None, None), + LuaValue::String(s) => ( + None, + Some(String::from_lua(LuaValue::String(s), lua)?), + None, + ), + LuaValue::Table(t) => ( + None, + None, + Some(Vec::::from_lua(LuaValue::Table(t), lua)?), + ), + value => { + return Err(LuaError::FromLuaConversionError { + from: value.type_name(), + to: "PromptOptions", + message: Some("Argument #3 must be a boolean, table, or nil".to_string()), + }) + } + }, + }; + /* + Make sure we got the required values for the specific prompt kind: + + - "Confirm" requires a message to be present so the user knows what they are confirming + - "Select" and "MultiSelect" both require a table of options to choose from + */ + if matches!(kind, PromptKind::Confirm) && text.is_none() { + return Err(LuaError::FromLuaConversionError { + from: "nil", + to: "PromptOptions", + message: Some("Argument #2 missing or nil".to_string()), + }); + } + if matches!(kind, PromptKind::Select | PromptKind::MultiSelect) && options.is_none() { + return Err(LuaError::FromLuaConversionError { + from: "nil", + to: "PromptOptions", + message: Some("Argument #3 missing or nil".to_string()), + }); + } + // All good, return the prompt options + Ok(Self { + kind, + text, + default_string, + default_bool, + options, + }) + } +} + +#[derive(Debug, Clone)] +pub enum PromptResult { + String(String), + Boolean(bool), + Index(usize), + Indices(Vec), + None, +} + +impl<'lua> IntoLua<'lua> for PromptResult { + fn into_lua(self, lua: &'lua Lua) -> LuaResult> { + Ok(match self { + Self::String(s) => LuaValue::String(lua.create_string(&s)?), + Self::Boolean(b) => LuaValue::Boolean(b), + Self::Index(i) => LuaValue::Number(i as f64), + Self::Indices(v) => v.into_lua(lua)?, + Self::None => LuaValue::Nil, + }) + } +} + +pub fn prompt(options: PromptOptions) -> LuaResult { + let theme = ColorfulTheme::default(); + match options.kind { + PromptKind::Text => { + let input: String = Input::with_theme(&theme) + .allow_empty(true) + .with_prompt(options.text.unwrap_or_default()) + .with_initial_text(options.default_string.unwrap_or_default()) + .interact_text() + .into_lua_err()?; + Ok(PromptResult::String(input)) + } + PromptKind::Confirm => { + let mut prompt = Confirm::with_theme(&theme); + if let Some(b) = options.default_bool { + prompt = prompt.default(b); + }; + let result = prompt + .with_prompt(&options.text.expect("Missing text in prompt options")) + .interact() + .into_lua_err()?; + Ok(PromptResult::Boolean(result)) + } + PromptKind::Select => { + let chosen = Select::with_theme(&theme) + .with_prompt(&options.text.unwrap_or_default()) + .items(&options.options.expect("Missing options in prompt options")) + .interact_opt() + .into_lua_err()?; + Ok(match chosen { + Some(idx) => PromptResult::Index(idx + 1), + None => PromptResult::None, + }) + } + PromptKind::MultiSelect => { + let chosen = MultiSelect::with_theme(&theme) + .with_prompt(&options.text.unwrap_or_default()) + .items(&options.options.expect("Missing options in prompt options")) + .interact_opt() + .into_lua_err()?; + Ok(match chosen { + None => PromptResult::None, + Some(indices) => { + PromptResult::Indices(indices.iter().map(|idx| *idx + 1).collect()) + } + }) + } + } +} diff --git a/crates/lune-std-task/Cargo.toml b/crates/lune-std-task/Cargo.toml index 127a6649..40c954c0 100644 --- a/crates/lune-std-task/Cargo.toml +++ b/crates/lune-std-task/Cargo.toml @@ -14,6 +14,6 @@ workspace = true mlua = "0.9.7" mlua-luau-scheduler = "0.0.1" -tokio = { version = "1", features = ["time"] } +tokio = { version = "1", default-features = false, features = ["time"] } lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-task/src/lib.rs b/crates/lune-std-task/src/lib.rs index 9909281f..47a78d59 100644 --- a/crates/lune-std-task/src/lib.rs +++ b/crates/lune-std-task/src/lib.rs @@ -16,7 +16,7 @@ use lune_utils::TableBuilder; Errors when out of memory, or if default Lua globals are missing. */ -pub fn module(lua: &Lua) -> LuaResult> { +pub fn module(lua: &Lua) -> LuaResult { let fns = Functions::new(lua)?; // Create wait & delay functions From 3b3fa65bfe2d8f8ad37a00a73075a62bf976758c Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 19:34:37 +0200 Subject: [PATCH 09/68] Create full enum for lune standard libraries --- Cargo.lock | 33 ++++++++ crates/lune-std-datetime/Cargo.toml | 5 ++ crates/lune-std-datetime/src/lib.rs | 15 ++++ crates/lune-std-fs/Cargo.toml | 5 ++ crates/lune-std-fs/src/lib.rs | 15 ++++ crates/lune-std-luau/Cargo.toml | 5 ++ crates/lune-std-luau/src/lib.rs | 15 ++++ crates/lune-std-net/Cargo.toml | 5 ++ crates/lune-std-net/src/lib.rs | 15 ++++ crates/lune-std-process/Cargo.toml | 5 ++ crates/lune-std-process/src/lib.rs | 15 ++++ crates/lune-std-regex/Cargo.toml | 5 ++ crates/lune-std-regex/src/lib.rs | 15 ++++ crates/lune-std-roblox/Cargo.toml | 5 ++ crates/lune-std-roblox/src/lib.rs | 15 ++++ crates/lune-std-serde/Cargo.toml | 5 ++ crates/lune-std-serde/src/lib.rs | 15 ++++ crates/lune-std/Cargo.toml | 2 + crates/lune-std/src/lib.rs | 4 + crates/lune-std/src/library.rs | 121 ++++++++++++++++++++++++++++ 20 files changed, 320 insertions(+) create mode 100644 crates/lune-std/src/library.rs diff --git a/Cargo.lock b/Cargo.lock index 8e585e0e..bec3b231 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1551,39 +1551,72 @@ dependencies = [ "lune-std-serde", "lune-std-stdio", "lune-std-task", + "mlua", ] [[package]] name = "lune-std-datetime" version = "0.8.3" +dependencies = [ + "lune-utils", + "mlua", +] [[package]] name = "lune-std-fs" version = "0.8.3" +dependencies = [ + "lune-utils", + "mlua", +] [[package]] name = "lune-std-luau" version = "0.8.3" +dependencies = [ + "lune-utils", + "mlua", +] [[package]] name = "lune-std-net" version = "0.8.3" +dependencies = [ + "lune-utils", + "mlua", +] [[package]] name = "lune-std-process" version = "0.8.3" +dependencies = [ + "lune-utils", + "mlua", +] [[package]] name = "lune-std-regex" version = "0.8.3" +dependencies = [ + "lune-utils", + "mlua", +] [[package]] name = "lune-std-roblox" version = "0.8.3" +dependencies = [ + "lune-utils", + "mlua", +] [[package]] name = "lune-std-serde" version = "0.8.3" +dependencies = [ + "lune-utils", + "mlua", +] [[package]] name = "lune-std-stdio" diff --git a/crates/lune-std-datetime/Cargo.toml b/crates/lune-std-datetime/Cargo.toml index cc1f85e0..043853b5 100644 --- a/crates/lune-std-datetime/Cargo.toml +++ b/crates/lune-std-datetime/Cargo.toml @@ -9,3 +9,8 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = "0.9.7" + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-datetime/src/lib.rs b/crates/lune-std-datetime/src/lib.rs index 2e802e78..5a1dfae8 100644 --- a/crates/lune-std-datetime/src/lib.rs +++ b/crates/lune-std-datetime/src/lib.rs @@ -1 +1,16 @@ #![allow(clippy::cargo_common_metadata)] + +use mlua::prelude::*; + +use lune_utils::TableBuilder; + +/** + Creates the `datetime` standard library module. + + # Errors + + Errors when out of memory. +*/ +pub fn module(lua: &Lua) -> LuaResult { + TableBuilder::new(lua)?.build_readonly() +} diff --git a/crates/lune-std-fs/Cargo.toml b/crates/lune-std-fs/Cargo.toml index 17a0996e..cbb2fccb 100644 --- a/crates/lune-std-fs/Cargo.toml +++ b/crates/lune-std-fs/Cargo.toml @@ -9,3 +9,8 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = "0.9.7" + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-fs/src/lib.rs b/crates/lune-std-fs/src/lib.rs index 2e802e78..81a1507e 100644 --- a/crates/lune-std-fs/src/lib.rs +++ b/crates/lune-std-fs/src/lib.rs @@ -1 +1,16 @@ #![allow(clippy::cargo_common_metadata)] + +use mlua::prelude::*; + +use lune_utils::TableBuilder; + +/** + Creates the `fs` standard library module. + + # Errors + + Errors when out of memory. +*/ +pub fn module(lua: &Lua) -> LuaResult { + TableBuilder::new(lua)?.build_readonly() +} diff --git a/crates/lune-std-luau/Cargo.toml b/crates/lune-std-luau/Cargo.toml index fe51e61d..dcd57c0a 100644 --- a/crates/lune-std-luau/Cargo.toml +++ b/crates/lune-std-luau/Cargo.toml @@ -9,3 +9,8 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = "0.9.7" + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-luau/src/lib.rs b/crates/lune-std-luau/src/lib.rs index 2e802e78..59b09349 100644 --- a/crates/lune-std-luau/src/lib.rs +++ b/crates/lune-std-luau/src/lib.rs @@ -1 +1,16 @@ #![allow(clippy::cargo_common_metadata)] + +use mlua::prelude::*; + +use lune_utils::TableBuilder; + +/** + Creates the `luau` standard library module. + + # Errors + + Errors when out of memory. +*/ +pub fn module(lua: &Lua) -> LuaResult { + TableBuilder::new(lua)?.build_readonly() +} diff --git a/crates/lune-std-net/Cargo.toml b/crates/lune-std-net/Cargo.toml index 6409e9db..ff106486 100644 --- a/crates/lune-std-net/Cargo.toml +++ b/crates/lune-std-net/Cargo.toml @@ -9,3 +9,8 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = "0.9.7" + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-net/src/lib.rs b/crates/lune-std-net/src/lib.rs index 2e802e78..24b8917b 100644 --- a/crates/lune-std-net/src/lib.rs +++ b/crates/lune-std-net/src/lib.rs @@ -1 +1,16 @@ #![allow(clippy::cargo_common_metadata)] + +use mlua::prelude::*; + +use lune_utils::TableBuilder; + +/** + Creates the `net` standard library module. + + # Errors + + Errors when out of memory. +*/ +pub fn module(lua: &Lua) -> LuaResult { + TableBuilder::new(lua)?.build_readonly() +} diff --git a/crates/lune-std-process/Cargo.toml b/crates/lune-std-process/Cargo.toml index 0c743bb7..7676d24e 100644 --- a/crates/lune-std-process/Cargo.toml +++ b/crates/lune-std-process/Cargo.toml @@ -9,3 +9,8 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = "0.9.7" + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-process/src/lib.rs b/crates/lune-std-process/src/lib.rs index 2e802e78..4490966a 100644 --- a/crates/lune-std-process/src/lib.rs +++ b/crates/lune-std-process/src/lib.rs @@ -1 +1,16 @@ #![allow(clippy::cargo_common_metadata)] + +use mlua::prelude::*; + +use lune_utils::TableBuilder; + +/** + Creates the `process` standard library module. + + # Errors + + Errors when out of memory. +*/ +pub fn module(lua: &Lua) -> LuaResult { + TableBuilder::new(lua)?.build_readonly() +} diff --git a/crates/lune-std-regex/Cargo.toml b/crates/lune-std-regex/Cargo.toml index 294e70d6..a8e2ecdf 100644 --- a/crates/lune-std-regex/Cargo.toml +++ b/crates/lune-std-regex/Cargo.toml @@ -9,3 +9,8 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = "0.9.7" + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-regex/src/lib.rs b/crates/lune-std-regex/src/lib.rs index 2e802e78..ad73214b 100644 --- a/crates/lune-std-regex/src/lib.rs +++ b/crates/lune-std-regex/src/lib.rs @@ -1 +1,16 @@ #![allow(clippy::cargo_common_metadata)] + +use mlua::prelude::*; + +use lune_utils::TableBuilder; + +/** + Creates the `regex` standard library module. + + # Errors + + Errors when out of memory. +*/ +pub fn module(lua: &Lua) -> LuaResult { + TableBuilder::new(lua)?.build_readonly() +} diff --git a/crates/lune-std-roblox/Cargo.toml b/crates/lune-std-roblox/Cargo.toml index d66ce67a..d84a73c1 100644 --- a/crates/lune-std-roblox/Cargo.toml +++ b/crates/lune-std-roblox/Cargo.toml @@ -9,3 +9,8 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = "0.9.7" + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-roblox/src/lib.rs b/crates/lune-std-roblox/src/lib.rs index 2e802e78..5f26a9e7 100644 --- a/crates/lune-std-roblox/src/lib.rs +++ b/crates/lune-std-roblox/src/lib.rs @@ -1 +1,16 @@ #![allow(clippy::cargo_common_metadata)] + +use mlua::prelude::*; + +use lune_utils::TableBuilder; + +/** + Creates the `roblox` standard library module. + + # Errors + + Errors when out of memory. +*/ +pub fn module(lua: &Lua) -> LuaResult { + TableBuilder::new(lua)?.build_readonly() +} diff --git a/crates/lune-std-serde/Cargo.toml b/crates/lune-std-serde/Cargo.toml index d83ea105..f34b1eae 100644 --- a/crates/lune-std-serde/Cargo.toml +++ b/crates/lune-std-serde/Cargo.toml @@ -9,3 +9,8 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = "0.9.7" + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-serde/src/lib.rs b/crates/lune-std-serde/src/lib.rs index 2e802e78..38f6980b 100644 --- a/crates/lune-std-serde/src/lib.rs +++ b/crates/lune-std-serde/src/lib.rs @@ -1 +1,16 @@ #![allow(clippy::cargo_common_metadata)] + +use mlua::prelude::*; + +use lune_utils::TableBuilder; + +/** + Creates the `serde` standard library module. + + # Errors + + Errors when out of memory. +*/ +pub fn module(lua: &Lua) -> LuaResult { + TableBuilder::new(lua)?.build_readonly() +} diff --git a/crates/lune-std/Cargo.toml b/crates/lune-std/Cargo.toml index b4a3dbd1..0a33e427 100644 --- a/crates/lune-std/Cargo.toml +++ b/crates/lune-std/Cargo.toml @@ -36,6 +36,8 @@ stdio = ["dep:lune-std-stdio"] task = ["dep:lune-std-task"] [dependencies] +mlua = "0.9.7" + lune-std-datetime = { optional = true, version = "0.8.3", path = "../lune-std-datetime" } lune-std-fs = { optional = true, version = "0.8.3", path = "../lune-std-fs" } lune-std-luau = { optional = true, version = "0.8.3", path = "../lune-std-luau" } diff --git a/crates/lune-std/src/lib.rs b/crates/lune-std/src/lib.rs index 2e802e78..8e9b1df6 100644 --- a/crates/lune-std/src/lib.rs +++ b/crates/lune-std/src/lib.rs @@ -1 +1,5 @@ #![allow(clippy::cargo_common_metadata)] + +mod library; + +pub use library::LuneStandardLibrary; diff --git a/crates/lune-std/src/library.rs b/crates/lune-std/src/library.rs new file mode 100644 index 00000000..b6e72041 --- /dev/null +++ b/crates/lune-std/src/library.rs @@ -0,0 +1,121 @@ +use std::str::FromStr; + +use mlua::prelude::*; + +/** + A standard library provided by Lune. +*/ +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[rustfmt::skip] +pub enum LuneStandardLibrary { + #[cfg(feature = "datetime")] DateTime, + #[cfg(feature = "fs")] Fs, + #[cfg(feature = "luau")] Luau, + #[cfg(feature = "net")] Net, + #[cfg(feature = "task")] Task, + #[cfg(feature = "process")] Process, + #[cfg(feature = "regex")] Regex, + #[cfg(feature = "serde")] Serde, + #[cfg(feature = "stdio")] Stdio, + #[cfg(feature = "roblox")] Roblox, +} + +impl LuneStandardLibrary { + /** + All available standard libraries. + */ + #[rustfmt::skip] + pub const ALL: &'static [Self] = &[ + #[cfg(feature = "datetime")] Self::DateTime, + #[cfg(feature = "fs")] Self::Fs, + #[cfg(feature = "luau")] Self::Luau, + #[cfg(feature = "net")] Self::Net, + #[cfg(feature = "task")] Self::Task, + #[cfg(feature = "process")] Self::Process, + #[cfg(feature = "regex")] Self::Regex, + #[cfg(feature = "serde")] Self::Serde, + #[cfg(feature = "stdio")] Self::Stdio, + #[cfg(feature = "roblox")] Self::Roblox, + ]; + + /** + Gets the name of the library, such as `datetime` or `fs`. + */ + #[must_use] + #[rustfmt::skip] + #[allow(unreachable_patterns)] + pub fn name(&self) -> &'static str { + match self { + #[cfg(feature = "datetime")] Self::DateTime => "datetime", + #[cfg(feature = "fs")] Self::Fs => "fs", + #[cfg(feature = "luau")] Self::Luau => "luau", + #[cfg(feature = "net")] Self::Net => "net", + #[cfg(feature = "task")] Self::Task => "task", + #[cfg(feature = "process")] Self::Process => "process", + #[cfg(feature = "regex")] Self::Regex => "regex", + #[cfg(feature = "serde")] Self::Serde => "serde", + #[cfg(feature = "stdio")] Self::Stdio => "stdio", + #[cfg(feature = "roblox")] Self::Roblox => "roblox", + + _ => unreachable!("no standard library enabled"), + } + } + + /** + Creates the Lua module for the library. + + # Errors + + If the library could not be created. + */ + #[rustfmt::skip] + #[allow(unreachable_patterns)] + pub fn module<'lua>(&self, lua: &'lua Lua) -> LuaResult> { + let res = match self { + #[cfg(feature = "datetime")] Self::DateTime => lune_std_datetime::module(lua), + #[cfg(feature = "fs")] Self::Fs => lune_std_fs::module(lua), + #[cfg(feature = "luau")] Self::Luau => lune_std_luau::module(lua), + #[cfg(feature = "net")] Self::Net => lune_std_net::module(lua), + #[cfg(feature = "task")] Self::Task => lune_std_task::module(lua), + #[cfg(feature = "process")] Self::Process => lune_std_process::module(lua), + #[cfg(feature = "regex")] Self::Regex => lune_std_regex::module(lua), + #[cfg(feature = "serde")] Self::Serde => lune_std_serde::module(lua), + #[cfg(feature = "stdio")] Self::Stdio => lune_std_stdio::module(lua), + #[cfg(feature = "roblox")] Self::Roblox => lune_std_roblox::module(lua), + + _ => unreachable!("no standard library enabled"), + }; + match res { + Ok(v) => v.into_lua_multi(lua), + Err(e) => Err(e.context(format!( + "Failed to create standard library '{}'", + self.name() + ))), + } + } +} + +impl FromStr for LuneStandardLibrary { + type Err = String; + #[rustfmt::skip] + fn from_str(s: &str) -> Result { + let low = s.trim().to_ascii_lowercase(); + Ok(match low.as_str() { + #[cfg(feature = "datetime")] "datetime" => Self::DateTime, + #[cfg(feature = "fs")] "fs" => Self::Fs, + #[cfg(feature = "luau")] "luau" => Self::Luau, + #[cfg(feature = "net")] "net" => Self::Net, + #[cfg(feature = "task")] "task" => Self::Task, + #[cfg(feature = "process")] "process" => Self::Process, + #[cfg(feature = "regex")] "regex" => Self::Regex, + #[cfg(feature = "serde")] "serde" => Self::Serde, + #[cfg(feature = "stdio")] "stdio" => Self::Stdio, + #[cfg(feature = "roblox")] "roblox" => Self::Roblox, + + _ => return Err(format!( + "Unknown standard library '{low}'\nValid libraries are: {}", + Self::ALL.iter().map(Self::name).collect::>().join(", ") + )), + }) + } +} From 121afae726d899c34aa9ac5e26291a303047ea7c Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 19:56:25 +0200 Subject: [PATCH 10/68] Add version string to lune-utils crate --- Cargo.lock | 1 + crates/lune-utils/Cargo.toml | 2 + crates/lune-utils/src/lib.rs | 2 + crates/lune-utils/src/version_string.rs | 66 +++++++++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 crates/lune-utils/src/version_string.rs diff --git a/Cargo.lock b/Cargo.lock index bec3b231..d8cac8fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1644,6 +1644,7 @@ name = "lune-utils" version = "0.8.3" dependencies = [ "mlua", + "once_cell", ] [[package]] diff --git a/crates/lune-utils/Cargo.toml b/crates/lune-utils/Cargo.toml index 33dc192d..b31ea428 100644 --- a/crates/lune-utils/Cargo.toml +++ b/crates/lune-utils/Cargo.toml @@ -12,3 +12,5 @@ workspace = true [dependencies] mlua = { version = "0.9.7", features = ["async"] } + +once_cell = "1.17" diff --git a/crates/lune-utils/src/lib.rs b/crates/lune-utils/src/lib.rs index ff27b800..cd9136ef 100644 --- a/crates/lune-utils/src/lib.rs +++ b/crates/lune-utils/src/lib.rs @@ -1,5 +1,7 @@ #![allow(clippy::cargo_common_metadata)] mod table_builder; +mod version_string; pub use self::table_builder::TableBuilder; +pub use self::version_string::get_version_string; diff --git a/crates/lune-utils/src/version_string.rs b/crates/lune-utils/src/version_string.rs new file mode 100644 index 00000000..73ba3a06 --- /dev/null +++ b/crates/lune-utils/src/version_string.rs @@ -0,0 +1,66 @@ +use std::sync::Arc; + +use mlua::prelude::*; +use once_cell::sync::Lazy; + +static VERSION_STRING: Lazy> = Lazy::new(create_version_string); + +/** + Returns the current Lune version string, in the format `Lune x.y.z+luau`. + + This version string is strongly guaranteed to follow the above + format and may safely be used for parsing & version comparisons. +*/ +#[must_use] +pub fn get_version_string() -> Arc { + Arc::clone(&VERSION_STRING) +} + +fn create_version_string() -> Arc { + // Extract the current Luau version from a fresh Lua state / VM that can't be accessed externally. + let luau_version_full = { + let temp_lua = Lua::new(); + + let luau_version_full = temp_lua + .globals() + .get::<_, LuaString>("_VERSION") + .expect("Missing _VERSION global"); + + luau_version_full + .to_str() + .context("Invalid utf8 found in _VERSION global") + .expect("Expected _VERSION global to be a string") + .to_string() + }; + + // Luau version is expected to be in the format "Luau 0.x" and sometimes "Luau 0.x.y" + assert!( + luau_version_full.starts_with("Luau 0."), + "_VERSION global is formatted incorrectly\ + \nFound string '{luau_version_full}'" + ); + let luau_version_noprefix = luau_version_full.strip_prefix("Luau 0.").unwrap().trim(); + + // We make some guarantees about the format of the _VERSION global, + // so make sure that the luau version also follows those rules. + if luau_version_noprefix.is_empty() { + panic!( + "_VERSION global is missing version number\ + \nFound string '{luau_version_full}'" + ) + } else if !luau_version_noprefix.chars().all(is_valid_version_char) { + panic!( + "_VERSION global contains invalid characters\ + \nFound string '{luau_version_full}'" + ) + } + + Arc::new(format!( + "Lune {}+{luau_version_noprefix}", + env!("CARGO_PKG_VERSION") + )) +} + +fn is_valid_version_char(c: char) -> bool { + matches!(c, '0'..='9' | '.') +} From 0bb978640d432ed2c521fe442152517f76e5cda7 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 20:01:20 +0200 Subject: [PATCH 11/68] Fix lune tests running from the wrong dir --- crates/lune/src/tests.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/lune/src/tests.rs b/crates/lune/src/tests.rs index b14566f2..666a1ce5 100644 --- a/crates/lune/src/tests.rs +++ b/crates/lune/src/tests.rs @@ -1,3 +1,4 @@ +use std::env::set_current_dir; use std::process::ExitCode; use anyhow::Result; @@ -13,12 +14,19 @@ macro_rules! create_tests { ($($name:ident: $value:expr,)*) => { $( #[tokio::test(flavor = "multi_thread")] async fn $name() -> Result { + // We need to change the current directory to the workspace root since + // we are in a sub-crate and tests would run relative to the sub-crate + let workspace_dir_str = format!("{}/../../", env!("CARGO_MANIFEST_DIR")); + let workspace_dir = std::path::PathBuf::from(workspace_dir_str).canonicalize()?; + set_current_dir(&workspace_dir)?; + // Disable styling for stdout and stderr since // some tests rely on output not being styled set_colors_enabled(false); set_colors_enabled_stderr(false); + // The rest of the test logic can continue as normal - let full_name = format!("tests/{}.luau", $value); + let full_name = format!("{}/tests/{}.luau", workspace_dir.display(), $value); let script = read_to_string(&full_name).await?; let mut lune = Runtime::new().with_args( ARGS From 136cb7d6d68cf77c589411f1e2b6f84d1056dbb7 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 20:38:14 +0200 Subject: [PATCH 12/68] Mostly port globals to lune-std crate --- Cargo.lock | 5 + crates/lune-std/Cargo.toml | 8 + crates/lune-std/src/global.rs | 92 ++++++ crates/lune-std/src/globals/g_table.rs | 5 + crates/lune-std/src/globals/mod.rs | 5 + crates/lune-std/src/globals/print.rs | 9 + crates/lune-std/src/globals/require/alias.rs | 74 +++++ .../lune-std/src/globals/require/builtin.rs | 14 + .../lune-std/src/globals/require/context.rs | 292 ++++++++++++++++++ crates/lune-std/src/globals/require/mod.rs | 96 ++++++ crates/lune-std/src/globals/require/path.rs | 129 ++++++++ crates/lune-std/src/globals/version.rs | 8 + crates/lune-std/src/globals/warn.rs | 9 + crates/lune-std/src/lib.rs | 5 +- crates/lune-std/src/library.rs | 14 +- 15 files changed, 760 insertions(+), 5 deletions(-) create mode 100644 crates/lune-std/src/global.rs create mode 100644 crates/lune-std/src/globals/g_table.rs create mode 100644 crates/lune-std/src/globals/mod.rs create mode 100644 crates/lune-std/src/globals/print.rs create mode 100644 crates/lune-std/src/globals/require/alias.rs create mode 100644 crates/lune-std/src/globals/require/builtin.rs create mode 100644 crates/lune-std/src/globals/require/context.rs create mode 100644 crates/lune-std/src/globals/require/mod.rs create mode 100644 crates/lune-std/src/globals/require/path.rs create mode 100644 crates/lune-std/src/globals/version.rs create mode 100644 crates/lune-std/src/globals/warn.rs diff --git a/Cargo.lock b/Cargo.lock index d8cac8fb..951790c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1551,7 +1551,12 @@ dependencies = [ "lune-std-serde", "lune-std-stdio", "lune-std-task", + "lune-utils", "mlua", + "mlua-luau-scheduler 0.0.1", + "path-clean", + "pathdiff", + "tokio", ] [[package]] diff --git a/crates/lune-std/Cargo.toml b/crates/lune-std/Cargo.toml index 0a33e427..883cdc0f 100644 --- a/crates/lune-std/Cargo.toml +++ b/crates/lune-std/Cargo.toml @@ -37,6 +37,14 @@ task = ["dep:lune-std-task"] [dependencies] mlua = "0.9.7" +mlua-luau-scheduler = "0.0.1" + +path-clean = "1.0" +pathdiff = "0.2" + +tokio = { version = "1", default-features = false, features = ["fs"] } + +lune-utils = { version = "0.8.3", path = "../lune-utils" } lune-std-datetime = { optional = true, version = "0.8.3", path = "../lune-std-datetime" } lune-std-fs = { optional = true, version = "0.8.3", path = "../lune-std-fs" } diff --git a/crates/lune-std/src/global.rs b/crates/lune-std/src/global.rs new file mode 100644 index 00000000..1c0944f6 --- /dev/null +++ b/crates/lune-std/src/global.rs @@ -0,0 +1,92 @@ +use std::str::FromStr; + +use mlua::prelude::*; + +/** + A standard global provided by Lune. +*/ +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub enum LuneStandardGlobal { + GTable, + Print, + Require, + Version, + Warn, +} + +impl LuneStandardGlobal { + /** + All available standard globals. + */ + pub const ALL: &'static [Self] = &[ + Self::GTable, + Self::Print, + Self::Require, + Self::Version, + Self::Warn, + ]; + + /** + Gets the name of the global, such as `_G` or `require`. + */ + #[must_use] + pub fn name(&self) -> &'static str { + match self { + Self::GTable => "_G", + Self::Print => "print", + Self::Require => "require", + Self::Version => "_VERSION", + Self::Warn => "warn", + } + } + + /** + Creates the Lua value for the global. + + # Errors + + If the global could not be created. + */ + #[rustfmt::skip] + #[allow(unreachable_patterns)] + pub fn create<'lua>(&self, lua: &'lua Lua) -> LuaResult> { + let res = match self { + Self::GTable => crate::globals::g_table::create(lua), + Self::Print => crate::globals::print::create(lua), + Self::Require => crate::globals::require::create(lua), + Self::Version => crate::globals::version::create(lua), + Self::Warn => crate::globals::warn::create(lua), + }; + match res { + Ok(v) => Ok(v), + Err(e) => Err(e.context(format!( + "Failed to create standard global '{}'", + self.name() + ))), + } + } +} + +impl FromStr for LuneStandardGlobal { + type Err = String; + fn from_str(s: &str) -> Result { + let low = s.trim().to_ascii_lowercase(); + Ok(match low.as_str() { + "_g" => Self::GTable, + "print" => Self::Print, + "require" => Self::Require, + "_version" => Self::Version, + "warn" => Self::Warn, + _ => { + return Err(format!( + "Unknown standard global '{low}'\nValid globals are: {}", + Self::ALL + .iter() + .map(Self::name) + .collect::>() + .join(", ") + )) + } + }) + } +} diff --git a/crates/lune-std/src/globals/g_table.rs b/crates/lune-std/src/globals/g_table.rs new file mode 100644 index 00000000..e21fa1e1 --- /dev/null +++ b/crates/lune-std/src/globals/g_table.rs @@ -0,0 +1,5 @@ +use mlua::prelude::*; + +pub fn create(lua: &Lua) -> LuaResult { + lua.create_table()?.into_lua(lua) +} diff --git a/crates/lune-std/src/globals/mod.rs b/crates/lune-std/src/globals/mod.rs new file mode 100644 index 00000000..b60e9a14 --- /dev/null +++ b/crates/lune-std/src/globals/mod.rs @@ -0,0 +1,5 @@ +pub mod g_table; +pub mod print; +pub mod require; +pub mod version; +pub mod warn; diff --git a/crates/lune-std/src/globals/print.rs b/crates/lune-std/src/globals/print.rs new file mode 100644 index 00000000..45ccdabb --- /dev/null +++ b/crates/lune-std/src/globals/print.rs @@ -0,0 +1,9 @@ +use mlua::prelude::*; + +pub fn create(lua: &Lua) -> LuaResult { + let f = lua.create_function(|_, args: LuaMultiValue| { + // TODO: Port this over from the old crate + Ok(()) + })?; + f.into_lua(lua) +} diff --git a/crates/lune-std/src/globals/require/alias.rs b/crates/lune-std/src/globals/require/alias.rs new file mode 100644 index 00000000..0818f888 --- /dev/null +++ b/crates/lune-std/src/globals/require/alias.rs @@ -0,0 +1,74 @@ +use mlua::prelude::*; + +use lune_utils::{ + luaurc::LuauRc, + paths::{make_absolute_and_clean, CWD}, +}; + +use super::context::*; + +pub(super) async fn require<'lua, 'ctx>( + lua: &'lua Lua, + ctx: &'ctx RequireContext, + source: &str, + alias: &str, + path: &str, +) -> LuaResult> +where + 'lua: 'ctx, +{ + let alias = alias.to_ascii_lowercase(); + + let parent = make_absolute_and_clean(source) + .parent() + .expect("how did a root path end up here..") + .to_path_buf(); + + // Try to gather the first luaurc and / or error we + // encounter to display better error messages to users + let mut first_luaurc = None; + let mut first_error = None; + let predicate = |rc: &LuauRc| { + if first_luaurc.is_none() { + first_luaurc.replace(rc.clone()); + } + if let Err(e) = rc.validate() { + if first_error.is_none() { + first_error.replace(e); + } + false + } else { + rc.find_alias(&alias).is_some() + } + }; + + // Try to find a luaurc that contains the alias we're searching for + let luaurc = LuauRc::read_recursive(parent, predicate) + .await + .ok_or_else(|| { + if let Some(error) = first_error { + LuaError::runtime(format!("error while parsing .luaurc file: {error}")) + } else if let Some(luaurc) = first_luaurc { + LuaError::runtime(format!( + "failed to find alias '{alias}' - known aliases:\n{}", + luaurc + .aliases() + .iter() + .map(|(name, path)| format!(" {name} > {path}")) + .collect::>() + .join("\n") + )) + } else { + LuaError::runtime(format!("failed to find alias '{alias}' (no .luaurc)")) + } + })?; + + // We now have our aliased path, our path require function just needs it + // in a slightly different format with both absolute + relative to cwd + let abs_path = luaurc.find_alias(&alias).unwrap().join(path); + let rel_path = pathdiff::diff_paths(&abs_path, CWD.as_path()).ok_or_else(|| { + LuaError::runtime(format!("failed to find relative path for alias '{alias}'")) + })?; + + super::path::require_abs_rel(lua, ctx, abs_path, rel_path).await +} diff --git a/crates/lune-std/src/globals/require/builtin.rs b/crates/lune-std/src/globals/require/builtin.rs new file mode 100644 index 00000000..d75ddf51 --- /dev/null +++ b/crates/lune-std/src/globals/require/builtin.rs @@ -0,0 +1,14 @@ +use mlua::prelude::*; + +use super::context::*; + +pub(super) async fn require<'lua, 'ctx>( + lua: &'lua Lua, + ctx: &'ctx RequireContext, + name: &str, +) -> LuaResult> +where + 'lua: 'ctx, +{ + ctx.load_library(lua, name) +} diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs new file mode 100644 index 00000000..102b8049 --- /dev/null +++ b/crates/lune-std/src/globals/require/context.rs @@ -0,0 +1,292 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + sync::Arc, +}; + +use mlua::prelude::*; +use mlua_luau_scheduler::LuaSchedulerExt; + +use tokio::{ + fs::read, + sync::{ + broadcast::{self, Sender}, + Mutex as AsyncMutex, + }, +}; + +use crate::library::LuneStandardLibrary; + +/** + Context containing cached results for all `require` operations. + + The cache uses absolute paths, so any given relative + path will first be transformed into an absolute path. +*/ +#[derive(Debug, Clone)] +pub(super) struct RequireContext { + cache_libraries: Arc>>>, + cache_results: Arc>>>, + cache_pending: Arc>>>, +} + +impl RequireContext { + /** + Creates a new require context for the given [`Lua`] struct. + + Note that this require context is global and only one require + context should be created per [`Lua`] struct, creating more + than one context may lead to undefined require-behavior. + */ + pub fn new() -> Self { + Self { + cache_libraries: Arc::new(AsyncMutex::new(HashMap::new())), + cache_results: Arc::new(AsyncMutex::new(HashMap::new())), + cache_pending: Arc::new(AsyncMutex::new(HashMap::new())), + } + } + + /** + Resolves the given `source` and `path` into require paths + to use, based on the current require context settings. + + This will resolve path segments such as `./`, `../`, ..., and + if the resolved path is not an absolute path, will create an + absolute path by prepending the current working directory. + */ + pub fn resolve_paths( + &self, + source: impl AsRef, + path: impl AsRef, + ) -> LuaResult<(PathBuf, PathBuf)> { + let path = PathBuf::from(source.as_ref()) + .parent() + .ok_or_else(|| LuaError::runtime("Failed to get parent path of source"))? + .join(path.as_ref()); + + let rel_path = path_clean::clean(path); + let abs_path = if rel_path.is_absolute() { + rel_path.to_path_buf() + } else { + CWD.join(&rel_path) + }; + + Ok((abs_path, rel_path)) + } + + /** + Checks if the given path has a cached require result. + */ + pub fn is_cached(&self, abs_path: impl AsRef) -> LuaResult { + let is_cached = self + .cache_results + .try_lock() + .expect("RequireContext may not be used from multiple threads") + .contains_key(abs_path.as_ref()); + Ok(is_cached) + } + + /** + Checks if the given path is currently being used in `require`. + */ + pub fn is_pending(&self, abs_path: impl AsRef) -> LuaResult { + let is_pending = self + .cache_pending + .try_lock() + .expect("RequireContext may not be used from multiple threads") + .contains_key(abs_path.as_ref()); + Ok(is_pending) + } + + /** + Gets the resulting value from the require cache. + + Will panic if the path has not been cached, use [`is_cached`] first. + */ + pub fn get_from_cache<'lua>( + &self, + lua: &'lua Lua, + abs_path: impl AsRef, + ) -> LuaResult> { + let results = self + .cache_results + .try_lock() + .expect("RequireContext may not be used from multiple threads"); + + let cached = results + .get(abs_path.as_ref()) + .expect("Path does not exist in results cache"); + match cached { + Err(e) => Err(e.clone()), + Ok(k) => { + let multi_vec = lua + .registry_value::>(k) + .expect("Missing require result in lua registry"); + Ok(LuaMultiValue::from_vec(multi_vec)) + } + } + } + + /** + Waits for the resulting value from the require cache. + + Will panic if the path has not been cached, use [`is_cached`] first. + */ + pub async fn wait_for_cache<'lua>( + &self, + lua: &'lua Lua, + abs_path: impl AsRef, + ) -> LuaResult> { + let mut thread_recv = { + let pending = self + .cache_pending + .try_lock() + .expect("RequireContext may not be used from multiple threads"); + let thread_id = pending + .get(abs_path.as_ref()) + .expect("Path is not currently pending require"); + thread_id.subscribe() + }; + + thread_recv.recv().await.into_lua_err()?; + + self.get_from_cache(lua, abs_path.as_ref()) + } + + async fn load<'lua>( + &self, + lua: &'lua Lua, + abs_path: impl AsRef, + rel_path: impl AsRef, + ) -> LuaResult { + let abs_path = abs_path.as_ref(); + let rel_path = rel_path.as_ref(); + + // Read the file at the given path, try to parse and + // load it into a new lua thread that we can schedule + let file_contents = read(&abs_path).await?; + let file_thread = lua + .load(file_contents) + .set_name(rel_path.to_string_lossy().to_string()); + + // Schedule the thread to run, wait for it to finish running + let thread_id = lua.push_thread_back(file_thread, ())?; + lua.track_thread(thread_id); + lua.wait_for_thread(thread_id).await; + let thread_res = lua.get_thread_result(thread_id).unwrap(); + + // Return the result of the thread, storing any lua value(s) in the registry + match thread_res { + Err(e) => Err(e), + Ok(v) => { + let multi_vec = v.into_vec(); + let multi_key = lua + .create_registry_value(multi_vec) + .expect("Failed to store require result in registry - out of memory"); + Ok(multi_key) + } + } + } + + /** + Loads (requires) the file at the given path. + */ + pub async fn load_with_caching<'lua>( + &self, + lua: &'lua Lua, + abs_path: impl AsRef, + rel_path: impl AsRef, + ) -> LuaResult> { + let abs_path = abs_path.as_ref(); + let rel_path = rel_path.as_ref(); + + // Set this abs path as currently pending + let (broadcast_tx, _) = broadcast::channel(1); + self.cache_pending + .try_lock() + .expect("RequireContext may not be used from multiple threads") + .insert(abs_path.to_path_buf(), broadcast_tx); + + // Try to load at this abs path + let load_res = self.load(lua, abs_path, rel_path).await; + let load_val = match &load_res { + Err(e) => Err(e.clone()), + Ok(k) => { + let multi_vec = lua + .registry_value::>(k) + .expect("Failed to fetch require result from registry"); + Ok(LuaMultiValue::from_vec(multi_vec)) + } + }; + + // NOTE: We use the async lock and not try_lock here because + // some other thread may be wanting to insert into the require + // cache at the same time, and that's not an actual error case + self.cache_results + .lock() + .await + .insert(abs_path.to_path_buf(), load_res); + + // Remove the pending thread id from the require context, + // broadcast a message to let any listeners know that this + // path has now finished the require process and is cached + let broadcast_tx = self + .cache_pending + .try_lock() + .expect("RequireContext may not be used from multiple threads") + .remove(abs_path) + .expect("Pending require broadcaster was unexpectedly removed"); + broadcast_tx.send(()).ok(); + + load_val + } + + /** + Loads (requires) the library with the given name. + */ + pub fn load_library<'lua>( + &self, + lua: &'lua Lua, + name: impl AsRef, + ) -> LuaResult> { + let library: LuneStandardLibrary = match name.as_ref().parse() { + Err(e) => return Err(LuaError::runtime(e)), + Ok(b) => b, + }; + + let mut cache = self + .cache_libraries + .try_lock() + .expect("RequireContext may not be used from multiple threads"); + + if let Some(res) = cache.get(&library) { + return match res { + Err(e) => return Err(e.clone()), + Ok(key) => { + let multi_vec = lua + .registry_value::>(key) + .expect("Missing library result in lua registry"); + Ok(LuaMultiValue::from_vec(multi_vec)) + } + }; + }; + + let result = library.module(lua); + + cache.insert( + library, + match result.clone() { + Err(e) => Err(e), + Ok(multi) => { + let multi_vec = multi.into_vec(); + let multi_key = lua + .create_registry_value(multi_vec) + .expect("Failed to store require result in registry - out of memory"); + Ok(multi_key) + } + }, + ); + + result + } +} diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs new file mode 100644 index 00000000..02927975 --- /dev/null +++ b/crates/lune-std/src/globals/require/mod.rs @@ -0,0 +1,96 @@ +use mlua::prelude::*; + +use lune_utils::TableBuilder; + +mod context; +use context::RequireContext; + +mod alias; +mod builtin; +mod path; + +const REQUIRE_IMPL: &str = r" +return require(source(), ...) +"; + +pub fn create(lua: &Lua) -> LuaResult { + lua.set_app_data(RequireContext::new()); + + /* + Require implementation needs a few workarounds: + + - Async functions run outside of the lua resumption cycle, + so the current lua thread, as well as its stack/debug info + is not available, meaning we have to use a normal function + + - Using the async require function directly in another lua function + would mean yielding across the metamethod/c-call boundary, meaning + we have to first load our two functions into a normal lua chunk + and then load that new chunk into our final require function + + Also note that we inspect the stack at level 2: + + 1. The current c / rust function + 2. The wrapper lua chunk defined above + 3. The lua chunk we are require-ing from + */ + + let require_fn = lua.create_async_function(require)?; + let get_source_fn = lua.create_function(move |lua, _: ()| match lua.inspect_stack(2) { + None => Err(LuaError::runtime( + "Failed to get stack info for require source", + )), + Some(info) => match info.source().source { + None => Err(LuaError::runtime( + "Stack info is missing source for require", + )), + Some(source) => lua.create_string(source.as_bytes()), + }, + })?; + + let require_env = TableBuilder::new(lua)? + .with_value("source", get_source_fn)? + .with_value("require", require_fn)? + .build_readonly()?; + + lua.load(REQUIRE_IMPL) + .set_name("require") + .set_environment(require_env) + .into_function()? + .into_lua(lua) +} + +async fn require<'lua>( + lua: &'lua Lua, + (source, path): (LuaString<'lua>, LuaString<'lua>), +) -> LuaResult> { + let source = source + .to_str() + .into_lua_err() + .context("Failed to parse require source as string")? + .to_string(); + + let path = path + .to_str() + .into_lua_err() + .context("Failed to parse require path as string")? + .to_string(); + + let context = lua + .app_data_ref() + .expect("Failed to get RequireContext from app data"); + + if let Some(builtin_name) = path + .strip_prefix("@lune/") + .map(|name| name.to_ascii_lowercase()) + { + builtin::require(lua, &context, &builtin_name).await + } else if let Some(aliased_path) = path.strip_prefix('@') { + let (alias, path) = aliased_path.split_once('/').ok_or(LuaError::runtime( + "Require with custom alias must contain '/' delimiter", + ))?; + alias::require(lua, &context, &source, alias, path).await + } else { + path::require(lua, &context, &source, &path).await + } +} diff --git a/crates/lune-std/src/globals/require/path.rs b/crates/lune-std/src/globals/require/path.rs new file mode 100644 index 00000000..7e8084f7 --- /dev/null +++ b/crates/lune-std/src/globals/require/path.rs @@ -0,0 +1,129 @@ +use std::path::{Path, PathBuf}; + +use mlua::prelude::*; +use mlua::Error::ExternalError; + +use super::context::*; + +pub(super) async fn require<'lua, 'ctx>( + lua: &'lua Lua, + ctx: &'ctx RequireContext, + source: &str, + path: &str, +) -> LuaResult> +where + 'lua: 'ctx, +{ + let (abs_path, rel_path) = ctx.resolve_paths(source, path)?; + require_abs_rel(lua, ctx, abs_path, rel_path).await +} + +pub(super) async fn require_abs_rel<'lua, 'ctx>( + lua: &'lua Lua, + ctx: &'ctx RequireContext, + abs_path: PathBuf, // Absolute to filesystem + rel_path: PathBuf, // Relative to CWD (for displaying) +) -> LuaResult> +where + 'lua: 'ctx, +{ + // 1. Try to require the exact path + match require_inner(lua, ctx, &abs_path, &rel_path).await { + Ok(res) => return Ok(res), + Err(err) => { + if !is_file_not_found_error(&err) { + return Err(err); + } + } + } + + // 2. Try to require the path with an added "luau" extension + // 3. Try to require the path with an added "lua" extension + for extension in ["luau", "lua"] { + match require_inner( + lua, + ctx, + &append_extension(&abs_path, extension), + &append_extension(&rel_path, extension), + ) + .await + { + Ok(res) => return Ok(res), + Err(err) => { + if !is_file_not_found_error(&err) { + return Err(err); + } + } + } + } + + // We didn't find any direct file paths, look + // for directories with "init" files in them... + let abs_init = abs_path.join("init"); + let rel_init = rel_path.join("init"); + + // 4. Try to require the init path with an added "luau" extension + // 5. Try to require the init path with an added "lua" extension + for extension in ["luau", "lua"] { + match require_inner( + lua, + ctx, + &append_extension(&abs_init, extension), + &append_extension(&rel_init, extension), + ) + .await + { + Ok(res) => return Ok(res), + Err(err) => { + if !is_file_not_found_error(&err) { + return Err(err); + } + } + } + } + + // Nothing left to try, throw an error + Err(LuaError::runtime(format!( + "No file exists at the path '{}'", + rel_path.display() + ))) +} + +async fn require_inner<'lua, 'ctx>( + lua: &'lua Lua, + ctx: &'ctx RequireContext, + abs_path: impl AsRef, + rel_path: impl AsRef, +) -> LuaResult> +where + 'lua: 'ctx, +{ + let abs_path = abs_path.as_ref(); + let rel_path = rel_path.as_ref(); + + if ctx.is_cached(abs_path)? { + ctx.get_from_cache(lua, abs_path) + } else if ctx.is_pending(abs_path)? { + ctx.wait_for_cache(lua, &abs_path).await + } else { + ctx.load_with_caching(lua, &abs_path, &rel_path).await + } +} + +fn append_extension(path: impl Into, ext: &'static str) -> PathBuf { + 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), + }; + new +} + +fn is_file_not_found_error(err: &LuaError) -> bool { + if let ExternalError(err) = err { + err.as_ref().downcast_ref::().is_some() + } else { + false + } +} diff --git a/crates/lune-std/src/globals/version.rs b/crates/lune-std/src/globals/version.rs new file mode 100644 index 00000000..29b4359f --- /dev/null +++ b/crates/lune-std/src/globals/version.rs @@ -0,0 +1,8 @@ +use mlua::prelude::*; + +use lune_utils::get_version_string; + +pub fn create(lua: &Lua) -> LuaResult { + let s = get_version_string().to_string(); + lua.create_string(s)?.into_lua(lua) +} diff --git a/crates/lune-std/src/globals/warn.rs b/crates/lune-std/src/globals/warn.rs new file mode 100644 index 00000000..45ccdabb --- /dev/null +++ b/crates/lune-std/src/globals/warn.rs @@ -0,0 +1,9 @@ +use mlua::prelude::*; + +pub fn create(lua: &Lua) -> LuaResult { + let f = lua.create_function(|_, args: LuaMultiValue| { + // TODO: Port this over from the old crate + Ok(()) + })?; + f.into_lua(lua) +} diff --git a/crates/lune-std/src/lib.rs b/crates/lune-std/src/lib.rs index 8e9b1df6..be7ff9b3 100644 --- a/crates/lune-std/src/lib.rs +++ b/crates/lune-std/src/lib.rs @@ -1,5 +1,8 @@ #![allow(clippy::cargo_common_metadata)] +mod global; +mod globals; mod library; -pub use library::LuneStandardLibrary; +pub use self::global::LuneStandardGlobal; +pub use self::library::LuneStandardLibrary; diff --git a/crates/lune-std/src/library.rs b/crates/lune-std/src/library.rs index b6e72041..dfede4fd 100644 --- a/crates/lune-std/src/library.rs +++ b/crates/lune-std/src/library.rs @@ -112,10 +112,16 @@ impl FromStr for LuneStandardLibrary { #[cfg(feature = "stdio")] "stdio" => Self::Stdio, #[cfg(feature = "roblox")] "roblox" => Self::Roblox, - _ => return Err(format!( - "Unknown standard library '{low}'\nValid libraries are: {}", - Self::ALL.iter().map(Self::name).collect::>().join(", ") - )), + _ => { + return Err(format!( + "Unknown standard library '{low}'\nValid libraries are: {}", + Self::ALL + .iter() + .map(Self::name) + .collect::>() + .join(", ") + )) + } }) } } From cf7e1f3cd95ce381c0f6efbd46ee6f5cc87c1231 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 20:50:18 +0200 Subject: [PATCH 13/68] Collect path-related functions in lune-utils crate --- Cargo.lock | 5 +- crates/lune-std/Cargo.toml | 3 - crates/lune-std/src/globals/require/alias.rs | 6 +- .../lune-std/src/globals/require/context.rs | 10 +-- crates/lune-utils/Cargo.toml | 3 + crates/lune-utils/src/lib.rs | 2 + crates/lune-utils/src/path.rs | 81 +++++++++++++++++++ 7 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 crates/lune-utils/src/path.rs diff --git a/Cargo.lock b/Cargo.lock index 951790c0..3e3a7538 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1554,8 +1554,6 @@ dependencies = [ "lune-utils", "mlua", "mlua-luau-scheduler 0.0.1", - "path-clean", - "pathdiff", "tokio", ] @@ -1648,8 +1646,11 @@ dependencies = [ name = "lune-utils" version = "0.8.3" dependencies = [ + "dunce", "mlua", "once_cell", + "path-clean", + "pathdiff", ] [[package]] diff --git a/crates/lune-std/Cargo.toml b/crates/lune-std/Cargo.toml index 883cdc0f..2293e35a 100644 --- a/crates/lune-std/Cargo.toml +++ b/crates/lune-std/Cargo.toml @@ -39,9 +39,6 @@ task = ["dep:lune-std-task"] mlua = "0.9.7" mlua-luau-scheduler = "0.0.1" -path-clean = "1.0" -pathdiff = "0.2" - tokio = { version = "1", default-features = false, features = ["fs"] } lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std/src/globals/require/alias.rs b/crates/lune-std/src/globals/require/alias.rs index 0818f888..38a822af 100644 --- a/crates/lune-std/src/globals/require/alias.rs +++ b/crates/lune-std/src/globals/require/alias.rs @@ -2,7 +2,7 @@ use mlua::prelude::*; use lune_utils::{ luaurc::LuauRc, - paths::{make_absolute_and_clean, CWD}, + path::{clean_path_and_make_absolute, diff_path, get_current_dir}, }; use super::context::*; @@ -19,7 +19,7 @@ where { let alias = alias.to_ascii_lowercase(); - let parent = make_absolute_and_clean(source) + let parent = clean_path_and_make_absolute(source) .parent() .expect("how did a root path end up here..") .to_path_buf(); @@ -66,7 +66,7 @@ where // We now have our aliased path, our path require function just needs it // in a slightly different format with both absolute + relative to cwd let abs_path = luaurc.find_alias(&alias).unwrap().join(path); - let rel_path = pathdiff::diff_paths(&abs_path, CWD.as_path()).ok_or_else(|| { + let rel_path = diff_path(&abs_path, get_current_dir()).ok_or_else(|| { LuaError::runtime(format!("failed to find relative path for alias '{alias}'")) })?; diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 102b8049..8e2ded37 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -15,6 +15,8 @@ use tokio::{ }, }; +use lune_utils::path::{clean_path, clean_path_and_make_absolute}; + use crate::library::LuneStandardLibrary; /** @@ -64,12 +66,8 @@ impl RequireContext { .ok_or_else(|| LuaError::runtime("Failed to get parent path of source"))? .join(path.as_ref()); - let rel_path = path_clean::clean(path); - let abs_path = if rel_path.is_absolute() { - rel_path.to_path_buf() - } else { - CWD.join(&rel_path) - }; + let abs_path = clean_path_and_make_absolute(path); + let rel_path = clean_path(path); Ok((abs_path, rel_path)) } diff --git a/crates/lune-utils/Cargo.toml b/crates/lune-utils/Cargo.toml index b31ea428..e3a2fed7 100644 --- a/crates/lune-utils/Cargo.toml +++ b/crates/lune-utils/Cargo.toml @@ -13,4 +13,7 @@ workspace = true [dependencies] mlua = { version = "0.9.7", features = ["async"] } +dunce = "1.0" once_cell = "1.17" +path-clean = "1.0" +pathdiff = "0.2" diff --git a/crates/lune-utils/src/lib.rs b/crates/lune-utils/src/lib.rs index cd9136ef..38738cc3 100644 --- a/crates/lune-utils/src/lib.rs +++ b/crates/lune-utils/src/lib.rs @@ -3,5 +3,7 @@ mod table_builder; mod version_string; +pub mod path; + pub use self::table_builder::TableBuilder; pub use self::version_string::get_version_string; diff --git a/crates/lune-utils/src/path.rs b/crates/lune-utils/src/path.rs new file mode 100644 index 00000000..a510e22c --- /dev/null +++ b/crates/lune-utils/src/path.rs @@ -0,0 +1,81 @@ +use std::{ + env::{current_dir, current_exe}, + path::{Path, PathBuf}, + sync::Arc, +}; + +use once_cell::sync::Lazy; +use path_clean::PathClean; + +static CWD: Lazy> = Lazy::new(create_cwd); +static EXE: Lazy> = Lazy::new(create_exe); + +fn create_cwd() -> Arc { + let cwd = current_dir().expect("failed to find current working directory"); + dunce::canonicalize(cwd) + .expect("failed to canonicalize current working directory") + .into() +} + +fn create_exe() -> Arc { + let exe = current_exe().expect("failed to find current executable"); + dunce::canonicalize(exe) + .expect("failed to canonicalize current executable") + .into() +} + +/** + Gets the current working directory as an absolute path. + + This absolute path is canonicalized and does not contain any `.` or `..` + components, and it is also in a friendly (non-UNC) format. +*/ +#[must_use] +pub fn get_current_dir() -> Arc { + Arc::clone(&CWD) +} + +/** + Gets the path to the current executable as an absolute path. + + This absolute path is canonicalized and does not contain any `.` or `..` + components, and it is also in a friendly (non-UNC) format. +*/ +#[must_use] +pub fn get_current_exe() -> Arc { + Arc::clone(&EXE) +} + +/** + Diffs two paths against each other. + + See the [`pathdiff`] crate for more information on what diffing paths does. +*/ +pub fn diff_path(path: impl AsRef, base: impl AsRef) -> Option { + pathdiff::diff_paths(path, base) +} + +/** + Cleans a path. + + See the [`path_clean`] crate for more information on what cleaning a path does. +*/ +pub fn clean_path(path: impl AsRef) -> PathBuf { + path.as_ref().clean() +} + +/** + Makes a path absolute and then cleans it. + + Relative paths are resolved against the current working directory. + + See the [`path_clean`] crate for more information on what cleaning a path does. +*/ +pub fn clean_path_and_make_absolute(path: impl AsRef) -> PathBuf { + let path = path.as_ref(); + if path.is_relative() { + CWD.join(path).clean() + } else { + path.clean() + } +} From a714efdac4c5a3a2d8f1ed8221fc812900df3423 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 21:06:14 +0200 Subject: [PATCH 14/68] Add luaurc to lune-utils crate, resolve issues with require in lune-std --- Cargo.lock | 3 + crates/lune-std/src/globals/require/alias.rs | 2 +- .../lune-std/src/globals/require/context.rs | 31 ++-- .../require/{builtin.rs => library.rs} | 2 +- crates/lune-std/src/globals/require/mod.rs | 11 +- crates/lune-std/src/globals/require/path.rs | 2 +- crates/lune-utils/Cargo.toml | 5 + crates/lune-utils/src/lib.rs | 2 + crates/lune-utils/src/luaurc.rs | 164 ++++++++++++++++++ 9 files changed, 196 insertions(+), 26 deletions(-) rename crates/lune-std/src/globals/require/{builtin.rs => library.rs} (83%) create mode 100644 crates/lune-utils/src/luaurc.rs diff --git a/Cargo.lock b/Cargo.lock index 3e3a7538..6c67588b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1651,6 +1651,9 @@ dependencies = [ "once_cell", "path-clean", "pathdiff", + "serde", + "serde_json", + "tokio", ] [[package]] diff --git a/crates/lune-std/src/globals/require/alias.rs b/crates/lune-std/src/globals/require/alias.rs index 38a822af..70e7aaf3 100644 --- a/crates/lune-std/src/globals/require/alias.rs +++ b/crates/lune-std/src/globals/require/alias.rs @@ -1,8 +1,8 @@ use mlua::prelude::*; use lune_utils::{ - luaurc::LuauRc, path::{clean_path_and_make_absolute, diff_path, get_current_dir}, + LuauRc, }; use super::context::*; diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 8e2ded37..0355d270 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -27,9 +27,9 @@ use crate::library::LuneStandardLibrary; */ #[derive(Debug, Clone)] pub(super) struct RequireContext { - cache_libraries: Arc>>>, - cache_results: Arc>>>, - cache_pending: Arc>>>, + libraries: Arc>>>, + results: Arc>>>, + pending: Arc>>>, } impl RequireContext { @@ -42,9 +42,9 @@ impl RequireContext { */ pub fn new() -> Self { Self { - cache_libraries: Arc::new(AsyncMutex::new(HashMap::new())), - cache_results: Arc::new(AsyncMutex::new(HashMap::new())), - cache_pending: Arc::new(AsyncMutex::new(HashMap::new())), + libraries: Arc::new(AsyncMutex::new(HashMap::new())), + results: Arc::new(AsyncMutex::new(HashMap::new())), + pending: Arc::new(AsyncMutex::new(HashMap::new())), } } @@ -57,7 +57,6 @@ impl RequireContext { absolute path by prepending the current working directory. */ pub fn resolve_paths( - &self, source: impl AsRef, path: impl AsRef, ) -> LuaResult<(PathBuf, PathBuf)> { @@ -66,7 +65,7 @@ impl RequireContext { .ok_or_else(|| LuaError::runtime("Failed to get parent path of source"))? .join(path.as_ref()); - let abs_path = clean_path_and_make_absolute(path); + let abs_path = clean_path_and_make_absolute(&path); let rel_path = clean_path(path); Ok((abs_path, rel_path)) @@ -77,7 +76,7 @@ impl RequireContext { */ pub fn is_cached(&self, abs_path: impl AsRef) -> LuaResult { let is_cached = self - .cache_results + .results .try_lock() .expect("RequireContext may not be used from multiple threads") .contains_key(abs_path.as_ref()); @@ -89,7 +88,7 @@ impl RequireContext { */ pub fn is_pending(&self, abs_path: impl AsRef) -> LuaResult { let is_pending = self - .cache_pending + .pending .try_lock() .expect("RequireContext may not be used from multiple threads") .contains_key(abs_path.as_ref()); @@ -107,7 +106,7 @@ impl RequireContext { abs_path: impl AsRef, ) -> LuaResult> { let results = self - .cache_results + .results .try_lock() .expect("RequireContext may not be used from multiple threads"); @@ -137,7 +136,7 @@ impl RequireContext { ) -> LuaResult> { let mut thread_recv = { let pending = self - .cache_pending + .pending .try_lock() .expect("RequireContext may not be used from multiple threads"); let thread_id = pending @@ -200,7 +199,7 @@ impl RequireContext { // Set this abs path as currently pending let (broadcast_tx, _) = broadcast::channel(1); - self.cache_pending + self.pending .try_lock() .expect("RequireContext may not be used from multiple threads") .insert(abs_path.to_path_buf(), broadcast_tx); @@ -220,7 +219,7 @@ impl RequireContext { // NOTE: We use the async lock and not try_lock here because // some other thread may be wanting to insert into the require // cache at the same time, and that's not an actual error case - self.cache_results + self.results .lock() .await .insert(abs_path.to_path_buf(), load_res); @@ -229,7 +228,7 @@ impl RequireContext { // broadcast a message to let any listeners know that this // path has now finished the require process and is cached let broadcast_tx = self - .cache_pending + .pending .try_lock() .expect("RequireContext may not be used from multiple threads") .remove(abs_path) @@ -253,7 +252,7 @@ impl RequireContext { }; let mut cache = self - .cache_libraries + .libraries .try_lock() .expect("RequireContext may not be used from multiple threads"); diff --git a/crates/lune-std/src/globals/require/builtin.rs b/crates/lune-std/src/globals/require/library.rs similarity index 83% rename from crates/lune-std/src/globals/require/builtin.rs rename to crates/lune-std/src/globals/require/library.rs index d75ddf51..b47ea928 100644 --- a/crates/lune-std/src/globals/require/builtin.rs +++ b/crates/lune-std/src/globals/require/library.rs @@ -2,7 +2,7 @@ use mlua::prelude::*; use super::context::*; -pub(super) async fn require<'lua, 'ctx>( +pub(super) fn require<'lua, 'ctx>( lua: &'lua Lua, ctx: &'ctx RequireContext, name: &str, diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 02927975..3876e363 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -6,7 +6,7 @@ mod context; use context::RequireContext; mod alias; -mod builtin; +mod library; mod path; const REQUIRE_IMPL: &str = r" @@ -36,7 +36,7 @@ pub fn create(lua: &Lua) -> LuaResult { */ let require_fn = lua.create_async_function(require)?; - let get_source_fn = lua.create_function(move |lua, _: ()| match lua.inspect_stack(2) { + let get_source_fn = lua.create_function(move |lua, (): ()| match lua.inspect_stack(2) { None => Err(LuaError::runtime( "Failed to get stack info for require source", )), @@ -80,11 +80,8 @@ async fn require<'lua>( .app_data_ref() .expect("Failed to get RequireContext from app data"); - if let Some(builtin_name) = path - .strip_prefix("@lune/") - .map(|name| name.to_ascii_lowercase()) - { - builtin::require(lua, &context, &builtin_name).await + if let Some(builtin_name) = path.strip_prefix("@lune/").map(str::to_ascii_lowercase) { + library::require(lua, &context, &builtin_name) } else if let Some(aliased_path) = path.strip_prefix('@') { let (alias, path) = aliased_path.split_once('/').ok_or(LuaError::runtime( "Require with custom alias must contain '/' delimiter", diff --git a/crates/lune-std/src/globals/require/path.rs b/crates/lune-std/src/globals/require/path.rs index 7e8084f7..1fabebf4 100644 --- a/crates/lune-std/src/globals/require/path.rs +++ b/crates/lune-std/src/globals/require/path.rs @@ -14,7 +14,7 @@ pub(super) async fn require<'lua, 'ctx>( where 'lua: 'ctx, { - let (abs_path, rel_path) = ctx.resolve_paths(source, path)?; + let (abs_path, rel_path) = RequireContext::resolve_paths(source, path)?; require_abs_rel(lua, ctx, abs_path, rel_path).await } diff --git a/crates/lune-utils/Cargo.toml b/crates/lune-utils/Cargo.toml index e3a2fed7..05c7020c 100644 --- a/crates/lune-utils/Cargo.toml +++ b/crates/lune-utils/Cargo.toml @@ -13,7 +13,12 @@ workspace = true [dependencies] mlua = { version = "0.9.7", features = ["async"] } +tokio = { version = "1", default-features = false, features = ["fs"] } + dunce = "1.0" once_cell = "1.17" path-clean = "1.0" pathdiff = "0.2" + +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/crates/lune-utils/src/lib.rs b/crates/lune-utils/src/lib.rs index 38738cc3..4dd2ed9c 100644 --- a/crates/lune-utils/src/lib.rs +++ b/crates/lune-utils/src/lib.rs @@ -1,9 +1,11 @@ #![allow(clippy::cargo_common_metadata)] +mod luaurc; mod table_builder; mod version_string; pub mod path; +pub use self::luaurc::LuauRc; pub use self::table_builder::TableBuilder; pub use self::version_string::get_version_string; diff --git a/crates/lune-utils/src/luaurc.rs b/crates/lune-utils/src/luaurc.rs new file mode 100644 index 00000000..415cc2da --- /dev/null +++ b/crates/lune-utils/src/luaurc.rs @@ -0,0 +1,164 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf, MAIN_SEPARATOR}, + sync::Arc, +}; + +use serde::{Deserialize, Serialize}; +use serde_json::Value as JsonValue; +use tokio::fs::read; + +use crate::path::{clean_path, clean_path_and_make_absolute}; + +const LUAURC_FILE: &str = ".luaurc"; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +enum LuauLanguageMode { + NoCheck, + NonStrict, + Strict, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct LuauRcConfig { + #[serde(skip_serializing_if = "Option::is_none")] + language_mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] + lint: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + lint_errors: Option, + #[serde(skip_serializing_if = "Option::is_none")] + type_errors: Option, + #[serde(skip_serializing_if = "Option::is_none")] + globals: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + paths: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + aliases: Option>, +} + +/** + A deserialized `.luaurc` file. + + Contains utility methods for validating and searching for aliases. +*/ +#[derive(Debug, Clone)] +pub struct LuauRc { + dir: Arc, + config: LuauRcConfig, +} + +impl LuauRc { + /** + Reads a `.luaurc` file from the given directory. + + If the file does not exist, or if it is invalid, this function returns `None`. + */ + pub async fn read(dir: impl AsRef) -> Option { + let dir = clean_path_and_make_absolute(dir); + let path = dir.join(LUAURC_FILE); + let bytes = read(&path).await.ok()?; + let config = serde_json::from_slice(&bytes).ok()?; + Some(Self { + dir: dir.into(), + config, + }) + } + + /** + Reads a `.luaurc` file from the given directory, and then recursively searches + for a `.luaurc` file in the parent directories if a predicate is not satisfied. + + If no `.luaurc` file exists, or if they are invalid, this function returns `None`. + */ + pub async fn read_recursive( + dir: impl AsRef, + mut predicate: impl FnMut(&Self) -> bool, + ) -> Option { + let mut current = clean_path_and_make_absolute(dir); + loop { + if let Some(rc) = Self::read(¤t).await { + if predicate(&rc) { + return Some(rc); + } + } + if let Some(parent) = current.parent() { + current = parent.to_path_buf(); + } else { + return None; + } + } + } + + /** + Validates that the `.luaurc` file is correct. + + This primarily validates aliases since they are not + validated during creation of the [`LuauRc`] struct. + + # Errors + + If an alias key is invalid. + */ + pub fn validate(&self) -> Result<(), String> { + if let Some(aliases) = &self.config.aliases { + for alias in aliases.keys() { + if !is_valid_alias_key(alias) { + return Err(format!("invalid alias key: {alias}")); + } + } + } + Ok(()) + } + + /** + Gets a copy of all aliases in the `.luaurc` file. + + Will return an empty map if there are no aliases. + */ + #[must_use] + pub fn aliases(&self) -> HashMap { + self.config.aliases.clone().unwrap_or_default() + } + + /** + Finds an alias in the `.luaurc` file by name. + + If the alias does not exist, this function returns `None`. + */ + #[must_use] + pub fn find_alias(&self, name: &str) -> Option { + self.config.aliases.as_ref().and_then(|aliases| { + aliases.iter().find_map(|(alias, path)| { + if alias + .trim_end_matches(MAIN_SEPARATOR) + .eq_ignore_ascii_case(name) + && is_valid_alias_key(alias) + { + Some(clean_path(self.dir.join(path))) + } else { + None + } + }) + }) + } +} + +fn is_valid_alias_key(alias: impl AsRef) -> bool { + let alias = alias.as_ref(); + if alias.is_empty() + || alias.starts_with('.') + || alias.starts_with("..") + || alias.chars().any(|c| c == MAIN_SEPARATOR) + { + false // Paths are not valid alias keys + } else { + alias.chars().all(is_valid_alias_char) + } +} + +fn is_valid_alias_char(c: char) -> bool { + c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.' +} From 5a52a51aa2281427b8326f4b2d317312904cfae1 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 21 Apr 2024 22:00:37 +0200 Subject: [PATCH 15/68] Fix version string using lune-utils version instead of lune version, add inject_globals to lune-std --- crates/lune-std/src/globals/version.rs | 2 +- crates/lune-std/src/lib.rs | 19 +++++++++++++++ crates/lune-utils/src/version_string.rs | 31 ++++++++++++++++--------- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/crates/lune-std/src/globals/version.rs b/crates/lune-std/src/globals/version.rs index 29b4359f..d1ff4553 100644 --- a/crates/lune-std/src/globals/version.rs +++ b/crates/lune-std/src/globals/version.rs @@ -3,6 +3,6 @@ use mlua::prelude::*; use lune_utils::get_version_string; pub fn create(lua: &Lua) -> LuaResult { - let s = get_version_string().to_string(); + let s = get_version_string(env!("CARGO_PKG_VERSION")); lua.create_string(s)?.into_lua(lua) } diff --git a/crates/lune-std/src/lib.rs b/crates/lune-std/src/lib.rs index be7ff9b3..f5d5c815 100644 --- a/crates/lune-std/src/lib.rs +++ b/crates/lune-std/src/lib.rs @@ -1,8 +1,27 @@ #![allow(clippy::cargo_common_metadata)] +use mlua::prelude::*; + mod global; mod globals; mod library; pub use self::global::LuneStandardGlobal; pub use self::library::LuneStandardLibrary; + +/** + Injects all standard globals into the given Lua state / VM. + + This includes all enabled standard libraries, which can + be used from Lua with `require("@lune/library-name")`. + + # Errors + + Errors when out of memory, or if *default* Lua globals are missing. +*/ +pub fn inject_globals(lua: &Lua) -> LuaResult<()> { + for global in LuneStandardGlobal::ALL { + lua.globals().set(global.name(), global.create(lua)?)?; + } + Ok(()) +} diff --git a/crates/lune-utils/src/version_string.rs b/crates/lune-utils/src/version_string.rs index 73ba3a06..6c4bbcf0 100644 --- a/crates/lune-utils/src/version_string.rs +++ b/crates/lune-utils/src/version_string.rs @@ -3,20 +3,32 @@ use std::sync::Arc; use mlua::prelude::*; use once_cell::sync::Lazy; -static VERSION_STRING: Lazy> = Lazy::new(create_version_string); +static LUAU_VERSION: Lazy> = Lazy::new(create_luau_version_string); /** - Returns the current Lune version string, in the format `Lune x.y.z+luau`. + Returns a Lune version string, in the format `Lune x.y.z+luau`. - This version string is strongly guaranteed to follow the above - format and may safely be used for parsing & version comparisons. + The version string passed should be the version of the Lune runtime, + obtained from `env!("CARGO_PKG_VERSION")` or a similar mechanism. + + # Panics + + Panics if the version string is empty or contains invalid characters. */ #[must_use] -pub fn get_version_string() -> Arc { - Arc::clone(&VERSION_STRING) +pub fn get_version_string(lune_version: impl AsRef) -> String { + let lune_version = lune_version.as_ref(); + + assert!(!lune_version.is_empty(), "Lune version string is empty"); + assert!( + lune_version.chars().all(is_valid_version_char), + "Lune version string contains invalid characters" + ); + + format!("Lune {lune_version}+{}", *LUAU_VERSION) } -fn create_version_string() -> Arc { +fn create_luau_version_string() -> Arc { // Extract the current Luau version from a fresh Lua state / VM that can't be accessed externally. let luau_version_full = { let temp_lua = Lua::new(); @@ -55,10 +67,7 @@ fn create_version_string() -> Arc { ) } - Arc::new(format!( - "Lune {}+{luau_version_noprefix}", - env!("CARGO_PKG_VERSION") - )) + luau_version_noprefix.to_string().into() } fn is_valid_version_char(c: char) -> bool { From f51a4a0f9e8e051dc841b59fcd2d49c09cd61ba8 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 22 Apr 2024 12:39:23 +0200 Subject: [PATCH 16/68] Reimplement stdio color and style apis, more strict types and helpful errors --- crates/lune-std-stdio/src/lib.rs | 12 +- crates/lune-std-stdio/src/style_and_color.rs | 195 +++++++++++++++++++ 2 files changed, 201 insertions(+), 6 deletions(-) create mode 100644 crates/lune-std-stdio/src/style_and_color.rs diff --git a/crates/lune-std-stdio/src/lib.rs b/crates/lune-std-stdio/src/lib.rs index b1f9d7ad..3c7c487e 100644 --- a/crates/lune-std-stdio/src/lib.rs +++ b/crates/lune-std-stdio/src/lib.rs @@ -8,8 +8,10 @@ use tokio::io::{stderr, stdin, stdout, AsyncReadExt, AsyncWriteExt}; use lune_utils::TableBuilder; mod prompt; +mod style_and_color; use self::prompt::{prompt, PromptOptions, PromptResult}; +use self::style_and_color::{ColorKind, StyleKind}; /** Creates the `stdio` standard library module. @@ -30,14 +32,12 @@ pub fn module(lua: &Lua) -> LuaResult { .build_readonly() } -fn stdio_color(_: &Lua, _color: String) -> LuaResult { - // TODO: Migrate from old crate - unimplemented!() +fn stdio_color(lua: &Lua, color: ColorKind) -> LuaResult { + color.ansi_escape_sequence().into_lua(lua) } -fn stdio_style(_: &Lua, _color: String) -> LuaResult { - // TODO: Migrate from old crate - unimplemented!() +fn stdio_style(lua: &Lua, style: StyleKind) -> LuaResult { + style.ansi_escape_sequence().into_lua(lua) } fn stdio_format(_: &Lua, _args: LuaMultiValue) -> LuaResult { diff --git a/crates/lune-std-stdio/src/style_and_color.rs b/crates/lune-std-stdio/src/style_and_color.rs new file mode 100644 index 00000000..4080118a --- /dev/null +++ b/crates/lune-std-stdio/src/style_and_color.rs @@ -0,0 +1,195 @@ +use std::str::FromStr; + +use mlua::prelude::*; + +const ESCAPE_SEQ_RESET: &str = "\x1b[0m"; + +/** + A color kind supported by the `stdio` standard library. +*/ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ColorKind { + Reset, + Black, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + White, +} + +impl ColorKind { + pub const ALL: [Self; 9] = [ + Self::Reset, + Self::Black, + Self::Red, + Self::Green, + Self::Yellow, + Self::Blue, + Self::Magenta, + Self::Cyan, + Self::White, + ]; + + /** + Returns the human-friendly name of this color kind. + */ + pub fn name(self) -> &'static str { + match self { + Self::Reset => "reset", + Self::Black => "black", + Self::Red => "red", + Self::Green => "green", + Self::Yellow => "yellow", + Self::Blue => "blue", + Self::Magenta => "magenta", + Self::Cyan => "cyan", + Self::White => "white", + } + } + + /** + Returns the ANSI escape sequence for the color kind. + */ + pub fn ansi_escape_sequence(self) -> &'static str { + match self { + Self::Reset => ESCAPE_SEQ_RESET, + Self::Black => "\x1b[30m", + Self::Red => "\x1b[31m", + Self::Green => "\x1b[32m", + Self::Yellow => "\x1b[33m", + Self::Blue => "\x1b[34m", + Self::Magenta => "\x1b[35m", + Self::Cyan => "\x1b[36m", + Self::White => "\x1b[37m", + } + } +} + +impl FromStr for ColorKind { + type Err = (); + fn from_str(s: &str) -> Result { + Ok(match s.trim().to_ascii_lowercase().as_str() { + "reset" => Self::Reset, + "black" => Self::Black, + "red" => Self::Red, + "green" => Self::Green, + "yellow" => Self::Yellow, + "blue" => Self::Blue, + // NOTE: Previous versions of Lune had this color as "purple" instead + // of "magenta", so we keep this here for backwards compatibility. + "magenta" | "purple" => Self::Magenta, + "cyan" => Self::Cyan, + "white" => Self::White, + _ => return Err(()), + }) + } +} + +impl FromLua<'_> for ColorKind { + fn from_lua(value: LuaValue, _: &Lua) -> LuaResult { + if let LuaValue::String(s) = value { + let s = s.to_str()?; + match s.parse() { + Ok(color) => Ok(color), + Err(()) => Err(LuaError::FromLuaConversionError { + from: "string", + to: "ColorKind", + message: Some(format!( + "Invalid color kind '{s}'\nValid kinds are: {}", + Self::ALL + .iter() + .map(|kind| kind.name()) + .collect::>() + .join(", ") + )), + }), + } + } else { + Err(LuaError::FromLuaConversionError { + from: value.type_name(), + to: "ColorKind", + message: None, + }) + } + } +} + +/** + A style kind supported by the `stdio` standard library. +*/ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum StyleKind { + Reset, + Bold, + Dim, +} + +impl StyleKind { + pub const ALL: [Self; 3] = [Self::Reset, Self::Bold, Self::Dim]; + + /** + Returns the human-friendly name for this style kind. + */ + pub fn name(self) -> &'static str { + match self { + Self::Reset => "reset", + Self::Bold => "bold", + Self::Dim => "dim", + } + } + + /** + Returns the ANSI escape sequence for this style kind. + */ + pub fn ansi_escape_sequence(self) -> &'static str { + match self { + Self::Reset => ESCAPE_SEQ_RESET, + Self::Bold => "\x1b[1m", + Self::Dim => "\x1b[2m", + } + } +} + +impl FromStr for StyleKind { + type Err = (); + fn from_str(s: &str) -> Result { + Ok(match s.trim().to_ascii_lowercase().as_str() { + "reset" => Self::Reset, + "bold" => Self::Bold, + "dim" => Self::Dim, + _ => return Err(()), + }) + } +} + +impl FromLua<'_> for StyleKind { + fn from_lua(value: LuaValue, _: &Lua) -> LuaResult { + if let LuaValue::String(s) = value { + let s = s.to_str()?; + match s.parse() { + Ok(style) => Ok(style), + Err(()) => Err(LuaError::FromLuaConversionError { + from: "string", + to: "StyleKind", + message: Some(format!( + "Invalid style kind '{s}'\nValid kinds are: {}", + Self::ALL + .iter() + .map(|kind| kind.name()) + .collect::>() + .join(", ") + )), + }), + } + } else { + Err(LuaError::FromLuaConversionError { + from: value.type_name(), + to: "StyleKind", + message: None, + }) + } + } +} From 5ad6b9a670a48e0d4ada78baec0f379e7859b7ea Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 22 Apr 2024 12:42:49 +0200 Subject: [PATCH 17/68] Start lune std and utils crate versions at 0.1.0 --- Cargo.lock | 24 ++++++++++++------------ crates/lune-std-datetime/Cargo.toml | 4 ++-- crates/lune-std-fs/Cargo.toml | 4 ++-- crates/lune-std-luau/Cargo.toml | 4 ++-- crates/lune-std-net/Cargo.toml | 4 ++-- crates/lune-std-process/Cargo.toml | 4 ++-- crates/lune-std-regex/Cargo.toml | 4 ++-- crates/lune-std-roblox/Cargo.toml | 4 ++-- crates/lune-std-serde/Cargo.toml | 4 ++-- crates/lune-std-stdio/Cargo.toml | 4 ++-- crates/lune-std-task/Cargo.toml | 4 ++-- crates/lune-std/Cargo.toml | 26 +++++++++++++------------- crates/lune-utils/Cargo.toml | 2 +- 13 files changed, 46 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c67588b..ca375175 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1539,7 +1539,7 @@ dependencies = [ [[package]] name = "lune-std" -version = "0.8.3" +version = "0.1.0" dependencies = [ "lune-std-datetime", "lune-std-fs", @@ -1559,7 +1559,7 @@ dependencies = [ [[package]] name = "lune-std-datetime" -version = "0.8.3" +version = "0.1.0" dependencies = [ "lune-utils", "mlua", @@ -1567,7 +1567,7 @@ dependencies = [ [[package]] name = "lune-std-fs" -version = "0.8.3" +version = "0.1.0" dependencies = [ "lune-utils", "mlua", @@ -1575,7 +1575,7 @@ dependencies = [ [[package]] name = "lune-std-luau" -version = "0.8.3" +version = "0.1.0" dependencies = [ "lune-utils", "mlua", @@ -1583,7 +1583,7 @@ dependencies = [ [[package]] name = "lune-std-net" -version = "0.8.3" +version = "0.1.0" dependencies = [ "lune-utils", "mlua", @@ -1591,7 +1591,7 @@ dependencies = [ [[package]] name = "lune-std-process" -version = "0.8.3" +version = "0.1.0" dependencies = [ "lune-utils", "mlua", @@ -1599,7 +1599,7 @@ dependencies = [ [[package]] name = "lune-std-regex" -version = "0.8.3" +version = "0.1.0" dependencies = [ "lune-utils", "mlua", @@ -1607,7 +1607,7 @@ dependencies = [ [[package]] name = "lune-std-roblox" -version = "0.8.3" +version = "0.1.0" dependencies = [ "lune-utils", "mlua", @@ -1615,7 +1615,7 @@ dependencies = [ [[package]] name = "lune-std-serde" -version = "0.8.3" +version = "0.1.0" dependencies = [ "lune-utils", "mlua", @@ -1623,7 +1623,7 @@ dependencies = [ [[package]] name = "lune-std-stdio" -version = "0.8.3" +version = "0.1.0" dependencies = [ "dialoguer", "lune-utils", @@ -1634,7 +1634,7 @@ dependencies = [ [[package]] name = "lune-std-task" -version = "0.8.3" +version = "0.1.0" dependencies = [ "lune-utils", "mlua", @@ -1644,7 +1644,7 @@ dependencies = [ [[package]] name = "lune-utils" -version = "0.8.3" +version = "0.1.0" dependencies = [ "dunce", "mlua", diff --git a/crates/lune-std-datetime/Cargo.toml b/crates/lune-std-datetime/Cargo.toml index 043853b5..e5ca3e43 100644 --- a/crates/lune-std-datetime/Cargo.toml +++ b/crates/lune-std-datetime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std-datetime" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -13,4 +13,4 @@ workspace = true [dependencies] mlua = "0.9.7" -lune-utils = { version = "0.8.3", path = "../lune-utils" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-fs/Cargo.toml b/crates/lune-std-fs/Cargo.toml index cbb2fccb..a28acd51 100644 --- a/crates/lune-std-fs/Cargo.toml +++ b/crates/lune-std-fs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std-fs" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -13,4 +13,4 @@ workspace = true [dependencies] mlua = "0.9.7" -lune-utils = { version = "0.8.3", path = "../lune-utils" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-luau/Cargo.toml b/crates/lune-std-luau/Cargo.toml index dcd57c0a..b651cea0 100644 --- a/crates/lune-std-luau/Cargo.toml +++ b/crates/lune-std-luau/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std-luau" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -13,4 +13,4 @@ workspace = true [dependencies] mlua = "0.9.7" -lune-utils = { version = "0.8.3", path = "../lune-utils" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-net/Cargo.toml b/crates/lune-std-net/Cargo.toml index ff106486..c28594d8 100644 --- a/crates/lune-std-net/Cargo.toml +++ b/crates/lune-std-net/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std-net" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -13,4 +13,4 @@ workspace = true [dependencies] mlua = "0.9.7" -lune-utils = { version = "0.8.3", path = "../lune-utils" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-process/Cargo.toml b/crates/lune-std-process/Cargo.toml index 7676d24e..9365c7f4 100644 --- a/crates/lune-std-process/Cargo.toml +++ b/crates/lune-std-process/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std-process" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -13,4 +13,4 @@ workspace = true [dependencies] mlua = "0.9.7" -lune-utils = { version = "0.8.3", path = "../lune-utils" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-regex/Cargo.toml b/crates/lune-std-regex/Cargo.toml index a8e2ecdf..d79ee33e 100644 --- a/crates/lune-std-regex/Cargo.toml +++ b/crates/lune-std-regex/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std-regex" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -13,4 +13,4 @@ workspace = true [dependencies] mlua = "0.9.7" -lune-utils = { version = "0.8.3", path = "../lune-utils" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-roblox/Cargo.toml b/crates/lune-std-roblox/Cargo.toml index d84a73c1..bb8ec8b9 100644 --- a/crates/lune-std-roblox/Cargo.toml +++ b/crates/lune-std-roblox/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std-roblox" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -13,4 +13,4 @@ workspace = true [dependencies] mlua = "0.9.7" -lune-utils = { version = "0.8.3", path = "../lune-utils" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-serde/Cargo.toml b/crates/lune-std-serde/Cargo.toml index f34b1eae..f5d1ca27 100644 --- a/crates/lune-std-serde/Cargo.toml +++ b/crates/lune-std-serde/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std-serde" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -13,4 +13,4 @@ workspace = true [dependencies] mlua = "0.9.7" -lune-utils = { version = "0.8.3", path = "../lune-utils" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-stdio/Cargo.toml b/crates/lune-std-stdio/Cargo.toml index c51398fa..c078a632 100644 --- a/crates/lune-std-stdio/Cargo.toml +++ b/crates/lune-std-stdio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std-stdio" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -17,4 +17,4 @@ mlua-luau-scheduler = "0.0.1" tokio = { version = "1", default-features = false } -lune-utils = { version = "0.8.3", path = "../lune-utils" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-task/Cargo.toml b/crates/lune-std-task/Cargo.toml index 40c954c0..66db85f1 100644 --- a/crates/lune-std-task/Cargo.toml +++ b/crates/lune-std-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std-task" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -16,4 +16,4 @@ mlua-luau-scheduler = "0.0.1" tokio = { version = "1", default-features = false, features = ["time"] } -lune-utils = { version = "0.8.3", path = "../lune-utils" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std/Cargo.toml b/crates/lune-std/Cargo.toml index 2293e35a..7168be16 100644 --- a/crates/lune-std/Cargo.toml +++ b/crates/lune-std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-std" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" @@ -41,15 +41,15 @@ mlua-luau-scheduler = "0.0.1" tokio = { version = "1", default-features = false, features = ["fs"] } -lune-utils = { version = "0.8.3", path = "../lune-utils" } - -lune-std-datetime = { optional = true, version = "0.8.3", path = "../lune-std-datetime" } -lune-std-fs = { optional = true, version = "0.8.3", path = "../lune-std-fs" } -lune-std-luau = { optional = true, version = "0.8.3", path = "../lune-std-luau" } -lune-std-net = { optional = true, version = "0.8.3", path = "../lune-std-net" } -lune-std-process = { optional = true, version = "0.8.3", path = "../lune-std-process" } -lune-std-regex = { optional = true, version = "0.8.3", path = "../lune-std-regex" } -lune-std-roblox = { optional = true, version = "0.8.3", path = "../lune-std-roblox" } -lune-std-serde = { optional = true, version = "0.8.3", path = "../lune-std-serde" } -lune-std-stdio = { optional = true, version = "0.8.3", path = "../lune-std-stdio" } -lune-std-task = { optional = true, version = "0.8.3", path = "../lune-std-task" } +lune-utils = { version = "0.1.0", path = "../lune-utils" } + +lune-std-datetime = { optional = true, version = "0.1.0", path = "../lune-std-datetime" } +lune-std-fs = { optional = true, version = "0.1.0", path = "../lune-std-fs" } +lune-std-luau = { optional = true, version = "0.1.0", path = "../lune-std-luau" } +lune-std-net = { optional = true, version = "0.1.0", path = "../lune-std-net" } +lune-std-process = { optional = true, version = "0.1.0", path = "../lune-std-process" } +lune-std-regex = { optional = true, version = "0.1.0", path = "../lune-std-regex" } +lune-std-roblox = { optional = true, version = "0.1.0", path = "../lune-std-roblox" } +lune-std-serde = { optional = true, version = "0.1.0", path = "../lune-std-serde" } +lune-std-stdio = { optional = true, version = "0.1.0", path = "../lune-std-stdio" } +lune-std-task = { optional = true, version = "0.1.0", path = "../lune-std-task" } diff --git a/crates/lune-utils/Cargo.toml b/crates/lune-utils/Cargo.toml index 05c7020c..0b46d304 100644 --- a/crates/lune-utils/Cargo.toml +++ b/crates/lune-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lune-utils" -version = "0.8.3" +version = "0.1.0" edition = "2021" license = "MPL-2.0" From b04c0c209d927ba3419469024142c3089d42332d Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 22 Apr 2024 12:52:54 +0200 Subject: [PATCH 18/68] Start implementing fmt module in lune-utils --- Cargo.lock | 1 + crates/lune-utils/Cargo.toml | 1 + crates/lune-utils/src/fmt/label.rs | 48 ++++++++++++++++++++++++++++++ crates/lune-utils/src/fmt/mod.rs | 3 ++ crates/lune-utils/src/lib.rs | 1 + 5 files changed, 54 insertions(+) create mode 100644 crates/lune-utils/src/fmt/label.rs create mode 100644 crates/lune-utils/src/fmt/mod.rs diff --git a/Cargo.lock b/Cargo.lock index ca375175..c09a251f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1646,6 +1646,7 @@ dependencies = [ name = "lune-utils" version = "0.1.0" dependencies = [ + "console", "dunce", "mlua", "once_cell", diff --git a/crates/lune-utils/Cargo.toml b/crates/lune-utils/Cargo.toml index 0b46d304..27eb317d 100644 --- a/crates/lune-utils/Cargo.toml +++ b/crates/lune-utils/Cargo.toml @@ -15,6 +15,7 @@ mlua = { version = "0.9.7", features = ["async"] } tokio = { version = "1", default-features = false, features = ["fs"] } +console = "0.15" dunce = "1.0" once_cell = "1.17" path-clean = "1.0" diff --git a/crates/lune-utils/src/fmt/label.rs b/crates/lune-utils/src/fmt/label.rs new file mode 100644 index 00000000..aa5d84ab --- /dev/null +++ b/crates/lune-utils/src/fmt/label.rs @@ -0,0 +1,48 @@ +use std::fmt; + +use console::{style, Color}; + +#[derive(Debug, Clone, Copy)] +pub enum Label { + Info, + Warn, + Error, +} + +impl Label { + /** + Returns the name of the label in lowercase. + */ + #[must_use] + pub fn name(&self) -> &str { + match self { + Self::Info => "info", + Self::Warn => "warn", + Self::Error => "error", + } + } + + /** + Returns the color of the label. + */ + #[must_use] + pub fn color(&self) -> Color { + match self { + Self::Info => Color::Blue, + Self::Warn => Color::Yellow, + Self::Error => Color::Red, + } + } +} + +impl fmt::Display for Label { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}{}{}", + style("[").dim(), + style(self.name().to_ascii_uppercase()).fg(self.color()), + style("]").dim() + ) + } +} diff --git a/crates/lune-utils/src/fmt/mod.rs b/crates/lune-utils/src/fmt/mod.rs new file mode 100644 index 00000000..340cf5b3 --- /dev/null +++ b/crates/lune-utils/src/fmt/mod.rs @@ -0,0 +1,3 @@ +mod label; + +pub use self::label::Label; diff --git a/crates/lune-utils/src/lib.rs b/crates/lune-utils/src/lib.rs index 4dd2ed9c..3357fe17 100644 --- a/crates/lune-utils/src/lib.rs +++ b/crates/lune-utils/src/lib.rs @@ -4,6 +4,7 @@ mod luaurc; mod table_builder; mod version_string; +pub mod fmt; pub mod path; pub use self::luaurc::LuauRc; From 896121ab0d06a82d81222b33463e9156fe0cf1f6 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 22 Apr 2024 13:00:55 +0200 Subject: [PATCH 19/68] Add example usage doc to label enum --- crates/lune-utils/src/fmt/label.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/lune-utils/src/fmt/label.rs b/crates/lune-utils/src/fmt/label.rs index aa5d84ab..5e2e290d 100644 --- a/crates/lune-utils/src/fmt/label.rs +++ b/crates/lune-utils/src/fmt/label.rs @@ -2,6 +2,24 @@ use std::fmt; use console::{style, Color}; +/** + Label enum used for consistent output formatting throughout Lune. + + # Example usage + + ```rs + use lune_utils::fmt::Label; + + println!("{} This is an info message", Label::Info); + // [INFO] This is an info message + + println!("{} This is a warning message", Label::Warn); + // [WARN] This is a warning message + + println!("{} This is an error message", Label::Error); + // [ERROR] This is an error message + ``` +*/ #[derive(Debug, Clone, Copy)] pub enum Label { Info, @@ -11,14 +29,14 @@ pub enum Label { impl Label { /** - Returns the name of the label in lowercase. + Returns the name of the label in all uppercase. */ #[must_use] pub fn name(&self) -> &str { match self { - Self::Info => "info", - Self::Warn => "warn", - Self::Error => "error", + Self::Info => "INFO", + Self::Warn => "WARN", + Self::Error => "ERROR", } } @@ -41,7 +59,7 @@ impl fmt::Display for Label { f, "{}{}{}", style("[").dim(), - style(self.name().to_ascii_uppercase()).fg(self.color()), + style(self.name()).fg(self.color()), style("]").dim() ) } From 4e00bcf0403481d953cc24f7edffc043a3d09400 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 22 Apr 2024 13:32:52 +0200 Subject: [PATCH 20/68] Start work on error parser in lune-utils --- crates/lune-utils/src/fmt/error.rs | 85 ++++++++++++++++++++++++++++++ crates/lune-utils/src/fmt/mod.rs | 2 + 2 files changed, 87 insertions(+) create mode 100644 crates/lune-utils/src/fmt/error.rs diff --git a/crates/lune-utils/src/fmt/error.rs b/crates/lune-utils/src/fmt/error.rs new file mode 100644 index 00000000..2ab61dbd --- /dev/null +++ b/crates/lune-utils/src/fmt/error.rs @@ -0,0 +1,85 @@ +use mlua::prelude::*; + +/** + Source of a stack trace line parsed from a [`LuaError`]. +*/ +#[derive(Debug, Clone, Copy)] +pub enum StackTraceSource { + /// Error originated from a C function. + C, + /// Error originated from a Rust function. + Rust, + /// Error originated from [`mlua`]. + Mlua, + /// Error originated from a Lua (user) function. + User, +} + +/** + Stack trace line parsed from a [`LuaError`]. +*/ +#[derive(Debug, Clone)] +pub struct StackTraceLine { + source: StackTraceSource, + path: Option, + line_number: Option, + function_name: Option, +} + +impl StackTraceLine { + #[must_use] + pub fn source(&self) -> StackTraceSource { + self.source + } + + #[must_use] + pub fn path(&self) -> Option<&str> { + self.path.as_deref() + } + + #[must_use] + pub fn line_number(&self) -> Option { + self.line_number + } + + #[must_use] + pub fn function_name(&self) -> Option<&str> { + self.function_name.as_deref() + } +} + +/** + Stack trace parsed from a [`LuaError`]. +*/ +#[derive(Debug, Clone)] +pub struct StackTrace { + lines: Vec, +} + +impl StackTrace { + #[must_use] + pub fn lines(&self) -> &[StackTraceLine] { + &self.lines + } +} + +/** + Error components parsed from a [`LuaError`]. +*/ +#[derive(Debug, Clone)] +pub struct ErrorComponents { + message: String, + trace: StackTrace, +} + +impl ErrorComponents { + #[must_use] + pub fn message(&self) -> &str { + &self.message + } + + #[must_use] + pub fn trace(&self) -> &StackTrace { + &self.trace + } +} diff --git a/crates/lune-utils/src/fmt/mod.rs b/crates/lune-utils/src/fmt/mod.rs index 340cf5b3..4d1e86a6 100644 --- a/crates/lune-utils/src/fmt/mod.rs +++ b/crates/lune-utils/src/fmt/mod.rs @@ -1,3 +1,5 @@ +mod error; mod label; +pub use self::error::{ErrorComponents, StackTrace, StackTraceLine, StackTraceSource}; pub use self::label::Label; From 7c702e4d349bf244ea44433d16a30f4ce7f689f4 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 22 Apr 2024 13:36:39 +0200 Subject: [PATCH 21/68] Enable mlua luau feature in all crates --- crates/lune-std-datetime/Cargo.toml | 2 +- crates/lune-std-fs/Cargo.toml | 2 +- crates/lune-std-luau/Cargo.toml | 2 +- crates/lune-std-net/Cargo.toml | 2 +- crates/lune-std-process/Cargo.toml | 2 +- crates/lune-std-regex/Cargo.toml | 2 +- crates/lune-std-roblox/Cargo.toml | 2 +- crates/lune-std-serde/Cargo.toml | 2 +- crates/lune-std-stdio/Cargo.toml | 2 +- crates/lune-std-task/Cargo.toml | 2 +- crates/lune-std/Cargo.toml | 2 +- crates/lune-utils/Cargo.toml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/lune-std-datetime/Cargo.toml b/crates/lune-std-datetime/Cargo.toml index e5ca3e43..07ed2727 100644 --- a/crates/lune-std-datetime/Cargo.toml +++ b/crates/lune-std-datetime/Cargo.toml @@ -11,6 +11,6 @@ path = "src/lib.rs" workspace = true [dependencies] -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-fs/Cargo.toml b/crates/lune-std-fs/Cargo.toml index a28acd51..253d96f8 100644 --- a/crates/lune-std-fs/Cargo.toml +++ b/crates/lune-std-fs/Cargo.toml @@ -11,6 +11,6 @@ path = "src/lib.rs" workspace = true [dependencies] -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-luau/Cargo.toml b/crates/lune-std-luau/Cargo.toml index b651cea0..d01584cf 100644 --- a/crates/lune-std-luau/Cargo.toml +++ b/crates/lune-std-luau/Cargo.toml @@ -11,6 +11,6 @@ path = "src/lib.rs" workspace = true [dependencies] -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-net/Cargo.toml b/crates/lune-std-net/Cargo.toml index c28594d8..4e28dbff 100644 --- a/crates/lune-std-net/Cargo.toml +++ b/crates/lune-std-net/Cargo.toml @@ -11,6 +11,6 @@ path = "src/lib.rs" workspace = true [dependencies] -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-process/Cargo.toml b/crates/lune-std-process/Cargo.toml index 9365c7f4..e44b6837 100644 --- a/crates/lune-std-process/Cargo.toml +++ b/crates/lune-std-process/Cargo.toml @@ -11,6 +11,6 @@ path = "src/lib.rs" workspace = true [dependencies] -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-regex/Cargo.toml b/crates/lune-std-regex/Cargo.toml index d79ee33e..b4821c75 100644 --- a/crates/lune-std-regex/Cargo.toml +++ b/crates/lune-std-regex/Cargo.toml @@ -11,6 +11,6 @@ path = "src/lib.rs" workspace = true [dependencies] -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-roblox/Cargo.toml b/crates/lune-std-roblox/Cargo.toml index bb8ec8b9..58a4200c 100644 --- a/crates/lune-std-roblox/Cargo.toml +++ b/crates/lune-std-roblox/Cargo.toml @@ -11,6 +11,6 @@ path = "src/lib.rs" workspace = true [dependencies] -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-serde/Cargo.toml b/crates/lune-std-serde/Cargo.toml index f5d1ca27..96afde85 100644 --- a/crates/lune-std-serde/Cargo.toml +++ b/crates/lune-std-serde/Cargo.toml @@ -11,6 +11,6 @@ path = "src/lib.rs" workspace = true [dependencies] -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } lune-utils = { version = "0.1.0", path = "../lune-utils" } diff --git a/crates/lune-std-stdio/Cargo.toml b/crates/lune-std-stdio/Cargo.toml index c078a632..a160fc81 100644 --- a/crates/lune-std-stdio/Cargo.toml +++ b/crates/lune-std-stdio/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] dialoguer = "0.11" -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } mlua-luau-scheduler = "0.0.1" tokio = { version = "1", default-features = false } diff --git a/crates/lune-std-task/Cargo.toml b/crates/lune-std-task/Cargo.toml index 66db85f1..6b8f77e9 100644 --- a/crates/lune-std-task/Cargo.toml +++ b/crates/lune-std-task/Cargo.toml @@ -11,7 +11,7 @@ path = "src/lib.rs" workspace = true [dependencies] -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } mlua-luau-scheduler = "0.0.1" tokio = { version = "1", default-features = false, features = ["time"] } diff --git a/crates/lune-std/Cargo.toml b/crates/lune-std/Cargo.toml index 7168be16..d85234ab 100644 --- a/crates/lune-std/Cargo.toml +++ b/crates/lune-std/Cargo.toml @@ -36,7 +36,7 @@ stdio = ["dep:lune-std-stdio"] task = ["dep:lune-std-task"] [dependencies] -mlua = "0.9.7" +mlua = { version = "0.9.7", features = ["luau"] } mlua-luau-scheduler = "0.0.1" tokio = { version = "1", default-features = false, features = ["fs"] } diff --git a/crates/lune-utils/Cargo.toml b/crates/lune-utils/Cargo.toml index 27eb317d..38f3fb4c 100644 --- a/crates/lune-utils/Cargo.toml +++ b/crates/lune-utils/Cargo.toml @@ -11,7 +11,7 @@ path = "src/lib.rs" workspace = true [dependencies] -mlua = { version = "0.9.7", features = ["async"] } +mlua = { version = "0.9.7", features = ["luau", "async"] } tokio = { version = "1", default-features = false, features = ["fs"] } From 6dad31df14311273044cd95042fe4a2b498756fc Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 22 Apr 2024 15:42:24 +0200 Subject: [PATCH 22/68] Initial working version of new error parser and display --- crates/lune-utils/src/fmt/error.rs | 85 --------- crates/lune-utils/src/fmt/error/components.rs | 152 ++++++++++++++++ crates/lune-utils/src/fmt/error/mod.rs | 5 + .../lune-utils/src/fmt/error/stack_trace.rs | 170 ++++++++++++++++++ crates/lune-utils/src/fmt/mod.rs | 3 + crates/lune-utils/src/fmt/tests.rs | 85 +++++++++ 6 files changed, 415 insertions(+), 85 deletions(-) delete mode 100644 crates/lune-utils/src/fmt/error.rs create mode 100644 crates/lune-utils/src/fmt/error/components.rs create mode 100644 crates/lune-utils/src/fmt/error/mod.rs create mode 100644 crates/lune-utils/src/fmt/error/stack_trace.rs create mode 100644 crates/lune-utils/src/fmt/tests.rs diff --git a/crates/lune-utils/src/fmt/error.rs b/crates/lune-utils/src/fmt/error.rs deleted file mode 100644 index 2ab61dbd..00000000 --- a/crates/lune-utils/src/fmt/error.rs +++ /dev/null @@ -1,85 +0,0 @@ -use mlua::prelude::*; - -/** - Source of a stack trace line parsed from a [`LuaError`]. -*/ -#[derive(Debug, Clone, Copy)] -pub enum StackTraceSource { - /// Error originated from a C function. - C, - /// Error originated from a Rust function. - Rust, - /// Error originated from [`mlua`]. - Mlua, - /// Error originated from a Lua (user) function. - User, -} - -/** - Stack trace line parsed from a [`LuaError`]. -*/ -#[derive(Debug, Clone)] -pub struct StackTraceLine { - source: StackTraceSource, - path: Option, - line_number: Option, - function_name: Option, -} - -impl StackTraceLine { - #[must_use] - pub fn source(&self) -> StackTraceSource { - self.source - } - - #[must_use] - pub fn path(&self) -> Option<&str> { - self.path.as_deref() - } - - #[must_use] - pub fn line_number(&self) -> Option { - self.line_number - } - - #[must_use] - pub fn function_name(&self) -> Option<&str> { - self.function_name.as_deref() - } -} - -/** - Stack trace parsed from a [`LuaError`]. -*/ -#[derive(Debug, Clone)] -pub struct StackTrace { - lines: Vec, -} - -impl StackTrace { - #[must_use] - pub fn lines(&self) -> &[StackTraceLine] { - &self.lines - } -} - -/** - Error components parsed from a [`LuaError`]. -*/ -#[derive(Debug, Clone)] -pub struct ErrorComponents { - message: String, - trace: StackTrace, -} - -impl ErrorComponents { - #[must_use] - pub fn message(&self) -> &str { - &self.message - } - - #[must_use] - pub fn trace(&self) -> &StackTrace { - &self.trace - } -} diff --git a/crates/lune-utils/src/fmt/error/components.rs b/crates/lune-utils/src/fmt/error/components.rs new file mode 100644 index 00000000..941b8d00 --- /dev/null +++ b/crates/lune-utils/src/fmt/error/components.rs @@ -0,0 +1,152 @@ +use std::fmt; +use std::str::FromStr; +use std::sync::Arc; + +use console::style; +use mlua::prelude::*; +use once_cell::sync::Lazy; + +use super::StackTrace; + +static STYLED_STACK_BEGIN: Lazy = Lazy::new(|| { + format!( + "{}{}{}", + style("[").dim(), + style("Stack Begin").blue(), + style("]").dim() + ) +}); + +static STYLED_STACK_END: Lazy = Lazy::new(|| { + format!( + "{}{}{}", + style("[").dim(), + style("Stack End").blue(), + style("]").dim() + ) +}); + +/** + Error components parsed from a [`LuaError`]. + + Can be used to display a human-friendly error message + and stack trace, in the following Roblox-inspired format: + + ```plaintext + Error message + [Stack Begin] + Stack trace line + Stack trace line + Stack trace line + [Stack End] + ``` +*/ +#[derive(Debug, Default, Clone)] +pub struct ErrorComponents { + messages: Vec, + trace: Option, +} + +impl ErrorComponents { + /** + Returns the error messages. + */ + #[must_use] + pub fn messages(&self) -> &[String] { + &self.messages + } + + /** + Returns the stack trace, if it exists. + */ + #[must_use] + pub fn trace(&self) -> Option<&StackTrace> { + self.trace.as_ref() + } + + /** + Returns `true` if the error has a non-empty stack trace. + + Note that a trace may still *exist*, but it may be empty. + */ + #[must_use] + pub fn has_trace(&self) -> bool { + self.trace + .as_ref() + .is_some_and(|trace| !trace.lines().is_empty()) + } +} + +impl fmt::Display for ErrorComponents { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for message in self.messages() { + writeln!(f, "{message}")?; + } + if self.has_trace() { + let trace = self.trace.as_ref().unwrap(); + writeln!(f, "{}", *STYLED_STACK_BEGIN)?; + for line in trace.lines() { + writeln!(f, "\t{line}")?; + } + writeln!(f, "{}", *STYLED_STACK_END)?; + } + Ok(()) + } +} + +impl From for ErrorComponents { + fn from(error: LuaError) -> Self { + fn lua_error_message(e: &LuaError) -> String { + if let LuaError::RuntimeError(s) = e { + s.to_string() + } else { + e.to_string() + } + } + + fn lua_stack_trace(source: &str) -> Option { + // FUTURE: Preserve a parsing error here somehow? + // Maybe we can emit parsing errors using tracing? + StackTrace::from_str(source).ok() + } + + // Extract any additional "context" messages before the actual error(s) + // The Arc is necessary here because mlua wraps all inner errors in an Arc + let mut error = Arc::new(error); + let mut messages = Vec::new(); + while let LuaError::WithContext { + ref context, + ref cause, + } = *error + { + messages.push(context.to_string()); + error = cause.clone(); + } + + // We will then try to extract any stack trace + let trace = if let LuaError::CallbackError { + ref traceback, + ref cause, + } = *error + { + messages.push(lua_error_message(cause)); + lua_stack_trace(traceback) + } else if let LuaError::RuntimeError(ref s) = *error { + // NOTE: Runtime errors may include tracebacks, but they're + // joined with error messages, so we need to split them out + if let Some(pos) = s.find("stack traceback:") { + let (message, traceback) = s.split_at(pos); + messages.push(message.trim().to_string()); + lua_stack_trace(traceback) + } else { + messages.push(s.to_string()); + None + } + } else { + messages.push(lua_error_message(&error)); + None + }; + + ErrorComponents { messages, trace } + } +} diff --git a/crates/lune-utils/src/fmt/error/mod.rs b/crates/lune-utils/src/fmt/error/mod.rs new file mode 100644 index 00000000..57ac11e3 --- /dev/null +++ b/crates/lune-utils/src/fmt/error/mod.rs @@ -0,0 +1,5 @@ +mod components; +mod stack_trace; + +pub use self::components::ErrorComponents; +pub use self::stack_trace::{StackTrace, StackTraceLine, StackTraceSource}; diff --git a/crates/lune-utils/src/fmt/error/stack_trace.rs b/crates/lune-utils/src/fmt/error/stack_trace.rs new file mode 100644 index 00000000..a33ec9ac --- /dev/null +++ b/crates/lune-utils/src/fmt/error/stack_trace.rs @@ -0,0 +1,170 @@ +use std::fmt; +use std::str::FromStr; + +fn parse_path(s: &str) -> Option<(&str, &str)> { + let path = s.strip_prefix("[string \"")?; + let (path, after) = path.split_once("\"]:")?; + + // Remove line number after any found colon, this may + // exist if the source path is from a rust source file + let path = match path.split_once(':') { + Some((before, _)) => before, + None => path, + }; + + Some((path, after)) +} + +fn parse_function_name(s: &str) -> Option<&str> { + s.strip_prefix("in function '") + .and_then(|s| s.strip_suffix('\'')) +} + +fn parse_line_number(s: &str) -> (Option, &str) { + match s.split_once(':') { + Some((before, after)) => (before.parse::().ok(), after), + None => (None, s), + } +} + +/** + Source of a stack trace line parsed from a [`LuaError`]. +*/ +#[derive(Debug, Default, Clone, Copy)] +pub enum StackTraceSource { + /// Error originated from a C / Rust function. + C, + /// Error originated from a Lua (user) function. + #[default] + Lua, +} + +/** + Stack trace line parsed from a [`LuaError`]. +*/ +#[derive(Debug, Default, Clone)] +pub struct StackTraceLine { + source: StackTraceSource, + path: Option, + line_number: Option, + function_name: Option, +} + +impl StackTraceLine { + /** + Returns the source of the stack trace line. + */ + #[must_use] + pub fn source(&self) -> StackTraceSource { + self.source + } + + /** + Returns the path, if it exists. + */ + #[must_use] + pub fn path(&self) -> Option<&str> { + self.path.as_deref() + } + + /** + Returns the line number, if it exists. + */ + #[must_use] + pub fn line_number(&self) -> Option { + self.line_number + } + + /** + Returns the function name, if it exists. + */ + #[must_use] + pub fn function_name(&self) -> Option<&str> { + self.function_name.as_deref() + } +} + +impl FromStr for StackTraceLine { + type Err = String; + fn from_str(s: &str) -> Result { + if let Some(after) = s.strip_prefix("[C]: ") { + let function_name = parse_function_name(after).map(ToString::to_string); + + Ok(Self { + source: StackTraceSource::C, + path: None, + line_number: None, + function_name, + }) + } else if let Some((path, after)) = parse_path(s) { + let (line_number, after) = parse_line_number(after); + let function_name = parse_function_name(after).map(ToString::to_string); + + Ok(Self { + source: StackTraceSource::Lua, + path: Some(path.to_string()), + line_number, + function_name, + }) + } else { + Err(String::from("unknown format")) + } + } +} + +impl fmt::Display for StackTraceLine { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if matches!(self.source, StackTraceSource::C) { + write!(f, "Script '[C]'")?; + } else { + write!(f, "Script '{}'", self.path.as_deref().unwrap_or("[?]"))?; + if let Some(line_number) = self.line_number { + write!(f, ", Line {line_number}")?; + } + } + if let Some(function_name) = self.function_name.as_deref() { + write!(f, " - function '{function_name}'")?; + } + Ok(()) + } +} + +/** + Stack trace parsed from a [`LuaError`]. +*/ +#[derive(Debug, Default, Clone)] +pub struct StackTrace { + lines: Vec, +} + +impl StackTrace { + /** + Returns the individual stack trace lines. + */ + #[must_use] + pub fn lines(&self) -> &[StackTraceLine] { + &self.lines + } +} + +impl FromStr for StackTrace { + type Err = String; + fn from_str(s: &str) -> Result { + let (_, after) = s + .split_once("stack traceback:") + .ok_or_else(|| String::from("missing 'stack traceback:' prefix"))?; + let lines = after + .trim() + .lines() + .filter_map(|line| { + let line = line.trim(); + if line.is_empty() { + None + } else { + Some(line.parse()) + } + }) + .collect::, _>>()?; + Ok(StackTrace { lines }) + } +} diff --git a/crates/lune-utils/src/fmt/mod.rs b/crates/lune-utils/src/fmt/mod.rs index 4d1e86a6..d3e33114 100644 --- a/crates/lune-utils/src/fmt/mod.rs +++ b/crates/lune-utils/src/fmt/mod.rs @@ -1,5 +1,8 @@ mod error; mod label; +#[cfg(test)] +mod tests; + pub use self::error::{ErrorComponents, StackTrace, StackTraceLine, StackTraceSource}; pub use self::label::Label; diff --git a/crates/lune-utils/src/fmt/tests.rs b/crates/lune-utils/src/fmt/tests.rs new file mode 100644 index 00000000..ff5a5f37 --- /dev/null +++ b/crates/lune-utils/src/fmt/tests.rs @@ -0,0 +1,85 @@ +use mlua::prelude::*; + +use crate::fmt::ErrorComponents; + +fn new_lua_result() -> LuaResult<()> { + let lua = Lua::new(); + + lua.globals() + .set( + "f", + LuaFunction::wrap(|_, (): ()| { + Err::<(), _>(LuaError::runtime("oh no, a runtime error")) + }), + ) + .unwrap(); + + lua.load("f()").set_name("chunk_name").eval() +} + +// Tests for error context stack +mod context { + use super::*; + + #[test] + fn preserves_original() { + let lua_error = new_lua_result().context("additional context").unwrap_err(); + let components = ErrorComponents::from(lua_error); + + assert_eq!(components.messages()[0], "additional context"); + assert_eq!(components.messages()[1], "oh no, a runtime error"); + } + + #[test] + fn preserves_levels() { + // NOTE: The behavior in mlua is to preserve a single level of context + // and not all levels (context gets replaced on each call to `context`) + let lua_error = new_lua_result() + .context("level 1") + .context("level 2") + .context("level 3") + .unwrap_err(); + let components = ErrorComponents::from(lua_error); + + assert_eq!( + components.messages(), + &["level 3", "oh no, a runtime error"] + ); + } +} + +// Tests for error components struct: separated messages + stack trace +mod error_components { + use super::*; + + #[test] + fn message() { + let lua_error = new_lua_result().unwrap_err(); + let components = ErrorComponents::from(lua_error); + + assert_eq!(components.messages()[0], "oh no, a runtime error"); + } + + #[test] + fn stack_begin_end() { + let lua_error = new_lua_result().unwrap_err(); + let formatted = format!("{}", ErrorComponents::from(lua_error)); + + assert!(formatted.contains("Stack Begin")); + assert!(formatted.contains("Stack End")); + } + + #[test] + fn stack_lines() { + let lua_error = new_lua_result().unwrap_err(); + let components = ErrorComponents::from(lua_error); + + let mut lines = components.trace().unwrap().lines().iter(); + let line_1 = lines.next().unwrap().to_string(); + let line_2 = lines.next().unwrap().to_string(); + assert!(lines.next().is_none()); + + assert_eq!(line_1, "Script '[C]' - function 'f'"); + assert_eq!(line_2, "Script 'chunk_name', Line 1"); + } +} From e983b601410e4a8cb7488568b152ed6486c0203b Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 22 Apr 2024 16:41:37 +0200 Subject: [PATCH 23/68] Add mechanism for using custom global version instead of lune-std crate version --- crates/lune-std/src/globals/version.rs | 29 +++++++++++++++++++++++++- crates/lune-std/src/lib.rs | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/lune-std/src/globals/version.rs b/crates/lune-std/src/globals/version.rs index d1ff4553..3eface4c 100644 --- a/crates/lune-std/src/globals/version.rs +++ b/crates/lune-std/src/globals/version.rs @@ -2,7 +2,34 @@ use mlua::prelude::*; use lune_utils::get_version_string; +struct Version(String); + +impl LuaUserData for Version {} + pub fn create(lua: &Lua) -> LuaResult { - let s = get_version_string(env!("CARGO_PKG_VERSION")); + let v = match lua.app_data_ref::() { + Some(v) => v.0.to_string(), + None => env!("CARGO_PKG_VERSION").to_string(), + }; + let s = get_version_string(v); lua.create_string(s)?.into_lua(lua) } + +/** + Overrides the version string to be used by the `_VERSION` global. + + The global will be a string in the format `Lune x.y.z+luau`, + where `x.y.z` is the string passed to this function. + + The version string passed should be the version of the Lune runtime, + obtained from `env!("CARGO_PKG_VERSION")` or a similar mechanism. + + # Panics + + Panics if the version string is empty or contains invalid characters. +*/ +pub fn set_global_version(lua: &Lua, version: impl Into) { + let v = version.into(); + let _ = get_version_string(&v); // Validate version string + lua.set_app_data(Version(v)); +} diff --git a/crates/lune-std/src/lib.rs b/crates/lune-std/src/lib.rs index f5d5c815..4f644271 100644 --- a/crates/lune-std/src/lib.rs +++ b/crates/lune-std/src/lib.rs @@ -7,6 +7,7 @@ mod globals; mod library; pub use self::global::LuneStandardGlobal; +pub use self::globals::version::set_global_version; pub use self::library::LuneStandardLibrary; /** From 3433fb4c4fb54119407912d0c4c8ecd892eb33f0 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Mon, 22 Apr 2024 18:11:32 +0200 Subject: [PATCH 24/68] Initial implementation of new value formatter --- crates/lune-utils/src/fmt/error/mod.rs | 3 + .../lune-utils/src/fmt/{ => error}/tests.rs | 0 crates/lune-utils/src/fmt/mod.rs | 5 +- crates/lune-utils/src/fmt/value/basic.rs | 73 +++++++++++++++ crates/lune-utils/src/fmt/value/config.rs | 48 ++++++++++ .../lune-utils/src/fmt/value/metamethods.rs | 29 ++++++ crates/lune-utils/src/fmt/value/mod.rs | 88 +++++++++++++++++++ crates/lune-utils/src/fmt/value/style.rs | 9 ++ 8 files changed, 252 insertions(+), 3 deletions(-) rename crates/lune-utils/src/fmt/{ => error}/tests.rs (100%) create mode 100644 crates/lune-utils/src/fmt/value/basic.rs create mode 100644 crates/lune-utils/src/fmt/value/config.rs create mode 100644 crates/lune-utils/src/fmt/value/metamethods.rs create mode 100644 crates/lune-utils/src/fmt/value/mod.rs create mode 100644 crates/lune-utils/src/fmt/value/style.rs diff --git a/crates/lune-utils/src/fmt/error/mod.rs b/crates/lune-utils/src/fmt/error/mod.rs index 57ac11e3..00d06586 100644 --- a/crates/lune-utils/src/fmt/error/mod.rs +++ b/crates/lune-utils/src/fmt/error/mod.rs @@ -1,5 +1,8 @@ mod components; mod stack_trace; +#[cfg(test)] +mod tests; + pub use self::components::ErrorComponents; pub use self::stack_trace::{StackTrace, StackTraceLine, StackTraceSource}; diff --git a/crates/lune-utils/src/fmt/tests.rs b/crates/lune-utils/src/fmt/error/tests.rs similarity index 100% rename from crates/lune-utils/src/fmt/tests.rs rename to crates/lune-utils/src/fmt/error/tests.rs diff --git a/crates/lune-utils/src/fmt/mod.rs b/crates/lune-utils/src/fmt/mod.rs index d3e33114..0011febb 100644 --- a/crates/lune-utils/src/fmt/mod.rs +++ b/crates/lune-utils/src/fmt/mod.rs @@ -1,8 +1,7 @@ mod error; mod label; - -#[cfg(test)] -mod tests; +mod value; pub use self::error::{ErrorComponents, StackTrace, StackTraceLine, StackTraceSource}; pub use self::label::Label; +pub use self::value::{pretty_format_multi_value, pretty_format_value, ValueFormatConfig}; diff --git a/crates/lune-utils/src/fmt/value/basic.rs b/crates/lune-utils/src/fmt/value/basic.rs new file mode 100644 index 00000000..5c392ce8 --- /dev/null +++ b/crates/lune-utils/src/fmt/value/basic.rs @@ -0,0 +1,73 @@ +use mlua::prelude::*; + +use super::{ + metamethods::{call_table_tostring_metamethod, call_userdata_tostring_metamethod}, + style::{COLOR_CYAN, COLOR_GREEN, COLOR_MAGENTA, COLOR_YELLOW}, +}; + +const STRING_REPLACEMENTS: &[(&str, &str)] = + &[("\"", r#"\""#), ("\t", r"\t"), ("\r", r"\r"), ("\n", r"\n")]; + +/** + Tries to return the given value as a plain string key. + + A plain string key must: + + - Start with an alphabetic character. + - Only contain alphanumeric characters and underscores. +*/ +pub(crate) fn lua_value_as_plain_string_key(value: &LuaValue) -> Option { + if let LuaValue::String(s) = value { + if let Ok(s) = s.to_str() { + let first_valid = s.chars().next().is_some_and(|c| c.is_ascii_alphabetic()); + let all_valid = s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_'); + if first_valid && all_valid { + return Some(s.to_string()); + } + } + } + None +} + +/** + Formats a Lua value into a pretty string. + + This does not recursively format tables. +*/ +pub(crate) fn format_value_styled(value: &LuaValue) -> String { + match value { + LuaValue::Nil => COLOR_YELLOW.apply_to("nil").to_string(), + LuaValue::Boolean(true) => COLOR_YELLOW.apply_to("true").to_string(), + LuaValue::Boolean(false) => COLOR_YELLOW.apply_to("false").to_string(), + LuaValue::Number(n) => COLOR_CYAN.apply_to(n).to_string(), + LuaValue::Integer(i) => COLOR_CYAN.apply_to(i).to_string(), + LuaValue::String(s) => COLOR_GREEN + .apply_to({ + let mut s = s.to_string_lossy().to_string(); + for (from, to) in STRING_REPLACEMENTS { + s = s.replace(from, to); + } + format!(r#""{s}""#) + }) + .to_string(), + LuaValue::Vector(_) => COLOR_MAGENTA.apply_to("").to_string(), + LuaValue::Thread(_) => COLOR_MAGENTA.apply_to("").to_string(), + LuaValue::Function(_) => COLOR_MAGENTA.apply_to("").to_string(), + LuaValue::LightUserData(_) => COLOR_MAGENTA.apply_to("").to_string(), + LuaValue::UserData(u) => { + if let Some(s) = call_userdata_tostring_metamethod(u) { + s + } else { + COLOR_MAGENTA.apply_to("").to_string() + } + } + LuaValue::Table(t) => { + if let Some(s) = call_table_tostring_metamethod(t) { + s + } else { + COLOR_MAGENTA.apply_to("").to_string() + } + } + _ => COLOR_MAGENTA.apply_to("").to_string(), + } +} diff --git a/crates/lune-utils/src/fmt/value/config.rs b/crates/lune-utils/src/fmt/value/config.rs new file mode 100644 index 00000000..eb7b2388 --- /dev/null +++ b/crates/lune-utils/src/fmt/value/config.rs @@ -0,0 +1,48 @@ +/** + Configuration for formatting values. +*/ +#[derive(Debug, Clone, Copy)] +pub struct ValueFormatConfig { + pub(super) max_depth: usize, + pub(super) colors_enabled: bool, +} + +impl ValueFormatConfig { + /** + Creates a new config with default values. + */ + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /** + Sets the maximum depth to which tables will be formatted. + */ + #[must_use] + pub fn with_max_depth(self, max_depth: usize) -> Self { + Self { max_depth, ..self } + } + + /** + Sets whether colors should be enabled. + + Colors are disabled by default. + */ + #[must_use] + pub fn with_colors_enabled(self, colors_enabled: bool) -> Self { + Self { + colors_enabled, + ..self + } + } +} + +impl Default for ValueFormatConfig { + fn default() -> Self { + Self { + max_depth: 3, + colors_enabled: false, + } + } +} diff --git a/crates/lune-utils/src/fmt/value/metamethods.rs b/crates/lune-utils/src/fmt/value/metamethods.rs new file mode 100644 index 00000000..8b00b1ad --- /dev/null +++ b/crates/lune-utils/src/fmt/value/metamethods.rs @@ -0,0 +1,29 @@ +use mlua::prelude::*; + +pub fn call_table_tostring_metamethod<'a>(tab: &'a LuaTable<'a>) -> Option { + let f = match tab.get_metatable() { + None => None, + Some(meta) => match meta.get::<_, LuaFunction>(LuaMetaMethod::ToString.name()) { + Ok(method) => Some(method), + Err(_) => None, + }, + }?; + match f.call::<_, String>(()) { + Ok(res) => Some(res), + Err(_) => None, + } +} + +pub fn call_userdata_tostring_metamethod<'a>(tab: &'a LuaAnyUserData<'a>) -> Option { + let f = match tab.get_metatable() { + Err(_) => None, + Ok(meta) => match meta.get::(LuaMetaMethod::ToString.name()) { + Ok(method) => Some(method), + Err(_) => None, + }, + }?; + match f.call::<_, String>(()) { + Ok(res) => Some(res), + Err(_) => None, + } +} diff --git a/crates/lune-utils/src/fmt/value/mod.rs b/crates/lune-utils/src/fmt/value/mod.rs new file mode 100644 index 00000000..8fb2e244 --- /dev/null +++ b/crates/lune-utils/src/fmt/value/mod.rs @@ -0,0 +1,88 @@ +use std::fmt::{self, Write as _}; + +use console::{colors_enabled, set_colors_enabled}; +use mlua::prelude::*; + +mod basic; +mod config; +mod metamethods; +mod style; + +pub use self::config::ValueFormatConfig; + +use self::basic::{format_value_styled, lua_value_as_plain_string_key}; +use self::style::STYLE_DIM; + +type FmtResult = Result; + +fn format_value_inner(value: &LuaValue, config: &ValueFormatConfig, depth: usize) -> FmtResult { + let mut buffer = String::new(); + + // TODO: Rewrite this section to not be recursive and + // keep track of any recursive references to tables. + if let LuaValue::Table(ref t) = value { + if depth >= config.max_depth { + write!(buffer, "{}", STYLE_DIM.apply_to("{ ... }"))?; + } else { + writeln!(buffer, "{}", STYLE_DIM.apply_to("{"))?; + + for res in t.clone().pairs::() { + let (key, value) = res.expect("conversion to LuaValue should never fail"); + let formatted = if let Some(plain_key) = lua_value_as_plain_string_key(&key) { + format!( + "{} {} {}{}", + plain_key, + STYLE_DIM.apply_to("="), + format_value_inner(&value, config, depth + 1)?, + STYLE_DIM.apply_to(","), + ) + } else { + format!( + "{}{}{} {} {}{}", + STYLE_DIM.apply_to("["), + format_value_inner(&key, config, depth + 1)?, + STYLE_DIM.apply_to("]"), + STYLE_DIM.apply_to("="), + format_value_inner(&value, config, depth + 1)?, + STYLE_DIM.apply_to(","), + ) + }; + buffer.push_str(&formatted); + } + + writeln!(buffer, "{}", STYLE_DIM.apply_to("}"))?; + } + } else { + write!(buffer, "{}", format_value_styled(value))?; + } + + Ok(buffer) +} + +/** + Formats a Lua value into a pretty string using the given config. +*/ +#[must_use] +#[allow(clippy::missing_panics_doc)] +pub fn pretty_format_value(value: &LuaValue, config: &ValueFormatConfig) -> String { + let colors_were_enabled = colors_enabled(); + set_colors_enabled(config.colors_enabled); + let res = format_value_inner(value, config, 0); + set_colors_enabled(colors_were_enabled); + res.expect("using fmt for writing into strings should never fail") +} + +/** + Formats a Lua multi-value into a pretty string using the given config. + + Each value will be separated by a space. +*/ +#[must_use] +#[allow(clippy::missing_panics_doc)] +pub fn pretty_format_multi_value(values: &LuaMultiValue, config: &ValueFormatConfig) -> String { + values + .into_iter() + .map(|value| pretty_format_value(value, config)) + .collect::>() + .join(" ") +} diff --git a/crates/lune-utils/src/fmt/value/style.rs b/crates/lune-utils/src/fmt/value/style.rs new file mode 100644 index 00000000..0a4dbe4d --- /dev/null +++ b/crates/lune-utils/src/fmt/value/style.rs @@ -0,0 +1,9 @@ +use console::Style; +use once_cell::sync::Lazy; + +pub static COLOR_GREEN: Lazy