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
- name: Install Just
uses: extractions/setup-just@v1
uses: extractions/setup-just@v2
- name: Install Tooling
uses: ok-nick/setup-aftman@v0.4.2
@ -41,7 +41,7 @@ jobs:
uses: actions/checkout@v4
- name: Install Just
uses: extractions/setup-just@v1
uses: extractions/setup-just@v2
- name: Install Tooling
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
- name: Get version from manifest
uses: SebRollen/toml-action@9062fbef52816d61278d24ce53c8070440e1e8dd
uses: SebRollen/toml-action@v1.2.0
id: get_version
with:
file: Cargo.toml
field: package.version
build:
dry-run:
name: Dry-run
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:
fail-fast: false
matrix:
@ -70,7 +88,7 @@ jobs:
targets: ${{ matrix.cargo-target }}
- name: Install Just
uses: extractions/setup-just@v1
uses: extractions/setup-just@v2
- name: Install build tooling (aarch64-unknown-linux-gnu)
if: matrix.cargo-target == 'aarch64-unknown-linux-gnu'
@ -86,24 +104,24 @@ jobs:
run: just zip-release ${{ matrix.cargo-target }}
- name: Upload release artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: release.zip
release:
name: Release
release-github:
name: Release (GitHub)
runs-on: ubuntu-latest
needs: ["init", "build"]
needs: ["init", "dry-run", "build"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Just
uses: extractions/setup-just@v1
uses: extractions/setup-just@v2
- name: Download releases
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
path: ./releases
@ -111,7 +129,7 @@ jobs:
run: just unpack-releases "./releases"
- name: Create release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@ -120,3 +138,21 @@ jobs:
fail_on_unmatched_files: true
files: ./releases/*.zip
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]]
name = "async-compression"
version = "0.4.8"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07dbbf24db18d609b1462965249abdf49129ccad073ec257da372adc83259c60"
checksum = "9c90a406b4495d129f00461241616194cb8a032c8d1c53c657f0961d5f8e0498"
dependencies = [
"brotli",
"flate2",
@ -205,17 +205,6 @@ version = "4.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "atomic-waker"
version = "1.1.2"
@ -324,9 +313,9 @@ dependencies = [
[[package]]
name = "brotli"
version = "4.0.0"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "125740193d7fee5cc63ab9e16c2fdc4e07c74ba755cc53b327d6ea029e9fc569"
checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@ -335,9 +324,9 @@ dependencies = [
[[package]]
name = "brotli-decompressor"
version = "3.0.0"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65622a320492e09b5e0ac436b14c54ff68199bac392d0e89a6832c4518eea525"
checksum = "e6221fe77a248b9117d431ad93761222e1cf8ff282d9d1d5d9f53d6299a1cf76"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@ -759,12 +748,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
[[package]]
name = "either"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
[[package]]
name = "encode_unicode"
version = "0.3.6"
@ -1367,15 +1350,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -1484,57 +1458,203 @@ name = "lune"
version = "0.8.3"
dependencies = [
"anyhow",
"async-compression",
"async-trait",
"blocking",
"bstr",
"chrono",
"chrono_lc",
"clap",
"console",
"dialoguer",
"directories",
"dunce",
"env_logger",
"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",
"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-body-util",
"hyper 1.3.1",
"hyper-tungstenite",
"hyper-util",
"include_dir",
"itertools",
"lz4_flex",
"lune-std-serde",
"lune-utils",
"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-luau-scheduler",
"once_cell",
"os_str_bytes",
"path-clean",
"pathdiff",
"pin-project",
"rand",
"rbx_binary",
"rbx_cookie",
"rbx_dom_weak",
"rbx_reflection",
"rbx_reflection_database",
"rbx_xml",
"regex",
"reqwest",
"rustyline",
"self_cell",
]
[[package]]
name = "lune-std-serde"
version = "0.1.0"
dependencies = [
"async-compression",
"bstr",
"lune-utils",
"lz4",
"mlua",
"serde",
"serde_json",
"serde_yaml",
"thiserror",
"tokio",
"tokio-tungstenite",
"toml",
"tracing",
"tracing-subscriber",
"urlencoding",
"zip_next",
]
[[package]]
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]]
@ -1557,15 +1677,6 @@ dependencies = [
"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]]
name = "lzma-rs"
version = "0.3.0"
@ -2624,12 +2735,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stdweb"
version = "0.4.20"
@ -2882,7 +2987,6 @@ dependencies = [
"signal-hook-registry",
"socket2",
"tokio-macros",
"tracing",
"windows-sys 0.48.0",
]
@ -3100,16 +3204,6 @@ dependencies = [
"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]]
name = "typed-arena"
version = "2.0.2"

View file

@ -1,41 +1,21 @@
[package]
name = "lune"
version = "0.8.3"
edition = "2021"
license = "MPL-2.0"
repository = "https://github.com/lune-org/lune"
description = "A standalone Luau runtime"
readme = "README.md"
keywords = ["cli", "lua", "luau", "runtime"]
categories = ["command-line-interface"]
[[bin]]
name = "lune"
path = "src/main.rs"
[lib]
name = "lune"
path = "src/lib.rs"
[features]
default = ["cli", "roblox"]
cli = [
"dep:anyhow",
"dep:env_logger",
"dep:clap",
"dep:include_dir",
"dep:rustyline",
"dep:zip_next",
]
roblox = [
"dep:glam",
"dep:rand",
"dep:rbx_cookie",
"dep:rbx_binary",
"dep:rbx_dom_weak",
"dep:rbx_reflection",
"dep:rbx_reflection_database",
"dep:rbx_xml",
[workspace]
resolver = "2"
default-members = ["crates/lune"]
members = [
"crates/lune",
"crates/lune-roblox",
"crates/lune-std",
"crates/lune-std-datetime",
"crates/lune-std-fs",
"crates/lune-std-luau",
"crates/lune-std-net",
"crates/lune-std-process",
"crates/lune-std-regex",
"crates/lune-std-roblox",
"crates/lune-std-serde",
"crates/lune-std-stdio",
"crates/lune-std-task",
"crates/lune-utils",
]
# Profile for building the release binary, with the following options set:
@ -53,99 +33,31 @@ opt-level = "z"
strip = true
lto = true
# All of the dependencies for Lune.
# Lints for all crates in the workspace
#
# Dependencies are categorized as following:
#
# 1. General dependencies with no specific features set
# 2. Large / core dependencies that have many different crates and / or features set
# 3. Dependencies for specific features of Lune, eg. the CLI or massive Roblox builtin library
#
[dependencies]
console = "0.15"
directories = "5.0"
futures-util = "0.3"
once_cell = "1.17"
thiserror = "1.0"
async-trait = "0.1"
dialoguer = "0.11"
dunce = "1.0"
lz4_flex = "0.11"
path-clean = "1.0"
pathdiff = "0.2"
pin-project = "1.0"
urlencoding = "2.1"
bstr = "1.9"
regex = "1.10"
self_cell = "1.0"
# 1. Error on all lints by default, then make cargo + clippy pedantic lints just warn
# 2. Selectively allow some lints that are _too_ pedantic, such as:
# - Casts between number types
# - Module naming conventions
# - Imports and multiple dependency versions
[workspace.lints.clippy]
all = { level = "deny", priority = -3 }
cargo = { level = "warn", priority = -2 }
pedantic = { level = "warn", priority = -1 }
### 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"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tokio = { version = "1.24", features = ["full", "tracing"] }
os_str_bytes = { version = "7.0", features = ["conversions"] }
similar_names = { level = "allow", priority = 1 }
unnecessary_wraps = { level = "allow", priority = 1 }
unnested_or_patterns = { level = "allow", priority = 1 }
unreadable_literal = { level = "allow", priority = 1 }
mlua-luau-scheduler = { version = "0.0.2" }
mlua = { version = "0.9.7", features = [
"luau",
"luau-jit",
"async",
"serialize",
] }
### SERDE
async-compression = { version = "0.4", features = [
"tokio",
"brotli",
"deflate",
"gzip",
"zlib",
] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde_yaml = "0.9"
toml = { version = "0.8", features = ["preserve_order"] }
### NET
hyper = { version = "1.1", features = ["full"] }
hyper-util = { version = "0.1", features = ["full"] }
http = "1.0"
http-body-util = { version = "0.1" }
hyper-tungstenite = { version = "0.13" }
reqwest = { version = "0.11", default-features = false, features = [
"rustls-tls",
] }
tokio-tungstenite = { version = "0.21", features = ["rustls-tls-webpki-roots"] }
### DATETIME
chrono = "=0.4.34" # NOTE: 0.4.35 does not compile with chrono_lc
chrono_lc = "0.1"
### CLI
anyhow = { optional = true, version = "1.0" }
env_logger = { optional = true, version = "0.11" }
itertools = "0.12"
clap = { optional = true, version = "4.1", features = ["derive"] }
include_dir = { optional = true, version = "0.7", features = ["glob"] }
rustyline = { optional = true, version = "14.0" }
zip_next = { optional = true, version = "1.1" }
### ROBLOX
glam = { optional = true, version = "0.27" }
rand = { optional = true, version = "0.8" }
rbx_cookie = { optional = true, version = "0.1.4", default-features = false }
rbx_binary = { optional = true, version = "0.7.3" }
rbx_dom_weak = { optional = true, version = "2.6.0" }
rbx_reflection = { optional = true, version = "4.4.0" }
rbx_reflection_database = { optional = true, version = "0.2.9" }
rbx_xml = { optional = true, version = "0.13.2" }
multiple_crate_versions = { level = "allow", priority = 1 }
module_inception = { level = "allow", priority = 1 }
module_name_repetitions = { level = "allow", priority = 1 }
needless_pass_by_value = { level = "allow", priority = 1 }
wildcard_imports = { level = "allow", priority = 1 }

View file

@ -1,4 +1,4 @@
[tools]
luau-lsp = "JohnnyMorganz/luau-lsp@1.27.0"
selene = "Kampfkarren/selene@0.26.1"
stylua = "JohnnyMorganz/StyLua@0.19.1"
luau-lsp = "JohnnyMorganz/luau-lsp@1.29.0"
selene = "Kampfkarren/selene@0.27.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;
/**
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<()> {
let name = name.as_ref();
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<()> {
let is_valid = matches!(
value.ty(),

View file

@ -2,7 +2,7 @@ use mlua::prelude::*;
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::*;
@ -65,8 +65,10 @@ impl<'lua> DomValueToLua<'lua> for LuaValue<'lua> {
// NOTE: Some values are either optional or default and we should handle
// that properly here since the userdata conversion above will always fail
DomValue::OptionalCFrame(None) => Ok(LuaValue::Nil),
DomValue::PhysicalProperties(dom::PhysicalProperties::Default) => Ok(LuaValue::Nil),
DomValue::OptionalCFrame(None)
| DomValue::PhysicalProperties(dom::PhysicalProperties::Default) => {
Ok(LuaValue::Nil)
}
_ => Err(e),
},

View file

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

View file

@ -10,4 +10,4 @@ mod util;
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 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};
@ -52,8 +54,7 @@ impl LuaExportsTable<'_> for Axes {
check(&e);
} else {
return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got userdata",
index
"Expected argument #{index} to be an EnumItem, got userdata",
)));
}
} else {

View file

@ -4,14 +4,16 @@ use mlua::prelude::*;
use rand::seq::SliceRandom;
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};
/**
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)]
pub struct BrickColor {

View file

@ -1,3 +1,5 @@
#![allow(clippy::items_after_statements)]
use core::fmt;
use std::ops;
@ -5,7 +7,9 @@ use glam::{EulerRot, Mat3, Mat4, Quat, Vec3};
use mlua::{prelude::*, Variadic};
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};
@ -14,7 +18,7 @@ use super::{super::*, Vector3};
Roblox datatype, backed by [`glam::Mat4`].
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)]
pub struct CFrame(pub Mat4);
@ -42,6 +46,7 @@ impl CFrame {
impl LuaExportsTable<'_> for CFrame {
const EXPORT_NAME: &'static str = "CFrame";
#[allow(clippy::too_many_lines)]
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let cframe_angles = |_, (rx, ry, rz): (f32, f32, f32)| {
Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz)))
@ -68,8 +73,7 @@ impl LuaExportsTable<'_> for CFrame {
Ok(CFrame(Mat4::from_cols(
rx.0.extend(0.0),
ry.0.extend(0.0),
rz.map(|r| r.0)
.unwrap_or_else(|| rx.0.cross(ry.0).normalize())
rz.map_or_else(|| rx.0.cross(ry.0).normalize(), |r| r.0)
.extend(0.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) {
// Methods
methods.add_method("Inverse", |_, this, ()| Ok(this.inverse()));
@ -226,34 +231,49 @@ impl LuaUserData for CFrame {
methods.add_method(
"ToWorldSpace",
|_, 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(
"ToObjectSpace",
|_, this, rhs: Variadic<LuaUserDataRef<CFrame>>| {
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(
"PointToWorldSpace",
|_, 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(
"PointToObjectSpace",
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
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(
"VectorToWorldSpace",
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
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(
@ -261,8 +281,10 @@ impl LuaUserData for CFrame {
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
let inverse = this.inverse();
let result = inverse - Vector3(inverse.position());
Ok(Variadic::from_iter(rhs.into_iter().map(|v3| result * *v3)))
Ok(rhs
.into_iter()
.map(|v3| result * *v3)
.collect::<Variadic<_>>())
},
);
#[rustfmt::skip]
@ -445,7 +467,7 @@ mod cframe_test {
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]
@ -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 std::ops;
@ -5,7 +7,9 @@ use glam::Vec3;
use mlua::prelude::*;
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::*;
@ -85,8 +89,7 @@ impl LuaExportsTable<'_> for Color3 {
b: (b as f32) / 255f32,
}),
_ => Err(LuaError::RuntimeError(format!(
"Hex color string '{}' contains invalid character",
trimmed
"Hex color string '{trimmed}' contains invalid character",
))),
}
};
@ -151,6 +154,7 @@ impl LuaUserData for Color3 {
let max = r.max(g).max(b);
let diff = max - min;
#[allow(clippy::float_cmp)]
let hue = (match max {
max if max == min => 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,
};
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use lune_utils::TableBuilder;
use crate::exports::LuaExportsTable;
use super::{super::*, Color3, ColorSequenceKeypoint};
/**
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)]
pub struct ColorSequence {
@ -87,9 +89,9 @@ impl fmt::Display for ColorSequence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (index, keypoint) in self.keypoints.iter().enumerate() {
if index < self.keypoints.len() - 1 {
write!(f, "{}, ", keypoint)?;
write!(f, "{keypoint}, ")?;
} else {
write!(f, "{}", keypoint)?;
write!(f, "{keypoint}")?;
}
}
Ok(())
@ -102,7 +104,7 @@ impl From<DomColorSequence> for ColorSequence {
keypoints: v
.keypoints
.iter()
.cloned()
.copied()
.map(ColorSequenceKeypoint::from)
.collect(),
}
@ -115,7 +117,7 @@ impl From<ColorSequence> for DomColorSequence {
keypoints: v
.keypoints
.iter()
.cloned()
.copied()
.map(DomColorSequenceKeypoint::from)
.collect(),
}

View file

@ -3,14 +3,16 @@ use core::fmt;
use mlua::prelude::*;
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};
/**
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)]
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.
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)]
pub struct EnumItem {

View file

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

View file

@ -1,9 +1,13 @@
#![allow(clippy::struct_excessive_bools)]
use core::fmt;
use mlua::prelude::*;
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};
@ -54,8 +58,7 @@ impl LuaExportsTable<'_> for Faces {
check(&e);
} else {
return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got userdata",
index
"Expected argument #{index} to be an EnumItem, got userdata",
)));
}
} else {

View file

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

View file

@ -5,14 +5,16 @@ use rbx_dom_weak::types::{
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};
/**
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)]
pub struct NumberSequence {
@ -91,9 +93,9 @@ impl fmt::Display for NumberSequence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (index, keypoint) in self.keypoints.iter().enumerate() {
if index < self.keypoints.len() - 1 {
write!(f, "{}, ", keypoint)?;
write!(f, "{keypoint}, ")?;
} else {
write!(f, "{}", keypoint)?;
write!(f, "{keypoint}")?;
}
}
Ok(())
@ -106,7 +108,7 @@ impl From<DomNumberSequence> for NumberSequence {
keypoints: v
.keypoints
.iter()
.cloned()
.copied()
.map(NumberSequenceKeypoint::from)
.collect(),
}
@ -119,7 +121,7 @@ impl From<NumberSequence> for DomNumberSequence {
keypoints: v
.keypoints
.iter()
.cloned()
.copied()
.map(DomNumberSequenceKeypoint::from)
.collect(),
}

View file

@ -3,14 +3,16 @@ use core::fmt;
use mlua::prelude::*;
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::*;
/**
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)]
pub struct NumberSequenceKeypoint {

View file

@ -3,14 +3,16 @@ use core::fmt;
use mlua::prelude::*;
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};
/**
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)]
pub struct PhysicalProperties {

View file

@ -4,7 +4,9 @@ use glam::Vec3;
use mlua::prelude::*;
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};

View file

@ -5,7 +5,9 @@ use glam::Vec2;
use mlua::prelude::*;
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};

View file

@ -4,7 +4,9 @@ use glam::{Mat4, Vec3};
use mlua::prelude::*;
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};

View file

@ -4,7 +4,9 @@ use glam::IVec3;
use mlua::prelude::*;
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};

View file

@ -4,14 +4,16 @@ use std::ops;
use mlua::prelude::*;
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::*;
/**
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)]
pub struct UDim {

View file

@ -1,3 +1,5 @@
#![allow(clippy::items_after_statements)]
use core::fmt;
use std::ops;
@ -5,14 +7,16 @@ use glam::Vec2;
use mlua::prelude::*;
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};
/**
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)]
pub struct UDim2 {

View file

@ -5,7 +5,9 @@ use glam::{Vec2, Vec3};
use mlua::prelude::*;
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::*;

View file

@ -5,7 +5,9 @@ use glam::IVec2;
use mlua::prelude::*;
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::*;

View file

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

View file

@ -5,7 +5,9 @@ use glam::IVec3;
use mlua::prelude::*;
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::*;

View file

@ -2,7 +2,7 @@ use std::path::Path;
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.
@ -58,6 +58,7 @@ impl DocumentKind {
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> {
let mut has_top_level_child = false;
let mut has_top_level_service = false;

View file

@ -15,7 +15,7 @@ pub use kind::*;
use postprocessing::*;
use crate::roblox::instance::{data_model, Instance};
use crate::instance::{data_model, Instance};
pub type DocumentResult<T> = Result<T, DocumentError>;
@ -78,6 +78,7 @@ impl Document {
| Model | Binary | `rbxm` |
| Model | Xml | `rbxmx` |
*/
#[must_use]
#[rustfmt::skip]
pub fn canonical_extension(kind: DocumentKind, format: DocumentFormat) -> &'static str {
match (kind, format) {
@ -113,6 +114,10 @@ impl Document {
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
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> {
let (format, dom) = Self::from_bytes_inner(bytes)?;
@ -125,6 +130,10 @@ impl Document {
This will automatically handle and detect if the document
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> {
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
with, meaning if the document is a binary document the output
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>> {
self.to_bytes_with_format(self.format)
@ -146,6 +159,10 @@ impl Document {
/**
Encodes the document as a vector of bytes, to
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>> {
let mut bytes = Vec::new();
@ -172,6 +189,7 @@ impl Document {
/**
Gets the kind this document was created with.
*/
#[must_use]
pub fn kind(&self) -> DocumentKind {
self.kind
}
@ -179,6 +197,7 @@ impl Document {
/**
Gets the format this document was created with.
*/
#[must_use]
pub fn format(&self) -> DocumentFormat {
self.format
}
@ -186,14 +205,17 @@ impl Document {
/**
Gets the file extension for this document.
*/
#[must_use]
pub fn extension(&self) -> &'static str {
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> {
if self.kind != DocumentKind::Place {
@ -219,7 +241,9 @@ impl 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>> {
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> {
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.
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> {
for i in &v {

View file

@ -3,7 +3,7 @@ use rbx_dom_weak::{
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) {
// Nothing here yet

View file

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

View file

@ -1,6 +1,6 @@
use mlua::prelude::*;
use crate::roblox::shared::{
use crate::shared::{
classes::{
add_class_restricted_getter, add_class_restricted_method,
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
* [`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> {
if matches!(class_is_a_service(&service_name), None | Some(false)) {
Err(LuaError::RuntimeError(format!(
"'{}' is not a valid service name",
service_name
"'{service_name}' is not a valid service name",
)))
} else if let Some(service) = this.find_child(|child| child.class == service_name) {
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
* [`FindService`](https://create.roblox.com/docs/reference/engine/classes/ServiceProvider#FindService)
@ -68,8 +67,7 @@ fn data_model_find_service(
) -> LuaResult<Option<Instance>> {
if matches!(class_is_a_service(&service_name), None | Some(false)) {
Err(LuaError::RuntimeError(format!(
"'{}' is not a valid service name",
service_name
"'{service_name}' is not a valid service name",
)))
} else if let Some(service) = this.find_child(|child| child.class == service_name) {
Ok(Some(service))

View file

@ -1,3 +1,5 @@
#![allow(clippy::missing_panics_doc)]
use std::{
collections::{BTreeMap, VecDeque},
fmt,
@ -12,10 +14,11 @@ use rbx_dom_weak::{
Instance as DomInstance, InstanceBuilder as DomInstanceBuilder, WeakDom,
};
use lune_utils::TableBuilder;
use crate::{
lune::util::TableBuilder,
roblox::exports::LuaExportsTable,
roblox::shared::instance::{class_exists, class_is_a},
exports::LuaExportsTable,
shared::instance::{class_exists, class_is_a},
};
pub(crate) mod base;
@ -54,9 +57,10 @@ impl Instance {
.get_by_ref(dom_ref)
.expect("Failed to find instance in document");
if instance.referent() == dom.root_ref() {
panic!("Instances can not be created from dom roots")
}
assert!(
!(instance.referent() == dom.root_ref()),
"Instances can not be created from dom roots"
);
Self {
dom_ref,
@ -76,9 +80,10 @@ impl Instance {
let dom = INTERNAL_DOM.lock().expect("Failed to lock document");
if let Some(instance) = dom.get_by_ref(dom_ref) {
if instance.referent() == dom.root_ref() {
panic!("Instances can not be created from dom roots")
}
assert!(
!(instance.referent() == dom.root_ref()),
"Instances can not be created from dom roots"
);
Some(Self {
dom_ref,
@ -154,7 +159,7 @@ impl Instance {
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());
}
@ -171,7 +176,8 @@ impl Instance {
* [`Clone`](https://create.roblox.com/docs/reference/engine/classes/Instance#Clone)
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 new_ref = dom.clone_within(self.dom_ref);
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)
on the Roblox Developer Hub
*/
#[must_use]
pub fn get_class_name(&self) -> &str {
self.class_name.as_str()
}
@ -286,7 +293,7 @@ impl Instance {
dom.get_by_ref_mut(self.dom_ref)
.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>) {
let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document");
let parent_ref = parent
.map(|parent| parent.dom_ref)
.unwrap_or_else(|| dom.root_ref());
let parent_ref = parent.map_or_else(|| dom.root_ref(), |parent| parent.dom_ref);
dom.transfer_within(self.dom_ref, parent_ref);
}
@ -663,9 +668,8 @@ impl Instance {
if predicate(ancestor) {
drop(dom); // Self::new needs mutex handle, drop it first
return Some(Self::new(ancestor_ref));
} else {
ancestor_ref = ancestor.parent();
}
ancestor_ref = ancestor.parent();
}
None
@ -699,9 +703,8 @@ impl Instance {
let queue_ref = queue_item.referent();
drop(dom); // Self::new needs mutex handle, drop it first
return Some(Self::new(queue_ref));
} else {
queue.extend(queue_item.children())
}
queue.extend(queue_item.children());
}
None
@ -717,8 +720,7 @@ impl LuaExportsTable<'_> for Instance {
Instance::new_orphaned(class_name).into_lua(lua)
} else {
Err(LuaError::RuntimeError(format!(
"Failed to create Instance - '{}' is not a valid class name",
class_name
"Failed to create Instance - '{class_name}' is not a valid class name",
)))
}
};
@ -756,7 +758,7 @@ impl LuaUserData for Instance {
impl Hash for Instance {
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")
}
/**
Inserts a method into the instance registry.
# Errors
- If the method already exists in the registry.
*/
pub fn insert_method<'lua>(
lua: &'lua Lua,
class_name: &str,
@ -80,6 +87,13 @@ impl InstanceRegistry {
Ok(())
}
/**
Inserts a property getter into the instance registry.
# Errors
- If the property already exists in the registry.
*/
pub fn insert_property_getter<'lua>(
lua: &'lua Lua,
class_name: &str,
@ -109,6 +123,13 @@ impl InstanceRegistry {
Ok(())
}
/**
Inserts a property setter into the instance registry.
# Errors
- If the property already exists in the registry.
*/
pub fn insert_property_setter<'lua>(
lua: &'lua Lua,
class_name: &str,
@ -138,6 +159,12 @@ impl InstanceRegistry {
Ok(())
}
/**
Finds a method in the instance registry.
Returns `None` if the method is not found.
*/
#[must_use]
pub fn find_method<'lua>(
lua: &'lua Lua,
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>(
lua: &'lua Lua,
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>(
lua: &'lua Lua,
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> {
let db = rbx_reflection_database::get();

View file

@ -1,7 +1,7 @@
use mlua::prelude::*;
use rbx_dom_weak::types::{MaterialColors, TerrainMaterials, Variant};
use crate::roblox::{
use crate::{
datatypes::types::{Color3, EnumItem},
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,
"SetMaterialColor",
terrain_set_material_color,
)
);
}
fn get_or_create_material_colors(instance: &Instance) -> MaterialColors {

View file

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

View file

@ -1,6 +1,8 @@
#![allow(clippy::cargo_common_metadata)]
use mlua::prelude::*;
use crate::lune::util::TableBuilder;
use lune_utils::TableBuilder;
pub mod datatypes;
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> {
// FUTURE: We can probably create these lazily as users
// 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 super::{property::DatabaseProperty, utils::*};
use crate::roblox::datatypes::{
use crate::datatypes::{
conversion::DomValueToLua, types::EnumItem, userdata_impl_eq, userdata_impl_to_string,
};
@ -28,6 +28,7 @@ impl DatabaseClass {
/**
Get the name of this class.
*/
#[must_use]
pub fn get_name(&self) -> String {
self.0.name.to_string()
}
@ -37,6 +38,7 @@ impl DatabaseClass {
May be `None` if no parent class exists.
*/
#[must_use]
pub fn get_superclass(&self) -> Option<String> {
let sup = self.0.superclass.as_ref()?;
Some(sup.to_string())
@ -45,6 +47,7 @@ impl DatabaseClass {
/**
Get all known properties for this class.
*/
#[must_use]
pub fn get_properties(&self) -> HashMap<String, DatabaseProperty> {
self.0
.properties
@ -56,6 +59,7 @@ impl DatabaseClass {
/**
Get all default values for properties of this class.
*/
#[must_use]
pub fn get_defaults(&self) -> HashMap<String, DomVariant> {
self.0
.default_properties
@ -71,7 +75,12 @@ impl DatabaseClass {
to players at runtime, and top-level class categories.
*/
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 enum_name = find_enum_name(inner, name).ok_or_else(|| {
LuaError::RuntimeError(format!(
"Failed to get default property '{}' - No enum descriptor was found",
name
"Failed to get default property '{name}' - No enum descriptor was found",
))
})?;
EnumItem::from_enum_name_and_value(&enum_name, value).ok_or_else(|| {
LuaError::RuntimeError(format!(
"Failed to get default property '{}' - Enum.{} does not contain numeric value {}",
name, enum_name, value
"Failed to get default property '{name}' - Enum.{enum_name} does not contain numeric value {value}",
))
})
}

View file

@ -4,7 +4,7 @@ use mlua::prelude::*;
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>;
@ -23,6 +23,7 @@ impl DatabaseEnum {
/**
Get the name of this enum.
*/
#[must_use]
pub fn get_name(&self) -> String {
self.0.name.to_string()
}
@ -31,8 +32,9 @@ impl DatabaseEnum {
Get all known members of this enum.
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> {
self.0
.items

View file

@ -4,7 +4,7 @@ use mlua::prelude::*;
use rbx_reflection::ReflectionDatabase;
use crate::roblox::datatypes::userdata_impl_eq;
use crate::datatypes::userdata_impl_eq;
mod class;
mod enums;
@ -30,6 +30,7 @@ impl Database {
/**
Creates a new database struct, referencing the bundled reflection database.
*/
#[must_use]
pub fn new() -> Self {
Self::default()
}
@ -40,6 +41,7 @@ impl Database {
This will follow the format `x.y.z.w`, which most
commonly looks something like `0.567.0.123456789`.
*/
#[must_use]
pub fn get_version(&self) -> String {
let [x, y, z, w] = self.0.version;
format!("{x}.{y}.{z}.{w}")
@ -48,15 +50,17 @@ impl Database {
/**
Retrieves a list of all currently known enum names.
*/
#[must_use]
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.
*/
#[must_use]
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 {
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) {
methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq);
methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string);
methods.add_method("GetEnumNames", |_, this, _: ()| Ok(this.get_enum_names()));
methods.add_method("GetClassNames", |_, this, _: ()| Ok(this.get_class_names()));
methods.add_method("GetEnumNames", |_, this, (): ()| Ok(this.get_enum_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("GetClass", |_, this, name: String| Ok(this.get_class(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 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 DbProp = &'static PropertyDescriptor<'static>;
@ -25,6 +25,7 @@ impl DatabaseProperty {
/**
Get the name of this property.
*/
#[must_use]
pub fn get_name(&self) -> String {
self.1.name.to_string()
}
@ -36,6 +37,7 @@ impl DatabaseProperty {
For enums this will be a string formatted as `Enum.EnumName`.
*/
#[must_use]
pub fn get_datatype_name(&self) -> String {
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.
*/
#[must_use]
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
.tags
.iter()
.copied()
.map(property_tag_to_str)
.collect::<Vec<_>>()
}

View file

@ -1,6 +1,6 @@
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 {
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!
*/
pub fn scriptability_to_str(scriptability: &Scriptability) -> &'static str {
pub fn scriptability_to_str(scriptability: Scriptability) -> &'static str {
match scriptability {
Scriptability::None => "None",
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 {
PropertyTag::Deprecated => "Deprecated",
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 {
ClassTag::Deprecated => "Deprecated",
ClassTag::NotBrowsable => "NotBrowsable",

View file

@ -2,7 +2,7 @@ use mlua::prelude::*;
use rbx_dom_weak::types::Variant as DomValue;
use crate::roblox::instance::Instance;
use crate::instance::Instance;
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)
} else {
Err(LuaError::RuntimeError(format!(
"{} is not a valid member of {}",
field_name, class_name
"{field_name} is not a valid member of {class_name}",
)))
}
});
@ -42,8 +41,7 @@ pub(crate) fn add_class_restricted_setter<'lua, F: LuaUserDataFields<'lua, Insta
field_getter(lua, this, value)
} else {
Err(LuaError::RuntimeError(format!(
"{} is not a valid member of {}",
field_name, class_name
"{field_name} is not a valid member of {class_name}",
)))
}
});
@ -64,8 +62,7 @@ pub(crate) fn add_class_restricted_method<'lua, M: LuaUserDataMethods<'lua, Inst
method(lua, this, args)
} else {
Err(LuaError::RuntimeError(format!(
"{} is not a valid member of {}",
method_name, class_name
"{method_name} is not a valid member of {class_name}",
)))
}
});
@ -92,8 +89,7 @@ pub(crate) fn add_class_restricted_method_mut<
method(lua, this, args)
} else {
Err(LuaError::RuntimeError(format!(
"{} is not a valid member of {}",
method_name, class_name
"{method_name} is not a valid member of {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.
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
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),
..Default::default()
},
_ => Default::default(),
_ => PropertyInfo::default(),
});
break;
} else if let Some(sup) = &class.superclass {
// No property found, we should look at the superclass
class_name = Cow::Borrowed(sup)
class_name = Cow::Borrowed(sup);
} else {
break;
}
@ -87,7 +87,7 @@ pub(crate) fn find_property_info(
break;
} else if let Some(sup) = &class.superclass {
// No default value found, we should look at the superclass
class_name = Cow::Borrowed(sup)
class_name = Cow::Borrowed(sup);
} else {
break;
}

View file

@ -1,3 +1,5 @@
#![allow(clippy::missing_errors_doc)]
use std::{any::type_name, cell::RefCell, fmt, ops};
use mlua::prelude::*;
@ -5,21 +7,29 @@ use mlua::prelude::*;
// Utility functions
type ListWriter = dyn Fn(&mut fmt::Formatter<'_>, bool, &str) -> fmt::Result;
#[must_use]
pub fn make_list_writer() -> Box<ListWriter> {
let first = RefCell::new(true);
Box::new(move |f, flag, literal| {
if flag {
if first.take() {
write!(f, "{}", literal)?;
write!(f, "{literal}")?;
} else {
write!(f, ", {}", literal)?;
write!(f, ", {literal}")?;
}
}
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>
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_lc::LocaleDate;
use crate::lune::util::TableBuilder;
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()
}
use crate::result::{DateTimeError, DateTimeResult};
use crate::values::DateTimeValues;
const DEFAULT_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
const DEFAULT_LOCALE: &str = "en";
@ -49,6 +26,7 @@ impl DateTime {
See [`chrono::DateTime::now`] for additional details.
*/
#[must_use]
pub fn now() -> Self {
Self { inner: Utc::now() }
}
@ -66,6 +44,10 @@ impl DateTime {
```
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> {
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`]
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> {
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`]
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> {
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.
*/
#[must_use]
pub fn format_string_local(&self, format: Option<&str>, locale: Option<&str>) -> String {
self.inner
.with_timezone(&Local)
@ -156,6 +147,7 @@ impl DateTime {
See [`chrono_lc::DateTime::formatl`] for additional details.
*/
#[must_use]
pub fn format_string_universal(&self, format: Option<&str>, locale: Option<&str>) -> String {
self.inner
.with_timezone(&Utc)
@ -171,6 +163,10 @@ impl DateTime {
`1996-12-19T16:39:57-08:00`, into a new `DateTime` struct.
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> {
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
`DateTime`, using the current local time zone.
*/
#[must_use]
pub fn to_local_time(self) -> DateTimeValues {
DateTimeValues::from(self.inner.with_timezone(&Local))
}
@ -189,6 +186,7 @@ impl DateTime {
Extracts individual date & time values from this
`DateTime`, using the universal (UTC) time zone.
*/
#[must_use]
pub fn to_universal_time(self) -> DateTimeValues {
DateTimeValues::from(self.inner.with_timezone(&Utc))
}
@ -198,6 +196,7 @@ impl DateTime {
See [`chrono::DateTime::to_rfc3339`] for additional details.
*/
#[must_use]
pub fn to_iso_date(self) -> String {
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 crate::lune::util::TableBuilder;
use lune_utils::TableBuilder;
use super::error::{DateTimeError, DateTimeResult};
use super::result::{DateTimeError, DateTimeResult};
#[derive(Debug, Clone, Copy)]
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
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
and from our non-timezone-aware DateTimeValues values struct
Conversion methods between chrono's timezone-aware `DateTime` to
and from our non-timezone-aware `DateTimeValues` values struct
*/
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)>,
}
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 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
// SAFETY: Since we only ever push dirs and files relative to the root, unwrap is safe
for (_, dir) in dirs.iter_mut() {
*dir = dir.strip_prefix(&normalized_root).unwrap().to_path_buf()
for (_, dir) in &mut dirs {
*dir = dir.strip_prefix(&normalized_root).unwrap().to_path_buf();
}
for (_, file) in files.iter_mut() {
*file = file.strip_prefix(&normalized_root).unwrap().to_path_buf()
for (_, file) in &mut files {
*file = file.strip_prefix(&normalized_root).unwrap().to_path_buf();
}
// 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::path::{PathBuf, MAIN_SEPARATOR};
@ -5,17 +7,24 @@ use bstr::{BString, ByteSlice};
use mlua::prelude::*;
use tokio::fs;
use crate::lune::util::TableBuilder;
use lune_utils::TableBuilder;
mod copy;
mod metadata;
mod options;
use copy::copy;
use metadata::FsMetadata;
use options::FsWriteOptions;
use self::copy::copy;
use self::metadata::FsMetadata;
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)?
.with_async_function("readFile", fs_read_file)?
.with_async_function("readDir", fs_read_dir)?

View file

@ -8,7 +8,7 @@ use std::{
use mlua::prelude::*;
use crate::lune::builtins::datetime::DateTime;
use lune_std_datetime::DateTime;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
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 crate::lune::util::TableBuilder;
use lune_utils::TableBuilder;
mod options;
use options::{LuauCompileOptions, LuauLoadOptions};
use self::options::{LuauCompileOptions, LuauLoadOptions};
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)?
.with_function("compile", compile_source)?
.with_function("load", load_source)?

View file

@ -1,8 +1,14 @@
#![allow(clippy::struct_field_names)]
use mlua::prelude::*;
use mlua::Compiler as LuaCompiler;
const DEFAULT_DEBUG_NAME: &str = "luau.load(...)";
/**
Options for compiling Lua source code.
*/
#[derive(Debug, Clone, Copy)]
pub struct LuauCompileOptions {
pub(crate) optimization_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(crate) debug_name: String,
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 crate::lune::{
builtins::serde::compress_decompress::{decompress, CompressDecompressFormat},
util::TableBuilder,
};
use lune_std_serde::{decompress, CompressDecompressFormat};
use lune_utils::TableBuilder;
use super::{config::RequestConfig, util::header_map_to_table};
@ -103,7 +101,7 @@ impl NetClient {
.and_then(|(_, value)| value.to_str().ok())
.and_then(CompressDecompressFormat::detect_from_header_str);
if let Some(format) = decompress_format {
res_bytes = decompress(format, res_bytes).await?;
res_bytes = decompress(res_bytes, format).await?;
res_decompressed = true;
}
}

View file

@ -83,7 +83,7 @@ impl FromLua<'_> for RequestConfig {
query: HashMap::new(),
headers: HashMap::new(),
body: None,
options: Default::default(),
options: RequestConfigOptions::default(),
})
} else if let LuaValue::Table(tab) = value {
// 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 mlua::prelude::*;
@ -10,7 +10,7 @@ mod server;
mod util;
mod websocket;
use crate::lune::util::TableBuilder;
use lune_utils::TableBuilder;
use self::{
client::{NetClient, NetClientBuilder},
@ -20,9 +20,16 @@ use self::{
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()
.headers(&[("User-Agent", create_user_agent_header(lua)?)])?
.build()?
@ -42,12 +49,13 @@ fn net_json_encode<'lua>(
lua: &'lua Lua,
(val, pretty): (LuaValue<'lua>, Option<bool>),
) -> LuaResult<LuaString<'lua>> {
EncodeDecodeConfig::from((EncodeDecodeFormat::Json, pretty.unwrap_or_default()))
.serialize_to_string(lua, val)
let config = EncodeDecodeConfig::from((EncodeDecodeFormat::Json, pretty.unwrap_or_default()));
encode(val, lua, config)
}
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> {

View file

@ -10,7 +10,7 @@ use tokio::{net::TcpListener, pin};
use mlua::prelude::*;
use mlua_luau_scheduler::LuaSpawnExt;
use crate::lune::util::TableBuilder;
use lune_utils::TableBuilder;
use super::config::ServeConfig;
@ -81,7 +81,7 @@ pub async fn serve<'lua>(
// Wait for either a new connection or a shutdown signal
tokio::select! {
_ = fut_accept => {}
() = fut_accept => {}
res = fut_shutdown => {
// 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
@ -97,8 +97,8 @@ pub async fn serve<'lua>(
TableBuilder::new(lua)?
.with_value("ip", addr.ip().to_string())?
.with_value("port", addr.port())?
.with_function("stop", move |lua, _: ()| match shutdown_tx.send(true) {
Ok(_) => Ok(()),
.with_function("stop", move |_, (): ()| match shutdown_tx.send(true) {
Ok(()) => Ok(()),
Err(_) => Err(LuaError::runtime("Server already stopped")),
})?
.build_readonly()

View file

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

View file

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

View file

@ -23,7 +23,7 @@ use hyper_tungstenite::{
WebSocketStream,
};
use crate::lune::util::TableBuilder;
use lune_utils::TableBuilder;
// Wrapper implementation for compatibility and changing colon syntax to dot syntax
const WEB_SOCKET_IMPL_LUA: &str = r#"
@ -155,7 +155,7 @@ where
}
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
});
@ -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?;
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::{
env::{self, consts},
path,
env::{
self,
consts::{ARCH, OS},
},
path::MAIN_SEPARATOR,
process::Stdio,
};
use mlua::prelude::*;
use lune_utils::TableBuilder;
use mlua_luau_scheduler::{Functions, LuaSpawnExt};
use os_str_bytes::RawOsString;
use tokio::io::AsyncWriteExt;
use crate::lune::util::{paths::CWD, TableBuilder};
mod tee_writer;
mod options;
use options::ProcessSpawnOptions;
mod tee_writer;
mod wait_for_child;
use wait_for_child::{wait_for_child, WaitForChildResult};
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
let cwd_str = {
let cwd_str = CWD.to_string_lossy().to_string();
if !cwd_str.ends_with(path::MAIN_SEPARATOR) {
format!("{cwd_str}{}", path::MAIN_SEPARATOR)
} else {
cwd_str
}
};
use self::options::ProcessSpawnOptions;
use self::wait_for_child::{wait_for_child, WaitForChildResult};
use lune_utils::path::get_current_dir;
/**
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
let os = lua.create_string(&consts::OS.to_lowercase())?;
let arch = lua.create_string(&consts::ARCH.to_lowercase())?;
let os = lua.create_string(&OS.to_lowercase())?;
let arch = lua.create_string(&ARCH.to_lowercase())?;
// Create readonly args array
let args_vec = lua
.app_data_ref::<Vec<String>>()
@ -94,33 +107,28 @@ fn process_env_set<'lua>(
Err(LuaError::RuntimeError(
"Key must not contain the NUL character".to_string(),
))
} else {
match value {
Some(value) => {
// Make sure value is valid, otherwise set_var will panic
if value.contains('\0') {
Err(LuaError::RuntimeError(
"Value must not contain the NUL character".to_string(),
))
} else {
env::set_var(&key, &value);
Ok(())
}
}
None => {
env::remove_var(&key);
Ok(())
}
} else if let Some(value) = value {
// Make sure value is valid, otherwise set_var will panic
if value.contains('\0') {
Err(LuaError::RuntimeError(
"Value must not contain the NUL character".to_string(),
))
} else {
env::set_var(&key, &value);
Ok(())
}
} else {
env::remove_var(&key);
Ok(())
}
}
fn process_env_iter<'lua>(
lua: &'lua Lua,
(_, _): (LuaValue<'lua>, ()),
(_, ()): (LuaValue<'lua>, ()),
) -> LuaResult<LuaFunction<'lua>> {
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)) => {
let raw_key = RawOsString::new(key);
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
some external signal, which is the only time we use this default
*/
let code = res.status.code().unwrap_or(match res.stderr.is_empty() {
true => 0,
false => 1,
});
let code = res
.status
.code()
.unwrap_or(i32::from(!res.stderr.is_empty()));
// Construct and return a readonly lua table with results
TableBuilder::new(lua)?
@ -174,9 +182,10 @@ async fn spawn_command(
let mut child = options
.into_command(program, args)
.stdin(match stdin.is_some() {
true => Stdio::piped(),
false => Stdio::null(),
.stdin(if stdin.is_some() {
Stdio::piped()
} else {
Stdio::null()
})
.stdout(stdout.as_stdio())
.stderr(stderr.as_stdio())

View file

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

View file

@ -56,7 +56,7 @@ impl<'lua> FromLua<'lua> for ProcessSpawnOptions {
"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() {
return Err(LuaError::runtime(

View file

@ -24,8 +24,7 @@ where
R: AsyncRead + Unpin,
{
Ok(match kind {
ProcessSpawnOptionsStdioKind::None => Vec::new(),
ProcessSpawnOptionsStdioKind::Forward => Vec::new(),
ProcessSpawnOptionsStdioKind::None | ProcessSpawnOptionsStdioKind::Forward => Vec::new(),
ProcessSpawnOptionsStdioKind::Default => {
let mut read_from =
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 crate::lune::util::TableBuilder;
use lune_utils::TableBuilder;
mod captures;
mod matches;
@ -10,7 +10,14 @@ mod regex;
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)?
.with_function("new", new_regex)?
.build_readonly()

View file

@ -46,7 +46,7 @@ impl LuaUserData for LuaRegex {
Ok(this
.inner
.split(&text)
.map(|s| s.to_string())
.map(ToString::to_string)
.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_luau_scheduler::LuaSpawnExt;
use once_cell::sync::OnceCell;
use crate::{
lune::util::TableBuilder,
roblox::{
self,
document::{Document, DocumentError, DocumentFormat, DocumentKind},
instance::{registry::InstanceRegistry, Instance},
reflection::Database as ReflectionDatabase,
},
use lune_roblox::{
document::{Document, DocumentError, DocumentFormat, DocumentKind},
instance::{registry::InstanceRegistry, Instance},
reflection::Database as ReflectionDatabase,
};
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 roblox_module = roblox::module(lua)?;
let roblox_module = lune_roblox::module(lua)?;
for pair in roblox_module.pairs::<LuaValue, LuaValue>() {
roblox_constants.push(pair?);
}
@ -116,16 +123,15 @@ fn implement_property(
Option<LuaFunction>,
),
) -> LuaResult<()> {
let property_setter = match property_setter {
Some(setter) => setter,
None => {
let property_name = property_name.clone();
lua.create_function(move |_, _: LuaMultiValue| {
Err::<(), _>(LuaError::runtime(format!(
"Property '{property_name}' is read-only"
)))
})?
}
let property_setter = if let Some(setter) = property_setter {
setter
} else {
let property_name = property_name.clone();
lua.create_function(move |_, _: LuaMultiValue| {
Err::<(), _>(LuaError::runtime(format!(
"Property '{property_name}' is read-only"
)))
})?
};
InstanceRegistry::insert_property_getter(lua, &class_name, &property_name, property_getter)
.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