Split lune into proper crates (#188)

This commit is contained in:
Filip Tibell 2024-05-12 13:30:32 +02:00 committed by GitHub
parent 3f53fc983c
commit de71558c5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
185 changed files with 4005 additions and 1897 deletions

View file

@ -24,7 +24,7 @@ jobs:
components: rustfmt components: rustfmt
- name: Install Just - name: Install Just
uses: extractions/setup-just@v1 uses: extractions/setup-just@v2
- name: Install Tooling - name: Install Tooling
uses: ok-nick/setup-aftman@v0.4.2 uses: ok-nick/setup-aftman@v0.4.2
@ -41,7 +41,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Just - name: Install Just
uses: extractions/setup-just@v1 uses: extractions/setup-just@v2
- name: Install Tooling - name: Install Tooling
uses: ok-nick/setup-aftman@v0.4.2 uses: ok-nick/setup-aftman@v0.4.2

View file

@ -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

View file

@ -21,14 +21,32 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Get version from manifest - name: Get version from manifest
uses: SebRollen/toml-action@9062fbef52816d61278d24ce53c8070440e1e8dd uses: SebRollen/toml-action@v1.2.0
id: get_version id: get_version
with: with:
file: Cargo.toml file: Cargo.toml
field: package.version field: package.version
build: dry-run:
name: Dry-run
needs: ["init"] needs: ["init"]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Publish (dry-run)
uses: katyo/publish-crates@v2
with:
dry-run: true
check-repo: true
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
build:
needs: ["init", "dry-run"]
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -70,7 +88,7 @@ jobs:
targets: ${{ matrix.cargo-target }} targets: ${{ matrix.cargo-target }}
- name: Install Just - name: Install Just
uses: extractions/setup-just@v1 uses: extractions/setup-just@v2
- name: Install build tooling (aarch64-unknown-linux-gnu) - name: Install build tooling (aarch64-unknown-linux-gnu)
if: matrix.cargo-target == 'aarch64-unknown-linux-gnu' if: matrix.cargo-target == 'aarch64-unknown-linux-gnu'
@ -86,24 +104,24 @@ jobs:
run: just zip-release ${{ matrix.cargo-target }} run: just zip-release ${{ matrix.cargo-target }}
- name: Upload release artifact - name: Upload release artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ${{ matrix.artifact-name }} name: ${{ matrix.artifact-name }}
path: release.zip path: release.zip
release: release-github:
name: Release name: Release (GitHub)
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: ["init", "build"] needs: ["init", "dry-run", "build"]
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Just - name: Install Just
uses: extractions/setup-just@v1 uses: extractions/setup-just@v2
- name: Download releases - name: Download releases
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
path: ./releases path: ./releases
@ -111,7 +129,7 @@ jobs:
run: just unpack-releases "./releases" run: just unpack-releases "./releases"
- name: Create release - name: Create release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
@ -120,3 +138,21 @@ jobs:
fail_on_unmatched_files: true fail_on_unmatched_files: true
files: ./releases/*.zip files: ./releases/*.zip
draft: true draft: true
release-crates:
name: Release (crates.io)
runs-on: ubuntu-latest
needs: ["init", "dry-run", "build"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Publish crates
uses: katyo/publish-crates@v2
with:
dry-run: false
check-repo: true
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}

270
Cargo.lock generated
View file

@ -163,9 +163,9 @@ dependencies = [
[[package]] [[package]]
name = "async-compression" name = "async-compression"
version = "0.4.8" version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07dbbf24db18d609b1462965249abdf49129ccad073ec257da372adc83259c60" checksum = "9c90a406b4495d129f00461241616194cb8a032c8d1c53c657f0961d5f8e0498"
dependencies = [ dependencies = [
"brotli", "brotli",
"flate2", "flate2",
@ -205,17 +205,6 @@ version = "4.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
[[package]]
name = "async-trait"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@ -324,9 +313,9 @@ dependencies = [
[[package]] [[package]]
name = "brotli" name = "brotli"
version = "4.0.0" version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "125740193d7fee5cc63ab9e16c2fdc4e07c74ba755cc53b327d6ea029e9fc569" checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
dependencies = [ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib",
"alloc-stdlib", "alloc-stdlib",
@ -335,9 +324,9 @@ dependencies = [
[[package]] [[package]]
name = "brotli-decompressor" name = "brotli-decompressor"
version = "3.0.0" version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65622a320492e09b5e0ac436b14c54ff68199bac392d0e89a6832c4518eea525" checksum = "e6221fe77a248b9117d431ad93761222e1cf8ff282d9d1d5d9f53d6299a1cf76"
dependencies = [ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib",
"alloc-stdlib", "alloc-stdlib",
@ -759,12 +748,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
[[package]]
name = "either"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
[[package]] [[package]]
name = "encode_unicode" name = "encode_unicode"
version = "0.3.6" version = "0.3.6"
@ -1367,15 +1350,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.11" version = "1.0.11"
@ -1484,57 +1458,203 @@ name = "lune"
version = "0.8.3" version = "0.8.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-compression",
"async-trait",
"blocking",
"bstr",
"chrono",
"chrono_lc",
"clap", "clap",
"console", "console",
"dialoguer", "dialoguer",
"directories", "directories",
"dunce",
"env_logger", "env_logger",
"futures-util", "futures-util",
"include_dir",
"lune-roblox",
"lune-std",
"lune-utils",
"mlua",
"mlua-luau-scheduler",
"once_cell",
"reqwest",
"rustyline",
"serde",
"serde_json",
"thiserror",
"tokio",
"tracing",
"tracing-subscriber",
"zip_next",
]
[[package]]
name = "lune-roblox"
version = "0.1.0"
dependencies = [
"glam", "glam",
"lune-utils",
"mlua",
"once_cell",
"rand",
"rbx_binary",
"rbx_dom_weak",
"rbx_reflection",
"rbx_reflection_database",
"rbx_xml",
"thiserror",
]
[[package]]
name = "lune-std"
version = "0.1.0"
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",
"lune-utils",
"mlua",
"mlua-luau-scheduler",
"serde",
"serde_json",
"tokio",
]
[[package]]
name = "lune-std-datetime"
version = "0.1.0"
dependencies = [
"chrono",
"chrono_lc",
"lune-utils",
"mlua",
"thiserror",
]
[[package]]
name = "lune-std-fs"
version = "0.1.0"
dependencies = [
"bstr",
"lune-std-datetime",
"lune-utils",
"mlua",
"tokio",
]
[[package]]
name = "lune-std-luau"
version = "0.1.0"
dependencies = [
"lune-utils",
"mlua",
]
[[package]]
name = "lune-std-net"
version = "0.1.0"
dependencies = [
"bstr",
"futures-util",
"http 1.1.0", "http 1.1.0",
"http-body-util", "http-body-util",
"hyper 1.3.1", "hyper 1.3.1",
"hyper-tungstenite", "hyper-tungstenite",
"hyper-util", "hyper-util",
"include_dir", "lune-std-serde",
"itertools", "lune-utils",
"lz4_flex", "mlua",
"mlua-luau-scheduler",
"reqwest",
"tokio",
"tokio-tungstenite",
"urlencoding",
]
[[package]]
name = "lune-std-process"
version = "0.1.0"
dependencies = [
"directories",
"lune-utils",
"mlua",
"mlua-luau-scheduler",
"os_str_bytes",
"pin-project",
"tokio",
]
[[package]]
name = "lune-std-regex"
version = "0.1.0"
dependencies = [
"lune-utils",
"mlua",
"regex",
"self_cell",
]
[[package]]
name = "lune-std-roblox"
version = "0.1.0"
dependencies = [
"lune-roblox",
"lune-utils",
"mlua", "mlua",
"mlua-luau-scheduler", "mlua-luau-scheduler",
"once_cell", "once_cell",
"os_str_bytes",
"path-clean",
"pathdiff",
"pin-project",
"rand",
"rbx_binary",
"rbx_cookie", "rbx_cookie",
"rbx_dom_weak", ]
"rbx_reflection",
"rbx_reflection_database", [[package]]
"rbx_xml", name = "lune-std-serde"
"regex", version = "0.1.0"
"reqwest", dependencies = [
"rustyline", "async-compression",
"self_cell", "bstr",
"lune-utils",
"lz4",
"mlua",
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"thiserror",
"tokio", "tokio",
"tokio-tungstenite",
"toml", "toml",
"tracing", ]
"tracing-subscriber",
"urlencoding", [[package]]
"zip_next", name = "lune-std-stdio"
version = "0.1.0"
dependencies = [
"dialoguer",
"lune-utils",
"mlua",
"mlua-luau-scheduler",
"tokio",
]
[[package]]
name = "lune-std-task"
version = "0.1.0"
dependencies = [
"lune-utils",
"mlua",
"mlua-luau-scheduler",
"tokio",
]
[[package]]
name = "lune-utils"
version = "0.1.0"
dependencies = [
"console",
"dunce",
"mlua",
"once_cell",
"path-clean",
"pathdiff",
"tokio",
] ]
[[package]] [[package]]
@ -1557,15 +1677,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "lz4_flex"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5"
dependencies = [
"twox-hash",
]
[[package]] [[package]]
name = "lzma-rs" name = "lzma-rs"
version = "0.3.0" version = "0.3.0"
@ -2624,12 +2735,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "stdweb" name = "stdweb"
version = "0.4.20" version = "0.4.20"
@ -2882,7 +2987,6 @@ dependencies = [
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"tracing",
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
@ -3100,16 +3204,6 @@ dependencies = [
"utf-8", "utf-8",
] ]
[[package]]
name = "twox-hash"
version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
"cfg-if",
"static_assertions",
]
[[package]] [[package]]
name = "typed-arena" name = "typed-arena"
version = "2.0.2" version = "2.0.2"

View file

@ -1,41 +1,21 @@
[package] [workspace]
name = "lune" resolver = "2"
version = "0.8.3" default-members = ["crates/lune"]
edition = "2021" members = [
license = "MPL-2.0" "crates/lune",
repository = "https://github.com/lune-org/lune" "crates/lune-roblox",
description = "A standalone Luau runtime" "crates/lune-std",
readme = "README.md" "crates/lune-std-datetime",
keywords = ["cli", "lua", "luau", "runtime"] "crates/lune-std-fs",
categories = ["command-line-interface"] "crates/lune-std-luau",
"crates/lune-std-net",
[[bin]] "crates/lune-std-process",
name = "lune" "crates/lune-std-regex",
path = "src/main.rs" "crates/lune-std-roblox",
"crates/lune-std-serde",
[lib] "crates/lune-std-stdio",
name = "lune" "crates/lune-std-task",
path = "src/lib.rs" "crates/lune-utils",
[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",
] ]
# Profile for building the release binary, with the following options set: # Profile for building the release binary, with the following options set:
@ -53,99 +33,31 @@ opt-level = "z"
strip = true strip = true
lto = true lto = true
# All of the dependencies for Lune. # Lints for all crates in the workspace
# #
# Dependencies are categorized as following: # 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:
# 1. General dependencies with no specific features set # - Casts between number types
# 2. Large / core dependencies that have many different crates and / or features set # - Module naming conventions
# 3. Dependencies for specific features of Lune, eg. the CLI or massive Roblox builtin library # - Imports and multiple dependency versions
# [workspace.lints.clippy]
[dependencies] all = { level = "deny", priority = -3 }
console = "0.15" cargo = { level = "warn", priority = -2 }
directories = "5.0" pedantic = { level = "warn", priority = -1 }
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 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 }
blocking = "1.5" similar_names = { level = "allow", priority = 1 }
tracing = "0.1" unnecessary_wraps = { level = "allow", priority = 1 }
tracing-subscriber = { version = "0.3", features = ["env-filter"] } unnested_or_patterns = { level = "allow", priority = 1 }
tokio = { version = "1.24", features = ["full", "tracing"] } unreadable_literal = { level = "allow", priority = 1 }
os_str_bytes = { version = "7.0", features = ["conversions"] }
mlua-luau-scheduler = { version = "0.0.2" } multiple_crate_versions = { level = "allow", priority = 1 }
mlua = { version = "0.9.7", features = [ module_inception = { level = "allow", priority = 1 }
"luau", module_name_repetitions = { level = "allow", priority = 1 }
"luau-jit", needless_pass_by_value = { level = "allow", priority = 1 }
"async", wildcard_imports = { level = "allow", priority = 1 }
"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" }

View file

@ -1,4 +1,4 @@
[tools] [tools]
luau-lsp = "JohnnyMorganz/luau-lsp@1.27.0" luau-lsp = "JohnnyMorganz/luau-lsp@1.29.0"
selene = "Kampfkarren/selene@0.26.1" selene = "Kampfkarren/selene@0.27.1"
stylua = "JohnnyMorganz/StyLua@0.19.1" stylua = "JohnnyMorganz/StyLua@0.20.0"

View file

@ -0,0 +1,27 @@
[package]
name = "lune-roblox"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[lib]
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
mlua = { version = "0.9.7", features = ["luau"] }
glam = "0.27"
rand = "0.8"
thiserror = "1.0"
once_cell = "1.17"
rbx_binary = "0.7.3"
rbx_dom_weak = "2.6.0"
rbx_reflection = "4.4.0"
rbx_reflection_database = "0.2.9"
rbx_xml = "0.13.2"
lune-utils = { version = "0.1.0", path = "../lune-utils" }

View file

@ -4,6 +4,15 @@ use rbx_dom_weak::types::{Variant as DomValue, VariantType as DomType};
use super::extension::DomValueExt; use super::extension::DomValueExt;
/**
Checks if the given name is a valid attribute name.
# Errors
- If the name starts with the prefix "RBX".
- If the name contains any characters other than alphanumeric characters and underscore.
- If the name is longer than 100 characters.
*/
pub fn ensure_valid_attribute_name(name: impl AsRef<str>) -> LuaResult<()> { pub fn ensure_valid_attribute_name(name: impl AsRef<str>) -> LuaResult<()> {
let name = name.as_ref(); let name = name.as_ref();
if name.to_ascii_uppercase().starts_with("RBX") { if name.to_ascii_uppercase().starts_with("RBX") {
@ -23,6 +32,13 @@ pub fn ensure_valid_attribute_name(name: impl AsRef<str>) -> LuaResult<()> {
} }
} }
/**
Checks if the given value is a valid attribute value.
# Errors
- If the value is not a valid attribute type.
*/
pub fn ensure_valid_attribute_value(value: &DomValue) -> LuaResult<()> { pub fn ensure_valid_attribute_value(value: &DomValue) -> LuaResult<()> {
let is_valid = matches!( let is_valid = matches!(
value.ty(), value.ty(),

View file

@ -2,7 +2,7 @@ use mlua::prelude::*;
use rbx_dom_weak::types::{Variant as DomValue, VariantType as DomType}; use rbx_dom_weak::types::{Variant as DomValue, VariantType as DomType};
use crate::roblox::{datatypes::extension::DomValueExt, instance::Instance}; use crate::{datatypes::extension::DomValueExt, instance::Instance};
use super::*; use super::*;
@ -65,8 +65,10 @@ impl<'lua> DomValueToLua<'lua> for LuaValue<'lua> {
// NOTE: Some values are either optional or default and we should handle // NOTE: Some values are either optional or default and we should handle
// that properly here since the userdata conversion above will always fail // that properly here since the userdata conversion above will always fail
DomValue::OptionalCFrame(None) => Ok(LuaValue::Nil), DomValue::OptionalCFrame(None)
DomValue::PhysicalProperties(dom::PhysicalProperties::Default) => Ok(LuaValue::Nil), | DomValue::PhysicalProperties(dom::PhysicalProperties::Default) => {
Ok(LuaValue::Nil)
}
_ => Err(e), _ => Err(e),
}, },

View file

@ -6,6 +6,7 @@ pub(crate) trait DomValueExt {
impl DomValueExt for DomType { impl DomValueExt for DomType {
fn variant_name(&self) -> Option<&'static str> { fn variant_name(&self) -> Option<&'static str> {
#[allow(clippy::enum_glob_use)]
use DomType::*; use DomType::*;
Some(match self { Some(match self {
Attributes => "Attributes", Attributes => "Attributes",

View file

@ -10,4 +10,4 @@ mod util;
use result::*; use result::*;
pub use crate::roblox::shared::userdata::*; pub use crate::shared::userdata::*;

View file

@ -3,7 +3,9 @@ use core::fmt;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::Axes as DomAxes; use rbx_dom_weak::types::Axes as DomAxes;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, EnumItem}; use super::{super::*, EnumItem};
@ -52,8 +54,7 @@ impl LuaExportsTable<'_> for Axes {
check(&e); check(&e);
} else { } else {
return Err(LuaError::RuntimeError(format!( return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got userdata", "Expected argument #{index} to be an EnumItem, got userdata",
index
))); )));
} }
} else { } else {

View file

@ -4,14 +4,16 @@ use mlua::prelude::*;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rbx_dom_weak::types::BrickColor as DomBrickColor; use rbx_dom_weak::types::BrickColor as DomBrickColor;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, Color3}; use super::{super::*, Color3};
/** /**
An implementation of the [BrickColor](https://create.roblox.com/docs/reference/engine/datatypes/BrickColor) Roblox datatype. An implementation of the [BrickColor](https://create.roblox.com/docs/reference/engine/datatypes/BrickColor) Roblox datatype.
This implements all documented properties, methods & constructors of the BrickColor class as of March 2023. This implements all documented properties, methods & constructors of the `BrickColor` class as of March 2023.
*/ */
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct BrickColor { pub struct BrickColor {

View file

@ -1,3 +1,5 @@
#![allow(clippy::items_after_statements)]
use core::fmt; use core::fmt;
use std::ops; use std::ops;
@ -5,7 +7,9 @@ use glam::{EulerRot, Mat3, Mat4, Quat, Vec3};
use mlua::{prelude::*, Variadic}; use mlua::{prelude::*, Variadic};
use rbx_dom_weak::types::{CFrame as DomCFrame, Matrix3 as DomMatrix3, Vector3 as DomVector3}; use rbx_dom_weak::types::{CFrame as DomCFrame, Matrix3 as DomMatrix3, Vector3 as DomVector3};
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, Vector3}; use super::{super::*, Vector3};
@ -14,7 +18,7 @@ use super::{super::*, Vector3};
Roblox datatype, backed by [`glam::Mat4`]. Roblox datatype, backed by [`glam::Mat4`].
This implements all documented properties, methods & This implements all documented properties, methods &
constructors of the CFrame class as of March 2023. constructors of the `CFrame` class as of March 2023.
*/ */
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct CFrame(pub Mat4); pub struct CFrame(pub Mat4);
@ -42,6 +46,7 @@ impl CFrame {
impl LuaExportsTable<'_> for CFrame { impl LuaExportsTable<'_> for CFrame {
const EXPORT_NAME: &'static str = "CFrame"; const EXPORT_NAME: &'static str = "CFrame";
#[allow(clippy::too_many_lines)]
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> { fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let cframe_angles = |_, (rx, ry, rz): (f32, f32, f32)| { let cframe_angles = |_, (rx, ry, rz): (f32, f32, f32)| {
Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz))) Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz)))
@ -68,8 +73,7 @@ impl LuaExportsTable<'_> for CFrame {
Ok(CFrame(Mat4::from_cols( Ok(CFrame(Mat4::from_cols(
rx.0.extend(0.0), rx.0.extend(0.0),
ry.0.extend(0.0), ry.0.extend(0.0),
rz.map(|r| r.0) rz.map_or_else(|| rx.0.cross(ry.0).normalize(), |r| r.0)
.unwrap_or_else(|| rx.0.cross(ry.0).normalize())
.extend(0.0), .extend(0.0),
pos.0.extend(1.0), pos.0.extend(1.0),
))) )))
@ -195,6 +199,7 @@ impl LuaUserData for CFrame {
}); });
} }
#[allow(clippy::too_many_lines)]
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
// Methods // Methods
methods.add_method("Inverse", |_, this, ()| Ok(this.inverse())); methods.add_method("Inverse", |_, this, ()| Ok(this.inverse()));
@ -226,34 +231,49 @@ impl LuaUserData for CFrame {
methods.add_method( methods.add_method(
"ToWorldSpace", "ToWorldSpace",
|_, this, rhs: Variadic<LuaUserDataRef<CFrame>>| { |_, this, rhs: Variadic<LuaUserDataRef<CFrame>>| {
Ok(Variadic::from_iter(rhs.into_iter().map(|cf| *this * *cf))) Ok(rhs
.into_iter()
.map(|cf| *this * *cf)
.collect::<Variadic<_>>())
}, },
); );
methods.add_method( methods.add_method(
"ToObjectSpace", "ToObjectSpace",
|_, this, rhs: Variadic<LuaUserDataRef<CFrame>>| { |_, this, rhs: Variadic<LuaUserDataRef<CFrame>>| {
let inverse = this.inverse(); let inverse = this.inverse();
Ok(Variadic::from_iter(rhs.into_iter().map(|cf| inverse * *cf))) Ok(rhs
.into_iter()
.map(|cf| inverse * *cf)
.collect::<Variadic<_>>())
}, },
); );
methods.add_method( methods.add_method(
"PointToWorldSpace", "PointToWorldSpace",
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| { |_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
Ok(Variadic::from_iter(rhs.into_iter().map(|v3| *this * *v3))) Ok(rhs
.into_iter()
.map(|v3| *this * *v3)
.collect::<Variadic<_>>())
}, },
); );
methods.add_method( methods.add_method(
"PointToObjectSpace", "PointToObjectSpace",
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| { |_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
let inverse = this.inverse(); let inverse = this.inverse();
Ok(Variadic::from_iter(rhs.into_iter().map(|v3| inverse * *v3))) Ok(rhs
.into_iter()
.map(|v3| inverse * *v3)
.collect::<Variadic<_>>())
}, },
); );
methods.add_method( methods.add_method(
"VectorToWorldSpace", "VectorToWorldSpace",
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| { |_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
let result = *this - Vector3(this.position()); let result = *this - Vector3(this.position());
Ok(Variadic::from_iter(rhs.into_iter().map(|v3| result * *v3))) Ok(rhs
.into_iter()
.map(|v3| result * *v3)
.collect::<Variadic<_>>())
}, },
); );
methods.add_method( methods.add_method(
@ -261,8 +281,10 @@ impl LuaUserData for CFrame {
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| { |_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
let inverse = this.inverse(); let inverse = this.inverse();
let result = inverse - Vector3(inverse.position()); let result = inverse - Vector3(inverse.position());
Ok(rhs
Ok(Variadic::from_iter(rhs.into_iter().map(|v3| result * *v3))) .into_iter()
.map(|v3| result * *v3)
.collect::<Variadic<_>>())
}, },
); );
#[rustfmt::skip] #[rustfmt::skip]
@ -445,7 +467,7 @@ mod cframe_test {
Vec3::new(1.0, 2.0, 3.0).extend(1.0), Vec3::new(1.0, 2.0, 3.0).extend(1.0),
)); ));
assert_eq!(CFrame::from(dom_cframe), cframe) assert_eq!(CFrame::from(dom_cframe), cframe);
} }
#[test] #[test]
@ -466,6 +488,6 @@ mod cframe_test {
), ),
); );
assert_eq!(DomCFrame::from(cframe), dom_cframe) assert_eq!(DomCFrame::from(cframe), dom_cframe);
} }
} }

View file

@ -1,3 +1,5 @@
#![allow(clippy::many_single_char_names)]
use core::fmt; use core::fmt;
use std::ops; use std::ops;
@ -5,7 +7,9 @@ use glam::Vec3;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::{Color3 as DomColor3, Color3uint8 as DomColor3uint8}; use rbx_dom_weak::types::{Color3 as DomColor3, Color3uint8 as DomColor3uint8};
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::super::*; use super::super::*;
@ -85,8 +89,7 @@ impl LuaExportsTable<'_> for Color3 {
b: (b as f32) / 255f32, b: (b as f32) / 255f32,
}), }),
_ => Err(LuaError::RuntimeError(format!( _ => Err(LuaError::RuntimeError(format!(
"Hex color string '{}' contains invalid character", "Hex color string '{trimmed}' contains invalid character",
trimmed
))), ))),
} }
}; };
@ -151,6 +154,7 @@ impl LuaUserData for Color3 {
let max = r.max(g).max(b); let max = r.max(g).max(b);
let diff = max - min; let diff = max - min;
#[allow(clippy::float_cmp)]
let hue = (match max { let hue = (match max {
max if max == min => 0.0, max if max == min => 0.0,
max if max == r => (g - b) / diff + (if g < b { 6.0 } else { 0.0 }), max if max == r => (g - b) / diff + (if g < b { 6.0 } else { 0.0 }),

View file

@ -5,14 +5,16 @@ use rbx_dom_weak::types::{
ColorSequence as DomColorSequence, ColorSequenceKeypoint as DomColorSequenceKeypoint, ColorSequence as DomColorSequence, ColorSequenceKeypoint as DomColorSequenceKeypoint,
}; };
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, Color3, ColorSequenceKeypoint}; use super::{super::*, Color3, ColorSequenceKeypoint};
/** /**
An implementation of the [ColorSequence](https://create.roblox.com/docs/reference/engine/datatypes/ColorSequence) Roblox datatype. An implementation of the [ColorSequence](https://create.roblox.com/docs/reference/engine/datatypes/ColorSequence) Roblox datatype.
This implements all documented properties, methods & constructors of the ColorSequence class as of March 2023. This implements all documented properties, methods & constructors of the `ColorSequence` class as of March 2023.
*/ */
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ColorSequence { pub struct ColorSequence {
@ -87,9 +89,9 @@ impl fmt::Display for ColorSequence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (index, keypoint) in self.keypoints.iter().enumerate() { for (index, keypoint) in self.keypoints.iter().enumerate() {
if index < self.keypoints.len() - 1 { if index < self.keypoints.len() - 1 {
write!(f, "{}, ", keypoint)?; write!(f, "{keypoint}, ")?;
} else { } else {
write!(f, "{}", keypoint)?; write!(f, "{keypoint}")?;
} }
} }
Ok(()) Ok(())
@ -102,7 +104,7 @@ impl From<DomColorSequence> for ColorSequence {
keypoints: v keypoints: v
.keypoints .keypoints
.iter() .iter()
.cloned() .copied()
.map(ColorSequenceKeypoint::from) .map(ColorSequenceKeypoint::from)
.collect(), .collect(),
} }
@ -115,7 +117,7 @@ impl From<ColorSequence> for DomColorSequence {
keypoints: v keypoints: v
.keypoints .keypoints
.iter() .iter()
.cloned() .copied()
.map(DomColorSequenceKeypoint::from) .map(DomColorSequenceKeypoint::from)
.collect(), .collect(),
} }

View file

@ -3,14 +3,16 @@ use core::fmt;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::ColorSequenceKeypoint as DomColorSequenceKeypoint; use rbx_dom_weak::types::ColorSequenceKeypoint as DomColorSequenceKeypoint;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, Color3}; use super::{super::*, Color3};
/** /**
An implementation of the [ColorSequenceKeypoint](https://create.roblox.com/docs/reference/engine/datatypes/ColorSequenceKeypoint) Roblox datatype. An implementation of the [ColorSequenceKeypoint](https://create.roblox.com/docs/reference/engine/datatypes/ColorSequenceKeypoint) Roblox datatype.
This implements all documented properties, methods & constructors of the ColorSequenceKeypoint class as of March 2023. This implements all documented properties, methods & constructors of the `ColorSequenceKeypoint` class as of March 2023.
*/ */
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct ColorSequenceKeypoint { pub struct ColorSequenceKeypoint {

View file

@ -8,7 +8,7 @@ use super::{super::*, Enum};
/** /**
An implementation of the [EnumItem](https://create.roblox.com/docs/reference/engine/datatypes/EnumItem) Roblox datatype. An implementation of the [EnumItem](https://create.roblox.com/docs/reference/engine/datatypes/EnumItem) Roblox datatype.
This implements all documented properties, methods & constructors of the EnumItem class as of March 2023. This implements all documented properties, methods & constructors of the `EnumItem` class as of March 2023.
*/ */
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EnumItem { pub struct EnumItem {

View file

@ -24,8 +24,7 @@ impl LuaUserData for Enums {
|_, _, name: String| match Enum::from_name(&name) { |_, _, name: String| match Enum::from_name(&name) {
Some(e) => Ok(e), Some(e) => Ok(e),
None => Err(LuaError::RuntimeError(format!( None => Err(LuaError::RuntimeError(format!(
"The enum '{}' does not exist", "The enum '{name}' does not exist",
name
))), ))),
}, },
); );

View file

@ -1,9 +1,13 @@
#![allow(clippy::struct_excessive_bools)]
use core::fmt; use core::fmt;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::Faces as DomFaces; use rbx_dom_weak::types::Faces as DomFaces;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, EnumItem}; use super::{super::*, EnumItem};
@ -54,8 +58,7 @@ impl LuaExportsTable<'_> for Faces {
check(&e); check(&e);
} else { } else {
return Err(LuaError::RuntimeError(format!( return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got userdata", "Expected argument #{index} to be an EnumItem, got userdata",
index
))); )));
} }
} else { } else {

View file

@ -6,7 +6,9 @@ use rbx_dom_weak::types::{
Font as DomFont, FontStyle as DomFontStyle, FontWeight as DomFontWeight, Font as DomFont, FontStyle as DomFontStyle, FontWeight as DomFontWeight,
}; };
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, EnumItem}; use super::{super::*, EnumItem};
@ -62,7 +64,7 @@ impl LuaExportsTable<'_> for Font {
let font_from_name = let font_from_name =
|_, (file, weight, style): (String, Option<FontWeight>, Option<FontStyle>)| { |_, (file, weight, style): (String, Option<FontWeight>, Option<FontStyle>)| {
Ok(Font { Ok(Font {
family: format!("rbxasset://fonts/families/{}.json", file), family: format!("rbxasset://fonts/families/{file}.json"),
weight: weight.unwrap_or_default(), weight: weight.unwrap_or_default(),
style: style.unwrap_or_default(), style: style.unwrap_or_default(),
cached_id: None, cached_id: None,
@ -72,7 +74,7 @@ impl LuaExportsTable<'_> for Font {
let font_from_id = let font_from_id =
|_, (id, weight, style): (i32, Option<FontWeight>, Option<FontStyle>)| { |_, (id, weight, style): (i32, Option<FontWeight>, Option<FontStyle>)| {
Ok(Font { Ok(Font {
family: format!("rbxassetid://{}", id), family: format!("rbxassetid://{id}"),
weight: weight.unwrap_or_default(), weight: weight.unwrap_or_default(),
style: style.unwrap_or_default(), style: style.unwrap_or_default(),
cached_id: None, cached_id: None,
@ -206,7 +208,7 @@ pub(crate) enum FontWeight {
} }
impl FontWeight { impl FontWeight {
pub(crate) fn as_u16(&self) -> u16 { pub(crate) fn as_u16(self) -> u16 {
match self { match self {
Self::Thin => 100, Self::Thin => 100,
Self::ExtraLight => 200, Self::ExtraLight => 200,
@ -288,12 +290,11 @@ impl<'lua> FromLua<'lua> for FontWeight {
if value.parent.desc.name == "FontWeight" { if value.parent.desc.name == "FontWeight" {
if let Ok(value) = FontWeight::from_str(&value.name) { if let Ok(value) = FontWeight::from_str(&value.name) {
return Ok(value); return Ok(value);
} else { }
message = Some(format!( message = Some(format!(
"Found unknown Enum.FontWeight value '{}'", "Found unknown Enum.FontWeight value '{}'",
value.name value.name
)); ));
}
} else { } else {
message = Some(format!( message = Some(format!(
"Expected Enum.FontWeight, got Enum.{}", "Expected Enum.FontWeight, got Enum.{}",
@ -316,7 +317,7 @@ impl<'lua> IntoLua<'lua> for FontWeight {
None => Err(LuaError::ToLuaConversionError { None => Err(LuaError::ToLuaConversionError {
from: "FontWeight", from: "FontWeight",
to: "EnumItem", to: "EnumItem",
message: Some(format!("Found unknown Enum.FontWeight value '{}'", self)), message: Some(format!("Found unknown Enum.FontWeight value '{self}'")),
}), }),
} }
} }
@ -329,7 +330,7 @@ pub(crate) enum FontStyle {
} }
impl FontStyle { impl FontStyle {
pub(crate) fn as_u8(&self) -> u8 { pub(crate) fn as_u8(self) -> u8 {
match self { match self {
Self::Normal => 0, Self::Normal => 0,
Self::Italic => 1, Self::Italic => 1,
@ -383,12 +384,11 @@ impl<'lua> FromLua<'lua> for FontStyle {
if value.parent.desc.name == "FontStyle" { if value.parent.desc.name == "FontStyle" {
if let Ok(value) = FontStyle::from_str(&value.name) { if let Ok(value) = FontStyle::from_str(&value.name) {
return Ok(value); return Ok(value);
} else { }
message = Some(format!( message = Some(format!(
"Found unknown Enum.FontStyle value '{}'", "Found unknown Enum.FontStyle value '{}'",
value.name value.name
)); ));
}
} else { } else {
message = Some(format!( message = Some(format!(
"Expected Enum.FontStyle, got Enum.{}", "Expected Enum.FontStyle, got Enum.{}",
@ -411,7 +411,7 @@ impl<'lua> IntoLua<'lua> for FontStyle {
None => Err(LuaError::ToLuaConversionError { None => Err(LuaError::ToLuaConversionError {
from: "FontStyle", from: "FontStyle",
to: "EnumItem", to: "EnumItem",
message: Some(format!("Found unknown Enum.FontStyle value '{}'", self)), message: Some(format!("Found unknown Enum.FontStyle value '{self}'")),
}), }),
} }
} }

View file

@ -3,14 +3,16 @@ use core::fmt;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::NumberRange as DomNumberRange; use rbx_dom_weak::types::NumberRange as DomNumberRange;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::super::*; use super::super::*;
/** /**
An implementation of the [NumberRange](https://create.roblox.com/docs/reference/engine/datatypes/NumberRange) Roblox datatype. An implementation of the [NumberRange](https://create.roblox.com/docs/reference/engine/datatypes/NumberRange) Roblox datatype.
This implements all documented properties, methods & constructors of the NumberRange class as of March 2023. This implements all documented properties, methods & constructors of the `NumberRange` class as of March 2023.
*/ */
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct NumberRange { pub struct NumberRange {

View file

@ -5,14 +5,16 @@ use rbx_dom_weak::types::{
NumberSequence as DomNumberSequence, NumberSequenceKeypoint as DomNumberSequenceKeypoint, NumberSequence as DomNumberSequence, NumberSequenceKeypoint as DomNumberSequenceKeypoint,
}; };
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, NumberSequenceKeypoint}; use super::{super::*, NumberSequenceKeypoint};
/** /**
An implementation of the [NumberSequence](https://create.roblox.com/docs/reference/engine/datatypes/NumberSequence) Roblox datatype. An implementation of the [NumberSequence](https://create.roblox.com/docs/reference/engine/datatypes/NumberSequence) Roblox datatype.
This implements all documented properties, methods & constructors of the NumberSequence class as of March 2023. This implements all documented properties, methods & constructors of the `NumberSequence` class as of March 2023.
*/ */
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct NumberSequence { pub struct NumberSequence {
@ -91,9 +93,9 @@ impl fmt::Display for NumberSequence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (index, keypoint) in self.keypoints.iter().enumerate() { for (index, keypoint) in self.keypoints.iter().enumerate() {
if index < self.keypoints.len() - 1 { if index < self.keypoints.len() - 1 {
write!(f, "{}, ", keypoint)?; write!(f, "{keypoint}, ")?;
} else { } else {
write!(f, "{}", keypoint)?; write!(f, "{keypoint}")?;
} }
} }
Ok(()) Ok(())
@ -106,7 +108,7 @@ impl From<DomNumberSequence> for NumberSequence {
keypoints: v keypoints: v
.keypoints .keypoints
.iter() .iter()
.cloned() .copied()
.map(NumberSequenceKeypoint::from) .map(NumberSequenceKeypoint::from)
.collect(), .collect(),
} }
@ -119,7 +121,7 @@ impl From<NumberSequence> for DomNumberSequence {
keypoints: v keypoints: v
.keypoints .keypoints
.iter() .iter()
.cloned() .copied()
.map(DomNumberSequenceKeypoint::from) .map(DomNumberSequenceKeypoint::from)
.collect(), .collect(),
} }

View file

@ -3,14 +3,16 @@ use core::fmt;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::NumberSequenceKeypoint as DomNumberSequenceKeypoint; use rbx_dom_weak::types::NumberSequenceKeypoint as DomNumberSequenceKeypoint;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::super::*; use super::super::*;
/** /**
An implementation of the [NumberSequenceKeypoint](https://create.roblox.com/docs/reference/engine/datatypes/NumberSequenceKeypoint) Roblox datatype. An implementation of the [NumberSequenceKeypoint](https://create.roblox.com/docs/reference/engine/datatypes/NumberSequenceKeypoint) Roblox datatype.
This implements all documented properties, methods & constructors of the NumberSequenceKeypoint class as of March 2023. This implements all documented properties, methods & constructors of the `NumberSequenceKeypoint` class as of March 2023.
*/ */
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct NumberSequenceKeypoint { pub struct NumberSequenceKeypoint {

View file

@ -3,14 +3,16 @@ use core::fmt;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::CustomPhysicalProperties as DomCustomPhysicalProperties; use rbx_dom_weak::types::CustomPhysicalProperties as DomCustomPhysicalProperties;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, EnumItem}; use super::{super::*, EnumItem};
/** /**
An implementation of the [PhysicalProperties](https://create.roblox.com/docs/reference/engine/datatypes/PhysicalProperties) Roblox datatype. An implementation of the [PhysicalProperties](https://create.roblox.com/docs/reference/engine/datatypes/PhysicalProperties) Roblox datatype.
This implements all documented properties, methods & constructors of the PhysicalProperties class as of March 2023. This implements all documented properties, methods & constructors of the `PhysicalProperties` class as of March 2023.
*/ */
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct PhysicalProperties { pub struct PhysicalProperties {

View file

@ -4,7 +4,9 @@ use glam::Vec3;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::Ray as DomRay; use rbx_dom_weak::types::Ray as DomRay;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, Vector3}; use super::{super::*, Vector3};

View file

@ -5,7 +5,9 @@ use glam::Vec2;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::Rect as DomRect; use rbx_dom_weak::types::Rect as DomRect;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, Vector2}; use super::{super::*, Vector2};

View file

@ -4,7 +4,9 @@ use glam::{Mat4, Vec3};
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::Region3 as DomRegion3; use rbx_dom_weak::types::Region3 as DomRegion3;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, CFrame, Vector3}; use super::{super::*, CFrame, Vector3};

View file

@ -4,7 +4,9 @@ use glam::IVec3;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::Region3int16 as DomRegion3int16; use rbx_dom_weak::types::Region3int16 as DomRegion3int16;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, Vector3int16}; use super::{super::*, Vector3int16};

View file

@ -4,14 +4,16 @@ use std::ops;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::UDim as DomUDim; use rbx_dom_weak::types::UDim as DomUDim;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::super::*; use super::super::*;
/** /**
An implementation of the [UDim](https://create.roblox.com/docs/reference/engine/datatypes/UDim) Roblox datatype. An implementation of the [UDim](https://create.roblox.com/docs/reference/engine/datatypes/UDim) Roblox datatype.
This implements all documented properties, methods & constructors of the UDim class as of March 2023. This implements all documented properties, methods & constructors of the `UDim` class as of March 2023.
*/ */
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct UDim { pub struct UDim {

View file

@ -1,3 +1,5 @@
#![allow(clippy::items_after_statements)]
use core::fmt; use core::fmt;
use std::ops; use std::ops;
@ -5,14 +7,16 @@ use glam::Vec2;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::UDim2 as DomUDim2; use rbx_dom_weak::types::UDim2 as DomUDim2;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, UDim}; use super::{super::*, UDim};
/** /**
An implementation of the [UDim2](https://create.roblox.com/docs/reference/engine/datatypes/UDim2) Roblox datatype. An implementation of the [UDim2](https://create.roblox.com/docs/reference/engine/datatypes/UDim2) Roblox datatype.
This implements all documented properties, methods & constructors of the UDim2 class as of March 2023. This implements all documented properties, methods & constructors of the `UDim2` class as of March 2023.
*/ */
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct UDim2 { pub struct UDim2 {

View file

@ -5,7 +5,9 @@ use glam::{Vec2, Vec3};
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::Vector2 as DomVector2; use rbx_dom_weak::types::Vector2 as DomVector2;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::super::*; use super::super::*;

View file

@ -5,7 +5,9 @@ use glam::IVec2;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::Vector2int16 as DomVector2int16; use rbx_dom_weak::types::Vector2int16 as DomVector2int16;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::super::*; use super::super::*;

View file

@ -5,10 +5,9 @@ use glam::Vec3;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::Vector3 as DomVector3; use rbx_dom_weak::types::Vector3 as DomVector3;
use crate::{ use lune_utils::TableBuilder;
lune::util::TableBuilder,
roblox::{datatypes::util::round_float_decimal, exports::LuaExportsTable}, use crate::{datatypes::util::round_float_decimal, exports::LuaExportsTable};
};
use super::{super::*, EnumItem}; use super::{super::*, EnumItem};
@ -37,8 +36,7 @@ impl LuaExportsTable<'_> for Vector3 {
"Z" => Vector3(Vec3::Z), "Z" => Vector3(Vec3::Z),
name => { name => {
return Err(LuaError::RuntimeError(format!( return Err(LuaError::RuntimeError(format!(
"Axis '{}' is not known", "Axis '{name}' is not known",
name
))) )))
} }
}) })
@ -61,8 +59,7 @@ impl LuaExportsTable<'_> for Vector3 {
"Back" => Vector3(Vec3::Z), "Back" => Vector3(Vec3::Z),
name => { name => {
return Err(LuaError::RuntimeError(format!( return Err(LuaError::RuntimeError(format!(
"NormalId '{}' is not known", "NormalId '{name}' is not known",
name
))) )))
} }
}) })

View file

@ -5,7 +5,9 @@ use glam::IVec3;
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::Vector3int16 as DomVector3int16; use rbx_dom_weak::types::Vector3int16 as DomVector3int16;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::super::*; use super::super::*;

View file

@ -2,7 +2,7 @@ use std::path::Path;
use rbx_dom_weak::WeakDom; use rbx_dom_weak::WeakDom;
use crate::roblox::shared::instance::class_is_a_service; use crate::shared::instance::class_is_a_service;
/** /**
A document kind specifier. A document kind specifier.
@ -58,6 +58,7 @@ impl DocumentKind {
Returns `None` if the given dom is empty and as such can not have its kind inferred. Returns `None` if the given dom is empty and as such can not have its kind inferred.
*/ */
#[must_use]
pub fn from_weak_dom(dom: &WeakDom) -> Option<Self> { pub fn from_weak_dom(dom: &WeakDom) -> Option<Self> {
let mut has_top_level_child = false; let mut has_top_level_child = false;
let mut has_top_level_service = false; let mut has_top_level_service = false;

View file

@ -15,7 +15,7 @@ pub use kind::*;
use postprocessing::*; use postprocessing::*;
use crate::roblox::instance::{data_model, Instance}; use crate::instance::{data_model, Instance};
pub type DocumentResult<T> = Result<T, DocumentError>; pub type DocumentResult<T> = Result<T, DocumentError>;
@ -78,6 +78,7 @@ impl Document {
| Model | Binary | `rbxm` | | Model | Binary | `rbxm` |
| Model | Xml | `rbxmx` | | Model | Xml | `rbxmx` |
*/ */
#[must_use]
#[rustfmt::skip] #[rustfmt::skip]
pub fn canonical_extension(kind: DocumentKind, format: DocumentFormat) -> &'static str { pub fn canonical_extension(kind: DocumentKind, format: DocumentFormat) -> &'static str {
match (kind, format) { match (kind, format) {
@ -113,6 +114,10 @@ impl Document {
Note that detection of model vs place file is heavily dependent on the structure Note that detection of model vs place file is heavily dependent on the structure
of the file, and a model file with services in it will detect as a place file, so of the file, and a model file with services in it will detect as a place file, so
if possible using [`Document::from_bytes`] with an explicit kind should be preferred. if possible using [`Document::from_bytes`] with an explicit kind should be preferred.
# Errors
Errors if the given bytes are not a valid roblox file.
*/ */
pub fn from_bytes_auto(bytes: impl AsRef<[u8]>) -> DocumentResult<Self> { pub fn from_bytes_auto(bytes: impl AsRef<[u8]>) -> DocumentResult<Self> {
let (format, dom) = Self::from_bytes_inner(bytes)?; let (format, dom) = Self::from_bytes_inner(bytes)?;
@ -125,6 +130,10 @@ impl Document {
This will automatically handle and detect if the document This will automatically handle and detect if the document
should be decoded using a roblox binary or roblox xml format. should be decoded using a roblox binary or roblox xml format.
# Errors
Errors if the given bytes are not a valid roblox file or not of the given kind.
*/ */
pub fn from_bytes(bytes: impl AsRef<[u8]>, kind: DocumentKind) -> DocumentResult<Self> { pub fn from_bytes(bytes: impl AsRef<[u8]>, kind: DocumentKind) -> DocumentResult<Self> {
let (format, dom) = Self::from_bytes_inner(bytes)?; let (format, dom) = Self::from_bytes_inner(bytes)?;
@ -138,6 +147,10 @@ impl Document {
This will use the same format that the document was created This will use the same format that the document was created
with, meaning if the document is a binary document the output with, meaning if the document is a binary document the output
will be binary, and vice versa for xml and other future formats. will be binary, and vice versa for xml and other future formats.
# Errors
Errors if the document can not be encoded.
*/ */
pub fn to_bytes(&self) -> DocumentResult<Vec<u8>> { pub fn to_bytes(&self) -> DocumentResult<Vec<u8>> {
self.to_bytes_with_format(self.format) self.to_bytes_with_format(self.format)
@ -146,6 +159,10 @@ impl Document {
/** /**
Encodes the document as a vector of bytes, to Encodes the document as a vector of bytes, to
be written to a file or sent over the network. be written to a file or sent over the network.
# Errors
Errors if the document can not be encoded.
*/ */
pub fn to_bytes_with_format(&self, format: DocumentFormat) -> DocumentResult<Vec<u8>> { pub fn to_bytes_with_format(&self, format: DocumentFormat) -> DocumentResult<Vec<u8>> {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
@ -172,6 +189,7 @@ impl Document {
/** /**
Gets the kind this document was created with. Gets the kind this document was created with.
*/ */
#[must_use]
pub fn kind(&self) -> DocumentKind { pub fn kind(&self) -> DocumentKind {
self.kind self.kind
} }
@ -179,6 +197,7 @@ impl Document {
/** /**
Gets the format this document was created with. Gets the format this document was created with.
*/ */
#[must_use]
pub fn format(&self) -> DocumentFormat { pub fn format(&self) -> DocumentFormat {
self.format self.format
} }
@ -186,14 +205,17 @@ impl Document {
/** /**
Gets the file extension for this document. Gets the file extension for this document.
*/ */
#[must_use]
pub fn extension(&self) -> &'static str { pub fn extension(&self) -> &'static str {
Self::canonical_extension(self.kind, self.format) Self::canonical_extension(self.kind, self.format)
} }
/** /**
Creates a DataModel instance out of this place document. Creates a `DataModel` instance out of this place document.
Will error if the document is not a place. # Errors
Errors if the document is not a place.
*/ */
pub fn into_data_model_instance(mut self) -> DocumentResult<Instance> { pub fn into_data_model_instance(mut self) -> DocumentResult<Instance> {
if self.kind != DocumentKind::Place { if self.kind != DocumentKind::Place {
@ -219,7 +241,9 @@ impl Document {
/** /**
Creates an array of instances out of this model document. Creates an array of instances out of this model document.
Will error if the document is not a model. # Errors
Errors if the document is not a model.
*/ */
pub fn into_instance_array(mut self) -> DocumentResult<Vec<Instance>> { pub fn into_instance_array(mut self) -> DocumentResult<Vec<Instance>> {
if self.kind != DocumentKind::Model { if self.kind != DocumentKind::Model {
@ -237,9 +261,11 @@ impl Document {
} }
/** /**
Creates a place document out of a DataModel instance. Creates a place document out of a `DataModel` instance.
Will error if the instance is not a DataModel. # Errors
Errors if the instance is not a `DataModel`.
*/ */
pub fn from_data_model_instance(i: Instance) -> DocumentResult<Self> { pub fn from_data_model_instance(i: Instance) -> DocumentResult<Self> {
if i.get_class_name() != data_model::CLASS_NAME { if i.get_class_name() != data_model::CLASS_NAME {
@ -266,7 +292,9 @@ impl Document {
/** /**
Creates a model document out of an array of instances. Creates a model document out of an array of instances.
Will error if any of the instances is a DataModel. # Errors
Errors if any of the instances is a `DataModel`.
*/ */
pub fn from_instance_array(v: Vec<Instance>) -> DocumentResult<Self> { pub fn from_instance_array(v: Vec<Instance>) -> DocumentResult<Self> {
for i in &v { for i in &v {

View file

@ -3,7 +3,7 @@ use rbx_dom_weak::{
Instance as DomInstance, WeakDom, Instance as DomInstance, WeakDom,
}; };
use crate::roblox::shared::instance::class_is_a; use crate::shared::instance::class_is_a;
pub fn postprocess_dom_for_place(_dom: &mut WeakDom) { pub fn postprocess_dom_for_place(_dom: &mut WeakDom) {
// Nothing here yet // Nothing here yet

View file

@ -1,3 +1,5 @@
#![allow(clippy::items_after_statements)]
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::{ use rbx_dom_weak::{
@ -5,7 +7,7 @@ use rbx_dom_weak::{
Instance as DomInstance, Instance as DomInstance,
}; };
use crate::roblox::{ use crate::{
datatypes::{ datatypes::{
attributes::{ensure_valid_attribute_name, ensure_valid_attribute_value}, attributes::{ensure_valid_attribute_name, ensure_valid_attribute_value},
conversion::{DomValueToLua, LuaToDomValue}, conversion::{DomValueToLua, LuaToDomValue},
@ -17,6 +19,7 @@ use crate::roblox::{
use super::{data_model, registry::InstanceRegistry, Instance}; use super::{data_model, registry::InstanceRegistry, Instance};
#[allow(clippy::too_many_lines)]
pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(m: &mut M) { pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(m: &mut M) {
m.add_meta_method(LuaMetaMethod::ToString, |lua, this, ()| { m.add_meta_method(LuaMetaMethod::ToString, |lua, this, ()| {
ensure_not_destroyed(this)?; ensure_not_destroyed(this)?;
@ -142,7 +145,7 @@ pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(m: &mut M) {
ensure_not_destroyed(this)?; ensure_not_destroyed(this)?;
let attributes = this.get_attributes(); let attributes = this.get_attributes();
let tab = lua.create_table_with_capacity(0, attributes.len())?; let tab = lua.create_table_with_capacity(0, attributes.len())?;
for (key, value) in attributes.into_iter() { for (key, value) in attributes {
tab.set(key, LuaValue::dom_value_to_lua(lua, &value)?)?; tab.set(key, LuaValue::dom_value_to_lua(lua, &value)?)?;
} }
Ok(tab) Ok(tab)
@ -227,8 +230,7 @@ fn instance_property_get<'lua>(
if let DomValue::Enum(enum_value) = prop { if let DomValue::Enum(enum_value) = prop {
let enum_name = info.enum_name.ok_or_else(|| { let enum_name = info.enum_name.ok_or_else(|| {
LuaError::RuntimeError(format!( LuaError::RuntimeError(format!(
"Failed to get property '{}' - encountered unknown enum", "Failed to get property '{prop_name}' - encountered unknown enum",
prop_name
)) ))
})?; })?;
EnumItem::from_enum_name_and_value(&enum_name, enum_value.to_u32()) EnumItem::from_enum_name_and_value(&enum_name, enum_value.to_u32())
@ -246,8 +248,7 @@ fn instance_property_get<'lua>(
EnumItem::from_enum_name_and_value(&enum_name, enum_value) EnumItem::from_enum_name_and_value(&enum_name, enum_value)
.ok_or_else(|| { .ok_or_else(|| {
LuaError::RuntimeError(format!( LuaError::RuntimeError(format!(
"Failed to get property '{}' - Enum.{} does not contain numeric value {}", "Failed to get property '{prop_name}' - Enum.{enum_name} does not contain numeric value {enum_value}",
prop_name, enum_name, enum_value
)) ))
})? })?
.into_lua(lua) .into_lua(lua)
@ -258,14 +259,12 @@ fn instance_property_get<'lua>(
Ok(LuaValue::Nil) Ok(LuaValue::Nil)
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"Failed to get property '{}' - missing default value", "Failed to get property '{prop_name}' - missing default value",
prop_name
))) )))
} }
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"Failed to get property '{}' - malformed property info", "Failed to get property '{prop_name}' - malformed property info",
prop_name
))) )))
} }
} else if let Some(inst) = this.find_child(|inst| inst.name == prop_name) { } else if let Some(inst) = this.find_child(|inst| inst.name == prop_name) {
@ -276,8 +275,7 @@ fn instance_property_get<'lua>(
Ok(LuaValue::Function(method)) Ok(LuaValue::Function(method))
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"{} is not a valid member of {}", "{prop_name} is not a valid member of {this}",
prop_name, this
))) )))
} }
} }
@ -347,16 +345,14 @@ fn instance_property_set<'lua>(
} }
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"Failed to set property '{}' - malformed property info", "Failed to set property '{prop_name}' - malformed property info",
prop_name
))) )))
} }
} else if let Some(setter) = InstanceRegistry::find_property_setter(lua, this, &prop_name) { } else if let Some(setter) = InstanceRegistry::find_property_setter(lua, this, &prop_name) {
setter.call((this.clone(), prop_value)) setter.call((this.clone(), prop_value))
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"{} is not a valid member of {}", "{prop_name} is not a valid member of {this}",
prop_name, this
))) )))
} }
} }

View file

@ -1,6 +1,6 @@
use mlua::prelude::*; use mlua::prelude::*;
use crate::roblox::shared::{ use crate::shared::{
classes::{ classes::{
add_class_restricted_getter, add_class_restricted_method, add_class_restricted_getter, add_class_restricted_method,
get_or_create_property_ref_instance, get_or_create_property_ref_instance,
@ -33,7 +33,7 @@ fn data_model_get_workspace(_: &Lua, this: &Instance) -> LuaResult<Instance> {
} }
/** /**
Gets or creates a service for this DataModel. Gets or creates a service for this `DataModel`.
### See Also ### See Also
* [`GetService`](https://create.roblox.com/docs/reference/engine/classes/ServiceProvider#GetService) * [`GetService`](https://create.roblox.com/docs/reference/engine/classes/ServiceProvider#GetService)
@ -42,8 +42,7 @@ fn data_model_get_workspace(_: &Lua, this: &Instance) -> LuaResult<Instance> {
fn data_model_get_service(_: &Lua, this: &Instance, service_name: String) -> LuaResult<Instance> { fn data_model_get_service(_: &Lua, this: &Instance, service_name: String) -> LuaResult<Instance> {
if matches!(class_is_a_service(&service_name), None | Some(false)) { if matches!(class_is_a_service(&service_name), None | Some(false)) {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"'{}' is not a valid service name", "'{service_name}' is not a valid service name",
service_name
))) )))
} else if let Some(service) = this.find_child(|child| child.class == service_name) { } else if let Some(service) = this.find_child(|child| child.class == service_name) {
Ok(service) Ok(service)
@ -55,7 +54,7 @@ fn data_model_get_service(_: &Lua, this: &Instance, service_name: String) -> Lua
} }
/** /**
Gets a service for this DataModel, if it exists. Gets a service for this `DataModel`, if it exists.
### See Also ### See Also
* [`FindService`](https://create.roblox.com/docs/reference/engine/classes/ServiceProvider#FindService) * [`FindService`](https://create.roblox.com/docs/reference/engine/classes/ServiceProvider#FindService)
@ -68,8 +67,7 @@ fn data_model_find_service(
) -> LuaResult<Option<Instance>> { ) -> LuaResult<Option<Instance>> {
if matches!(class_is_a_service(&service_name), None | Some(false)) { if matches!(class_is_a_service(&service_name), None | Some(false)) {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"'{}' is not a valid service name", "'{service_name}' is not a valid service name",
service_name
))) )))
} else if let Some(service) = this.find_child(|child| child.class == service_name) { } else if let Some(service) = this.find_child(|child| child.class == service_name) {
Ok(Some(service)) Ok(Some(service))

View file

@ -1,3 +1,5 @@
#![allow(clippy::missing_panics_doc)]
use std::{ use std::{
collections::{BTreeMap, VecDeque}, collections::{BTreeMap, VecDeque},
fmt, fmt,
@ -12,10 +14,11 @@ use rbx_dom_weak::{
Instance as DomInstance, InstanceBuilder as DomInstanceBuilder, WeakDom, Instance as DomInstance, InstanceBuilder as DomInstanceBuilder, WeakDom,
}; };
use lune_utils::TableBuilder;
use crate::{ use crate::{
lune::util::TableBuilder, exports::LuaExportsTable,
roblox::exports::LuaExportsTable, shared::instance::{class_exists, class_is_a},
roblox::shared::instance::{class_exists, class_is_a},
}; };
pub(crate) mod base; pub(crate) mod base;
@ -54,9 +57,10 @@ impl Instance {
.get_by_ref(dom_ref) .get_by_ref(dom_ref)
.expect("Failed to find instance in document"); .expect("Failed to find instance in document");
if instance.referent() == dom.root_ref() { assert!(
panic!("Instances can not be created from dom roots") !(instance.referent() == dom.root_ref()),
} "Instances can not be created from dom roots"
);
Self { Self {
dom_ref, dom_ref,
@ -76,9 +80,10 @@ impl Instance {
let dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let dom = INTERNAL_DOM.lock().expect("Failed to lock document");
if let Some(instance) = dom.get_by_ref(dom_ref) { if let Some(instance) = dom.get_by_ref(dom_ref) {
if instance.referent() == dom.root_ref() { assert!(
panic!("Instances can not be created from dom roots") !(instance.referent() == dom.root_ref()),
} "Instances can not be created from dom roots"
);
Some(Self { Some(Self {
dom_ref, dom_ref,
@ -154,7 +159,7 @@ impl Instance {
let cloned = dom.clone_multiple_into_external(referents, external_dom); let cloned = dom.clone_multiple_into_external(referents, external_dom);
for referent in cloned.iter() { for referent in &cloned {
external_dom.transfer_within(*referent, external_dom.root_ref()); external_dom.transfer_within(*referent, external_dom.root_ref());
} }
@ -171,7 +176,8 @@ impl Instance {
* [`Clone`](https://create.roblox.com/docs/reference/engine/classes/Instance#Clone) * [`Clone`](https://create.roblox.com/docs/reference/engine/classes/Instance#Clone)
on the Roblox Developer Hub on the Roblox Developer Hub
*/ */
pub fn clone_instance(&self) -> Instance { #[must_use]
pub fn clone_instance(&self) -> Self {
let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document");
let new_ref = dom.clone_within(self.dom_ref); let new_ref = dom.clone_within(self.dom_ref);
drop(dom); // Self::new needs mutex handle, drop it first drop(dom); // Self::new needs mutex handle, drop it first
@ -254,6 +260,7 @@ impl Instance {
* [`ClassName`](https://create.roblox.com/docs/reference/engine/classes/Instance#ClassName) * [`ClassName`](https://create.roblox.com/docs/reference/engine/classes/Instance#ClassName)
on the Roblox Developer Hub on the Roblox Developer Hub
*/ */
#[must_use]
pub fn get_class_name(&self) -> &str { pub fn get_class_name(&self) -> &str {
self.class_name.as_str() self.class_name.as_str()
} }
@ -286,7 +293,7 @@ impl Instance {
dom.get_by_ref_mut(self.dom_ref) dom.get_by_ref_mut(self.dom_ref)
.expect("Failed to find instance in document") .expect("Failed to find instance in document")
.name = name.into() .name = name.into();
} }
/** /**
@ -326,9 +333,7 @@ impl Instance {
pub fn set_parent(&self, parent: Option<Instance>) { pub fn set_parent(&self, parent: Option<Instance>) {
let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document");
let parent_ref = parent let parent_ref = parent.map_or_else(|| dom.root_ref(), |parent| parent.dom_ref);
.map(|parent| parent.dom_ref)
.unwrap_or_else(|| dom.root_ref());
dom.transfer_within(self.dom_ref, parent_ref); dom.transfer_within(self.dom_ref, parent_ref);
} }
@ -663,9 +668,8 @@ impl Instance {
if predicate(ancestor) { if predicate(ancestor) {
drop(dom); // Self::new needs mutex handle, drop it first drop(dom); // Self::new needs mutex handle, drop it first
return Some(Self::new(ancestor_ref)); return Some(Self::new(ancestor_ref));
} else {
ancestor_ref = ancestor.parent();
} }
ancestor_ref = ancestor.parent();
} }
None None
@ -699,9 +703,8 @@ impl Instance {
let queue_ref = queue_item.referent(); let queue_ref = queue_item.referent();
drop(dom); // Self::new needs mutex handle, drop it first drop(dom); // Self::new needs mutex handle, drop it first
return Some(Self::new(queue_ref)); return Some(Self::new(queue_ref));
} else {
queue.extend(queue_item.children())
} }
queue.extend(queue_item.children());
} }
None None
@ -717,8 +720,7 @@ impl LuaExportsTable<'_> for Instance {
Instance::new_orphaned(class_name).into_lua(lua) Instance::new_orphaned(class_name).into_lua(lua)
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"Failed to create Instance - '{}' is not a valid class name", "Failed to create Instance - '{class_name}' is not a valid class name",
class_name
))) )))
} }
}; };
@ -756,7 +758,7 @@ impl LuaUserData for Instance {
impl Hash for Instance { impl Hash for Instance {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.dom_ref.hash(state) self.dom_ref.hash(state);
} }
} }

View file

@ -51,6 +51,13 @@ impl InstanceRegistry {
.expect("Missing InstanceRegistry in app data") .expect("Missing InstanceRegistry in app data")
} }
/**
Inserts a method into the instance registry.
# Errors
- If the method already exists in the registry.
*/
pub fn insert_method<'lua>( pub fn insert_method<'lua>(
lua: &'lua Lua, lua: &'lua Lua,
class_name: &str, class_name: &str,
@ -80,6 +87,13 @@ impl InstanceRegistry {
Ok(()) Ok(())
} }
/**
Inserts a property getter into the instance registry.
# Errors
- If the property already exists in the registry.
*/
pub fn insert_property_getter<'lua>( pub fn insert_property_getter<'lua>(
lua: &'lua Lua, lua: &'lua Lua,
class_name: &str, class_name: &str,
@ -109,6 +123,13 @@ impl InstanceRegistry {
Ok(()) Ok(())
} }
/**
Inserts a property setter into the instance registry.
# Errors
- If the property already exists in the registry.
*/
pub fn insert_property_setter<'lua>( pub fn insert_property_setter<'lua>(
lua: &'lua Lua, lua: &'lua Lua,
class_name: &str, class_name: &str,
@ -138,6 +159,12 @@ impl InstanceRegistry {
Ok(()) Ok(())
} }
/**
Finds a method in the instance registry.
Returns `None` if the method is not found.
*/
#[must_use]
pub fn find_method<'lua>( pub fn find_method<'lua>(
lua: &'lua Lua, lua: &'lua Lua,
instance: &Instance, instance: &Instance,
@ -159,6 +186,12 @@ impl InstanceRegistry {
}) })
} }
/**
Finds a property getter in the instance registry.
Returns `None` if the property getter is not found.
*/
#[must_use]
pub fn find_property_getter<'lua>( pub fn find_property_getter<'lua>(
lua: &'lua Lua, lua: &'lua Lua,
instance: &Instance, instance: &Instance,
@ -180,6 +213,12 @@ impl InstanceRegistry {
}) })
} }
/**
Finds a property setter in the instance registry.
Returns `None` if the property setter is not found.
*/
#[must_use]
pub fn find_property_setter<'lua>( pub fn find_property_setter<'lua>(
lua: &'lua Lua, lua: &'lua Lua,
instance: &Instance, instance: &Instance,
@ -202,6 +241,16 @@ impl InstanceRegistry {
} }
} }
/**
Gets the class name chain for a given class name.
The chain starts with the given class name and ends with the root class.
# Panics
Panics if the class name is not valid.
*/
#[must_use]
pub fn class_name_chain(class_name: &str) -> Vec<&str> { pub fn class_name_chain(class_name: &str) -> Vec<&str> {
let db = rbx_reflection_database::get(); let db = rbx_reflection_database::get();

View file

@ -1,7 +1,7 @@
use mlua::prelude::*; use mlua::prelude::*;
use rbx_dom_weak::types::{MaterialColors, TerrainMaterials, Variant}; use rbx_dom_weak::types::{MaterialColors, TerrainMaterials, Variant};
use crate::roblox::{ use crate::{
datatypes::types::{Color3, EnumItem}, datatypes::types::{Color3, EnumItem},
shared::classes::{add_class_restricted_method, add_class_restricted_method_mut}, shared::classes::{add_class_restricted_method, add_class_restricted_method_mut},
}; };
@ -23,7 +23,7 @@ pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(methods: &mut M)
CLASS_NAME, CLASS_NAME,
"SetMaterialColor", "SetMaterialColor",
terrain_set_material_color, terrain_set_material_color,
) );
} }
fn get_or_create_material_colors(instance: &Instance) -> MaterialColors { fn get_or_create_material_colors(instance: &Instance) -> MaterialColors {

View file

@ -1,8 +1,6 @@
use mlua::prelude::*; use mlua::prelude::*;
use crate::roblox::shared::classes::{ use crate::shared::classes::{add_class_restricted_getter, get_or_create_property_ref_instance};
add_class_restricted_getter, get_or_create_property_ref_instance,
};
use super::Instance; use super::Instance;

View file

@ -1,6 +1,8 @@
#![allow(clippy::cargo_common_metadata)]
use mlua::prelude::*; use mlua::prelude::*;
use crate::lune::util::TableBuilder; use lune_utils::TableBuilder;
pub mod datatypes; pub mod datatypes;
pub mod document; pub mod document;
@ -46,6 +48,16 @@ fn create_all_exports(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
]) ])
} }
/**
Creates a table containing all the Roblox datatypes, classes, and singletons.
Note that this is not guaranteed to contain any value unless indexed directly,
it may be optimized to use lazy initialization in the future.
# Errors
Errors when out of memory or when a value cannot be created.
*/
pub fn module(lua: &Lua) -> LuaResult<LuaTable> { pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
// FUTURE: We can probably create these lazily as users // FUTURE: We can probably create these lazily as users
// index the main exports (this return value) table and // index the main exports (this return value) table and

View file

@ -7,7 +7,7 @@ use rbx_dom_weak::types::Variant as DomVariant;
use rbx_reflection::{ClassDescriptor, DataType}; use rbx_reflection::{ClassDescriptor, DataType};
use super::{property::DatabaseProperty, utils::*}; use super::{property::DatabaseProperty, utils::*};
use crate::roblox::datatypes::{ use crate::datatypes::{
conversion::DomValueToLua, types::EnumItem, userdata_impl_eq, userdata_impl_to_string, conversion::DomValueToLua, types::EnumItem, userdata_impl_eq, userdata_impl_to_string,
}; };
@ -28,6 +28,7 @@ impl DatabaseClass {
/** /**
Get the name of this class. Get the name of this class.
*/ */
#[must_use]
pub fn get_name(&self) -> String { pub fn get_name(&self) -> String {
self.0.name.to_string() self.0.name.to_string()
} }
@ -37,6 +38,7 @@ impl DatabaseClass {
May be `None` if no parent class exists. May be `None` if no parent class exists.
*/ */
#[must_use]
pub fn get_superclass(&self) -> Option<String> { pub fn get_superclass(&self) -> Option<String> {
let sup = self.0.superclass.as_ref()?; let sup = self.0.superclass.as_ref()?;
Some(sup.to_string()) Some(sup.to_string())
@ -45,6 +47,7 @@ impl DatabaseClass {
/** /**
Get all known properties for this class. Get all known properties for this class.
*/ */
#[must_use]
pub fn get_properties(&self) -> HashMap<String, DatabaseProperty> { pub fn get_properties(&self) -> HashMap<String, DatabaseProperty> {
self.0 self.0
.properties .properties
@ -56,6 +59,7 @@ impl DatabaseClass {
/** /**
Get all default values for properties of this class. Get all default values for properties of this class.
*/ */
#[must_use]
pub fn get_defaults(&self) -> HashMap<String, DomVariant> { pub fn get_defaults(&self) -> HashMap<String, DomVariant> {
self.0 self.0
.default_properties .default_properties
@ -71,7 +75,12 @@ impl DatabaseClass {
to players at runtime, and top-level class categories. to players at runtime, and top-level class categories.
*/ */
pub fn get_tags_str(&self) -> Vec<&'static str> { pub fn get_tags_str(&self) -> Vec<&'static str> {
self.0.tags.iter().map(class_tag_to_str).collect::<Vec<_>>() self.0
.tags
.iter()
.copied()
.map(class_tag_to_str)
.collect::<Vec<_>>()
} }
} }
@ -135,14 +144,12 @@ fn make_enum_value(inner: DbClass, name: impl AsRef<str>, value: u32) -> LuaResu
let name = name.as_ref(); let name = name.as_ref();
let enum_name = find_enum_name(inner, name).ok_or_else(|| { let enum_name = find_enum_name(inner, name).ok_or_else(|| {
LuaError::RuntimeError(format!( LuaError::RuntimeError(format!(
"Failed to get default property '{}' - No enum descriptor was found", "Failed to get default property '{name}' - No enum descriptor was found",
name
)) ))
})?; })?;
EnumItem::from_enum_name_and_value(&enum_name, value).ok_or_else(|| { EnumItem::from_enum_name_and_value(&enum_name, value).ok_or_else(|| {
LuaError::RuntimeError(format!( LuaError::RuntimeError(format!(
"Failed to get default property '{}' - Enum.{} does not contain numeric value {}", "Failed to get default property '{name}' - Enum.{enum_name} does not contain numeric value {value}",
name, enum_name, value
)) ))
}) })
} }

View file

@ -4,7 +4,7 @@ use mlua::prelude::*;
use rbx_reflection::EnumDescriptor; use rbx_reflection::EnumDescriptor;
use crate::roblox::datatypes::{userdata_impl_eq, userdata_impl_to_string}; use crate::datatypes::{userdata_impl_eq, userdata_impl_to_string};
type DbEnum = &'static EnumDescriptor<'static>; type DbEnum = &'static EnumDescriptor<'static>;
@ -23,6 +23,7 @@ impl DatabaseEnum {
/** /**
Get the name of this enum. Get the name of this enum.
*/ */
#[must_use]
pub fn get_name(&self) -> String { pub fn get_name(&self) -> String {
self.0.name.to_string() self.0.name.to_string()
} }
@ -31,8 +32,9 @@ impl DatabaseEnum {
Get all known members of this enum. Get all known members of this enum.
Note that this is a direct map of name -> enum values, Note that this is a direct map of name -> enum values,
and does not actually use the EnumItem datatype itself. and does not actually use the `EnumItem` datatype itself.
*/ */
#[must_use]
pub fn get_items(&self) -> HashMap<String, u32> { pub fn get_items(&self) -> HashMap<String, u32> {
self.0 self.0
.items .items

View file

@ -4,7 +4,7 @@ use mlua::prelude::*;
use rbx_reflection::ReflectionDatabase; use rbx_reflection::ReflectionDatabase;
use crate::roblox::datatypes::userdata_impl_eq; use crate::datatypes::userdata_impl_eq;
mod class; mod class;
mod enums; mod enums;
@ -30,6 +30,7 @@ impl Database {
/** /**
Creates a new database struct, referencing the bundled reflection database. Creates a new database struct, referencing the bundled reflection database.
*/ */
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
@ -40,6 +41,7 @@ impl Database {
This will follow the format `x.y.z.w`, which most This will follow the format `x.y.z.w`, which most
commonly looks something like `0.567.0.123456789`. commonly looks something like `0.567.0.123456789`.
*/ */
#[must_use]
pub fn get_version(&self) -> String { pub fn get_version(&self) -> String {
let [x, y, z, w] = self.0.version; let [x, y, z, w] = self.0.version;
format!("{x}.{y}.{z}.{w}") format!("{x}.{y}.{z}.{w}")
@ -48,15 +50,17 @@ impl Database {
/** /**
Retrieves a list of all currently known enum names. Retrieves a list of all currently known enum names.
*/ */
#[must_use]
pub fn get_enum_names(&self) -> Vec<String> { pub fn get_enum_names(&self) -> Vec<String> {
self.0.enums.keys().map(|e| e.to_string()).collect() self.0.enums.keys().map(ToString::to_string).collect()
} }
/** /**
Retrieves a list of all currently known class names. Retrieves a list of all currently known class names.
*/ */
#[must_use]
pub fn get_class_names(&self) -> Vec<String> { pub fn get_class_names(&self) -> Vec<String> {
self.0.classes.keys().map(|e| e.to_string()).collect() self.0.classes.keys().map(ToString::to_string).collect()
} }
/** /**
@ -108,14 +112,17 @@ impl Database {
impl LuaUserData for Database { impl LuaUserData for Database {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("Version", |_, this| Ok(this.get_version())) fields.add_field_method_get("Version", |_, this| Ok(this.get_version()));
} }
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq);
methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string); methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string);
methods.add_method("GetEnumNames", |_, this, _: ()| Ok(this.get_enum_names())); methods.add_method("GetEnumNames", |_, this, (): ()| Ok(this.get_enum_names()));
methods.add_method("GetClassNames", |_, this, _: ()| Ok(this.get_class_names())); methods.add_method(
"GetClassNames",
|_, this, (): ()| Ok(this.get_class_names()),
);
methods.add_method("GetEnum", |_, this, name: String| Ok(this.get_enum(name))); methods.add_method("GetEnum", |_, this, name: String| Ok(this.get_enum(name)));
methods.add_method("GetClass", |_, this, name: String| Ok(this.get_class(name))); methods.add_method("GetClass", |_, this, name: String| Ok(this.get_class(name)));
methods.add_method("FindEnum", |_, this, name: String| Ok(this.find_enum(name))); methods.add_method("FindEnum", |_, this, name: String| Ok(this.find_enum(name)));

View file

@ -5,7 +5,7 @@ use mlua::prelude::*;
use rbx_reflection::{ClassDescriptor, PropertyDescriptor}; use rbx_reflection::{ClassDescriptor, PropertyDescriptor};
use super::utils::*; use super::utils::*;
use crate::roblox::datatypes::{userdata_impl_eq, userdata_impl_to_string}; use crate::datatypes::{userdata_impl_eq, userdata_impl_to_string};
type DbClass = &'static ClassDescriptor<'static>; type DbClass = &'static ClassDescriptor<'static>;
type DbProp = &'static PropertyDescriptor<'static>; type DbProp = &'static PropertyDescriptor<'static>;
@ -25,6 +25,7 @@ impl DatabaseProperty {
/** /**
Get the name of this property. Get the name of this property.
*/ */
#[must_use]
pub fn get_name(&self) -> String { pub fn get_name(&self) -> String {
self.1.name.to_string() self.1.name.to_string()
} }
@ -36,6 +37,7 @@ impl DatabaseProperty {
For enums this will be a string formatted as `Enum.EnumName`. For enums this will be a string formatted as `Enum.EnumName`.
*/ */
#[must_use]
pub fn get_datatype_name(&self) -> String { pub fn get_datatype_name(&self) -> String {
data_type_to_str(self.1.data_type.clone()) data_type_to_str(self.1.data_type.clone())
} }
@ -45,8 +47,9 @@ impl DatabaseProperty {
All properties are writable and readable in Lune even if scriptability is not. All properties are writable and readable in Lune even if scriptability is not.
*/ */
#[must_use]
pub fn get_scriptability_str(&self) -> &'static str { pub fn get_scriptability_str(&self) -> &'static str {
scriptability_to_str(&self.1.scriptability) scriptability_to_str(self.1.scriptability)
} }
/** /**
@ -59,6 +62,7 @@ impl DatabaseProperty {
self.1 self.1
.tags .tags
.iter() .iter()
.copied()
.map(property_tag_to_str) .map(property_tag_to_str)
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }

View file

@ -1,6 +1,6 @@
use rbx_reflection::{ClassTag, DataType, PropertyTag, Scriptability}; use rbx_reflection::{ClassTag, DataType, PropertyTag, Scriptability};
use crate::roblox::datatypes::extension::DomValueExt; use crate::datatypes::extension::DomValueExt;
pub fn data_type_to_str(data_type: DataType) -> String { pub fn data_type_to_str(data_type: DataType) -> String {
match data_type { match data_type {
@ -17,7 +17,7 @@ pub fn data_type_to_str(data_type: DataType) -> String {
NOTE: Remember to add any new strings here to typedefs too! NOTE: Remember to add any new strings here to typedefs too!
*/ */
pub fn scriptability_to_str(scriptability: &Scriptability) -> &'static str { pub fn scriptability_to_str(scriptability: Scriptability) -> &'static str {
match scriptability { match scriptability {
Scriptability::None => "None", Scriptability::None => "None",
Scriptability::Custom => "Custom", Scriptability::Custom => "Custom",
@ -28,7 +28,7 @@ pub fn scriptability_to_str(scriptability: &Scriptability) -> &'static str {
} }
} }
pub fn property_tag_to_str(tag: &PropertyTag) -> &'static str { pub fn property_tag_to_str(tag: PropertyTag) -> &'static str {
match tag { match tag {
PropertyTag::Deprecated => "Deprecated", PropertyTag::Deprecated => "Deprecated",
PropertyTag::Hidden => "Hidden", PropertyTag::Hidden => "Hidden",
@ -41,7 +41,7 @@ pub fn property_tag_to_str(tag: &PropertyTag) -> &'static str {
} }
} }
pub fn class_tag_to_str(tag: &ClassTag) -> &'static str { pub fn class_tag_to_str(tag: ClassTag) -> &'static str {
match tag { match tag {
ClassTag::Deprecated => "Deprecated", ClassTag::Deprecated => "Deprecated",
ClassTag::NotBrowsable => "NotBrowsable", ClassTag::NotBrowsable => "NotBrowsable",

View file

@ -2,7 +2,7 @@ use mlua::prelude::*;
use rbx_dom_weak::types::Variant as DomValue; use rbx_dom_weak::types::Variant as DomValue;
use crate::roblox::instance::Instance; use crate::instance::Instance;
use super::instance::class_is_a; use super::instance::class_is_a;
@ -20,8 +20,7 @@ pub(crate) fn add_class_restricted_getter<'lua, F: LuaUserDataFields<'lua, Insta
field_getter(lua, this) field_getter(lua, this)
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"{} is not a valid member of {}", "{field_name} is not a valid member of {class_name}",
field_name, class_name
))) )))
} }
}); });
@ -42,8 +41,7 @@ pub(crate) fn add_class_restricted_setter<'lua, F: LuaUserDataFields<'lua, Insta
field_getter(lua, this, value) field_getter(lua, this, value)
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"{} is not a valid member of {}", "{field_name} is not a valid member of {class_name}",
field_name, class_name
))) )))
} }
}); });
@ -64,8 +62,7 @@ pub(crate) fn add_class_restricted_method<'lua, M: LuaUserDataMethods<'lua, Inst
method(lua, this, args) method(lua, this, args)
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"{} is not a valid member of {}", "{method_name} is not a valid member of {class_name}",
method_name, class_name
))) )))
} }
}); });
@ -92,8 +89,7 @@ pub(crate) fn add_class_restricted_method_mut<
method(lua, this, args) method(lua, this, args)
} else { } else {
Err(LuaError::RuntimeError(format!( Err(LuaError::RuntimeError(format!(
"{} is not a valid member of {}", "{method_name} is not a valid member of {class_name}",
method_name, class_name
))) )))
} }
}); });
@ -102,7 +98,7 @@ pub(crate) fn add_class_restricted_method_mut<
/** /**
Gets or creates the instance child with the given reference prop name and class name. Gets or creates the instance child with the given reference prop name and class name.
Note that the class name here must be an exact match, it is not checked using IsA. Note that the class name here must be an exact match, it is not checked using `IsA`.
The instance may be in one of several states but this function will guarantee that the The instance may be in one of several states but this function will guarantee that the
property reference is correct and that the instance exists after it has been called: property reference is correct and that the instance exists after it has been called:

View file

@ -60,12 +60,12 @@ pub(crate) fn find_property_info(
value_type: Some(*value_type), value_type: Some(*value_type),
..Default::default() ..Default::default()
}, },
_ => Default::default(), _ => PropertyInfo::default(),
}); });
break; break;
} else if let Some(sup) = &class.superclass { } else if let Some(sup) = &class.superclass {
// No property found, we should look at the superclass // No property found, we should look at the superclass
class_name = Cow::Borrowed(sup) class_name = Cow::Borrowed(sup);
} else { } else {
break; break;
} }
@ -87,7 +87,7 @@ pub(crate) fn find_property_info(
break; break;
} else if let Some(sup) = &class.superclass { } else if let Some(sup) = &class.superclass {
// No default value found, we should look at the superclass // No default value found, we should look at the superclass
class_name = Cow::Borrowed(sup) class_name = Cow::Borrowed(sup);
} else { } else {
break; break;
} }

View file

@ -1,3 +1,5 @@
#![allow(clippy::missing_errors_doc)]
use std::{any::type_name, cell::RefCell, fmt, ops}; use std::{any::type_name, cell::RefCell, fmt, ops};
use mlua::prelude::*; use mlua::prelude::*;
@ -5,21 +7,29 @@ use mlua::prelude::*;
// Utility functions // Utility functions
type ListWriter = dyn Fn(&mut fmt::Formatter<'_>, bool, &str) -> fmt::Result; type ListWriter = dyn Fn(&mut fmt::Formatter<'_>, bool, &str) -> fmt::Result;
#[must_use]
pub fn make_list_writer() -> Box<ListWriter> { pub fn make_list_writer() -> Box<ListWriter> {
let first = RefCell::new(true); let first = RefCell::new(true);
Box::new(move |f, flag, literal| { Box::new(move |f, flag, literal| {
if flag { if flag {
if first.take() { if first.take() {
write!(f, "{}", literal)?; write!(f, "{literal}")?;
} else { } else {
write!(f, ", {}", literal)?; write!(f, ", {literal}")?;
} }
} }
Ok::<_, fmt::Error>(()) Ok::<_, fmt::Error>(())
}) })
} }
// Userdata metamethod implementations /**
Userdata metamethod implementations
Note that many of these return [`LuaResult`] even though they don't
return any errors - this is for consistency reasons and to make it
easier to add these blanket implementations to [`LuaUserData`] impls.
*/
pub fn userdata_impl_to_string<D>(_: &Lua, datatype: &D, _: ()) -> LuaResult<String> pub fn userdata_impl_to_string<D>(_: &Lua, datatype: &D, _: ()) -> LuaResult<String>
where where

View file

@ -0,0 +1,20 @@
[package]
name = "lune-std-datetime"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[lib]
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
mlua = { version = "0.9.7", features = ["luau"] }
thiserror = "1.0"
chrono = "=0.4.34" # NOTE: 0.4.35 does not compile with chrono_lc
chrono_lc = "0.1"
lune-utils = { version = "0.1.0", path = "../lune-utils" }

View file

@ -6,31 +6,8 @@ use chrono::prelude::*;
use chrono::DateTime as ChronoDateTime; use chrono::DateTime as ChronoDateTime;
use chrono_lc::LocaleDate; use chrono_lc::LocaleDate;
use crate::lune::util::TableBuilder; use crate::result::{DateTimeError, DateTimeResult};
use crate::values::DateTimeValues;
mod error;
mod values;
use error::*;
use values::*;
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
TableBuilder::new(lua)?
.with_function("fromIsoDate", |_, iso_date: String| {
Ok(DateTime::from_iso_date(iso_date)?)
})?
.with_function("fromLocalTime", |_, values| {
Ok(DateTime::from_local_time(&values)?)
})?
.with_function("fromUniversalTime", |_, values| {
Ok(DateTime::from_universal_time(&values)?)
})?
.with_function("fromUnixTimestamp", |_, timestamp| {
Ok(DateTime::from_unix_timestamp_float(timestamp)?)
})?
.with_function("now", |_, ()| Ok(DateTime::now()))?
.build_readonly()
}
const DEFAULT_FORMAT: &str = "%Y-%m-%d %H:%M:%S"; const DEFAULT_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
const DEFAULT_LOCALE: &str = "en"; const DEFAULT_LOCALE: &str = "en";
@ -49,6 +26,7 @@ impl DateTime {
See [`chrono::DateTime::now`] for additional details. See [`chrono::DateTime::now`] for additional details.
*/ */
#[must_use]
pub fn now() -> Self { pub fn now() -> Self {
Self { inner: Utc::now() } Self { inner: Utc::now() }
} }
@ -66,6 +44,10 @@ impl DateTime {
``` ```
See [`chrono::DateTime::from_timestamp`] for additional details. See [`chrono::DateTime::from_timestamp`] for additional details.
# Errors
Returns an error if the input value is out of range.
*/ */
pub fn from_unix_timestamp_float(unix_timestamp: f64) -> DateTimeResult<Self> { pub fn from_unix_timestamp_float(unix_timestamp: f64) -> DateTimeResult<Self> {
let whole = unix_timestamp.trunc() as i64; let whole = unix_timestamp.trunc() as i64;
@ -84,6 +66,10 @@ impl DateTime {
See [`chrono::NaiveDate::from_ymd_opt`] and [`chrono::NaiveTime::from_hms_milli_opt`] See [`chrono::NaiveDate::from_ymd_opt`] and [`chrono::NaiveTime::from_hms_milli_opt`]
for additional details and cases where this constructor may return an error. for additional details and cases where this constructor may return an error.
# Errors
Returns an error if the date or time values are invalid.
*/ */
pub fn from_universal_time(values: &DateTimeValues) -> DateTimeResult<Self> { pub fn from_universal_time(values: &DateTimeValues) -> DateTimeResult<Self> {
let date = NaiveDate::from_ymd_opt(values.year, values.month, values.day) let date = NaiveDate::from_ymd_opt(values.year, values.month, values.day)
@ -108,6 +94,10 @@ impl DateTime {
See [`chrono::NaiveDate::from_ymd_opt`] and [`chrono::NaiveTime::from_hms_milli_opt`] See [`chrono::NaiveDate::from_ymd_opt`] and [`chrono::NaiveTime::from_hms_milli_opt`]
for additional details and cases where this constructor may return an error. for additional details and cases where this constructor may return an error.
# Errors
Returns an error if the date or time values are invalid or ambiguous.
*/ */
pub fn from_local_time(values: &DateTimeValues) -> DateTimeResult<Self> { pub fn from_local_time(values: &DateTimeValues) -> DateTimeResult<Self> {
let date = NaiveDate::from_ymd_opt(values.year, values.month, values.day) let date = NaiveDate::from_ymd_opt(values.year, values.month, values.day)
@ -138,6 +128,7 @@ impl DateTime {
See [`chrono_lc::DateTime::formatl`] for additional details. See [`chrono_lc::DateTime::formatl`] for additional details.
*/ */
#[must_use]
pub fn format_string_local(&self, format: Option<&str>, locale: Option<&str>) -> String { pub fn format_string_local(&self, format: Option<&str>, locale: Option<&str>) -> String {
self.inner self.inner
.with_timezone(&Local) .with_timezone(&Local)
@ -156,6 +147,7 @@ impl DateTime {
See [`chrono_lc::DateTime::formatl`] for additional details. See [`chrono_lc::DateTime::formatl`] for additional details.
*/ */
#[must_use]
pub fn format_string_universal(&self, format: Option<&str>, locale: Option<&str>) -> String { pub fn format_string_universal(&self, format: Option<&str>, locale: Option<&str>) -> String {
self.inner self.inner
.with_timezone(&Utc) .with_timezone(&Utc)
@ -171,6 +163,10 @@ impl DateTime {
`1996-12-19T16:39:57-08:00`, into a new `DateTime` struct. `1996-12-19T16:39:57-08:00`, into a new `DateTime` struct.
See [`chrono::DateTime::parse_from_rfc3339`] for additional details. See [`chrono::DateTime::parse_from_rfc3339`] for additional details.
# Errors
Returns an error if the input string is not a valid RFC 3339 date-time.
*/ */
pub fn from_iso_date(iso_date: impl AsRef<str>) -> DateTimeResult<Self> { pub fn from_iso_date(iso_date: impl AsRef<str>) -> DateTimeResult<Self> {
let inner = ChronoDateTime::parse_from_rfc3339(iso_date.as_ref())?.with_timezone(&Utc); let inner = ChronoDateTime::parse_from_rfc3339(iso_date.as_ref())?.with_timezone(&Utc);
@ -181,6 +177,7 @@ impl DateTime {
Extracts individual date & time values from this Extracts individual date & time values from this
`DateTime`, using the current local time zone. `DateTime`, using the current local time zone.
*/ */
#[must_use]
pub fn to_local_time(self) -> DateTimeValues { pub fn to_local_time(self) -> DateTimeValues {
DateTimeValues::from(self.inner.with_timezone(&Local)) DateTimeValues::from(self.inner.with_timezone(&Local))
} }
@ -189,6 +186,7 @@ impl DateTime {
Extracts individual date & time values from this Extracts individual date & time values from this
`DateTime`, using the universal (UTC) time zone. `DateTime`, using the universal (UTC) time zone.
*/ */
#[must_use]
pub fn to_universal_time(self) -> DateTimeValues { pub fn to_universal_time(self) -> DateTimeValues {
DateTimeValues::from(self.inner.with_timezone(&Utc)) DateTimeValues::from(self.inner.with_timezone(&Utc))
} }
@ -198,6 +196,7 @@ impl DateTime {
See [`chrono::DateTime::to_rfc3339`] for additional details. See [`chrono::DateTime::to_rfc3339`] for additional details.
*/ */
#[must_use]
pub fn to_iso_date(self) -> String { pub fn to_iso_date(self) -> String {
self.inner.to_rfc3339() self.inner.to_rfc3339()
} }

View file

@ -0,0 +1,36 @@
#![allow(clippy::cargo_common_metadata)]
use mlua::prelude::*;
use lune_utils::TableBuilder;
mod date_time;
mod result;
mod values;
pub use self::date_time::DateTime;
/**
Creates the `datetime` standard library module.
# Errors
Errors when out of memory.
*/
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
TableBuilder::new(lua)?
.with_function("fromIsoDate", |_, iso_date: String| {
Ok(DateTime::from_iso_date(iso_date)?)
})?
.with_function("fromLocalTime", |_, values| {
Ok(DateTime::from_local_time(&values)?)
})?
.with_function("fromUniversalTime", |_, values| {
Ok(DateTime::from_universal_time(&values)?)
})?
.with_function("fromUnixTimestamp", |_, timestamp| {
Ok(DateTime::from_unix_timestamp_float(timestamp)?)
})?
.with_function("now", |_, ()| Ok(DateTime::now()))?
.build_readonly()
}

View file

@ -2,9 +2,9 @@ use mlua::prelude::*;
use chrono::prelude::*; use chrono::prelude::*;
use crate::lune::util::TableBuilder; use lune_utils::TableBuilder;
use super::error::{DateTimeError, DateTimeResult}; use super::result::{DateTimeError, DateTimeResult};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct DateTimeValues { pub struct DateTimeValues {
@ -61,9 +61,9 @@ where
} }
/** /**
Conversion methods between DateTimeValues and plain lua tables Conversion methods between `DateTimeValues` and plain lua tables
Note that the IntoLua implementation here uses a read-only table, Note that the `IntoLua` implementation here uses a read-only table,
since we generally want to convert into lua when we know we have since we generally want to convert into lua when we know we have
a fixed point in time, and we guarantee that it doesn't change a fixed point in time, and we guarantee that it doesn't change
*/ */
@ -118,8 +118,8 @@ impl IntoLua<'_> for DateTimeValues {
} }
/** /**
Conversion methods between chrono's timezone-aware DateTime to Conversion methods between chrono's timezone-aware `DateTime` to
and from our non-timezone-aware DateTimeValues values struct and from our non-timezone-aware `DateTimeValues` values struct
*/ */
impl<T: TimeZone> From<DateTime<T>> for DateTimeValues { impl<T: TimeZone> From<DateTime<T>> for DateTimeValues {

View file

@ -0,0 +1,21 @@
[package]
name = "lune-std-fs"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[lib]
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
mlua = { version = "0.9.7", features = ["luau"] }
bstr = "1.9"
tokio = { version = "1", default-features = false, features = ["fs"] }
lune-utils = { version = "0.1.0", path = "../lune-utils" }
lune-std-datetime = { version = "0.1.0", path = "../lune-std-datetime" }

View file

@ -13,7 +13,7 @@ pub struct CopyContents {
pub files: Vec<(usize, PathBuf)>, pub files: Vec<(usize, PathBuf)>,
} }
async fn get_contents_at(root: PathBuf, _options: FsWriteOptions) -> LuaResult<CopyContents> { async fn get_contents_at(root: PathBuf, _: FsWriteOptions) -> LuaResult<CopyContents> {
let mut dirs = Vec::new(); let mut dirs = Vec::new();
let mut files = Vec::new(); let mut files = Vec::new();
@ -53,11 +53,11 @@ async fn get_contents_at(root: PathBuf, _options: FsWriteOptions) -> LuaResult<C
// Ensure that all directory and file paths are relative to the root path // Ensure that all directory and file paths are relative to the root path
// SAFETY: Since we only ever push dirs and files relative to the root, unwrap is safe // SAFETY: Since we only ever push dirs and files relative to the root, unwrap is safe
for (_, dir) in dirs.iter_mut() { for (_, dir) in &mut dirs {
*dir = dir.strip_prefix(&normalized_root).unwrap().to_path_buf() *dir = dir.strip_prefix(&normalized_root).unwrap().to_path_buf();
} }
for (_, file) in files.iter_mut() { for (_, file) in &mut files {
*file = file.strip_prefix(&normalized_root).unwrap().to_path_buf() *file = file.strip_prefix(&normalized_root).unwrap().to_path_buf();
} }
// FUTURE: Deduplicate paths such that these directories: // FUTURE: Deduplicate paths such that these directories:

View file

@ -1,3 +1,5 @@
#![allow(clippy::cargo_common_metadata)]
use std::io::ErrorKind as IoErrorKind; use std::io::ErrorKind as IoErrorKind;
use std::path::{PathBuf, MAIN_SEPARATOR}; use std::path::{PathBuf, MAIN_SEPARATOR};
@ -5,17 +7,24 @@ use bstr::{BString, ByteSlice};
use mlua::prelude::*; use mlua::prelude::*;
use tokio::fs; use tokio::fs;
use crate::lune::util::TableBuilder; use lune_utils::TableBuilder;
mod copy; mod copy;
mod metadata; mod metadata;
mod options; mod options;
use copy::copy; use self::copy::copy;
use metadata::FsMetadata; use self::metadata::FsMetadata;
use options::FsWriteOptions; use self::options::FsWriteOptions;
pub fn create(lua: &Lua) -> LuaResult<LuaTable> { /**
Creates the `fs` standard library module.
# Errors
Errors when out of memory.
*/
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
TableBuilder::new(lua)? TableBuilder::new(lua)?
.with_async_function("readFile", fs_read_file)? .with_async_function("readFile", fs_read_file)?
.with_async_function("readDir", fs_read_dir)? .with_async_function("readDir", fs_read_dir)?

View file

@ -8,7 +8,7 @@ use std::{
use mlua::prelude::*; use mlua::prelude::*;
use crate::lune::builtins::datetime::DateTime; use lune_std_datetime::DateTime;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FsMetadataKind { pub enum FsMetadataKind {

View file

@ -0,0 +1,16 @@
[package]
name = "lune-std-luau"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[lib]
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
mlua = { version = "0.9.7", features = ["luau"] }
lune-utils = { version = "0.1.0", path = "../lune-utils" }

View file

@ -1,13 +1,23 @@
#![allow(clippy::cargo_common_metadata)]
use mlua::prelude::*; use mlua::prelude::*;
use crate::lune::util::TableBuilder; use lune_utils::TableBuilder;
mod options; mod options;
use options::{LuauCompileOptions, LuauLoadOptions};
use self::options::{LuauCompileOptions, LuauLoadOptions};
const BYTECODE_ERROR_BYTE: u8 = 0; const BYTECODE_ERROR_BYTE: u8 = 0;
pub fn create(lua: &Lua) -> LuaResult<LuaTable> { /**
Creates the `luau` standard library module.
# Errors
Errors when out of memory.
*/
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
TableBuilder::new(lua)? TableBuilder::new(lua)?
.with_function("compile", compile_source)? .with_function("compile", compile_source)?
.with_function("load", load_source)? .with_function("load", load_source)?

View file

@ -1,8 +1,14 @@
#![allow(clippy::struct_field_names)]
use mlua::prelude::*; use mlua::prelude::*;
use mlua::Compiler as LuaCompiler; use mlua::Compiler as LuaCompiler;
const DEFAULT_DEBUG_NAME: &str = "luau.load(...)"; const DEFAULT_DEBUG_NAME: &str = "luau.load(...)";
/**
Options for compiling Lua source code.
*/
#[derive(Debug, Clone, Copy)]
pub struct LuauCompileOptions { pub struct LuauCompileOptions {
pub(crate) optimization_level: u8, pub(crate) optimization_level: u8,
pub(crate) coverage_level: u8, pub(crate) coverage_level: u8,
@ -73,6 +79,10 @@ impl<'lua> FromLua<'lua> for LuauCompileOptions {
} }
} }
/**
Options for loading Lua source code.
*/
#[derive(Debug, Clone)]
pub struct LuauLoadOptions<'lua> { pub struct LuauLoadOptions<'lua> {
pub(crate) debug_name: String, pub(crate) debug_name: String,
pub(crate) environment: Option<LuaTable<'lua>>, pub(crate) environment: Option<LuaTable<'lua>>,

View file

@ -0,0 +1,37 @@
[package]
name = "lune-std-net"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[lib]
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
mlua = { version = "0.9.7", features = ["luau"] }
mlua-luau-scheduler = "0.0.2"
bstr = "1.9"
futures-util = "0.3"
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"] }
urlencoding = "2.1"
tokio = { version = "1", default-features = false, features = [
"sync",
"net",
"macros",
] }
lune-utils = { version = "0.1.0", path = "../lune-utils" }
lune-std-serde = { version = "0.1.0", path = "../lune-std-serde" }

View file

@ -4,10 +4,8 @@ use mlua::prelude::*;
use reqwest::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_ENCODING}; use reqwest::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_ENCODING};
use crate::lune::{ use lune_std_serde::{decompress, CompressDecompressFormat};
builtins::serde::compress_decompress::{decompress, CompressDecompressFormat}, use lune_utils::TableBuilder;
util::TableBuilder,
};
use super::{config::RequestConfig, util::header_map_to_table}; use super::{config::RequestConfig, util::header_map_to_table};
@ -103,7 +101,7 @@ impl NetClient {
.and_then(|(_, value)| value.to_str().ok()) .and_then(|(_, value)| value.to_str().ok())
.and_then(CompressDecompressFormat::detect_from_header_str); .and_then(CompressDecompressFormat::detect_from_header_str);
if let Some(format) = decompress_format { if let Some(format) = decompress_format {
res_bytes = decompress(format, res_bytes).await?; res_bytes = decompress(res_bytes, format).await?;
res_decompressed = true; res_decompressed = true;
} }
} }

View file

@ -83,7 +83,7 @@ impl FromLua<'_> for RequestConfig {
query: HashMap::new(), query: HashMap::new(),
headers: HashMap::new(), headers: HashMap::new(),
body: None, body: None,
options: Default::default(), options: RequestConfigOptions::default(),
}) })
} else if let LuaValue::Table(tab) = value { } else if let LuaValue::Table(tab) = value {
// If we got a table we are able to configure the entire request // If we got a table we are able to configure the entire request

View file

@ -1,4 +1,4 @@
#![allow(unused_variables)] #![allow(clippy::cargo_common_metadata)]
use bstr::BString; use bstr::BString;
use mlua::prelude::*; use mlua::prelude::*;
@ -10,7 +10,7 @@ mod server;
mod util; mod util;
mod websocket; mod websocket;
use crate::lune::util::TableBuilder; use lune_utils::TableBuilder;
use self::{ use self::{
client::{NetClient, NetClientBuilder}, client::{NetClient, NetClientBuilder},
@ -20,9 +20,16 @@ use self::{
websocket::NetWebSocket, websocket::NetWebSocket,
}; };
use super::serde::encode_decode::{EncodeDecodeConfig, EncodeDecodeFormat}; use lune_std_serde::{decode, encode, EncodeDecodeConfig, EncodeDecodeFormat};
pub fn create(lua: &Lua) -> LuaResult<LuaTable> { /**
Creates the `net` standard library module.
# Errors
Errors when out of memory.
*/
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
NetClientBuilder::new() NetClientBuilder::new()
.headers(&[("User-Agent", create_user_agent_header(lua)?)])? .headers(&[("User-Agent", create_user_agent_header(lua)?)])?
.build()? .build()?
@ -42,12 +49,13 @@ fn net_json_encode<'lua>(
lua: &'lua Lua, lua: &'lua Lua,
(val, pretty): (LuaValue<'lua>, Option<bool>), (val, pretty): (LuaValue<'lua>, Option<bool>),
) -> LuaResult<LuaString<'lua>> { ) -> LuaResult<LuaString<'lua>> {
EncodeDecodeConfig::from((EncodeDecodeFormat::Json, pretty.unwrap_or_default())) let config = EncodeDecodeConfig::from((EncodeDecodeFormat::Json, pretty.unwrap_or_default()));
.serialize_to_string(lua, val) encode(val, lua, config)
} }
fn net_json_decode(lua: &Lua, json: BString) -> LuaResult<LuaValue> { fn net_json_decode(lua: &Lua, json: BString) -> LuaResult<LuaValue> {
EncodeDecodeConfig::from(EncodeDecodeFormat::Json).deserialize_from_string(lua, json) let config = EncodeDecodeConfig::from(EncodeDecodeFormat::Json);
decode(json, lua, config)
} }
async fn net_request(lua: &Lua, config: RequestConfig) -> LuaResult<LuaTable> { async fn net_request(lua: &Lua, config: RequestConfig) -> LuaResult<LuaTable> {

View file

@ -10,7 +10,7 @@ use tokio::{net::TcpListener, pin};
use mlua::prelude::*; use mlua::prelude::*;
use mlua_luau_scheduler::LuaSpawnExt; use mlua_luau_scheduler::LuaSpawnExt;
use crate::lune::util::TableBuilder; use lune_utils::TableBuilder;
use super::config::ServeConfig; use super::config::ServeConfig;
@ -81,7 +81,7 @@ pub async fn serve<'lua>(
// Wait for either a new connection or a shutdown signal // Wait for either a new connection or a shutdown signal
tokio::select! { tokio::select! {
_ = fut_accept => {} () = fut_accept => {}
res = fut_shutdown => { res = fut_shutdown => {
// NOTE: We will only get a RecvError here if the serve handle is dropped, // NOTE: We will only get a RecvError here if the serve handle is dropped,
// this means lua has garbage collected it and the user does not want // this means lua has garbage collected it and the user does not want
@ -97,8 +97,8 @@ pub async fn serve<'lua>(
TableBuilder::new(lua)? TableBuilder::new(lua)?
.with_value("ip", addr.ip().to_string())? .with_value("ip", addr.ip().to_string())?
.with_value("port", addr.port())? .with_value("port", addr.port())?
.with_function("stop", move |lua, _: ()| match shutdown_tx.send(true) { .with_function("stop", move |_, (): ()| match shutdown_tx.send(true) {
Ok(_) => Ok(()), Ok(()) => Ok(()),
Err(_) => Err(LuaError::runtime("Server already stopped")), Err(_) => Err(LuaError::runtime("Server already stopped")),
})? })?
.build_readonly() .build_readonly()

View file

@ -4,7 +4,7 @@ use http::request::Parts;
use mlua::prelude::*; use mlua::prelude::*;
use crate::lune::util::TableBuilder; use lune_utils::TableBuilder;
pub(super) struct LuaRequest { pub(super) struct LuaRequest {
pub(super) _remote_addr: SocketAddr, pub(super) _remote_addr: SocketAddr,

View file

@ -5,7 +5,7 @@ use reqwest::header::HeaderMap;
use mlua::prelude::*; use mlua::prelude::*;
use crate::lune::util::TableBuilder; use lune_utils::TableBuilder;
pub fn create_user_agent_header(lua: &Lua) -> LuaResult<String> { pub fn create_user_agent_header(lua: &Lua) -> LuaResult<String> {
let version_global = lua let version_global = lua
@ -28,7 +28,7 @@ pub fn header_map_to_table(
remove_content_headers: bool, remove_content_headers: bool,
) -> LuaResult<LuaTable> { ) -> LuaResult<LuaTable> {
let mut res_headers: HashMap<String, Vec<String>> = HashMap::new(); let mut res_headers: HashMap<String, Vec<String>> = HashMap::new();
for (name, value) in headers.iter() { for (name, value) in &headers {
let name = name.as_str(); let name = name.as_str();
let value = value.to_str().unwrap().to_owned(); let value = value.to_str().unwrap().to_owned();
if let Some(existing) = res_headers.get_mut(name) { if let Some(existing) = res_headers.get_mut(name) {

View file

@ -23,7 +23,7 @@ use hyper_tungstenite::{
WebSocketStream, WebSocketStream,
}; };
use crate::lune::util::TableBuilder; use lune_utils::TableBuilder;
// Wrapper implementation for compatibility and changing colon syntax to dot syntax // Wrapper implementation for compatibility and changing colon syntax to dot syntax
const WEB_SOCKET_IMPL_LUA: &str = r#" const WEB_SOCKET_IMPL_LUA: &str = r#"
@ -155,7 +155,7 @@ where
} }
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("close", |lua, this, code: Option<u16>| async move { methods.add_async_method("close", |_, this, code: Option<u16>| async move {
this.close(code).await this.close(code).await
}); });
@ -172,7 +172,7 @@ where
}, },
); );
methods.add_async_method("next", |lua, this, _: ()| async move { methods.add_async_method("next", |lua, this, (): ()| async move {
let msg = this.next().await?; let msg = this.next().await?;
if let Some(WsMessage::Close(Some(frame))) = msg.as_ref() { if let Some(WsMessage::Close(Some(frame))) = msg.as_ref() {

View file

@ -0,0 +1,26 @@
[package]
name = "lune-std-process"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[lib]
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
mlua = { version = "0.9.7", features = ["luau"] }
mlua-luau-scheduler = "0.0.2"
directories = "5.0"
pin-project = "1.0"
os_str_bytes = { version = "7.0", features = ["conversions"] }
tokio = { version = "1", default-features = false, features = [
"sync",
"process",
] }
lune-utils = { version = "0.1.0", path = "../lune-utils" }

View file

@ -1,36 +1,49 @@
#![allow(clippy::cargo_common_metadata)]
use std::{ use std::{
env::{self, consts}, env::{
path, self,
consts::{ARCH, OS},
},
path::MAIN_SEPARATOR,
process::Stdio, process::Stdio,
}; };
use mlua::prelude::*; use mlua::prelude::*;
use lune_utils::TableBuilder;
use mlua_luau_scheduler::{Functions, LuaSpawnExt}; use mlua_luau_scheduler::{Functions, LuaSpawnExt};
use os_str_bytes::RawOsString; use os_str_bytes::RawOsString;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use crate::lune::util::{paths::CWD, TableBuilder};
mod tee_writer;
mod options; mod options;
use options::ProcessSpawnOptions; mod tee_writer;
mod wait_for_child; mod wait_for_child;
use wait_for_child::{wait_for_child, WaitForChildResult};
pub fn create(lua: &Lua) -> LuaResult<LuaTable> { use self::options::ProcessSpawnOptions;
let cwd_str = { use self::wait_for_child::{wait_for_child, WaitForChildResult};
let cwd_str = CWD.to_string_lossy().to_string();
if !cwd_str.ends_with(path::MAIN_SEPARATOR) { use lune_utils::path::get_current_dir;
format!("{cwd_str}{}", path::MAIN_SEPARATOR)
} else { /**
cwd_str Creates the `process` standard library module.
# Errors
Errors when out of memory.
*/
#[allow(clippy::missing_panics_doc)]
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
let mut cwd_str = get_current_dir()
.to_str()
.expect("cwd should be valid UTF-8")
.to_string();
if !cwd_str.ends_with(MAIN_SEPARATOR) {
cwd_str.push(MAIN_SEPARATOR);
} }
};
// Create constants for OS & processor architecture // Create constants for OS & processor architecture
let os = lua.create_string(&consts::OS.to_lowercase())?; let os = lua.create_string(&OS.to_lowercase())?;
let arch = lua.create_string(&consts::ARCH.to_lowercase())?; let arch = lua.create_string(&ARCH.to_lowercase())?;
// Create readonly args array // Create readonly args array
let args_vec = lua let args_vec = lua
.app_data_ref::<Vec<String>>() .app_data_ref::<Vec<String>>()
@ -94,9 +107,7 @@ fn process_env_set<'lua>(
Err(LuaError::RuntimeError( Err(LuaError::RuntimeError(
"Key must not contain the NUL character".to_string(), "Key must not contain the NUL character".to_string(),
)) ))
} else { } else if let Some(value) = value {
match value {
Some(value) => {
// Make sure value is valid, otherwise set_var will panic // Make sure value is valid, otherwise set_var will panic
if value.contains('\0') { if value.contains('\0') {
Err(LuaError::RuntimeError( Err(LuaError::RuntimeError(
@ -106,21 +117,18 @@ fn process_env_set<'lua>(
env::set_var(&key, &value); env::set_var(&key, &value);
Ok(()) Ok(())
} }
} } else {
None => {
env::remove_var(&key); env::remove_var(&key);
Ok(()) Ok(())
} }
}
}
} }
fn process_env_iter<'lua>( fn process_env_iter<'lua>(
lua: &'lua Lua, lua: &'lua Lua,
(_, _): (LuaValue<'lua>, ()), (_, ()): (LuaValue<'lua>, ()),
) -> LuaResult<LuaFunction<'lua>> { ) -> LuaResult<LuaFunction<'lua>> {
let mut vars = env::vars_os().collect::<Vec<_>>().into_iter(); let mut vars = env::vars_os().collect::<Vec<_>>().into_iter();
lua.create_function_mut(move |lua, _: ()| match vars.next() { lua.create_function_mut(move |lua, (): ()| match vars.next() {
Some((key, value)) => { Some((key, value)) => {
let raw_key = RawOsString::new(key); let raw_key = RawOsString::new(key);
let raw_value = RawOsString::new(value); let raw_value = RawOsString::new(value);
@ -149,10 +157,10 @@ async fn process_spawn(
An exit code may be missing if the process was terminated by An exit code may be missing if the process was terminated by
some external signal, which is the only time we use this default some external signal, which is the only time we use this default
*/ */
let code = res.status.code().unwrap_or(match res.stderr.is_empty() { let code = res
true => 0, .status
false => 1, .code()
}); .unwrap_or(i32::from(!res.stderr.is_empty()));
// Construct and return a readonly lua table with results // Construct and return a readonly lua table with results
TableBuilder::new(lua)? TableBuilder::new(lua)?
@ -174,9 +182,10 @@ async fn spawn_command(
let mut child = options let mut child = options
.into_command(program, args) .into_command(program, args)
.stdin(match stdin.is_some() { .stdin(if stdin.is_some() {
true => Stdio::piped(), Stdio::piped()
false => Stdio::null(), } else {
Stdio::null()
}) })
.stdout(stdout.as_stdio()) .stdout(stdout.as_stdio())
.stderr(stderr.as_stdio()) .stderr(stderr.as_stdio())

View file

@ -1,6 +1,5 @@
use std::{fmt, process::Stdio, str::FromStr}; use std::{fmt, process::Stdio, str::FromStr};
use itertools::Itertools;
use mlua::prelude::*; use mlua::prelude::*;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
@ -55,6 +54,7 @@ impl FromStr for ProcessSpawnOptionsStdioKind {
ProcessSpawnOptionsStdioKind::all() ProcessSpawnOptionsStdioKind::all()
.iter() .iter()
.map(|k| format!("'{k}'")) .map(|k| format!("'{k}'"))
.collect::<Vec<_>>()
.join(", ") .join(", ")
))) )))
} }

View file

@ -56,7 +56,7 @@ impl<'lua> FromLua<'lua> for ProcessSpawnOptions {
"Invalid value for option 'cwd' - failed to get home directory", "Invalid value for option 'cwd' - failed to get home directory",
) )
})?; })?;
cwd = user_dirs.home_dir().join(stripped) cwd = user_dirs.home_dir().join(stripped);
} }
if !cwd.exists() { if !cwd.exists() {
return Err(LuaError::runtime( return Err(LuaError::runtime(

View file

@ -24,8 +24,7 @@ where
R: AsyncRead + Unpin, R: AsyncRead + Unpin,
{ {
Ok(match kind { Ok(match kind {
ProcessSpawnOptionsStdioKind::None => Vec::new(), ProcessSpawnOptionsStdioKind::None | ProcessSpawnOptionsStdioKind::Forward => Vec::new(),
ProcessSpawnOptionsStdioKind::Forward => Vec::new(),
ProcessSpawnOptionsStdioKind::Default => { ProcessSpawnOptionsStdioKind::Default => {
let mut read_from = let mut read_from =
read_from.expect("read_from must be Some when stdio kind is Default"); read_from.expect("read_from must be Some when stdio kind is Default");

View file

@ -0,0 +1,19 @@
[package]
name = "lune-std-regex"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[lib]
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
mlua = { version = "0.9.7", features = ["luau"] }
regex = "1.10"
self_cell = "1.0"
lune-utils = { version = "0.1.0", path = "../lune-utils" }

View file

@ -1,8 +1,8 @@
#![allow(clippy::module_inception)] #![allow(clippy::cargo_common_metadata)]
use mlua::prelude::*; use mlua::prelude::*;
use crate::lune::util::TableBuilder; use lune_utils::TableBuilder;
mod captures; mod captures;
mod matches; mod matches;
@ -10,7 +10,14 @@ mod regex;
use self::regex::LuaRegex; use self::regex::LuaRegex;
pub fn create(lua: &Lua) -> LuaResult<LuaTable> { /**
Creates the `regex` standard library module.
# Errors
Errors when out of memory.
*/
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
TableBuilder::new(lua)? TableBuilder::new(lua)?
.with_function("new", new_regex)? .with_function("new", new_regex)?
.build_readonly() .build_readonly()

View file

@ -46,7 +46,7 @@ impl LuaUserData for LuaRegex {
Ok(this Ok(this
.inner .inner
.split(&text) .split(&text)
.map(|s| s.to_string()) .map(ToString::to_string)
.collect::<Vec<_>>()) .collect::<Vec<_>>())
}); });

View file

@ -0,0 +1,21 @@
[package]
name = "lune-std-roblox"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[lib]
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
mlua = { version = "0.9.7", features = ["luau"] }
mlua-luau-scheduler = "0.0.2"
once_cell = "1.17"
rbx_cookie = { version = "0.1.4", default-features = false }
lune-utils = { version = "0.1.0", path = "../lune-utils" }
lune-roblox = { version = "0.1.0", path = "../lune-roblox" }

View file

@ -1,23 +1,30 @@
#![allow(clippy::cargo_common_metadata)]
use mlua::prelude::*; use mlua::prelude::*;
use mlua_luau_scheduler::LuaSpawnExt; use mlua_luau_scheduler::LuaSpawnExt;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use crate::{ use lune_roblox::{
lune::util::TableBuilder,
roblox::{
self,
document::{Document, DocumentError, DocumentFormat, DocumentKind}, document::{Document, DocumentError, DocumentFormat, DocumentKind},
instance::{registry::InstanceRegistry, Instance}, instance::{registry::InstanceRegistry, Instance},
reflection::Database as ReflectionDatabase, reflection::Database as ReflectionDatabase,
},
}; };
static REFLECTION_DATABASE: OnceCell<ReflectionDatabase> = OnceCell::new(); static REFLECTION_DATABASE: OnceCell<ReflectionDatabase> = OnceCell::new();
pub fn create(lua: &Lua) -> LuaResult<LuaTable> { use lune_utils::TableBuilder;
/**
Creates the `roblox` standard library module.
# Errors
Errors when out of memory.
*/
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
let mut roblox_constants = Vec::new(); let mut roblox_constants = Vec::new();
let roblox_module = roblox::module(lua)?; let roblox_module = lune_roblox::module(lua)?;
for pair in roblox_module.pairs::<LuaValue, LuaValue>() { for pair in roblox_module.pairs::<LuaValue, LuaValue>() {
roblox_constants.push(pair?); roblox_constants.push(pair?);
} }
@ -116,16 +123,15 @@ fn implement_property(
Option<LuaFunction>, Option<LuaFunction>,
), ),
) -> LuaResult<()> { ) -> LuaResult<()> {
let property_setter = match property_setter { let property_setter = if let Some(setter) = property_setter {
Some(setter) => setter, setter
None => { } else {
let property_name = property_name.clone(); let property_name = property_name.clone();
lua.create_function(move |_, _: LuaMultiValue| { lua.create_function(move |_, _: LuaMultiValue| {
Err::<(), _>(LuaError::runtime(format!( Err::<(), _>(LuaError::runtime(format!(
"Property '{property_name}' is read-only" "Property '{property_name}' is read-only"
))) )))
})? })?
}
}; };
InstanceRegistry::insert_property_getter(lua, &class_name, &property_name, property_getter) InstanceRegistry::insert_property_getter(lua, &class_name, &property_name, property_getter)
.into_lua_err()?; .into_lua_err()?;

View file

@ -0,0 +1,35 @@
[package]
name = "lune-std-serde"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[lib]
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
mlua = { version = "0.9.7", features = ["luau"] }
async-compression = { version = "0.4", features = [
"tokio",
"brotli",
"deflate",
"gzip",
"zlib",
] }
bstr = "1.9"
lz4 = "1.24"
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"] }
tokio = { version = "1", default-features = false, features = [
"rt",
"io-util",
] }
lune-utils = { version = "0.1.0", path = "../lune-utils" }

Some files were not shown because too many files have changed in this diff Show more