forked from rojo-rbx/rojo
Compare commits
21 Commits
v7.1.0
...
unpack-com
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88d2d1f193 | ||
|
|
2e7c4b6dff | ||
|
|
e5dbee1073 | ||
|
|
c06463b61d | ||
|
|
10341e3776 | ||
|
|
824cdc5dcd | ||
|
|
7aa7a35aa5 | ||
|
|
79b57b3359 | ||
|
|
c7aeffe586 | ||
|
|
79c02f2457 | ||
|
|
b9ed68fa9e | ||
|
|
6c6d6c9c8d | ||
|
|
e169d7be68 | ||
|
|
192fd7d4dd | ||
|
|
1f1193e857 | ||
|
|
0a412ade88 | ||
|
|
3cef2fe9aa | ||
|
|
18e53f06fe | ||
|
|
eaac539087 | ||
|
|
57005c4fd5 | ||
|
|
ea58999a2a |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
patreon: lpghatguy
|
||||||
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
@@ -11,29 +11,49 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
name: Build and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust_version: [stable, "1.55.0"]
|
rust_version: [stable, 1.55.0]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Install Rust
|
||||||
run: rustup default ${{ matrix.rust_version }}
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust_version }}
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --locked --verbose
|
run: cargo build --locked --verbose
|
||||||
|
|
||||||
- name: Run tests
|
- name: Test
|
||||||
run: cargo test --locked --verbose
|
run: cargo test --locked --verbose
|
||||||
|
|
||||||
- name: Rustfmt and Clippy
|
lint:
|
||||||
run: |
|
name: Rustfmt and Clippy
|
||||||
cargo fmt -- --check
|
runs-on: ubuntu-latest
|
||||||
cargo clippy
|
|
||||||
if: matrix.rust_version == 'stable'
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Rustfmt
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
|
- name: Clippy
|
||||||
|
run: cargo clippy
|
||||||
199
.github/workflows/release.yml
vendored
199
.github/workflows/release.yml
vendored
@@ -2,65 +2,152 @@ name: Release
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ["*"]
|
tags: ["v*"]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
windows:
|
create-release:
|
||||||
runs-on: windows-latest
|
name: Create Release
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- name: Build release binary
|
|
||||||
run: cargo build --verbose --locked --release
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: rojo-win64
|
|
||||||
path: target/release/rojo.exe
|
|
||||||
|
|
||||||
macos:
|
|
||||||
runs-on: macos-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- name: Install Rust
|
|
||||||
run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
||||||
|
|
||||||
- name: Build release binary
|
|
||||||
run: |
|
|
||||||
source $HOME/.cargo/env
|
|
||||||
cargo build --verbose --locked --release
|
|
||||||
env:
|
|
||||||
OPENSSL_STATIC: 1
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: rojo-macos
|
|
||||||
path: target/release/rojo
|
|
||||||
|
|
||||||
linux:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- name: Create Release
|
||||||
with:
|
id: create_release
|
||||||
submodules: true
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref }}
|
||||||
|
release_name: ${{ github.ref }}
|
||||||
|
draft: true
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
- name: Build
|
build-plugin:
|
||||||
run: cargo build --locked --verbose --release
|
needs: ["create-release"]
|
||||||
env:
|
name: Build Roblox Studio Plugin
|
||||||
OPENSSL_STATIC: 1
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Setup Foreman
|
||||||
uses: actions/upload-artifact@v1
|
uses: Roblox/setup-foreman@v1
|
||||||
with:
|
with:
|
||||||
name: rojo-linux
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
path: target/release/rojo
|
|
||||||
|
- name: Build Plugin
|
||||||
|
run: rojo build plugin --output Rojo.rbxm
|
||||||
|
|
||||||
|
- name: Upload Plugin to Release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: Rojo.rbxm
|
||||||
|
asset_name: Rojo.rbxm
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: Upload Plugin to Artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Rojo.rbxm
|
||||||
|
path: Rojo.rbxm
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: ["create-release"]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# https://doc.rust-lang.org/rustc/platform-support.html
|
||||||
|
#
|
||||||
|
# FIXME: After the Rojo VS Code extension updates, add architecture
|
||||||
|
# names to each of these releases. We'll rename win64 to windows and add
|
||||||
|
# -x86_64 to each release.
|
||||||
|
include:
|
||||||
|
- host: linux
|
||||||
|
os: ubuntu-latest
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
label: linux
|
||||||
|
|
||||||
|
- host: windows
|
||||||
|
os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
label: win64
|
||||||
|
|
||||||
|
- host: macos
|
||||||
|
os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
label: macos
|
||||||
|
|
||||||
|
- host: macos
|
||||||
|
os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
label: macos-aarch64
|
||||||
|
|
||||||
|
name: Build (${{ matrix.target }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
env:
|
||||||
|
BIN: rojo
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Get Version from Tag
|
||||||
|
shell: bash
|
||||||
|
# https://github.community/t/how-to-get-just-the-tag-name/16241/7#M1027
|
||||||
|
run: |
|
||||||
|
echo "PROJECT_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
echo "Version is: ${{ env.PROJECT_VERSION }}"
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
|
||||||
|
- name: Build Release
|
||||||
|
run: cargo build --release --locked --verbose
|
||||||
|
env:
|
||||||
|
# Build into a known directory so we can find our build artifact more
|
||||||
|
# easily.
|
||||||
|
CARGO_TARGET_DIR: output
|
||||||
|
|
||||||
|
# On platforms that use OpenSSL, ensure it is statically linked to
|
||||||
|
# make binaries more portable.
|
||||||
|
OPENSSL_STATIC: 1
|
||||||
|
|
||||||
|
- name: Create Release Archive
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir staging
|
||||||
|
|
||||||
|
if [ "${{ matrix.host }}" = "windows" ]; then
|
||||||
|
cp "output/release/$BIN.exe" staging/
|
||||||
|
cd staging
|
||||||
|
7z a ../release.zip *
|
||||||
|
else
|
||||||
|
cp "output/release/$BIN" staging/
|
||||||
|
cd staging
|
||||||
|
zip ../release.zip *
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload Archive to Release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: release.zip
|
||||||
|
asset_name: ${{ env.BIN }}-${{ env.PROJECT_VERSION }}-${{ matrix.label }}.zip
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: Upload Archive to Artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ${{ env.BIN }}-${{ env.PROJECT_VERSION }}-${{ matrix.label }}.zip
|
||||||
|
path: release.zip
|
||||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,6 +1,20 @@
|
|||||||
# Rojo Changelog
|
# Rojo Changelog
|
||||||
|
|
||||||
## Unreleased Changes
|
## Unreleased Changes
|
||||||
|
* Switched from structopt to clap for command line argument parsing.
|
||||||
|
* Significantly improved performance of building and serving. ([#548])
|
||||||
|
* Fixed `init.meta.json` when used with `init.lua` and related files. ([#549])
|
||||||
|
|
||||||
|
[#548]: https://github.com/rojo-rbx/rojo/pull/548
|
||||||
|
[#549]: https://github.com/rojo-rbx/rojo/pull/549
|
||||||
|
|
||||||
|
## [7.1.1] - May 26, 2022
|
||||||
|
* Fixed sourcemap command not stripping paths correctly ([#544])
|
||||||
|
* Fixed Studio plugin settings not saving correctly.
|
||||||
|
|
||||||
|
[#544]: https://github.com/rojo-rbx/rojo/pull/544
|
||||||
|
[#545]: https://github.com/rojo-rbx/rojo/pull/545
|
||||||
|
[7.1.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.1.1
|
||||||
|
|
||||||
## [7.1.0] - May 22, 2022
|
## [7.1.0] - May 22, 2022
|
||||||
* Added support for specifying an address to be used by default in project files. ([#507])
|
* Added support for specifying an address to be used by default in project files. ([#507])
|
||||||
|
|||||||
@@ -49,11 +49,9 @@ The Rojo release process is pretty manual right now. If you need to do it, here'
|
|||||||
* `cargo publish`
|
* `cargo publish`
|
||||||
8. Publish the Plugin
|
8. Publish the Plugin
|
||||||
* `cargo run -- upload plugin --asset_id 6415005344`
|
* `cargo run -- upload plugin --asset_id 6415005344`
|
||||||
* `cargo run -- build plugin --output Rojo.rbxm`
|
|
||||||
9. Push commits and tags
|
9. Push commits and tags
|
||||||
* `git push && git push --tags`
|
* `git push && git push --tags`
|
||||||
10. Copy GitHub release content from previous release
|
10. Copy GitHub release content from previous release
|
||||||
* Update the leading text with a summary about the release
|
* Update the leading text with a summary about the release
|
||||||
* Paste the changelog notes (as-is!) from [`CHANGELOG.md`](CHANGELOG.md)
|
* Paste the changelog notes (as-is!) from [`CHANGELOG.md`](CHANGELOG.md)
|
||||||
* Write a small summary of each major feature
|
* Write a small summary of each major feature
|
||||||
* Attach release artifacts from GitHub Actions for each platform
|
|
||||||
|
|||||||
1315
Cargo.lock
generated
1315
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
33
Cargo.toml
33
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "7.1.0"
|
version = "7.1.1"
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
description = "Enables professional-grade development tools for Roblox developers"
|
description = "Enables professional-grade development tools for Roblox developers"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
@@ -27,11 +27,10 @@ default = []
|
|||||||
# Enable this feature to live-reload assets from the web UI.
|
# Enable this feature to live-reload assets from the web UI.
|
||||||
dev_live_assets = []
|
dev_live_assets = []
|
||||||
|
|
||||||
|
profile-with-tracy = ["profiling/profile-with-tracy", "tracy-client"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = ["crates/*"]
|
||||||
"rojo-insta-ext",
|
|
||||||
"memofs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "librojo"
|
name = "librojo"
|
||||||
@@ -42,7 +41,7 @@ name = "build"
|
|||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
memofs = { version = "0.2.0", path = "memofs" }
|
memofs = { version = "0.2.0", path = "crates/memofs" }
|
||||||
|
|
||||||
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
||||||
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
|
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
|
||||||
@@ -52,7 +51,7 @@ memofs = { version = "0.2.0", path = "memofs" }
|
|||||||
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||||
|
|
||||||
rbx_binary = "0.6.4"
|
rbx_binary = "0.6.4"
|
||||||
rbx_dom_weak = "2.3.0"
|
rbx_dom_weak = "2.4.0"
|
||||||
rbx_reflection = "4.2.0"
|
rbx_reflection = "4.2.0"
|
||||||
rbx_reflection_database = "0.2.2"
|
rbx_reflection_database = "0.2.2"
|
||||||
rbx_xml = "0.12.3"
|
rbx_xml = "0.12.3"
|
||||||
@@ -69,29 +68,28 @@ globset = "0.4.8"
|
|||||||
humantime = "2.1.0"
|
humantime = "2.1.0"
|
||||||
hyper = { version = "0.14.13", features = ["server", "tcp", "http1"] }
|
hyper = { version = "0.14.13", features = ["server", "tcp", "http1"] }
|
||||||
jod-thread = "0.1.2"
|
jod-thread = "0.1.2"
|
||||||
lazy_static = "1.4.0"
|
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
notify = "4.0.17"
|
notify = "4.0.17"
|
||||||
opener = "0.5.0"
|
opener = "0.5.0"
|
||||||
regex = "1.5.4"
|
reqwest = { version = "0.11.10", features = ["blocking", "json"] }
|
||||||
reqwest = "0.9.24"
|
|
||||||
ritz = "0.1.0"
|
ritz = "0.1.0"
|
||||||
rlua = "0.17.1"
|
|
||||||
roblox_install = "1.0.0"
|
roblox_install = "1.0.0"
|
||||||
serde = { version = "1.0.130", features = ["derive", "rc"] }
|
serde = { version = "1.0.130", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0.68"
|
serde_json = "1.0.68"
|
||||||
structopt = "0.3.23"
|
|
||||||
termcolor = "1.1.2"
|
termcolor = "1.1.2"
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.30"
|
||||||
tokio = { version = "1.12.0", features = ["rt", "rt-multi-thread"] }
|
tokio = { version = "1.12.0", features = ["rt", "rt-multi-thread"] }
|
||||||
uuid = { version = "0.8.2", features = ["v4", "serde"] }
|
uuid = { version = "1.0.0", features = ["v4", "serde"] }
|
||||||
|
clap = { version = "3.1.18", features = ["derive"] }
|
||||||
|
profiling = "1.0.6"
|
||||||
|
tracy-client = { version = "0.13.2", optional = true }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winreg = "0.9.0"
|
winreg = "0.10.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
memofs = { version = "0.2.0", path = "memofs" }
|
memofs = { version = "0.2.0", path = "crates/memofs" }
|
||||||
|
|
||||||
embed-resource = "1.6.4"
|
embed-resource = "1.6.4"
|
||||||
anyhow = "1.0.44"
|
anyhow = "1.0.44"
|
||||||
@@ -100,13 +98,12 @@ fs-err = "2.6.0"
|
|||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rojo-insta-ext = { path = "rojo-insta-ext" }
|
rojo-insta-ext = { path = "crates/rojo-insta-ext" }
|
||||||
|
|
||||||
criterion = "0.3.5"
|
criterion = "0.3.5"
|
||||||
insta = { version = "1.8.0", features = ["redactions"] }
|
insta = { version = "1.8.0", features = ["redactions"] }
|
||||||
lazy_static = "1.4.0"
|
|
||||||
paste = "1.0.5"
|
paste = "1.0.5"
|
||||||
pretty_assertions = "0.7.2"
|
pretty_assertions = "1.2.1"
|
||||||
serde_yaml = "0.8.21"
|
serde_yaml = "0.8.21"
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
walkdir = "2.3.2"
|
walkdir = "2.3.2"
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
watchexec -c -w plugin "sh -c './bin/install-dev-plugin.sh'"
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DIR="$( mktemp -d )"
|
|
||||||
PLUGIN_FILE="$DIR/Rojo.rbxm"
|
|
||||||
TESTEZ_FILE="$DIR/TestEZ.rbxm"
|
|
||||||
|
|
||||||
rojo build plugin -o "$PLUGIN_FILE"
|
|
||||||
rojo build plugin/testez.project.json -o "$TESTEZ_FILE"
|
|
||||||
remodel bin/mark-plugin-as-dev.lua "$PLUGIN_FILE" "$TESTEZ_FILE" 2>/dev/null
|
|
||||||
|
|
||||||
cp "$PLUGIN_FILE" "$LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm"
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
rojo build plugin -o "$LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm"
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
local pluginPath, testezPath = ...
|
|
||||||
|
|
||||||
local plugin = remodel.readModelFile(pluginPath)[1]
|
|
||||||
local testez = remodel.readModelFile(testezPath)[1]
|
|
||||||
|
|
||||||
local marker = Instance.new("Folder")
|
|
||||||
marker.Name = "ROJO_DEV_BUILD"
|
|
||||||
marker.Parent = plugin
|
|
||||||
|
|
||||||
testez.Parent = plugin
|
|
||||||
|
|
||||||
remodel.writeModelFile(plugin, pluginPath)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
local pluginPath, placePath = ...
|
|
||||||
|
|
||||||
local plugin = remodel.readModelFile(pluginPath)[1]
|
|
||||||
local place = remodel.readPlaceFile(placePath)
|
|
||||||
|
|
||||||
plugin.Parent = place:GetService("ReplicatedStorage")
|
|
||||||
|
|
||||||
remodel.writePlaceFile(place, placePath)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
./bin/run-cli-tests.sh
|
|
||||||
./bin/run-plugin-tests.sh
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cargo test --all --locked
|
|
||||||
cargo fmt -- --check
|
|
||||||
|
|
||||||
touch src/lib.rs # Nudge Rust source to make Clippy actually check things
|
|
||||||
cargo clippy
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DIR="$( mktemp -d )"
|
|
||||||
PLUGIN_FILE="$DIR/Rojo.rbxmx"
|
|
||||||
PLACE_FILE="$DIR/RojoTestPlace.rbxlx"
|
|
||||||
|
|
||||||
rojo build plugin -o "$PLUGIN_FILE"
|
|
||||||
rojo build plugin/place.project.json -o "$PLACE_FILE"
|
|
||||||
|
|
||||||
remodel bin/put-plugin-in-test-place.lua "$PLUGIN_FILE" "$PLACE_FILE"
|
|
||||||
|
|
||||||
run-in-roblox -s plugin/testBootstrap.server.lua "$PLACE_FILE"
|
|
||||||
|
|
||||||
luacheck plugin/src plugin/log plugin/http
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
[tools]
|
[tools]
|
||||||
rojo = { source = "rojo-rbx/rojo", version = "6.1.0" }
|
rojo = { source = "rojo-rbx/rojo", version = "7.1.1" }
|
||||||
run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
|
run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
|
||||||
|
selene = { source = "Kampfkarren/selene", version = "0.17.0" }
|
||||||
|
|||||||
@@ -154,150 +154,146 @@ function App:render()
|
|||||||
value = self.props.plugin,
|
value = self.props.plugin,
|
||||||
}, {
|
}, {
|
||||||
e(Theme.StudioProvider, nil, {
|
e(Theme.StudioProvider, nil, {
|
||||||
e(PluginSettings.StudioProvider, {
|
gui = e(StudioPluginGui, {
|
||||||
plugin = self.props.plugin,
|
id = pluginName,
|
||||||
|
title = pluginName,
|
||||||
|
active = self.state.guiEnabled,
|
||||||
|
|
||||||
|
initDockState = Enum.InitialDockState.Right,
|
||||||
|
initEnabled = false,
|
||||||
|
overridePreviousState = false,
|
||||||
|
floatingSize = Vector2.new(300, 200),
|
||||||
|
minimumSize = Vector2.new(300, 200),
|
||||||
|
|
||||||
|
zIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
||||||
|
|
||||||
|
onInitialState = function(initialState)
|
||||||
|
self:setState({
|
||||||
|
guiEnabled = initialState,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
|
||||||
|
onClose = function()
|
||||||
|
self:setState({
|
||||||
|
guiEnabled = false,
|
||||||
|
})
|
||||||
|
end,
|
||||||
}, {
|
}, {
|
||||||
gui = e(StudioPluginGui, {
|
NotConnectedPage = createPageElement(AppStatus.NotConnected, {
|
||||||
id = pluginName,
|
host = self.host,
|
||||||
title = pluginName,
|
onHostChange = self.setHost,
|
||||||
active = self.state.guiEnabled,
|
port = self.port,
|
||||||
|
onPortChange = self.setPort,
|
||||||
|
|
||||||
initDockState = Enum.InitialDockState.Right,
|
onConnect = function()
|
||||||
initEnabled = false,
|
self:startSession()
|
||||||
overridePreviousState = false,
|
end,
|
||||||
floatingSize = Vector2.new(300, 200),
|
|
||||||
minimumSize = Vector2.new(300, 200),
|
|
||||||
|
|
||||||
zIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
onNavigateSettings = function()
|
||||||
|
|
||||||
onInitialState = function(initialState)
|
|
||||||
self:setState({
|
self:setState({
|
||||||
guiEnabled = initialState,
|
appStatus = AppStatus.Settings,
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Connecting = createPageElement(AppStatus.Connecting),
|
||||||
|
|
||||||
|
Connected = createPageElement(AppStatus.Connected, {
|
||||||
|
projectName = self.state.projectName,
|
||||||
|
address = self.state.address,
|
||||||
|
|
||||||
|
onDisconnect = function()
|
||||||
|
self:endSession()
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Settings = createPageElement(AppStatus.Settings, {
|
||||||
|
onBack = function()
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.NotConnected,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Error = createPageElement(AppStatus.Error, {
|
||||||
|
errorMessage = self.state.errorMessage,
|
||||||
|
|
||||||
onClose = function()
|
onClose = function()
|
||||||
self:setState({
|
self:setState({
|
||||||
guiEnabled = false,
|
appStatus = AppStatus.NotConnected,
|
||||||
|
toolbarIcon = Assets.Images.PluginButton,
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
}, {
|
|
||||||
NotConnectedPage = createPageElement(AppStatus.NotConnected, {
|
|
||||||
host = self.host,
|
|
||||||
onHostChange = self.setHost,
|
|
||||||
port = self.port,
|
|
||||||
onPortChange = self.setPort,
|
|
||||||
|
|
||||||
onConnect = function()
|
|
||||||
self:startSession()
|
|
||||||
end,
|
|
||||||
|
|
||||||
onNavigateSettings = function()
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.Settings,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Connecting = createPageElement(AppStatus.Connecting),
|
|
||||||
|
|
||||||
Connected = createPageElement(AppStatus.Connected, {
|
|
||||||
projectName = self.state.projectName,
|
|
||||||
address = self.state.address,
|
|
||||||
|
|
||||||
onDisconnect = function()
|
|
||||||
self:endSession()
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Settings = createPageElement(AppStatus.Settings, {
|
|
||||||
onBack = function()
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotConnected,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Error = createPageElement(AppStatus.Error, {
|
|
||||||
errorMessage = self.state.errorMessage,
|
|
||||||
|
|
||||||
onClose = function()
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotConnected,
|
|
||||||
toolbarIcon = Assets.Images.PluginButton,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Background = Theme.with(function(theme)
|
|
||||||
return e("Frame", {
|
|
||||||
Size = UDim2.new(1, 0, 1, 0),
|
|
||||||
BackgroundColor3 = theme.BackgroundColor,
|
|
||||||
ZIndex = 0,
|
|
||||||
BorderSizePixel = 0,
|
|
||||||
})
|
|
||||||
end),
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
toggleAction = e(StudioPluginAction, {
|
Background = Theme.with(function(theme)
|
||||||
name = "RojoConnection",
|
return e("Frame", {
|
||||||
title = "Rojo: Connect/Disconnect",
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
description = "Toggles the server for a Rojo sync session",
|
BackgroundColor3 = theme.BackgroundColor,
|
||||||
icon = Assets.Images.PluginButton,
|
ZIndex = 0,
|
||||||
bindable = true,
|
BorderSizePixel = 0,
|
||||||
onTriggered = function()
|
|
||||||
if self.serveSession == nil or self.serveSession:getStatus() == ServeSession.Status.NotStarted then
|
|
||||||
self:startSession()
|
|
||||||
elseif self.serveSession ~= nil and self.serveSession:getStatus() == ServeSession.Status.Connected then
|
|
||||||
self:endSession()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
connectAction = e(StudioPluginAction, {
|
|
||||||
name = "RojoConnect",
|
|
||||||
title = "Rojo: Connect",
|
|
||||||
description = "Connects the server for a Rojo sync session",
|
|
||||||
icon = Assets.Images.PluginButton,
|
|
||||||
bindable = true,
|
|
||||||
onTriggered = function()
|
|
||||||
if self.serveSession == nil or self.serveSession:getStatus() == ServeSession.Status.NotStarted then
|
|
||||||
self:startSession()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
disconnectAction = e(StudioPluginAction, {
|
|
||||||
name = "RojoDisconnect",
|
|
||||||
title = "Rojo: Disconnect",
|
|
||||||
description = "Disconnects the server for a Rojo sync session",
|
|
||||||
icon = Assets.Images.PluginButton,
|
|
||||||
bindable = true,
|
|
||||||
onTriggered = function()
|
|
||||||
if self.serveSession ~= nil and self.serveSession:getStatus() == ServeSession.Status.Connected then
|
|
||||||
self:endSession()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
toolbar = e(StudioToolbar, {
|
|
||||||
name = pluginName,
|
|
||||||
}, {
|
|
||||||
button = e(StudioToggleButton, {
|
|
||||||
name = "Rojo",
|
|
||||||
tooltip = "Show or hide the Rojo panel",
|
|
||||||
icon = self.state.toolbarIcon,
|
|
||||||
active = self.state.guiEnabled,
|
|
||||||
enabled = true,
|
|
||||||
onClick = function()
|
|
||||||
self:setState(function(state)
|
|
||||||
return {
|
|
||||||
guiEnabled = not state.guiEnabled,
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
end,
|
|
||||||
})
|
})
|
||||||
}),
|
end),
|
||||||
|
}),
|
||||||
|
|
||||||
|
toggleAction = e(StudioPluginAction, {
|
||||||
|
name = "RojoConnection",
|
||||||
|
title = "Rojo: Connect/Disconnect",
|
||||||
|
description = "Toggles the server for a Rojo sync session",
|
||||||
|
icon = Assets.Images.PluginButton,
|
||||||
|
bindable = true,
|
||||||
|
onTriggered = function()
|
||||||
|
if self.serveSession == nil or self.serveSession:getStatus() == ServeSession.Status.NotStarted then
|
||||||
|
self:startSession()
|
||||||
|
elseif self.serveSession ~= nil and self.serveSession:getStatus() == ServeSession.Status.Connected then
|
||||||
|
self:endSession()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
connectAction = e(StudioPluginAction, {
|
||||||
|
name = "RojoConnect",
|
||||||
|
title = "Rojo: Connect",
|
||||||
|
description = "Connects the server for a Rojo sync session",
|
||||||
|
icon = Assets.Images.PluginButton,
|
||||||
|
bindable = true,
|
||||||
|
onTriggered = function()
|
||||||
|
if self.serveSession == nil or self.serveSession:getStatus() == ServeSession.Status.NotStarted then
|
||||||
|
self:startSession()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
disconnectAction = e(StudioPluginAction, {
|
||||||
|
name = "RojoDisconnect",
|
||||||
|
title = "Rojo: Disconnect",
|
||||||
|
description = "Disconnects the server for a Rojo sync session",
|
||||||
|
icon = Assets.Images.PluginButton,
|
||||||
|
bindable = true,
|
||||||
|
onTriggered = function()
|
||||||
|
if self.serveSession ~= nil and self.serveSession:getStatus() == ServeSession.Status.Connected then
|
||||||
|
self:endSession()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
toolbar = e(StudioToolbar, {
|
||||||
|
name = pluginName,
|
||||||
|
}, {
|
||||||
|
button = e(StudioToggleButton, {
|
||||||
|
name = "Rojo",
|
||||||
|
tooltip = "Show or hide the Rojo panel",
|
||||||
|
icon = self.state.toolbarIcon,
|
||||||
|
active = self.state.guiEnabled,
|
||||||
|
enabled = true,
|
||||||
|
onClick = function()
|
||||||
|
self:setState(function(state)
|
||||||
|
return {
|
||||||
|
guiEnabled = not state.guiEnabled,
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
|
|||||||
return strict("Config", {
|
return strict("Config", {
|
||||||
isDevBuild = isDevBuild,
|
isDevBuild = isDevBuild,
|
||||||
codename = "Epiphany",
|
codename = "Epiphany",
|
||||||
version = {7, 1, 0},
|
version = {7, 1, 1},
|
||||||
expectedServerVersionString = "7.0 or newer",
|
expectedServerVersionString = "7.0 or newer",
|
||||||
protocolVersion = 4,
|
protocolVersion = 4,
|
||||||
defaultHost = "localhost",
|
defaultHost = "localhost",
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
assertion_line: 98
|
||||||
|
expression: contents
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="LocalScript" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">issue_546</string>
|
||||||
|
<bool name="Disabled">true</bool>
|
||||||
|
<string name="Source">print("Hello, world!")</string>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
2
rojo-test/build-tests/issue_546/README.md
Normal file
2
rojo-test/build-tests/issue_546/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Issue #546 (https://github.com/rojo-rbx/rojo/issues/546)
|
||||||
|
Regression from Rojo 6.2.0 to Rojo 7.0.0. Meta files named as init.meta.json should apply after init.client.lua and other init files.
|
||||||
6
rojo-test/build-tests/issue_546/default.project.json
Normal file
6
rojo-test/build-tests/issue_546/default.project.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "issue_546",
|
||||||
|
"tree": {
|
||||||
|
"$path": "hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
rojo-test/build-tests/issue_546/hello/init.client.lua
Normal file
1
rojo-test/build-tests/issue_546/hello/init.client.lua
Normal file
@@ -0,0 +1 @@
|
|||||||
|
print("Hello, world!")
|
||||||
5
rojo-test/build-tests/issue_546/hello/init.meta.json
Normal file
5
rojo-test/build-tests/issue_546/hello/init.meta.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"Disabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -291,7 +291,7 @@ fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: Ref) -> Option<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let patch_set = compute_patch_set(snapshot.as_ref(), &tree, id);
|
let patch_set = compute_patch_set(snapshot, &tree, id);
|
||||||
apply_patch_set(tree, patch_set)
|
apply_patch_set(tree, patch_set)
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
@@ -334,7 +334,7 @@ fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: Ref) -> Option<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let patch_set = compute_patch_set(snapshot.as_ref(), &tree, id);
|
let patch_set = compute_patch_set(snapshot, &tree, id);
|
||||||
apply_patch_set(tree, patch_set)
|
apply_patch_set(tree, patch_set)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{BufWriter, Write},
|
io::{BufWriter, Write},
|
||||||
|
mem::forget,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use clap::Parser;
|
||||||
use fs_err::File;
|
use fs_err::File;
|
||||||
use memofs::Vfs;
|
use memofs::Vfs;
|
||||||
use structopt::StructOpt;
|
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
use crate::serve_session::ServeSession;
|
use crate::serve_session::ServeSession;
|
||||||
@@ -17,20 +18,20 @@ const UNKNOWN_OUTPUT_KIND_ERR: &str = "Could not detect what kind of file to bui
|
|||||||
Expected output file to end in .rbxl, .rbxlx, .rbxm, or .rbxmx.";
|
Expected output file to end in .rbxl, .rbxlx, .rbxm, or .rbxmx.";
|
||||||
|
|
||||||
/// Generates a model or place file from the Rojo project.
|
/// Generates a model or place file from the Rojo project.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct BuildCommand {
|
pub struct BuildCommand {
|
||||||
/// Path to the project to serve. Defaults to the current directory.
|
/// Path to the project to serve. Defaults to the current directory.
|
||||||
#[structopt(default_value = "")]
|
#[clap(default_value = "")]
|
||||||
pub project: PathBuf,
|
pub project: PathBuf,
|
||||||
|
|
||||||
/// Where to output the result.
|
/// Where to output the result.
|
||||||
///
|
///
|
||||||
/// Should end in .rbxm, .rbxl, .rbxmx, or .rbxlx.
|
/// Should end in .rbxm, .rbxl, .rbxmx, or .rbxlx.
|
||||||
#[structopt(long, short)]
|
#[clap(long, short)]
|
||||||
pub output: PathBuf,
|
pub output: PathBuf,
|
||||||
|
|
||||||
/// Whether to automatically rebuild when any input files change.
|
/// Whether to automatically rebuild when any input files change.
|
||||||
#[structopt(long)]
|
#[clap(long)]
|
||||||
pub watch: bool,
|
pub watch: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +62,10 @@ impl BuildCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Avoid dropping ServeSession: it's potentially VERY expensive to drop
|
||||||
|
// and we're about to exit anyways.
|
||||||
|
forget(session);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,6 +102,7 @@ fn xml_encode_config() -> rbx_xml::EncodeOptions {
|
|||||||
rbx_xml::EncodeOptions::new().property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
|
rbx_xml::EncodeOptions::new().property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[profiling::function]
|
||||||
fn write_model(
|
fn write_model(
|
||||||
session: &ServeSession,
|
session: &ServeSession,
|
||||||
output: &Path,
|
output: &Path,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use structopt::StructOpt;
|
use clap::Parser;
|
||||||
|
|
||||||
/// Open Rojo's documentation in your browser.
|
/// Open Rojo's documentation in your browser.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct DocCommand {}
|
pub struct DocCommand {}
|
||||||
|
|
||||||
impl DocCommand {
|
impl DocCommand {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use structopt::StructOpt;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::project::Project;
|
use crate::project::Project;
|
||||||
|
|
||||||
/// Reformat a Rojo project using the standard JSON formatting rules.
|
/// Reformat a Rojo project using the standard JSON formatting rules.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct FmtProjectCommand {
|
pub struct FmtProjectCommand {
|
||||||
/// Path to the project to format. Defaults to the current directory.
|
/// Path to the project to format. Defaults to the current directory.
|
||||||
#[structopt(default_value = "")]
|
#[clap(default_value = "")]
|
||||||
pub project: PathBuf,
|
pub project: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use std::process::{Command, Stdio};
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{bail, format_err};
|
use anyhow::{bail, format_err};
|
||||||
|
use clap::Parser;
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use fs_err::OpenOptions;
|
use fs_err::OpenOptions;
|
||||||
use structopt::StructOpt;
|
|
||||||
|
|
||||||
use super::resolve_path;
|
use super::resolve_path;
|
||||||
|
|
||||||
@@ -22,14 +22,14 @@ static PLACE_README: &str = include_str!("../../assets/default-place-project/REA
|
|||||||
static PLACE_GIT_IGNORE: &str = include_str!("../../assets/default-place-project/gitignore.txt");
|
static PLACE_GIT_IGNORE: &str = include_str!("../../assets/default-place-project/gitignore.txt");
|
||||||
|
|
||||||
/// Initializes a new Rojo project.
|
/// Initializes a new Rojo project.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct InitCommand {
|
pub struct InitCommand {
|
||||||
/// Path to the place to create the project. Defaults to the current directory.
|
/// Path to the place to create the project. Defaults to the current directory.
|
||||||
#[structopt(default_value = "")]
|
#[clap(default_value = "")]
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
|
||||||
/// The kind of project to create, 'place' or 'model'. Defaults to place.
|
/// The kind of project to create, 'place' or 'model'. Defaults to place.
|
||||||
#[structopt(long, default_value = "place")]
|
#[clap(long, default_value = "place")]
|
||||||
pub kind: InitKind,
|
pub kind: InitKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Defines Rojo's CLI through structopt types.
|
//! Defines Rojo's CLI through clap types.
|
||||||
|
|
||||||
mod build;
|
mod build;
|
||||||
mod doc;
|
mod doc;
|
||||||
@@ -7,11 +7,12 @@ mod init;
|
|||||||
mod plugin;
|
mod plugin;
|
||||||
mod serve;
|
mod serve;
|
||||||
mod sourcemap;
|
mod sourcemap;
|
||||||
|
mod unpack;
|
||||||
mod upload;
|
mod upload;
|
||||||
|
|
||||||
use std::{borrow::Cow, env, path::Path, str::FromStr};
|
use std::{borrow::Cow, env, path::Path, str::FromStr};
|
||||||
|
|
||||||
use structopt::StructOpt;
|
use clap::Parser;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub use self::build::BuildCommand;
|
pub use self::build::BuildCommand;
|
||||||
@@ -21,17 +22,18 @@ pub use self::init::{InitCommand, InitKind};
|
|||||||
pub use self::plugin::{PluginCommand, PluginSubcommand};
|
pub use self::plugin::{PluginCommand, PluginSubcommand};
|
||||||
pub use self::serve::ServeCommand;
|
pub use self::serve::ServeCommand;
|
||||||
pub use self::sourcemap::SourcemapCommand;
|
pub use self::sourcemap::SourcemapCommand;
|
||||||
|
pub use self::unpack::UnpackCommand;
|
||||||
pub use self::upload::UploadCommand;
|
pub use self::upload::UploadCommand;
|
||||||
|
|
||||||
/// Command line options that Rojo accepts, defined using the structopt crate.
|
/// Command line options that Rojo accepts, defined using the clap crate.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
#[structopt(name = "Rojo", about, author)]
|
#[clap(name = "Rojo", version, about, author)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
#[structopt(flatten)]
|
#[clap(flatten)]
|
||||||
pub global: GlobalOptions,
|
pub global: GlobalOptions,
|
||||||
|
|
||||||
/// Subcommand to run in this invocation.
|
/// Subcommand to run in this invocation.
|
||||||
#[structopt(subcommand)]
|
#[clap(subcommand)]
|
||||||
pub subcommand: Subcommand,
|
pub subcommand: Subcommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,18 +48,19 @@ impl Options {
|
|||||||
Subcommand::FmtProject(subcommand) => subcommand.run(),
|
Subcommand::FmtProject(subcommand) => subcommand.run(),
|
||||||
Subcommand::Doc(subcommand) => subcommand.run(),
|
Subcommand::Doc(subcommand) => subcommand.run(),
|
||||||
Subcommand::Plugin(subcommand) => subcommand.run(),
|
Subcommand::Plugin(subcommand) => subcommand.run(),
|
||||||
|
Subcommand::Unpack(subcommand) => subcommand.run(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct GlobalOptions {
|
pub struct GlobalOptions {
|
||||||
/// Sets verbosity level. Can be specified multiple times.
|
/// Sets verbosity level. Can be specified multiple times.
|
||||||
#[structopt(long("verbose"), short, global(true), parse(from_occurrences))]
|
#[clap(long("verbose"), short, global(true), parse(from_occurrences))]
|
||||||
pub verbosity: u8,
|
pub verbosity: u8,
|
||||||
|
|
||||||
/// Set color behavior. Valid values are auto, always, and never.
|
/// Set color behavior. Valid values are auto, always, and never.
|
||||||
#[structopt(long("color"), global(true), default_value("auto"))]
|
#[clap(long("color"), global(true), default_value("auto"))]
|
||||||
pub color: ColorChoice,
|
pub color: ColorChoice,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +112,7 @@ pub struct ColorChoiceParseError {
|
|||||||
attempted: String,
|
attempted: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub enum Subcommand {
|
pub enum Subcommand {
|
||||||
Init(InitCommand),
|
Init(InitCommand),
|
||||||
Serve(ServeCommand),
|
Serve(ServeCommand),
|
||||||
@@ -119,6 +122,7 @@ pub enum Subcommand {
|
|||||||
FmtProject(FmtProjectCommand),
|
FmtProject(FmtProjectCommand),
|
||||||
Doc(DocCommand),
|
Doc(DocCommand),
|
||||||
Plugin(PluginCommand),
|
Plugin(PluginCommand),
|
||||||
|
Unpack(UnpackCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn resolve_path(path: &Path) -> Cow<'_, Path> {
|
pub(super) fn resolve_path(path: &Path) -> Cow<'_, Path> {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ use std::{
|
|||||||
io::BufWriter,
|
io::BufWriter,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
use memofs::{InMemoryFs, Vfs, VfsSnapshot};
|
use memofs::{InMemoryFs, Vfs, VfsSnapshot};
|
||||||
use roblox_install::RobloxStudio;
|
use roblox_install::RobloxStudio;
|
||||||
use structopt::StructOpt;
|
|
||||||
|
|
||||||
use crate::serve_session::ServeSession;
|
use crate::serve_session::ServeSession;
|
||||||
|
|
||||||
@@ -13,14 +13,14 @@ static PLUGIN_BINCODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/plugin.
|
|||||||
static PLUGIN_FILE_NAME: &str = "RojoManagedPlugin.rbxm";
|
static PLUGIN_FILE_NAME: &str = "RojoManagedPlugin.rbxm";
|
||||||
|
|
||||||
/// Install Rojo's plugin.
|
/// Install Rojo's plugin.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct PluginCommand {
|
pub struct PluginCommand {
|
||||||
#[structopt(subcommand)]
|
#[clap(subcommand)]
|
||||||
subcommand: PluginSubcommand,
|
subcommand: PluginSubcommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manages Rojo's Roblox Studio plugin.
|
/// Manages Rojo's Roblox Studio plugin.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub enum PluginSubcommand {
|
pub enum PluginSubcommand {
|
||||||
/// Install the plugin in Roblox Studio's plugins folder. If the plugin is
|
/// Install the plugin in Roblox Studio's plugins folder. If the plugin is
|
||||||
/// already installed, installing it again will overwrite the current plugin
|
/// already installed, installing it again will overwrite the current plugin
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
use memofs::Vfs;
|
use memofs::Vfs;
|
||||||
use structopt::StructOpt;
|
|
||||||
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
|
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
|
||||||
|
|
||||||
use crate::{serve_session::ServeSession, web::LiveServer};
|
use crate::{serve_session::ServeSession, web::LiveServer};
|
||||||
@@ -17,19 +17,19 @@ const DEFAULT_BIND_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
|
|||||||
const DEFAULT_PORT: u16 = 34872;
|
const DEFAULT_PORT: u16 = 34872;
|
||||||
|
|
||||||
/// Expose a Rojo project to the Rojo Studio plugin.
|
/// Expose a Rojo project to the Rojo Studio plugin.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct ServeCommand {
|
pub struct ServeCommand {
|
||||||
/// Path to the project to serve. Defaults to the current directory.
|
/// Path to the project to serve. Defaults to the current directory.
|
||||||
#[structopt(default_value = "")]
|
#[clap(default_value = "")]
|
||||||
pub project: PathBuf,
|
pub project: PathBuf,
|
||||||
|
|
||||||
/// The IP address to listen on. Defaults to `127.0.0.1`.
|
/// The IP address to listen on. Defaults to `127.0.0.1`.
|
||||||
#[structopt(long)]
|
#[clap(long)]
|
||||||
pub address: Option<IpAddr>,
|
pub address: Option<IpAddr>,
|
||||||
|
|
||||||
/// The port to listen on. Defaults to the project's preference, or `34872` if
|
/// The port to listen on. Defaults to the project's preference, or `34872` if
|
||||||
/// it has none.
|
/// it has none.
|
||||||
#[structopt(long)]
|
#[clap(long)]
|
||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
use fs_err::File;
|
use fs_err::File;
|
||||||
use memofs::Vfs;
|
use memofs::Vfs;
|
||||||
use rbx_dom_weak::types::Ref;
|
use rbx_dom_weak::types::Ref;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use structopt::StructOpt;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
serve_session::ServeSession,
|
serve_session::ServeSession,
|
||||||
@@ -33,22 +33,22 @@ struct SourcemapNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a sourcemap file from the Rojo project.
|
/// Generates a sourcemap file from the Rojo project.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct SourcemapCommand {
|
pub struct SourcemapCommand {
|
||||||
/// Path to the project to use for the sourcemap. Defaults to the current
|
/// Path to the project to use for the sourcemap. Defaults to the current
|
||||||
/// directory.
|
/// directory.
|
||||||
#[structopt(default_value = "")]
|
#[clap(default_value = "")]
|
||||||
pub project: PathBuf,
|
pub project: PathBuf,
|
||||||
|
|
||||||
/// Where to output the sourcemap. Omit this to use stdout instead of
|
/// Where to output the sourcemap. Omit this to use stdout instead of
|
||||||
/// writing to a file.
|
/// writing to a file.
|
||||||
///
|
///
|
||||||
/// Should end in .json.
|
/// Should end in .json.
|
||||||
#[structopt(long, short)]
|
#[clap(long, short)]
|
||||||
pub output: Option<PathBuf>,
|
pub output: Option<PathBuf>,
|
||||||
|
|
||||||
/// If non-script files should be included or not. Defaults to false.
|
/// If non-script files should be included or not. Defaults to false.
|
||||||
#[structopt(long)]
|
#[clap(long)]
|
||||||
pub include_non_scripts: bool,
|
pub include_non_scripts: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,9 +56,6 @@ impl SourcemapCommand {
|
|||||||
pub fn run(self) -> anyhow::Result<()> {
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
let project_path = resolve_path(&self.project);
|
let project_path = resolve_path(&self.project);
|
||||||
|
|
||||||
let mut project_dir = project_path.to_path_buf();
|
|
||||||
project_dir.pop();
|
|
||||||
|
|
||||||
log::trace!("Constructing in-memory filesystem");
|
log::trace!("Constructing in-memory filesystem");
|
||||||
let vfs = Vfs::new_default();
|
let vfs = Vfs::new_default();
|
||||||
|
|
||||||
@@ -71,7 +68,7 @@ impl SourcemapCommand {
|
|||||||
filter_non_scripts
|
filter_non_scripts
|
||||||
};
|
};
|
||||||
|
|
||||||
let root_node = recurse_create_node(&tree, tree.get_root_id(), &project_dir, filter);
|
let root_node = recurse_create_node(&tree, tree.get_root_id(), session.root_dir(), filter);
|
||||||
|
|
||||||
if let Some(output_path) = self.output {
|
if let Some(output_path) = self.output {
|
||||||
let mut file = BufWriter::new(File::create(&output_path)?);
|
let mut file = BufWriter::new(File::create(&output_path)?);
|
||||||
|
|||||||
65
src/cli/unpack.rs
Normal file
65
src/cli/unpack.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
use std::{io::BufReader, path::PathBuf};
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
use clap::Parser;
|
||||||
|
use fs_err::File;
|
||||||
|
use rbx_dom_weak::{Instance, WeakDom};
|
||||||
|
|
||||||
|
use crate::{Project, ProjectNode};
|
||||||
|
|
||||||
|
use super::resolve_path;
|
||||||
|
|
||||||
|
/// Unpack a Roblox place file into an existing Rojo project.
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct UnpackCommand {
|
||||||
|
/// Path to the project to unpack. Defaults to the current directory.
|
||||||
|
#[clap(long, default_value = "")]
|
||||||
|
pub project: PathBuf,
|
||||||
|
|
||||||
|
/// Path to the place to unpack from.
|
||||||
|
pub place: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnpackCommand {
|
||||||
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
|
let project_path = resolve_path(&self.project);
|
||||||
|
let project = match Project::load_fuzzy(&project_path)? {
|
||||||
|
Some(project) => project,
|
||||||
|
None => bail!("No project file was found; rojo unpack requires a project file."),
|
||||||
|
};
|
||||||
|
|
||||||
|
let place_ext = self
|
||||||
|
.place
|
||||||
|
.extension()
|
||||||
|
.and_then(|ext| ext.to_str())
|
||||||
|
.map(|ext| ext.to_lowercase());
|
||||||
|
|
||||||
|
let file = BufReader::new(File::open(&self.place)?);
|
||||||
|
|
||||||
|
let dom = match place_ext.as_deref() {
|
||||||
|
Some("rbxl") => rbx_binary::from_reader(file)?,
|
||||||
|
Some("rbxlx") => rbx_xml::from_reader_default(file)?,
|
||||||
|
Some(_) | None => bail!("Place files must end in .rbxl or .rbxlx"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let context = Context { project, dom };
|
||||||
|
context.unpack();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Context {
|
||||||
|
project: Project,
|
||||||
|
dom: WeakDom,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
fn unpack(&self) {
|
||||||
|
self.unpack_node(&self.project.tree, self.dom.root());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack_node(&self, node: &ProjectNode, instance: &Instance) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,38 +2,38 @@ use std::path::PathBuf;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{bail, format_err, Context};
|
use anyhow::{bail, format_err, Context};
|
||||||
|
use clap::Parser;
|
||||||
use memofs::Vfs;
|
use memofs::Vfs;
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT},
|
header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT},
|
||||||
StatusCode,
|
StatusCode,
|
||||||
};
|
};
|
||||||
use structopt::StructOpt;
|
|
||||||
|
|
||||||
use crate::{auth_cookie::get_auth_cookie, serve_session::ServeSession};
|
use crate::{auth_cookie::get_auth_cookie, serve_session::ServeSession};
|
||||||
|
|
||||||
use super::resolve_path;
|
use super::resolve_path;
|
||||||
|
|
||||||
/// Builds the project and uploads it to Roblox.
|
/// Builds the project and uploads it to Roblox.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct UploadCommand {
|
pub struct UploadCommand {
|
||||||
/// Path to the project to upload. Defaults to the current directory.
|
/// Path to the project to upload. Defaults to the current directory.
|
||||||
#[structopt(default_value = "")]
|
#[clap(default_value = "")]
|
||||||
pub project: PathBuf,
|
pub project: PathBuf,
|
||||||
|
|
||||||
/// Authenication cookie to use. If not specified, Rojo will attempt to find one from the system automatically.
|
/// Authenication cookie to use. If not specified, Rojo will attempt to find one from the system automatically.
|
||||||
#[structopt(long)]
|
#[clap(long)]
|
||||||
pub cookie: Option<String>,
|
pub cookie: Option<String>,
|
||||||
|
|
||||||
/// API key obtained from create.roblox.com/credentials. Rojo will use the Open Cloud API when this is provided. Only supports uploading to a place.
|
/// API key obtained from create.roblox.com/credentials. Rojo will use the Open Cloud API when this is provided. Only supports uploading to a place.
|
||||||
#[structopt(long = "api_key")]
|
#[clap(long = "api_key")]
|
||||||
pub api_key: Option<String>,
|
pub api_key: Option<String>,
|
||||||
|
|
||||||
/// The Universe ID of the given place. Required when using the Open Cloud API.
|
/// The Universe ID of the given place. Required when using the Open Cloud API.
|
||||||
#[structopt(long = "universe_id")]
|
#[clap(long = "universe_id")]
|
||||||
pub universe_id: Option<u64>,
|
pub universe_id: Option<u64>,
|
||||||
|
|
||||||
/// Asset ID to upload to.
|
/// Asset ID to upload to.
|
||||||
#[structopt(long = "asset_id")]
|
#[clap(long = "asset_id")]
|
||||||
pub asset_id: u64,
|
pub asset_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ fn do_upload(buffer: Vec<u8>, asset_id: u64, cookie: &str) -> anyhow::Result<()>
|
|||||||
asset_id
|
asset_id
|
||||||
);
|
);
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
|
|
||||||
let build_request = move || {
|
let build_request = move || {
|
||||||
client
|
client
|
||||||
@@ -172,10 +172,10 @@ fn do_upload_open_cloud(
|
|||||||
universe_id, asset_id
|
universe_id, asset_id
|
||||||
);
|
);
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
|
|
||||||
log::debug!("Uploading to Roblox...");
|
log::debug!("Uploading to Roblox...");
|
||||||
let mut response = client
|
let response = client
|
||||||
.post(&url)
|
.post(&url)
|
||||||
.header("x-api-key", api_key)
|
.header("x-api-key", api_key)
|
||||||
.header(CONTENT_TYPE, "application/xml")
|
.header(CONTENT_TYPE, "application/xml")
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
use std::{env, panic, process};
|
use std::{env, panic, process};
|
||||||
|
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
use structopt::StructOpt;
|
use clap::Parser;
|
||||||
|
|
||||||
use librojo::cli::Options;
|
use librojo::cli::Options;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
#[cfg(feature = "profile-with-tracy")]
|
||||||
|
tracy_client::Client::start();
|
||||||
|
|
||||||
panic::set_hook(Box::new(|panic_info| {
|
panic::set_hook(Box::new(|panic_info| {
|
||||||
// PanicInfo's payload is usually a &'static str or String.
|
// PanicInfo's payload is usually a &'static str or String.
|
||||||
// See: https://doc.rust-lang.org/beta/std/panic/struct.PanicInfo.html#method.payload
|
// See: https://doc.rust-lang.org/beta/std/panic/struct.PanicInfo.html#method.payload
|
||||||
@@ -49,7 +52,7 @@ fn main() {
|
|||||||
process::exit(1);
|
process::exit(1);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let options = Options::from_args();
|
let options = Options::parse();
|
||||||
|
|
||||||
let log_filter = match options.global.verbosity {
|
let log_filter = match options.global.verbosity {
|
||||||
0 => "info",
|
0 => "info",
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ impl<T: Clone> MessageQueue<T> {
|
|||||||
/// This method is only useful in tests. Non-test code should use subscribe
|
/// This method is only useful in tests. Non-test code should use subscribe
|
||||||
/// instead.
|
/// instead.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[allow(unused)]
|
||||||
pub fn subscribe_any(&self) -> oneshot::Receiver<(u32, Vec<T>)> {
|
pub fn subscribe_any(&self) -> oneshot::Receiver<(u32, Vec<T>)> {
|
||||||
let cursor = {
|
let cursor = {
|
||||||
let messages = self.messages.read().unwrap();
|
let messages = self.messages.read().unwrap();
|
||||||
|
|||||||
@@ -36,17 +36,3 @@ where
|
|||||||
|
|
||||||
seq.end()
|
seq.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize_option_absolute<S, T>(
|
|
||||||
maybe_path: &Option<T>,
|
|
||||||
serializer: S,
|
|
||||||
) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
T: AsRef<Path>,
|
|
||||||
{
|
|
||||||
match maybe_path {
|
|
||||||
Some(path) => serialize_absolute(path, serializer),
|
|
||||||
None => serializer.serialize_none(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ impl ServeSession {
|
|||||||
let snapshot = snapshot_from_vfs(&instance_context, &vfs, &start_path)?;
|
let snapshot = snapshot_from_vfs(&instance_context, &vfs, &start_path)?;
|
||||||
|
|
||||||
log::trace!("Computing initial patch set");
|
log::trace!("Computing initial patch set");
|
||||||
let patch_set = compute_patch_set(snapshot.as_ref(), &tree, root_id);
|
let patch_set = compute_patch_set(snapshot, &tree, root_id);
|
||||||
|
|
||||||
log::trace!("Applying initial patch set");
|
log::trace!("Applying initial patch set");
|
||||||
apply_patch_set(&mut tree, patch_set);
|
apply_patch_set(&mut tree, patch_set);
|
||||||
@@ -216,6 +216,10 @@ impl ServeSession {
|
|||||||
pub fn serve_address(&self) -> Option<IpAddr> {
|
pub fn serve_address(&self) -> Option<IpAddr> {
|
||||||
self.root_project.serve_address
|
self.root_project.serve_address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn root_dir(&self) -> &Path {
|
||||||
|
self.root_project.folder_location()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{borrow::Cow, collections::HashMap};
|
|||||||
|
|
||||||
use rbx_dom_weak::{
|
use rbx_dom_weak::{
|
||||||
types::{Ref, Variant},
|
types::{Ref, Variant},
|
||||||
WeakDom,
|
Instance, WeakDom,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -102,22 +102,29 @@ impl InstanceSnapshot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_tree(tree: &WeakDom, id: Ref) -> Self {
|
#[profiling::function]
|
||||||
let instance = tree.get_by_ref(id).expect("instance did not exist in tree");
|
pub fn from_tree(tree: WeakDom, id: Ref) -> Self {
|
||||||
|
let (_, mut raw_tree) = tree.into_raw();
|
||||||
|
Self::from_raw_tree(&mut raw_tree, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_raw_tree(raw_tree: &mut HashMap<Ref, Instance>, id: Ref) -> Self {
|
||||||
|
let instance = raw_tree
|
||||||
|
.remove(&id)
|
||||||
|
.expect("instance did not exist in tree");
|
||||||
|
|
||||||
let children = instance
|
let children = instance
|
||||||
.children()
|
.children()
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.map(|&id| Self::from_raw_tree(raw_tree, id))
|
||||||
.map(|id| Self::from_tree(tree, id))
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
snapshot_id: Some(id),
|
snapshot_id: Some(id),
|
||||||
metadata: InstanceMetadata::default(),
|
metadata: InstanceMetadata::default(),
|
||||||
name: Cow::Owned(instance.name.clone()),
|
name: Cow::Owned(instance.name),
|
||||||
class_name: Cow::Owned(instance.class.clone()),
|
class_name: Cow::Owned(instance.class),
|
||||||
properties: instance.properties.clone(),
|
properties: instance.properties,
|
||||||
children,
|
children,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
//! Defines the algorithm for applying generated patches.
|
//! Defines the algorithm for applying generated patches.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
mem::take,
|
||||||
|
};
|
||||||
|
|
||||||
use rbx_dom_weak::types::{Ref, Variant};
|
use rbx_dom_weak::types::{Ref, Variant};
|
||||||
|
|
||||||
@@ -12,21 +15,31 @@ use super::{
|
|||||||
/// Consumes the input `PatchSet`, applying all of its prescribed changes to the
|
/// Consumes the input `PatchSet`, applying all of its prescribed changes to the
|
||||||
/// tree and returns an `AppliedPatchSet`, which can be used to keep another
|
/// tree and returns an `AppliedPatchSet`, which can be used to keep another
|
||||||
/// tree in sync with Rojo's.
|
/// tree in sync with Rojo's.
|
||||||
|
#[profiling::function]
|
||||||
pub fn apply_patch_set(tree: &mut RojoTree, patch_set: PatchSet) -> AppliedPatchSet {
|
pub fn apply_patch_set(tree: &mut RojoTree, patch_set: PatchSet) -> AppliedPatchSet {
|
||||||
let mut context = PatchApplyContext::default();
|
let mut context = PatchApplyContext::default();
|
||||||
|
|
||||||
for removed_id in patch_set.removed_instances {
|
{
|
||||||
apply_remove_instance(&mut context, tree, removed_id);
|
profiling::scope!("removals");
|
||||||
|
for removed_id in patch_set.removed_instances {
|
||||||
|
apply_remove_instance(&mut context, tree, removed_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for add_patch in patch_set.added_instances {
|
{
|
||||||
apply_add_child(&mut context, tree, add_patch.parent_id, add_patch.instance);
|
profiling::scope!("additions");
|
||||||
|
for add_patch in patch_set.added_instances {
|
||||||
|
apply_add_child(&mut context, tree, add_patch.parent_id, add_patch.instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates need to be applied after additions, which reduces the complexity
|
{
|
||||||
// of updates significantly.
|
profiling::scope!("updates");
|
||||||
for update_patch in patch_set.updated_instances {
|
// Updates need to be applied after additions, which reduces the complexity
|
||||||
apply_update_child(&mut context, tree, update_patch);
|
// of updates significantly.
|
||||||
|
for update_patch in patch_set.updated_instances {
|
||||||
|
apply_update_child(&mut context, tree, update_patch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
finalize_patch_application(context, tree)
|
finalize_patch_application(context, tree)
|
||||||
@@ -55,20 +68,9 @@ struct PatchApplyContext {
|
|||||||
/// eachother.
|
/// eachother.
|
||||||
snapshot_id_to_instance_id: HashMap<Ref, Ref>,
|
snapshot_id_to_instance_id: HashMap<Ref, Ref>,
|
||||||
|
|
||||||
/// The properties of instances added by the current `PatchSet`.
|
/// Tracks all of the instances added by this patch that have refs that need
|
||||||
///
|
/// to be rewritten.
|
||||||
/// Instances added to the tree can refer to eachother via Ref properties,
|
has_refs_to_rewrite: HashSet<Ref>,
|
||||||
/// but we need to make sure they're correctly transformed from snapshot
|
|
||||||
/// space into tree space (via `snapshot_id_to_instance_id`).
|
|
||||||
///
|
|
||||||
/// It's not possible to do that transformation for refs that refer to added
|
|
||||||
/// instances until all the instances have actually been inserted into the
|
|
||||||
/// tree. For simplicity, we defer application of _all_ properties on added
|
|
||||||
/// instances instead of just Refs.
|
|
||||||
///
|
|
||||||
/// This doesn't affect updated instances, since they're always applied
|
|
||||||
/// after we've added all the instances from the patch.
|
|
||||||
added_instance_properties: HashMap<Ref, HashMap<String, Variant>>,
|
|
||||||
|
|
||||||
/// The current applied patch result, describing changes made to the tree.
|
/// The current applied patch result, describing changes made to the tree.
|
||||||
applied_patch_set: AppliedPatchSet,
|
applied_patch_set: AppliedPatchSet,
|
||||||
@@ -84,23 +86,22 @@ struct PatchApplyContext {
|
|||||||
/// The remaining Ref properties need to be handled during patch application,
|
/// The remaining Ref properties need to be handled during patch application,
|
||||||
/// where we build up a map of snapshot IDs to instance IDs as they're created,
|
/// where we build up a map of snapshot IDs to instance IDs as they're created,
|
||||||
/// then apply properties all at once at the end.
|
/// then apply properties all at once at the end.
|
||||||
|
#[profiling::function]
|
||||||
fn finalize_patch_application(context: PatchApplyContext, tree: &mut RojoTree) -> AppliedPatchSet {
|
fn finalize_patch_application(context: PatchApplyContext, tree: &mut RojoTree) -> AppliedPatchSet {
|
||||||
for (id, properties) in context.added_instance_properties {
|
for id in context.has_refs_to_rewrite {
|
||||||
// This should always succeed since instances marked as added in our
|
// This should always succeed since instances marked as added in our
|
||||||
// patch should be added without fail.
|
// patch should be added without fail.
|
||||||
let mut instance = tree
|
let mut instance = tree
|
||||||
.get_instance_mut(id)
|
.get_instance_mut(id)
|
||||||
.expect("Invalid instance ID in deferred property map");
|
.expect("Invalid instance ID in deferred property map");
|
||||||
|
|
||||||
for (key, mut property_value) in properties {
|
for value in instance.properties_mut().values_mut() {
|
||||||
if let Variant::Ref(referent) = property_value {
|
if let Variant::Ref(referent) = value {
|
||||||
if let Some(&instance_referent) = context.snapshot_id_to_instance_id.get(&referent)
|
if let Some(&instance_referent) = context.snapshot_id_to_instance_id.get(&referent)
|
||||||
{
|
{
|
||||||
property_value = Variant::Ref(instance_referent);
|
*value = Variant::Ref(instance_referent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.properties_mut().insert(key, property_value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,24 +117,24 @@ fn apply_add_child(
|
|||||||
context: &mut PatchApplyContext,
|
context: &mut PatchApplyContext,
|
||||||
tree: &mut RojoTree,
|
tree: &mut RojoTree,
|
||||||
parent_id: Ref,
|
parent_id: Ref,
|
||||||
snapshot: InstanceSnapshot,
|
mut snapshot: InstanceSnapshot,
|
||||||
) {
|
) {
|
||||||
let snapshot_id = snapshot.snapshot_id;
|
let snapshot_id = snapshot.snapshot_id;
|
||||||
let properties = snapshot.properties;
|
let children = take(&mut snapshot.children);
|
||||||
let children = snapshot.children;
|
|
||||||
|
|
||||||
// Property application is deferred until after all children
|
// If an object we're adding has a non-null referent, we'll note this
|
||||||
// are constructed. This helps apply referents correctly.
|
// instance down as needing to be revisited later.
|
||||||
let remaining_snapshot = InstanceSnapshot::new()
|
let has_refs = snapshot.properties.values().any(|value| match value {
|
||||||
.name(snapshot.name)
|
Variant::Ref(value) => value.is_some(),
|
||||||
.class_name(snapshot.class_name)
|
_ => false,
|
||||||
.metadata(snapshot.metadata)
|
});
|
||||||
.snapshot_id(snapshot.snapshot_id);
|
|
||||||
|
|
||||||
let id = tree.insert_instance(parent_id, remaining_snapshot);
|
let id = tree.insert_instance(parent_id, snapshot);
|
||||||
context.applied_patch_set.added.push(id);
|
context.applied_patch_set.added.push(id);
|
||||||
|
|
||||||
context.added_instance_properties.insert(id, properties);
|
if has_refs {
|
||||||
|
context.has_refs_to_rewrite.insert(id);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(snapshot_id) = snapshot_id {
|
if let Some(snapshot_id) = snapshot_id {
|
||||||
context.snapshot_id_to_instance_id.insert(snapshot_id, id);
|
context.snapshot_id_to_instance_id.insert(snapshot_id, id);
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
//! Defines the algorithm for computing a roughly-minimal patch set given an
|
//! Defines the algorithm for computing a roughly-minimal patch set given an
|
||||||
//! existing instance tree and an instance snapshot.
|
//! existing instance tree and an instance snapshot.
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
mem::take,
|
||||||
|
};
|
||||||
|
|
||||||
use rbx_dom_weak::types::{Ref, Variant};
|
use rbx_dom_weak::types::{Ref, Variant};
|
||||||
|
|
||||||
@@ -10,11 +13,8 @@ use super::{
|
|||||||
InstanceSnapshot, InstanceWithMeta, RojoTree,
|
InstanceSnapshot, InstanceWithMeta, RojoTree,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn compute_patch_set(
|
#[profiling::function]
|
||||||
snapshot: Option<&InstanceSnapshot>,
|
pub fn compute_patch_set(snapshot: Option<InstanceSnapshot>, tree: &RojoTree, id: Ref) -> PatchSet {
|
||||||
tree: &RojoTree,
|
|
||||||
id: Ref,
|
|
||||||
) -> PatchSet {
|
|
||||||
let mut patch_set = PatchSet::new();
|
let mut patch_set = PatchSet::new();
|
||||||
|
|
||||||
if let Some(snapshot) = snapshot {
|
if let Some(snapshot) = snapshot {
|
||||||
@@ -74,7 +74,7 @@ fn rewrite_refs_in_snapshot(context: &ComputePatchContext, snapshot: &mut Instan
|
|||||||
|
|
||||||
fn compute_patch_set_internal(
|
fn compute_patch_set_internal(
|
||||||
context: &mut ComputePatchContext,
|
context: &mut ComputePatchContext,
|
||||||
snapshot: &InstanceSnapshot,
|
mut snapshot: InstanceSnapshot,
|
||||||
tree: &RojoTree,
|
tree: &RojoTree,
|
||||||
id: Ref,
|
id: Ref,
|
||||||
patch_set: &mut PatchSet,
|
patch_set: &mut PatchSet,
|
||||||
@@ -87,12 +87,12 @@ fn compute_patch_set_internal(
|
|||||||
.get_instance(id)
|
.get_instance(id)
|
||||||
.expect("Instance did not exist in tree");
|
.expect("Instance did not exist in tree");
|
||||||
|
|
||||||
compute_property_patches(snapshot, &instance, patch_set);
|
compute_property_patches(&mut snapshot, &instance, patch_set);
|
||||||
compute_children_patches(context, snapshot, tree, id, patch_set);
|
compute_children_patches(context, &mut snapshot, tree, id, patch_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_property_patches(
|
fn compute_property_patches(
|
||||||
snapshot: &InstanceSnapshot,
|
snapshot: &mut InstanceSnapshot,
|
||||||
instance: &InstanceWithMeta,
|
instance: &InstanceWithMeta,
|
||||||
patch_set: &mut PatchSet,
|
patch_set: &mut PatchSet,
|
||||||
) {
|
) {
|
||||||
@@ -102,32 +102,32 @@ fn compute_property_patches(
|
|||||||
let changed_name = if snapshot.name == instance.name() {
|
let changed_name = if snapshot.name == instance.name() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(snapshot.name.clone().into_owned())
|
Some(take(&mut snapshot.name).into_owned())
|
||||||
};
|
};
|
||||||
|
|
||||||
let changed_class_name = if snapshot.class_name == instance.class_name() {
|
let changed_class_name = if snapshot.class_name == instance.class_name() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(snapshot.class_name.clone().into_owned())
|
Some(take(&mut snapshot.class_name).into_owned())
|
||||||
};
|
};
|
||||||
|
|
||||||
let changed_metadata = if &snapshot.metadata == instance.metadata() {
|
let changed_metadata = if &snapshot.metadata == instance.metadata() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(snapshot.metadata.clone())
|
Some(take(&mut snapshot.metadata))
|
||||||
};
|
};
|
||||||
|
|
||||||
for (name, snapshot_value) in &snapshot.properties {
|
for (name, snapshot_value) in take(&mut snapshot.properties) {
|
||||||
visited_properties.insert(name.as_str());
|
visited_properties.insert(name.clone());
|
||||||
|
|
||||||
match instance.properties().get(name) {
|
match instance.properties().get(&name) {
|
||||||
Some(instance_value) => {
|
Some(instance_value) => {
|
||||||
if snapshot_value != instance_value {
|
if &snapshot_value != instance_value {
|
||||||
changed_properties.insert(name.clone(), Some(snapshot_value.clone()));
|
changed_properties.insert(name, Some(snapshot_value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
changed_properties.insert(name.clone(), Some(snapshot_value.clone()));
|
changed_properties.insert(name, Some(snapshot_value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ fn compute_property_patches(
|
|||||||
|
|
||||||
fn compute_children_patches(
|
fn compute_children_patches(
|
||||||
context: &mut ComputePatchContext,
|
context: &mut ComputePatchContext,
|
||||||
snapshot: &InstanceSnapshot,
|
snapshot: &mut InstanceSnapshot,
|
||||||
tree: &RojoTree,
|
tree: &RojoTree,
|
||||||
id: Ref,
|
id: Ref,
|
||||||
patch_set: &mut PatchSet,
|
patch_set: &mut PatchSet,
|
||||||
@@ -172,7 +172,7 @@ fn compute_children_patches(
|
|||||||
|
|
||||||
let mut paired_instances = vec![false; instance_children.len()];
|
let mut paired_instances = vec![false; instance_children.len()];
|
||||||
|
|
||||||
for snapshot_child in snapshot.children.iter() {
|
for snapshot_child in take(&mut snapshot.children) {
|
||||||
let matching_instance =
|
let matching_instance =
|
||||||
instance_children
|
instance_children
|
||||||
.iter()
|
.iter()
|
||||||
@@ -209,7 +209,7 @@ fn compute_children_patches(
|
|||||||
None => {
|
None => {
|
||||||
patch_set.added_instances.push(PatchAdd {
|
patch_set.added_instances.push(PatchAdd {
|
||||||
parent_id: id,
|
parent_id: id,
|
||||||
instance: snapshot_child.clone(),
|
instance: snapshot_child,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,7 +257,7 @@ mod test {
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let patch_set = compute_patch_set(Some(&snapshot), &tree, root_id);
|
let patch_set = compute_patch_set(Some(snapshot), &tree, root_id);
|
||||||
|
|
||||||
let expected_patch_set = PatchSet {
|
let expected_patch_set = PatchSet {
|
||||||
updated_instances: vec![PatchUpdate {
|
updated_instances: vec![PatchUpdate {
|
||||||
@@ -307,7 +307,7 @@ mod test {
|
|||||||
class_name: Cow::Borrowed("foo"),
|
class_name: Cow::Borrowed("foo"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let patch_set = compute_patch_set(Some(&snapshot), &tree, root_id);
|
let patch_set = compute_patch_set(Some(snapshot), &tree, root_id);
|
||||||
|
|
||||||
let expected_patch_set = PatchSet {
|
let expected_patch_set = PatchSet {
|
||||||
added_instances: vec![PatchAdd {
|
added_instances: vec![PatchAdd {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ fn set_name_and_class_name() {
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let patch_set = compute_patch_set(Some(&snapshot), &tree, tree.get_root_id());
|
let patch_set = compute_patch_set(Some(snapshot), &tree, tree.get_root_id());
|
||||||
let patch_value = redactions.redacted_yaml(patch_set);
|
let patch_value = redactions.redacted_yaml(patch_set);
|
||||||
|
|
||||||
assert_yaml_snapshot!(patch_value);
|
assert_yaml_snapshot!(patch_value);
|
||||||
@@ -47,7 +47,7 @@ fn set_property() {
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let patch_set = compute_patch_set(Some(&snapshot), &tree, tree.get_root_id());
|
let patch_set = compute_patch_set(Some(snapshot), &tree, tree.get_root_id());
|
||||||
let patch_value = redactions.redacted_yaml(patch_set);
|
let patch_value = redactions.redacted_yaml(patch_set);
|
||||||
|
|
||||||
assert_yaml_snapshot!(patch_value);
|
assert_yaml_snapshot!(patch_value);
|
||||||
@@ -78,7 +78,7 @@ fn remove_property() {
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let patch_set = compute_patch_set(Some(&snapshot), &tree, tree.get_root_id());
|
let patch_set = compute_patch_set(Some(snapshot), &tree, tree.get_root_id());
|
||||||
let patch_value = redactions.redacted_yaml(patch_set);
|
let patch_value = redactions.redacted_yaml(patch_set);
|
||||||
|
|
||||||
assert_yaml_snapshot!(patch_value);
|
assert_yaml_snapshot!(patch_value);
|
||||||
@@ -107,7 +107,7 @@ fn add_child() {
|
|||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
let patch_set = compute_patch_set(Some(&snapshot), &tree, tree.get_root_id());
|
let patch_set = compute_patch_set(Some(snapshot), &tree, tree.get_root_id());
|
||||||
let patch_value = redactions.redacted_yaml(patch_set);
|
let patch_value = redactions.redacted_yaml(patch_set);
|
||||||
|
|
||||||
assert_yaml_snapshot!(patch_value);
|
assert_yaml_snapshot!(patch_value);
|
||||||
@@ -139,7 +139,7 @@ fn remove_child() {
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let patch_set = compute_patch_set(Some(&snapshot), &tree, tree.get_root_id());
|
let patch_set = compute_patch_set(Some(snapshot), &tree, tree.get_root_id());
|
||||||
let patch_value = redactions.redacted_yaml(patch_set);
|
let patch_value = redactions.redacted_yaml(patch_set);
|
||||||
|
|
||||||
assert_yaml_snapshot!(patch_value);
|
assert_yaml_snapshot!(patch_value);
|
||||||
|
|||||||
@@ -87,8 +87,9 @@ impl RojoTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_instance(&mut self, parent_ref: Ref, snapshot: InstanceSnapshot) -> Ref {
|
pub fn insert_instance(&mut self, parent_ref: Ref, snapshot: InstanceSnapshot) -> Ref {
|
||||||
let builder = InstanceBuilder::new(snapshot.class_name.to_owned())
|
let builder = InstanceBuilder::empty()
|
||||||
.with_name(snapshot.name.to_owned())
|
.with_class(snapshot.class_name.into_owned())
|
||||||
|
.with_name(snapshot.name.into_owned())
|
||||||
.with_properties(snapshot.properties);
|
.with_properties(snapshot.properties);
|
||||||
|
|
||||||
let referent = self.inner.insert(parent_ref, builder);
|
let referent = self.inner.insert(parent_ref, builder);
|
||||||
|
|||||||
@@ -10,6 +10,40 @@ pub fn snapshot_dir(
|
|||||||
context: &InstanceContext,
|
context: &InstanceContext,
|
||||||
vfs: &Vfs,
|
vfs: &Vfs,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
|
) -> anyhow::Result<Option<InstanceSnapshot>> {
|
||||||
|
let mut snapshot = match snapshot_dir_no_meta(context, vfs, path)? {
|
||||||
|
Some(snapshot) => snapshot,
|
||||||
|
None => return Ok(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(mut meta) = dir_meta(vfs, path)? {
|
||||||
|
meta.apply_all(&mut snapshot)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(snapshot))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the meta file that should be applied for this directory, if it
|
||||||
|
/// exists.
|
||||||
|
pub fn dir_meta(vfs: &Vfs, path: &Path) -> anyhow::Result<Option<DirectoryMetadata>> {
|
||||||
|
let meta_path = path.join("init.meta.json");
|
||||||
|
|
||||||
|
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
|
||||||
|
let metadata = DirectoryMetadata::from_slice(&meta_contents, meta_path)?;
|
||||||
|
Ok(Some(metadata))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Snapshot a directory without applying meta files; useful for if the
|
||||||
|
/// directory's ClassName will change before metadata should be applied. For
|
||||||
|
/// example, this can happen if the directory contains an `init.client.lua`
|
||||||
|
/// file.
|
||||||
|
pub fn snapshot_dir_no_meta(
|
||||||
|
context: &InstanceContext,
|
||||||
|
vfs: &Vfs,
|
||||||
|
path: &Path,
|
||||||
) -> anyhow::Result<Option<InstanceSnapshot>> {
|
) -> anyhow::Result<Option<InstanceSnapshot>> {
|
||||||
let passes_filter_rules = |child: &DirEntry| {
|
let passes_filter_rules = |child: &DirEntry| {
|
||||||
context
|
context
|
||||||
@@ -52,7 +86,7 @@ pub fn snapshot_dir(
|
|||||||
path.join("init.client.lua"),
|
path.join("init.client.lua"),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut snapshot = InstanceSnapshot::new()
|
let snapshot = InstanceSnapshot::new()
|
||||||
.name(instance_name)
|
.name(instance_name)
|
||||||
.class_name("Folder")
|
.class_name("Folder")
|
||||||
.children(snapshot_children)
|
.children(snapshot_children)
|
||||||
@@ -63,11 +97,6 @@ pub fn snapshot_dir(
|
|||||||
.context(context),
|
.context(context),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? {
|
|
||||||
let mut metadata = DirectoryMetadata::from_slice(&meta_contents, meta_path)?;
|
|
||||||
metadata.apply_all(&mut snapshot)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(snapshot))
|
Ok(Some(snapshot))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ use memofs::{IoResultExt, Vfs};
|
|||||||
|
|
||||||
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
|
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
|
||||||
|
|
||||||
use super::{dir::snapshot_dir, meta_file::AdjacentMetadata, util::match_trailing};
|
use super::{
|
||||||
|
dir::{dir_meta, snapshot_dir_no_meta},
|
||||||
|
meta_file::AdjacentMetadata,
|
||||||
|
util::match_trailing,
|
||||||
|
};
|
||||||
|
|
||||||
/// Core routine for turning Lua files into snapshots.
|
/// Core routine for turning Lua files into snapshots.
|
||||||
pub fn snapshot_lua(
|
pub fn snapshot_lua(
|
||||||
@@ -66,7 +70,7 @@ pub fn snapshot_lua_init(
|
|||||||
init_path: &Path,
|
init_path: &Path,
|
||||||
) -> anyhow::Result<Option<InstanceSnapshot>> {
|
) -> anyhow::Result<Option<InstanceSnapshot>> {
|
||||||
let folder_path = init_path.parent().unwrap();
|
let folder_path = init_path.parent().unwrap();
|
||||||
let dir_snapshot = snapshot_dir(context, vfs, folder_path)?.unwrap();
|
let dir_snapshot = snapshot_dir_no_meta(context, vfs, folder_path)?.unwrap();
|
||||||
|
|
||||||
if dir_snapshot.class_name != "Folder" {
|
if dir_snapshot.class_name != "Folder" {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
@@ -86,6 +90,10 @@ pub fn snapshot_lua_init(
|
|||||||
init_snapshot.children = dir_snapshot.children;
|
init_snapshot.children = dir_snapshot.children;
|
||||||
init_snapshot.metadata = dir_snapshot.metadata;
|
init_snapshot.metadata = dir_snapshot.metadata;
|
||||||
|
|
||||||
|
if let Some(mut meta) = dir_meta(vfs, folder_path)? {
|
||||||
|
meta.apply_all(&mut init_snapshot)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Some(init_snapshot))
|
Ok(Some(init_snapshot))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ pub use self::project::snapshot_project_node;
|
|||||||
|
|
||||||
/// The main entrypoint to the snapshot function. This function can be pointed
|
/// The main entrypoint to the snapshot function. This function can be pointed
|
||||||
/// at any path and will return something if Rojo knows how to deal with it.
|
/// at any path and will return something if Rojo knows how to deal with it.
|
||||||
|
#[profiling::function]
|
||||||
pub fn snapshot_from_vfs(
|
pub fn snapshot_from_vfs(
|
||||||
context: &InstanceContext,
|
context: &InstanceContext,
|
||||||
vfs: &Vfs,
|
vfs: &Vfs,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
|
|||||||
|
|
||||||
use super::util::PathExt;
|
use super::util::PathExt;
|
||||||
|
|
||||||
|
#[profiling::function]
|
||||||
pub fn snapshot_rbxm(
|
pub fn snapshot_rbxm(
|
||||||
context: &InstanceContext,
|
context: &InstanceContext,
|
||||||
vfs: &Vfs,
|
vfs: &Vfs,
|
||||||
@@ -21,7 +22,8 @@ pub fn snapshot_rbxm(
|
|||||||
let children = root_instance.children();
|
let children = root_instance.children();
|
||||||
|
|
||||||
if children.len() == 1 {
|
if children.len() == 1 {
|
||||||
let snapshot = InstanceSnapshot::from_tree(&temp_tree, children[0])
|
let child = children[0];
|
||||||
|
let snapshot = InstanceSnapshot::from_tree(temp_tree, child)
|
||||||
.name(name)
|
.name(name)
|
||||||
.metadata(
|
.metadata(
|
||||||
InstanceMetadata::new()
|
InstanceMetadata::new()
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ pub fn snapshot_rbxmx(
|
|||||||
let children = root_instance.children();
|
let children = root_instance.children();
|
||||||
|
|
||||||
if children.len() == 1 {
|
if children.len() == 1 {
|
||||||
let snapshot = InstanceSnapshot::from_tree(&temp_tree, children[0])
|
let child = children[0];
|
||||||
|
let snapshot = InstanceSnapshot::from_tree(temp_tree, child)
|
||||||
.name(name)
|
.name(name)
|
||||||
.metadata(
|
.metadata(
|
||||||
InstanceMetadata::new()
|
InstanceMetadata::new()
|
||||||
|
|||||||
@@ -141,14 +141,14 @@ impl TestServeSession {
|
|||||||
|
|
||||||
pub fn get_api_rojo(&self) -> Result<ServerInfoResponse, reqwest::Error> {
|
pub fn get_api_rojo(&self) -> Result<ServerInfoResponse, reqwest::Error> {
|
||||||
let url = format!("http://localhost:{}/api/rojo", self.port);
|
let url = format!("http://localhost:{}/api/rojo", self.port);
|
||||||
let body = reqwest::get(&url)?.text()?;
|
let body = reqwest::blocking::get(&url)?.text()?;
|
||||||
|
|
||||||
Ok(serde_json::from_str(&body).expect("Server returned malformed response"))
|
Ok(serde_json::from_str(&body).expect("Server returned malformed response"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_api_read(&self, id: Ref) -> Result<ReadResponse, reqwest::Error> {
|
pub fn get_api_read(&self, id: Ref) -> Result<ReadResponse, reqwest::Error> {
|
||||||
let url = format!("http://localhost:{}/api/read/{}", self.port, id);
|
let url = format!("http://localhost:{}/api/read/{}", self.port, id);
|
||||||
let body = reqwest::get(&url)?.text()?;
|
let body = reqwest::blocking::get(&url)?.text()?;
|
||||||
|
|
||||||
Ok(serde_json::from_str(&body).expect("Server returned malformed response"))
|
Ok(serde_json::from_str(&body).expect("Server returned malformed response"))
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ impl TestServeSession {
|
|||||||
) -> Result<SubscribeResponse<'static>, reqwest::Error> {
|
) -> Result<SubscribeResponse<'static>, reqwest::Error> {
|
||||||
let url = format!("http://localhost:{}/api/subscribe/{}", self.port, cursor);
|
let url = format!("http://localhost:{}/api/subscribe/{}", self.port, cursor);
|
||||||
|
|
||||||
reqwest::get(&url)?.json()
|
reqwest::blocking::get(&url)?.json()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,11 +36,13 @@ gen_build_tests! {
|
|||||||
init_meta_class_name,
|
init_meta_class_name,
|
||||||
init_meta_properties,
|
init_meta_properties,
|
||||||
init_with_children,
|
init_with_children,
|
||||||
|
issue_546,
|
||||||
json_as_lua,
|
json_as_lua,
|
||||||
json_model_in_folder,
|
json_model_in_folder,
|
||||||
json_model_legacy_name,
|
json_model_legacy_name,
|
||||||
module_in_folder,
|
module_in_folder,
|
||||||
module_init,
|
module_init,
|
||||||
|
optional,
|
||||||
project_composed_default,
|
project_composed_default,
|
||||||
project_composed_file,
|
project_composed_file,
|
||||||
project_root_name,
|
project_root_name,
|
||||||
@@ -53,7 +55,6 @@ gen_build_tests! {
|
|||||||
txt,
|
txt,
|
||||||
txt_in_folder,
|
txt_in_folder,
|
||||||
unresolved_values,
|
unresolved_values,
|
||||||
optional,
|
|
||||||
weldconstraint,
|
weldconstraint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user