forked from rojo-rbx/rojo
Compare commits
1 Commits
v7.1.1-per
...
project-cr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b96a236333 |
187
Cargo.lock
generated
187
Cargo.lock
generated
@@ -690,19 +690,6 @@ dependencies = [
|
|||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "generator"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c1d9279ca822891c1a4dae06d185612cf8fc6acfe5dff37781b41297811b12ee"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"rustversion",
|
|
||||||
"winapi 0.3.9",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.12.4"
|
version = "0.12.4"
|
||||||
@@ -751,6 +738,7 @@ dependencies = [
|
|||||||
"fnv",
|
"fnv",
|
||||||
"log",
|
"log",
|
||||||
"regex",
|
"regex",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1042,19 +1030,6 @@ dependencies = [
|
|||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "loom"
|
|
||||||
version = "0.5.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"generator",
|
|
||||||
"scoped-tls",
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lz4"
|
name = "lz4"
|
||||||
version = "1.23.3"
|
version = "1.23.3"
|
||||||
@@ -1081,15 +1056,6 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matchers"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
|
||||||
dependencies = [
|
|
||||||
"regex-automata",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matches"
|
name = "matches"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@@ -1527,26 +1493,6 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "profiling"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f61dcf0b917cd75d4521d7343d1ffff3d1583054133c9b5cbea3375c703c40d"
|
|
||||||
dependencies = [
|
|
||||||
"profiling-procmacros",
|
|
||||||
"tracy-client",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "profiling-procmacros"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "98eee3c112f2a6f784b6713fe1d7fb7d6506e066121c0a49371fdb976f72bae5"
|
|
||||||
dependencies = [
|
|
||||||
"quote 1.0.18",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "0.6.13"
|
version = "0.6.13"
|
||||||
@@ -1633,11 +1579,11 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_binary"
|
name = "rbx_binary"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
source = "git+https://github.com/rojo-rbx/rbx-dom#4fa99b4190009f417ce63c86b2c6725e32c0090e"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68f424adb7a0a24ab4bd153be141035f1404ae40affed902fd2721b42cca7f86"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"lz4",
|
"lz4",
|
||||||
"profiling",
|
|
||||||
"rbx_dom_weak",
|
"rbx_dom_weak",
|
||||||
"rbx_reflection",
|
"rbx_reflection",
|
||||||
"rbx_reflection_database",
|
"rbx_reflection_database",
|
||||||
@@ -1647,7 +1593,8 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_dom_weak"
|
name = "rbx_dom_weak"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
source = "git+https://github.com/rojo-rbx/rbx-dom#4fa99b4190009f417ce63c86b2c6725e32c0090e"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f7f524fb18f30d7065c82c4e87f747705679329810207e96169c6d4ec922d1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rbx_types",
|
"rbx_types",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1656,7 +1603,8 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_reflection"
|
name = "rbx_reflection"
|
||||||
version = "4.2.0"
|
version = "4.2.0"
|
||||||
source = "git+https://github.com/rojo-rbx/rbx-dom#4fa99b4190009f417ce63c86b2c6725e32c0090e"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8915748c8a3b2a92540e4e35e99ebd548df2d62b0a6cf38ae5d0081f0e611d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rbx_types",
|
"rbx_types",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1665,7 +1613,8 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_reflection_database"
|
name = "rbx_reflection_database"
|
||||||
version = "0.2.4+roblox-504"
|
version = "0.2.4+roblox-504"
|
||||||
source = "git+https://github.com/rojo-rbx/rbx-dom#4fa99b4190009f417ce63c86b2c6725e32c0090e"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b41e8da85aa697cd04cef48e6dd7d96992786d2e322bafe1d3cc93045f4de1e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"rbx_reflection",
|
"rbx_reflection",
|
||||||
@@ -1676,7 +1625,8 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_types"
|
name = "rbx_types"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
source = "git+https://github.com/rojo-rbx/rbx-dom#4fa99b4190009f417ce63c86b2c6725e32c0090e"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d637383aa560cb675b7ea7a7778b945dab065ccc7c158f77b5455e27efadc6df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.11.0",
|
"base64 0.11.0",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
@@ -1690,7 +1640,8 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_xml"
|
name = "rbx_xml"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
source = "git+https://github.com/rojo-rbx/rbx-dom#4fa99b4190009f417ce63c86b2c6725e32c0090e"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67387cd246cdec9251dd2451672541499ae6ce0a47c768b3ea9ee0a1becda9dd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.11.0",
|
"base64 0.11.0",
|
||||||
"log",
|
"log",
|
||||||
@@ -1736,9 +1687,6 @@ name = "regex-automata"
|
|||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
dependencies = [
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
@@ -1875,7 +1823,6 @@ dependencies = [
|
|||||||
"opener",
|
"opener",
|
||||||
"paste",
|
"paste",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"profiling",
|
|
||||||
"rbx_binary",
|
"rbx_binary",
|
||||||
"rbx_dom_weak",
|
"rbx_dom_weak",
|
||||||
"rbx_reflection",
|
"rbx_reflection",
|
||||||
@@ -1885,6 +1832,7 @@ dependencies = [
|
|||||||
"ritz",
|
"ritz",
|
||||||
"roblox_install",
|
"roblox_install",
|
||||||
"rojo-insta-ext",
|
"rojo-insta-ext",
|
||||||
|
"rojo-project",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
@@ -1892,7 +1840,6 @@ dependencies = [
|
|||||||
"termcolor",
|
"termcolor",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracy-client",
|
|
||||||
"uuid",
|
"uuid",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"winreg 0.10.1",
|
"winreg 0.10.1",
|
||||||
@@ -1906,6 +1853,20 @@ dependencies = [
|
|||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rojo-project"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"globset",
|
||||||
|
"log",
|
||||||
|
"rbx_dom_weak",
|
||||||
|
"rbx_reflection",
|
||||||
|
"rbx_reflection_database",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.21"
|
version = "0.1.21"
|
||||||
@@ -1921,12 +1882,6 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.10"
|
version = "1.0.10"
|
||||||
@@ -1952,12 +1907,6 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scoped-tls"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -2070,15 +2019,6 @@ dependencies = [
|
|||||||
"opaque-debug",
|
"opaque-debug",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sharded-slab"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "similar"
|
name = "similar"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
@@ -2091,12 +2031,6 @@ version = "0.4.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
|
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smallvec"
|
|
||||||
version = "1.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "snax"
|
name = "snax"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -2207,15 +2141,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thread_local"
|
|
||||||
version = "1.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinytemplate"
|
name = "tinytemplate"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@@ -2327,56 +2252,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f"
|
checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"valuable",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-log"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
"log",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-subscriber"
|
|
||||||
version = "0.3.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596"
|
|
||||||
dependencies = [
|
|
||||||
"ansi_term",
|
|
||||||
"lazy_static",
|
|
||||||
"matchers",
|
|
||||||
"regex",
|
|
||||||
"sharded-slab",
|
|
||||||
"smallvec",
|
|
||||||
"thread_local",
|
|
||||||
"tracing",
|
|
||||||
"tracing-core",
|
|
||||||
"tracing-log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracy-client"
|
|
||||||
version = "0.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42ebfe7a24c18b5ba86d8920c124b41b942352f863fbe0c84d3d63428fa1860f"
|
|
||||||
dependencies = [
|
|
||||||
"loom",
|
|
||||||
"once_cell",
|
|
||||||
"tracy-client-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracy-client-sys"
|
|
||||||
version = "0.17.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9012b9dfeccaff16e93f5a8b02336125113a80a769902e679d334cbdd4d83f3b"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2452,12 +2327,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "valuable"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|||||||
21
Cargo.toml
21
Cargo.toml
@@ -27,8 +27,6 @@ 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 = ["crates/*"]
|
members = ["crates/*"]
|
||||||
|
|
||||||
@@ -41,6 +39,7 @@ name = "build"
|
|||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rojo-project = { path = "crates/rojo-project" }
|
||||||
memofs = { version = "0.2.0", path = "crates/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
|
||||||
@@ -50,17 +49,11 @@ memofs = { version = "0.2.0", path = "crates/memofs" }
|
|||||||
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
||||||
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||||
|
|
||||||
rbx_binary = { git = "https://github.com/rojo-rbx/rbx-dom" }
|
rbx_binary = "0.6.4"
|
||||||
rbx_dom_weak = { git = "https://github.com/rojo-rbx/rbx-dom" }
|
rbx_dom_weak = "2.3.0"
|
||||||
rbx_reflection = { git = "https://github.com/rojo-rbx/rbx-dom" }
|
rbx_reflection = "4.2.0"
|
||||||
rbx_reflection_database = { git = "https://github.com/rojo-rbx/rbx-dom" }
|
rbx_reflection_database = "0.2.2"
|
||||||
rbx_xml = { git = "https://github.com/rojo-rbx/rbx-dom" }
|
rbx_xml = "0.12.3"
|
||||||
|
|
||||||
# rbx_binary = "0.6.4"
|
|
||||||
# rbx_dom_weak = "2.3.0"
|
|
||||||
# rbx_reflection = "4.2.0"
|
|
||||||
# rbx_reflection_database = "0.2.2"
|
|
||||||
# rbx_xml = "0.12.3"
|
|
||||||
|
|
||||||
anyhow = "1.0.44"
|
anyhow = "1.0.44"
|
||||||
backtrace = "0.3.61"
|
backtrace = "0.3.61"
|
||||||
@@ -88,8 +81,6 @@ 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 = "1.0.0", features = ["v4", "serde"] }
|
uuid = { version = "1.0.0", features = ["v4", "serde"] }
|
||||||
clap = { version = "3.1.18", features = ["derive"] }
|
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.10.1"
|
winreg = "0.10.1"
|
||||||
|
|||||||
16
crates/rojo-project/Cargo.toml
Normal file
16
crates/rojo-project/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "rojo-project"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.57"
|
||||||
|
globset = { version = "0.4.8", features = ["serde1"] }
|
||||||
|
log = "0.4.17"
|
||||||
|
rbx_dom_weak = "2.3.0"
|
||||||
|
rbx_reflection = "4.2.0"
|
||||||
|
rbx_reflection_database = "0.2.4"
|
||||||
|
serde = { version = "1.0.137", features = ["derive"] }
|
||||||
|
serde_json = "1.0.81"
|
||||||
4
crates/rojo-project/README.md
Normal file
4
crates/rojo-project/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# rojo-project
|
||||||
|
Project file format crate for [Rojo].
|
||||||
|
|
||||||
|
[Rojo]: https://rojo.space
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
//! Wrapper around globset's Glob type that has better serialization
|
|
||||||
//! characteristics by coupling Glob and GlobMatcher into a single type.
|
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use globset::{Glob as InnerGlob, GlobMatcher};
|
use globset::{Glob as InnerGlob, GlobMatcher};
|
||||||
@@ -8,6 +5,8 @@ use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
|
|||||||
|
|
||||||
pub use globset::Error;
|
pub use globset::Error;
|
||||||
|
|
||||||
|
/// Wrapper around globset's Glob type that has better serialization
|
||||||
|
/// characteristics by coupling Glob and GlobMatcher into a single type.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Glob {
|
pub struct Glob {
|
||||||
inner: InnerGlob,
|
inner: InnerGlob,
|
||||||
7
crates/rojo-project/src/lib.rs
Normal file
7
crates/rojo-project/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod glob;
|
||||||
|
mod path_serializer;
|
||||||
|
mod project;
|
||||||
|
mod resolution;
|
||||||
|
|
||||||
|
pub use project::{OptionalPathNode, PathNode, Project, ProjectNode};
|
||||||
|
pub use resolution::{AmbiguousValue, UnresolvedValue};
|
||||||
21
crates/rojo-project/src/path_serializer.rs
Normal file
21
crates/rojo-project/src/path_serializer.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//! Path serializer is used to serialize absolute paths in a cross-platform way,
|
||||||
|
//! by replacing all directory separators with /.
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::Serializer;
|
||||||
|
|
||||||
|
pub fn serialize_absolute<S, T>(path: T, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
T: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let as_str = path
|
||||||
|
.as_ref()
|
||||||
|
.as_os_str()
|
||||||
|
.to_str()
|
||||||
|
.expect("Invalid Unicode in file path, cannot serialize");
|
||||||
|
let replaced = as_str.replace("\\", "/");
|
||||||
|
|
||||||
|
serializer.serialize_str(&replaced)
|
||||||
|
}
|
||||||
363
crates/rojo-project/src/project.rs
Normal file
363
crates/rojo-project/src/project.rs
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
|
use std::fs;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::glob::Glob;
|
||||||
|
use crate::resolution::UnresolvedValue;
|
||||||
|
|
||||||
|
static PROJECT_FILENAME: &str = "default.project.json";
|
||||||
|
|
||||||
|
/// Contains all of the configuration for a Rojo-managed project.
|
||||||
|
///
|
||||||
|
/// Rojo project files are stored in `.project.json` files.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||||
|
pub struct Project {
|
||||||
|
/// The name of the top-level instance described by the project.
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// The tree of instances described by this project. Projects always
|
||||||
|
/// describe at least one instance.
|
||||||
|
pub tree: ProjectNode,
|
||||||
|
|
||||||
|
/// If specified, sets the default port that `rojo serve` should use when
|
||||||
|
/// using this project for live sync.
|
||||||
|
///
|
||||||
|
/// Can be overriden with the `--port` flag.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub serve_port: Option<u16>,
|
||||||
|
|
||||||
|
/// If specified, sets the default IP address that `rojo serve` should use
|
||||||
|
/// when using this project for live sync.
|
||||||
|
///
|
||||||
|
/// Can be overridden with the `--address` flag.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub serve_address: Option<IpAddr>,
|
||||||
|
|
||||||
|
/// If specified, contains the set of place IDs that this project is
|
||||||
|
/// compatible with when doing live sync.
|
||||||
|
///
|
||||||
|
/// This setting is intended to help prevent syncing a Rojo project into the
|
||||||
|
/// wrong Roblox place.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub serve_place_ids: Option<HashSet<u64>>,
|
||||||
|
|
||||||
|
/// If specified, sets the current place's place ID when connecting to the
|
||||||
|
/// Rojo server from Roblox Studio.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub place_id: Option<u64>,
|
||||||
|
|
||||||
|
/// If specified, sets the current place's game ID when connecting to the
|
||||||
|
/// Rojo server from Roblox Studio.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub game_id: Option<u64>,
|
||||||
|
|
||||||
|
/// A list of globs, relative to the folder the project file is in, that
|
||||||
|
/// match files that should be excluded if Rojo encounters them.
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub glob_ignore_paths: Vec<Glob>,
|
||||||
|
|
||||||
|
/// The path to the file that this project came from. Relative paths in the
|
||||||
|
/// project should be considered relative to the parent of this field, also
|
||||||
|
/// given by `Project::folder_location`.
|
||||||
|
#[serde(skip)]
|
||||||
|
pub file_location: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Project {
|
||||||
|
/// Tells whether the given path describes a Rojo project.
|
||||||
|
pub fn is_project_file(path: &Path) -> bool {
|
||||||
|
path.file_name()
|
||||||
|
.and_then(|name| name.to_str())
|
||||||
|
.map(|name| name.ends_with(".project.json"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads a project file from a slice and a path that indicates where the
|
||||||
|
/// project should resolve paths relative to.
|
||||||
|
pub fn load_from_slice(contents: &[u8], project_file_location: &Path) -> anyhow::Result<Self> {
|
||||||
|
let mut project: Self = serde_json::from_slice(&contents).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Error parsing Rojo project at {}",
|
||||||
|
project_file_location.display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
project.file_location = project_file_location.to_path_buf();
|
||||||
|
project.check_compatibility();
|
||||||
|
Ok(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fuzzy-find a Rojo project and load it.
|
||||||
|
pub fn load_fuzzy(fuzzy_project_location: &Path) -> anyhow::Result<Option<Self>> {
|
||||||
|
if let Some(project_path) = Self::locate(fuzzy_project_location) {
|
||||||
|
let project = Self::load_exact(&project_path)?;
|
||||||
|
|
||||||
|
Ok(Some(project))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gives the path that all project file paths should resolve relative to.
|
||||||
|
pub fn folder_location(&self) -> &Path {
|
||||||
|
self.file_location.parent().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to locate a project represented by the given path.
|
||||||
|
///
|
||||||
|
/// This will find a project if the path refers to a `.project.json` file,
|
||||||
|
/// or is a folder that contains a `default.project.json` file.
|
||||||
|
fn locate(path: &Path) -> Option<PathBuf> {
|
||||||
|
let meta = fs::metadata(path).ok()?;
|
||||||
|
|
||||||
|
if meta.is_file() {
|
||||||
|
if Project::is_project_file(path) {
|
||||||
|
Some(path.to_path_buf())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let child_path = path.join(PROJECT_FILENAME);
|
||||||
|
let child_meta = fs::metadata(&child_path).ok()?;
|
||||||
|
|
||||||
|
if child_meta.is_file() {
|
||||||
|
Some(child_path)
|
||||||
|
} else {
|
||||||
|
// This is a folder with the same name as a Rojo default project
|
||||||
|
// file.
|
||||||
|
//
|
||||||
|
// That's pretty weird, but we can roll with it.
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_exact(project_file_location: &Path) -> anyhow::Result<Self> {
|
||||||
|
let contents = fs::read_to_string(project_file_location)?;
|
||||||
|
|
||||||
|
let mut project: Project = serde_json::from_str(&contents).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Error parsing Rojo project at {}",
|
||||||
|
project_file_location.display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
project.file_location = project_file_location.to_path_buf();
|
||||||
|
project.check_compatibility();
|
||||||
|
|
||||||
|
Ok(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if there are any compatibility issues with this project file and
|
||||||
|
/// warns the user if there are any.
|
||||||
|
fn check_compatibility(&self) {
|
||||||
|
self.tree.validate_reserved_names();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
|
||||||
|
pub struct OptionalPathNode {
|
||||||
|
#[serde(serialize_with = "crate::path_serializer::serialize_absolute")]
|
||||||
|
pub optional: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OptionalPathNode {
|
||||||
|
pub fn new(optional: PathBuf) -> Self {
|
||||||
|
OptionalPathNode { optional }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes a path that is either optional or required
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum PathNode {
|
||||||
|
Required(#[serde(serialize_with = "crate::path_serializer::serialize_absolute")] PathBuf),
|
||||||
|
Optional(OptionalPathNode),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PathNode {
|
||||||
|
pub fn path(&self) -> &Path {
|
||||||
|
match self {
|
||||||
|
PathNode::Required(pathbuf) => &pathbuf,
|
||||||
|
PathNode::Optional(OptionalPathNode { optional }) => &optional,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes an instance and its descendants in a project.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
|
||||||
|
pub struct ProjectNode {
|
||||||
|
/// If set, defines the ClassName of the described instance.
|
||||||
|
///
|
||||||
|
/// `$className` MUST be set if `$path` is not set.
|
||||||
|
///
|
||||||
|
/// `$className` CANNOT be set if `$path` is set and the instance described
|
||||||
|
/// by that path has a ClassName other than Folder.
|
||||||
|
#[serde(rename = "$className", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub class_name: Option<String>,
|
||||||
|
|
||||||
|
/// Contains all of the children of the described instance.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub children: BTreeMap<String, ProjectNode>,
|
||||||
|
|
||||||
|
/// The properties that will be assigned to the resulting instance.
|
||||||
|
#[serde(
|
||||||
|
rename = "$properties",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "HashMap::is_empty"
|
||||||
|
)]
|
||||||
|
pub properties: HashMap<String, UnresolvedValue>,
|
||||||
|
|
||||||
|
/// Defines the behavior when Rojo encounters unknown instances in Roblox
|
||||||
|
/// Studio during live sync. `$ignoreUnknownInstances` should be considered
|
||||||
|
/// a large hammer and used with care.
|
||||||
|
///
|
||||||
|
/// If set to `true`, those instances will be left alone. This may cause
|
||||||
|
/// issues when files that turn into instances are removed while Rojo is not
|
||||||
|
/// running.
|
||||||
|
///
|
||||||
|
/// If set to `false`, Rojo will destroy any instances it does not
|
||||||
|
/// recognize.
|
||||||
|
///
|
||||||
|
/// If unset, its default value depends on other settings:
|
||||||
|
/// - If `$path` is not set, defaults to `true`
|
||||||
|
/// - If `$path` is set, defaults to `false`
|
||||||
|
#[serde(
|
||||||
|
rename = "$ignoreUnknownInstances",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub ignore_unknown_instances: Option<bool>,
|
||||||
|
|
||||||
|
/// Defines that this instance should come from the given file path. This
|
||||||
|
/// path can point to any file type supported by Rojo, including Lua files
|
||||||
|
/// (`.lua`), Roblox models (`.rbxm`, `.rbxmx`), and localization table
|
||||||
|
/// spreadsheets (`.csv`).
|
||||||
|
#[serde(rename = "$path", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub path: Option<PathNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProjectNode {
|
||||||
|
fn validate_reserved_names(&self) {
|
||||||
|
for (name, child) in &self.children {
|
||||||
|
if name.starts_with('$') {
|
||||||
|
log::warn!(
|
||||||
|
"Keys starting with '$' are reserved by Rojo to ensure forward compatibility."
|
||||||
|
);
|
||||||
|
log::warn!(
|
||||||
|
"This project uses the key '{}', which should be renamed.",
|
||||||
|
name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
child.validate_reserved_names();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_node_required() {
|
||||||
|
let path_node: PathNode = serde_json::from_str(r#""src""#).unwrap();
|
||||||
|
assert_eq!(path_node, PathNode::Required(PathBuf::from("src")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_node_optional() {
|
||||||
|
let path_node: PathNode = serde_json::from_str(r#"{ "optional": "src" }"#).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
path_node,
|
||||||
|
PathNode::Optional(OptionalPathNode::new(PathBuf::from("src")))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn project_node_required() {
|
||||||
|
let project_node: ProjectNode = serde_json::from_str(
|
||||||
|
r#"{
|
||||||
|
"$path": "src"
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
project_node.path,
|
||||||
|
Some(PathNode::Required(PathBuf::from("src")))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn project_node_optional() {
|
||||||
|
let project_node: ProjectNode = serde_json::from_str(
|
||||||
|
r#"{
|
||||||
|
"$path": { "optional": "src" }
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
project_node.path,
|
||||||
|
Some(PathNode::Optional(OptionalPathNode::new(PathBuf::from(
|
||||||
|
"src"
|
||||||
|
))))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn project_node_none() {
|
||||||
|
let project_node: ProjectNode = serde_json::from_str(
|
||||||
|
r#"{
|
||||||
|
"$className": "Folder"
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(project_node.path, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn project_node_optional_serialize_absolute() {
|
||||||
|
let project_node: ProjectNode = serde_json::from_str(
|
||||||
|
r#"{
|
||||||
|
"$path": { "optional": "..\\src" }
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&project_node).unwrap();
|
||||||
|
assert_eq!(serialized, r#"{"$path":{"optional":"../src"}}"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn project_node_optional_serialize_absolute_no_change() {
|
||||||
|
let project_node: ProjectNode = serde_json::from_str(
|
||||||
|
r#"{
|
||||||
|
"$path": { "optional": "../src" }
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&project_node).unwrap();
|
||||||
|
assert_eq!(serialized, r#"{"$path":{"optional":"../src"}}"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn project_node_optional_serialize_optional() {
|
||||||
|
let project_node: ProjectNode = serde_json::from_str(
|
||||||
|
r#"{
|
||||||
|
"$path": "..\\src"
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&project_node).unwrap();
|
||||||
|
assert_eq!(serialized, r#"{"$path":"../src"}"#);
|
||||||
|
}
|
||||||
|
}
|
||||||
294
crates/rojo-project/src/resolution.rs
Normal file
294
crates/rojo-project/src/resolution.rs
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
use anyhow::format_err;
|
||||||
|
use rbx_dom_weak::types::{
|
||||||
|
CFrame, Color3, Content, Enum, Matrix3, Tags, Variant, VariantType, Vector2, Vector3,
|
||||||
|
};
|
||||||
|
use rbx_reflection::{DataType, PropertyDescriptor};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// A user-friendly version of `Variant` that supports specifying ambiguous
|
||||||
|
/// values. Ambiguous values need a reflection database to be resolved to a
|
||||||
|
/// usable value.
|
||||||
|
///
|
||||||
|
/// This type is used in Rojo projects and JSON models to make specifying the
|
||||||
|
/// most common types of properties, like strings or vectors, much easier.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum UnresolvedValue {
|
||||||
|
FullyQualified(Variant),
|
||||||
|
Ambiguous(AmbiguousValue),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnresolvedValue {
|
||||||
|
pub fn resolve(self, class_name: &str, prop_name: &str) -> anyhow::Result<Variant> {
|
||||||
|
match self {
|
||||||
|
UnresolvedValue::FullyQualified(full) => Ok(full),
|
||||||
|
UnresolvedValue::Ambiguous(partial) => partial.resolve(class_name, prop_name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum AmbiguousValue {
|
||||||
|
Bool(bool),
|
||||||
|
String(String),
|
||||||
|
StringArray(Vec<String>),
|
||||||
|
Number(f64),
|
||||||
|
Array2([f64; 2]),
|
||||||
|
Array3([f64; 3]),
|
||||||
|
Array4([f64; 4]),
|
||||||
|
Array12([f64; 12]),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AmbiguousValue {
|
||||||
|
pub fn resolve(self, class_name: &str, prop_name: &str) -> anyhow::Result<Variant> {
|
||||||
|
let property = find_descriptor(class_name, prop_name)
|
||||||
|
.ok_or_else(|| format_err!("Unknown property {}.{}", class_name, prop_name))?;
|
||||||
|
|
||||||
|
match &property.data_type {
|
||||||
|
DataType::Enum(enum_name) => {
|
||||||
|
let database = rbx_reflection_database::get();
|
||||||
|
|
||||||
|
let enum_descriptor = database.enums.get(enum_name).ok_or_else(|| {
|
||||||
|
format_err!("Unknown enum {}. This is a Rojo bug!", enum_name)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let error = |what: &str| {
|
||||||
|
let mut all_values = enum_descriptor
|
||||||
|
.items
|
||||||
|
.keys()
|
||||||
|
.map(|value| value.borrow())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
all_values.sort();
|
||||||
|
|
||||||
|
let examples = nonexhaustive_list(&all_values);
|
||||||
|
|
||||||
|
format_err!(
|
||||||
|
"Invalid value for property {}.{}. Got {} but \
|
||||||
|
expected a member of the {} enum such as {}",
|
||||||
|
class_name,
|
||||||
|
prop_name,
|
||||||
|
what,
|
||||||
|
enum_name,
|
||||||
|
examples,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = match self {
|
||||||
|
AmbiguousValue::String(value) => value,
|
||||||
|
unresolved => return Err(error(unresolved.describe())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let resolved = enum_descriptor
|
||||||
|
.items
|
||||||
|
.get(value.as_str())
|
||||||
|
.ok_or_else(|| error(value.as_str()))?;
|
||||||
|
|
||||||
|
Ok(Enum::from_u32(*resolved).into())
|
||||||
|
}
|
||||||
|
DataType::Value(variant_ty) => match (variant_ty, self) {
|
||||||
|
(VariantType::Bool, AmbiguousValue::Bool(value)) => Ok(value.into()),
|
||||||
|
|
||||||
|
(VariantType::Float32, AmbiguousValue::Number(value)) => Ok((value as f32).into()),
|
||||||
|
(VariantType::Float64, AmbiguousValue::Number(value)) => Ok(value.into()),
|
||||||
|
(VariantType::Int32, AmbiguousValue::Number(value)) => Ok((value as i32).into()),
|
||||||
|
(VariantType::Int64, AmbiguousValue::Number(value)) => Ok((value as i64).into()),
|
||||||
|
|
||||||
|
(VariantType::String, AmbiguousValue::String(value)) => Ok(value.into()),
|
||||||
|
(VariantType::Tags, AmbiguousValue::StringArray(value)) => {
|
||||||
|
Ok(Tags::from(value).into())
|
||||||
|
}
|
||||||
|
(VariantType::Content, AmbiguousValue::String(value)) => {
|
||||||
|
Ok(Content::from(value).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
(VariantType::Vector2, AmbiguousValue::Array2(value)) => {
|
||||||
|
Ok(Vector2::new(value[0] as f32, value[1] as f32).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
(VariantType::Vector3, AmbiguousValue::Array3(value)) => {
|
||||||
|
Ok(Vector3::new(value[0] as f32, value[1] as f32, value[2] as f32).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
(VariantType::Color3, AmbiguousValue::Array3(value)) => {
|
||||||
|
Ok(Color3::new(value[0] as f32, value[1] as f32, value[2] as f32).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
(VariantType::CFrame, AmbiguousValue::Array12(value)) => {
|
||||||
|
let value = value.map(|v| v as f32);
|
||||||
|
let pos = Vector3::new(value[0], value[1], value[2]);
|
||||||
|
let orientation = Matrix3::new(
|
||||||
|
Vector3::new(value[3], value[4], value[5]),
|
||||||
|
Vector3::new(value[6], value[7], value[8]),
|
||||||
|
Vector3::new(value[9], value[10], value[11]),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(CFrame::new(pos, orientation).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
(_, unresolved) => Err(format_err!(
|
||||||
|
"Wrong type of value for property {}.{}. Expected {:?}, got {}",
|
||||||
|
class_name,
|
||||||
|
prop_name,
|
||||||
|
variant_ty,
|
||||||
|
unresolved.describe(),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
_ => Err(format_err!(
|
||||||
|
"Unknown data type for property {}.{}",
|
||||||
|
class_name,
|
||||||
|
prop_name
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn describe(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
AmbiguousValue::Bool(_) => "a bool",
|
||||||
|
AmbiguousValue::String(_) => "a string",
|
||||||
|
AmbiguousValue::StringArray(_) => "an array of strings",
|
||||||
|
AmbiguousValue::Number(_) => "a number",
|
||||||
|
AmbiguousValue::Array2(_) => "an array of two numbers",
|
||||||
|
AmbiguousValue::Array3(_) => "an array of three numbers",
|
||||||
|
AmbiguousValue::Array4(_) => "an array of four numbers",
|
||||||
|
AmbiguousValue::Array12(_) => "an array of twelve numbers",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_descriptor(
|
||||||
|
class_name: &str,
|
||||||
|
prop_name: &str,
|
||||||
|
) -> Option<&'static PropertyDescriptor<'static>> {
|
||||||
|
let database = rbx_reflection_database::get();
|
||||||
|
let mut current_class_name = class_name;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let class = database.classes.get(current_class_name)?;
|
||||||
|
if let Some(descriptor) = class.properties.get(prop_name) {
|
||||||
|
return Some(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_class_name = class.superclass.as_deref()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Outputs a string containing up to MAX_ITEMS entries from the given list. If
|
||||||
|
/// there are more than MAX_ITEMS items, the number of remaining items will be
|
||||||
|
/// listed.
|
||||||
|
fn nonexhaustive_list(values: &[&str]) -> String {
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
const MAX_ITEMS: usize = 8;
|
||||||
|
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
|
let last_index = values.len() - 1;
|
||||||
|
let main_length = last_index.min(9);
|
||||||
|
|
||||||
|
let main_list = &values[..main_length];
|
||||||
|
for value in main_list {
|
||||||
|
output.push_str(value);
|
||||||
|
output.push_str(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if values.len() > MAX_ITEMS {
|
||||||
|
write!(output, "or {} more", values.len() - main_length).unwrap();
|
||||||
|
} else {
|
||||||
|
output.push_str("or ");
|
||||||
|
output.push_str(values[values.len() - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn resolve(class: &str, prop: &str, json_value: &str) -> Variant {
|
||||||
|
let unresolved: UnresolvedValue = serde_json::from_str(json_value).unwrap();
|
||||||
|
unresolved.resolve(class, prop).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bools() {
|
||||||
|
assert_eq!(resolve("BoolValue", "Value", "false"), Variant::Bool(false));
|
||||||
|
|
||||||
|
// Script.Disabled is inherited from BaseScript
|
||||||
|
assert_eq!(resolve("Script", "Disabled", "true"), Variant::Bool(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn strings() {
|
||||||
|
// String literals can stay as strings
|
||||||
|
assert_eq!(
|
||||||
|
resolve("StringValue", "Value", "\"Hello!\""),
|
||||||
|
Variant::String("Hello!".into()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// String literals can also turn into Content
|
||||||
|
assert_eq!(
|
||||||
|
resolve("Sky", "MoonTextureId", "\"rbxassetid://12345\""),
|
||||||
|
Variant::Content("rbxassetid://12345".into()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// What about BinaryString values? For forward-compatibility reasons, we
|
||||||
|
// don't support any shorthands for BinaryString.
|
||||||
|
//
|
||||||
|
// assert_eq!(
|
||||||
|
// resolve("Folder", "Tags", "\"a\\u0000b\\u0000c\""),
|
||||||
|
// Variant::BinaryString(b"a\0b\0c".to_vec().into()),
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn numbers() {
|
||||||
|
assert_eq!(
|
||||||
|
resolve("Part", "CollisionGroupId", "123"),
|
||||||
|
Variant::Int32(123),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
resolve("Folder", "SourceAssetId", "532413"),
|
||||||
|
Variant::Int64(532413),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(resolve("Part", "Transparency", "1"), Variant::Float32(1.0));
|
||||||
|
assert_eq!(resolve("NumberValue", "Value", "1"), Variant::Float64(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vectors() {
|
||||||
|
assert_eq!(
|
||||||
|
resolve("ParticleEmitter", "SpreadAngle", "[1, 2]"),
|
||||||
|
Variant::Vector2(Vector2::new(1.0, 2.0)),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
resolve("Part", "Position", "[4, 5, 6]"),
|
||||||
|
Variant::Vector3(Vector3::new(4.0, 5.0, 6.0)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn colors() {
|
||||||
|
assert_eq!(
|
||||||
|
resolve("Part", "Color", "[1, 1, 1]"),
|
||||||
|
Variant::Color3(Color3::new(1.0, 1.0, 1.0)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// There aren't any user-facing Color3uint8 properties. If there are
|
||||||
|
// some, we should treat them the same in the future.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enums() {
|
||||||
|
assert_eq!(
|
||||||
|
resolve("Lighting", "Technology", "\"Voxel\""),
|
||||||
|
Variant::Enum(Enum::from_u32(1)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -291,7 +291,7 @@ fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: Ref) -> Option<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let patch_set = compute_patch_set(snapshot, &tree, id);
|
let patch_set = compute_patch_set(snapshot.as_ref(), &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, &tree, id);
|
let patch_set = compute_patch_set(snapshot.as_ref(), &tree, id);
|
||||||
apply_patch_set(tree, patch_set)
|
apply_patch_set(tree, patch_set)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{BufWriter, Write},
|
io::{BufWriter, Write},
|
||||||
mem::ManuallyDrop,
|
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,14 +61,6 @@ impl BuildCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Never drop ServeSession, because it's VERY expensive to drop and
|
|
||||||
// we're about to exit anyways.
|
|
||||||
//
|
|
||||||
// This is kind of evil; if this function is ever called outside of the
|
|
||||||
// context of a CLI, this will leak a large object forever. However, the
|
|
||||||
// performance benefits of leaking it outweigh the cost at this time.
|
|
||||||
let _session = ManuallyDrop::new(session);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +97,6 @@ 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,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ mod tree_view;
|
|||||||
|
|
||||||
mod auth_cookie;
|
mod auth_cookie;
|
||||||
mod change_processor;
|
mod change_processor;
|
||||||
mod glob;
|
|
||||||
mod lua_ast;
|
mod lua_ast;
|
||||||
mod message_queue;
|
mod message_queue;
|
||||||
mod multimap;
|
mod multimap;
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ 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
|
||||||
|
|||||||
380
src/project.rs
380
src/project.rs
@@ -1,379 +1,3 @@
|
|||||||
use std::{
|
pub use rojo_project::{OptionalPathNode, PathNode, Project, ProjectNode};
|
||||||
collections::{BTreeMap, HashMap, HashSet},
|
|
||||||
fs, io,
|
|
||||||
net::IpAddr,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
pub use anyhow::Error as ProjectError;
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::{glob::Glob, resolution::UnresolvedValue};
|
|
||||||
|
|
||||||
static PROJECT_FILENAME: &str = "default.project.json";
|
|
||||||
|
|
||||||
/// Error type returned by any function that handles projects.
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
#[error(transparent)]
|
|
||||||
pub struct ProjectError(#[from] Error);
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
enum Error {
|
|
||||||
#[error(transparent)]
|
|
||||||
Io {
|
|
||||||
#[from]
|
|
||||||
source: io::Error,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("Error parsing Rojo project in path {}", .path.display())]
|
|
||||||
Json {
|
|
||||||
source: serde_json::Error,
|
|
||||||
path: PathBuf,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Contains all of the configuration for a Rojo-managed project.
|
|
||||||
///
|
|
||||||
/// Project files are stored in `.project.json` files.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
|
||||||
pub struct Project {
|
|
||||||
/// The name of the top-level instance described by the project.
|
|
||||||
pub name: String,
|
|
||||||
|
|
||||||
/// The tree of instances described by this project. Projects always
|
|
||||||
/// describe at least one instance.
|
|
||||||
pub tree: ProjectNode,
|
|
||||||
|
|
||||||
/// If specified, sets the default port that `rojo serve` should use when
|
|
||||||
/// using this project for live sync.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub serve_port: Option<u16>,
|
|
||||||
|
|
||||||
/// If specified, contains the set of place IDs that this project is
|
|
||||||
/// compatible with when doing live sync.
|
|
||||||
///
|
|
||||||
/// This setting is intended to help prevent syncing a Rojo project into the
|
|
||||||
/// wrong Roblox place.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub serve_place_ids: Option<HashSet<u64>>,
|
|
||||||
|
|
||||||
/// If specified, sets the current place's place ID when connecting to the
|
|
||||||
/// Rojo server from Roblox Studio.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub place_id: Option<u64>,
|
|
||||||
|
|
||||||
/// If specified, sets the current place's game ID when connecting to the
|
|
||||||
/// Rojo server from Roblox Studio.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub game_id: Option<u64>,
|
|
||||||
|
|
||||||
/// If specified, this address will be used in place of the default address
|
|
||||||
/// As long as --address is unprovided.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub serve_address: Option<IpAddr>,
|
|
||||||
|
|
||||||
/// A list of globs, relative to the folder the project file is in, that
|
|
||||||
/// match files that should be excluded if Rojo encounters them.
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
||||||
pub glob_ignore_paths: Vec<Glob>,
|
|
||||||
|
|
||||||
/// The path to the file that this project came from. Relative paths in the
|
|
||||||
/// project should be considered relative to the parent of this field, also
|
|
||||||
/// given by `Project::folder_location`.
|
|
||||||
#[serde(skip)]
|
|
||||||
pub file_location: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Project {
|
|
||||||
/// Tells whether the given path describes a Rojo project.
|
|
||||||
pub fn is_project_file(path: &Path) -> bool {
|
|
||||||
path.file_name()
|
|
||||||
.and_then(|name| name.to_str())
|
|
||||||
.map(|name| name.ends_with(".project.json"))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to locate a project represented by the given path.
|
|
||||||
///
|
|
||||||
/// This will find a project if the path refers to a `.project.json` file,
|
|
||||||
/// or is a folder that contains a `default.project.json` file.
|
|
||||||
fn locate(path: &Path) -> Option<PathBuf> {
|
|
||||||
let meta = fs::metadata(path).ok()?;
|
|
||||||
|
|
||||||
if meta.is_file() {
|
|
||||||
if Project::is_project_file(path) {
|
|
||||||
Some(path.to_path_buf())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let child_path = path.join(PROJECT_FILENAME);
|
|
||||||
let child_meta = fs::metadata(&child_path).ok()?;
|
|
||||||
|
|
||||||
if child_meta.is_file() {
|
|
||||||
Some(child_path)
|
|
||||||
} else {
|
|
||||||
// This is a folder with the same name as a Rojo default project
|
|
||||||
// file.
|
|
||||||
//
|
|
||||||
// That's pretty weird, but we can roll with it.
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_from_slice(
|
|
||||||
contents: &[u8],
|
|
||||||
project_file_location: &Path,
|
|
||||||
) -> Result<Self, ProjectError> {
|
|
||||||
let mut project: Self =
|
|
||||||
serde_json::from_slice(&contents).map_err(|source| Error::Json {
|
|
||||||
source,
|
|
||||||
path: project_file_location.to_owned(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
project.file_location = project_file_location.to_path_buf();
|
|
||||||
project.check_compatibility();
|
|
||||||
Ok(project)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_fuzzy(fuzzy_project_location: &Path) -> Result<Option<Self>, ProjectError> {
|
|
||||||
if let Some(project_path) = Self::locate(fuzzy_project_location) {
|
|
||||||
let project = Self::load_exact(&project_path)?;
|
|
||||||
|
|
||||||
Ok(Some(project))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_exact(project_file_location: &Path) -> Result<Self, Error> {
|
|
||||||
let contents = fs::read_to_string(project_file_location)?;
|
|
||||||
|
|
||||||
let mut project: Project =
|
|
||||||
serde_json::from_str(&contents).map_err(|source| Error::Json {
|
|
||||||
source,
|
|
||||||
path: project_file_location.to_owned(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
project.file_location = project_file_location.to_path_buf();
|
|
||||||
project.check_compatibility();
|
|
||||||
|
|
||||||
Ok(project)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if there are any compatibility issues with this project file and
|
|
||||||
/// warns the user if there are any.
|
|
||||||
fn check_compatibility(&self) {
|
|
||||||
self.tree.validate_reserved_names();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn folder_location(&self) -> &Path {
|
|
||||||
self.file_location.parent().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
|
|
||||||
pub struct OptionalPathNode {
|
|
||||||
#[serde(serialize_with = "crate::path_serializer::serialize_absolute")]
|
|
||||||
pub optional: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OptionalPathNode {
|
|
||||||
pub fn new(optional: PathBuf) -> Self {
|
|
||||||
OptionalPathNode { optional }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes a path that is either optional or required
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum PathNode {
|
|
||||||
Required(#[serde(serialize_with = "crate::path_serializer::serialize_absolute")] PathBuf),
|
|
||||||
Optional(OptionalPathNode),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PathNode {
|
|
||||||
pub fn path(&self) -> &Path {
|
|
||||||
match self {
|
|
||||||
PathNode::Required(pathbuf) => &pathbuf,
|
|
||||||
PathNode::Optional(OptionalPathNode { optional }) => &optional,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes an instance and its descendants in a project.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
|
|
||||||
pub struct ProjectNode {
|
|
||||||
/// If set, defines the ClassName of the described instance.
|
|
||||||
///
|
|
||||||
/// `$className` MUST be set if `$path` is not set.
|
|
||||||
///
|
|
||||||
/// `$className` CANNOT be set if `$path` is set and the instance described
|
|
||||||
/// by that path has a ClassName other than Folder.
|
|
||||||
#[serde(rename = "$className", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub class_name: Option<String>,
|
|
||||||
|
|
||||||
/// Contains all of the children of the described instance.
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub children: BTreeMap<String, ProjectNode>,
|
|
||||||
|
|
||||||
/// The properties that will be assigned to the resulting instance.
|
|
||||||
///
|
|
||||||
// TODO: Is this legal to set if $path is set?
|
|
||||||
#[serde(
|
|
||||||
rename = "$properties",
|
|
||||||
default,
|
|
||||||
skip_serializing_if = "HashMap::is_empty"
|
|
||||||
)]
|
|
||||||
pub properties: HashMap<String, UnresolvedValue>,
|
|
||||||
|
|
||||||
/// Defines the behavior when Rojo encounters unknown instances in Roblox
|
|
||||||
/// Studio during live sync. `$ignoreUnknownInstances` should be considered
|
|
||||||
/// a large hammer and used with care.
|
|
||||||
///
|
|
||||||
/// If set to `true`, those instances will be left alone. This may cause
|
|
||||||
/// issues when files that turn into instances are removed while Rojo is not
|
|
||||||
/// running.
|
|
||||||
///
|
|
||||||
/// If set to `false`, Rojo will destroy any instances it does not
|
|
||||||
/// recognize.
|
|
||||||
///
|
|
||||||
/// If unset, its default value depends on other settings:
|
|
||||||
/// - If `$path` is not set, defaults to `true`
|
|
||||||
/// - If `$path` is set, defaults to `false`
|
|
||||||
#[serde(
|
|
||||||
rename = "$ignoreUnknownInstances",
|
|
||||||
skip_serializing_if = "Option::is_none"
|
|
||||||
)]
|
|
||||||
pub ignore_unknown_instances: Option<bool>,
|
|
||||||
|
|
||||||
/// Defines that this instance should come from the given file path. This
|
|
||||||
/// path can point to any file type supported by Rojo, including Lua files
|
|
||||||
/// (`.lua`), Roblox models (`.rbxm`, `.rbxmx`), and localization table
|
|
||||||
/// spreadsheets (`.csv`).
|
|
||||||
#[serde(rename = "$path", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub path: Option<PathNode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProjectNode {
|
|
||||||
fn validate_reserved_names(&self) {
|
|
||||||
for (name, child) in &self.children {
|
|
||||||
if name.starts_with('$') {
|
|
||||||
log::warn!(
|
|
||||||
"Keys starting with '$' are reserved by Rojo to ensure forward compatibility."
|
|
||||||
);
|
|
||||||
log::warn!(
|
|
||||||
"This project uses the key '{}', which should be renamed.",
|
|
||||||
name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
child.validate_reserved_names();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn path_node_required() {
|
|
||||||
let path_node: PathNode = serde_json::from_str(r#""src""#).unwrap();
|
|
||||||
assert_eq!(path_node, PathNode::Required(PathBuf::from("src")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn path_node_optional() {
|
|
||||||
let path_node: PathNode = serde_json::from_str(r#"{ "optional": "src" }"#).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
path_node,
|
|
||||||
PathNode::Optional(OptionalPathNode::new(PathBuf::from("src")))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn project_node_required() {
|
|
||||||
let project_node: ProjectNode = serde_json::from_str(
|
|
||||||
r#"{
|
|
||||||
"$path": "src"
|
|
||||||
}"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
project_node.path,
|
|
||||||
Some(PathNode::Required(PathBuf::from("src")))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn project_node_optional() {
|
|
||||||
let project_node: ProjectNode = serde_json::from_str(
|
|
||||||
r#"{
|
|
||||||
"$path": { "optional": "src" }
|
|
||||||
}"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
project_node.path,
|
|
||||||
Some(PathNode::Optional(OptionalPathNode::new(PathBuf::from(
|
|
||||||
"src"
|
|
||||||
))))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn project_node_none() {
|
|
||||||
let project_node: ProjectNode = serde_json::from_str(
|
|
||||||
r#"{
|
|
||||||
"$className": "Folder"
|
|
||||||
}"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(project_node.path, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn project_node_optional_serialize_absolute() {
|
|
||||||
let project_node: ProjectNode = serde_json::from_str(
|
|
||||||
r#"{
|
|
||||||
"$path": { "optional": "..\\src" }
|
|
||||||
}"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let serialized = serde_json::to_string(&project_node).unwrap();
|
|
||||||
assert_eq!(serialized, r#"{"$path":{"optional":"../src"}}"#);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn project_node_optional_serialize_absolute_no_change() {
|
|
||||||
let project_node: ProjectNode = serde_json::from_str(
|
|
||||||
r#"{
|
|
||||||
"$path": { "optional": "../src" }
|
|
||||||
}"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let serialized = serde_json::to_string(&project_node).unwrap();
|
|
||||||
assert_eq!(serialized, r#"{"$path":{"optional":"../src"}}"#);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn project_node_optional_serialize_optional() {
|
|
||||||
let project_node: ProjectNode = serde_json::from_str(
|
|
||||||
r#"{
|
|
||||||
"$path": "..\\src"
|
|
||||||
}"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let serialized = serde_json::to_string(&project_node).unwrap();
|
|
||||||
assert_eq!(serialized, r#"{"$path":"../src"}"#);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use thiserror::Error;
|
|||||||
use crate::{
|
use crate::{
|
||||||
change_processor::ChangeProcessor,
|
change_processor::ChangeProcessor,
|
||||||
message_queue::MessageQueue,
|
message_queue::MessageQueue,
|
||||||
project::{Project, ProjectError},
|
project::Project,
|
||||||
session_id::SessionId,
|
session_id::SessionId,
|
||||||
snapshot::{
|
snapshot::{
|
||||||
apply_patch_set, compute_patch_set, AppliedPatchSet, InstanceContext, InstanceSnapshot,
|
apply_patch_set, compute_patch_set, AppliedPatchSet, InstanceContext, InstanceSnapshot,
|
||||||
@@ -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, &tree, root_id);
|
let patch_set = compute_patch_set(snapshot.as_ref(), &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);
|
||||||
@@ -237,12 +237,6 @@ pub enum ServeSessionError {
|
|||||||
source: io::Error,
|
source: io::Error,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error(transparent)]
|
|
||||||
Project {
|
|
||||||
#[from]
|
|
||||||
source: ProjectError,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Other {
|
Other {
|
||||||
#[from]
|
#[from]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{borrow::Cow, collections::HashMap};
|
|||||||
|
|
||||||
use rbx_dom_weak::{
|
use rbx_dom_weak::{
|
||||||
types::{Ref, Variant},
|
types::{Ref, Variant},
|
||||||
Instance, WeakDom,
|
WeakDom,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -102,29 +102,22 @@ impl InstanceSnapshot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[profiling::function]
|
pub fn from_tree(tree: &WeakDom, id: Ref) -> Self {
|
||||||
pub fn from_tree(tree: WeakDom, id: Ref) -> Self {
|
let instance = tree.get_by_ref(id).expect("instance did not exist in tree");
|
||||||
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()
|
||||||
.map(|&id| Self::from_raw_tree(raw_tree, id))
|
.copied()
|
||||||
|
.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),
|
name: Cow::Owned(instance.name.clone()),
|
||||||
class_name: Cow::Owned(instance.class),
|
class_name: Cow::Owned(instance.class.clone()),
|
||||||
properties: instance.properties,
|
properties: instance.properties.clone(),
|
||||||
children,
|
children,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use rojo_project::glob::Glob;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{glob::Glob, path_serializer, project::ProjectNode};
|
use crate::{path_serializer, project::ProjectNode};
|
||||||
|
|
||||||
/// Rojo-specific metadata that can be associated with an instance or a snapshot
|
/// Rojo-specific metadata that can be associated with an instance or a snapshot
|
||||||
/// of an instance.
|
/// of an instance.
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
//! Defines the algorithm for applying generated patches.
|
//! Defines the algorithm for applying generated patches.
|
||||||
|
|
||||||
use std::{
|
use std::collections::HashMap;
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
mem::take,
|
|
||||||
};
|
|
||||||
|
|
||||||
use rbx_dom_weak::types::{Ref, Variant};
|
use rbx_dom_weak::types::{Ref, Variant};
|
||||||
|
|
||||||
@@ -15,31 +12,21 @@ 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 {
|
||||||
profiling::scope!("removals");
|
apply_remove_instance(&mut context, tree, removed_id);
|
||||||
for removed_id in patch_set.removed_instances {
|
|
||||||
apply_remove_instance(&mut context, tree, removed_id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
for add_patch in patch_set.added_instances {
|
||||||
profiling::scope!("additions");
|
apply_add_child(&mut context, tree, add_patch.parent_id, add_patch.instance);
|
||||||
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
|
||||||
profiling::scope!("updates");
|
// of updates significantly.
|
||||||
// Updates need to be applied after additions, which reduces the complexity
|
for update_patch in patch_set.updated_instances {
|
||||||
// of updates significantly.
|
apply_update_child(&mut context, tree, update_patch);
|
||||||
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)
|
||||||
@@ -68,9 +55,20 @@ struct PatchApplyContext {
|
|||||||
/// eachother.
|
/// eachother.
|
||||||
snapshot_id_to_instance_id: HashMap<Ref, Ref>,
|
snapshot_id_to_instance_id: HashMap<Ref, Ref>,
|
||||||
|
|
||||||
/// Tracks all of the instances added by this patch that have refs that need
|
/// The properties of instances added by the current `PatchSet`.
|
||||||
/// to be rewritten.
|
///
|
||||||
has_refs_to_rewrite: HashSet<Ref>,
|
/// Instances added to the tree can refer to eachother via Ref properties,
|
||||||
|
/// 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,
|
||||||
@@ -86,22 +84,23 @@ 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 in context.has_refs_to_rewrite {
|
for (id, properties) in context.added_instance_properties {
|
||||||
// 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 value in instance.properties_mut().values_mut() {
|
for (key, mut property_value) in properties {
|
||||||
if let Variant::Ref(referent) = value {
|
if let Variant::Ref(referent) = property_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)
|
||||||
{
|
{
|
||||||
*value = Variant::Ref(instance_referent);
|
property_value = Variant::Ref(instance_referent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance.properties_mut().insert(key, property_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,24 +116,24 @@ fn apply_add_child(
|
|||||||
context: &mut PatchApplyContext,
|
context: &mut PatchApplyContext,
|
||||||
tree: &mut RojoTree,
|
tree: &mut RojoTree,
|
||||||
parent_id: Ref,
|
parent_id: Ref,
|
||||||
mut snapshot: InstanceSnapshot,
|
snapshot: InstanceSnapshot,
|
||||||
) {
|
) {
|
||||||
let snapshot_id = snapshot.snapshot_id;
|
let snapshot_id = snapshot.snapshot_id;
|
||||||
let children = take(&mut snapshot.children);
|
let properties = snapshot.properties;
|
||||||
|
let children = snapshot.children;
|
||||||
|
|
||||||
// If an object we're adding has a non-null referent, we'll note this
|
// Property application is deferred until after all children
|
||||||
// instance down as needing to be revisited later.
|
// are constructed. This helps apply referents correctly.
|
||||||
let has_refs = snapshot.properties.values().any(|value| match value {
|
let remaining_snapshot = InstanceSnapshot::new()
|
||||||
Variant::Ref(value) => value.is_some(),
|
.name(snapshot.name)
|
||||||
_ => false,
|
.class_name(snapshot.class_name)
|
||||||
});
|
.metadata(snapshot.metadata)
|
||||||
|
.snapshot_id(snapshot.snapshot_id);
|
||||||
|
|
||||||
let id = tree.insert_instance(parent_id, snapshot);
|
let id = tree.insert_instance(parent_id, remaining_snapshot);
|
||||||
context.applied_patch_set.added.push(id);
|
context.applied_patch_set.added.push(id);
|
||||||
|
|
||||||
if has_refs {
|
context.added_instance_properties.insert(id, properties);
|
||||||
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,10 +1,7 @@
|
|||||||
//! 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::{
|
use std::collections::{HashMap, HashSet};
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
mem::take,
|
|
||||||
};
|
|
||||||
|
|
||||||
use rbx_dom_weak::types::{Ref, Variant};
|
use rbx_dom_weak::types::{Ref, Variant};
|
||||||
|
|
||||||
@@ -13,8 +10,11 @@ use super::{
|
|||||||
InstanceSnapshot, InstanceWithMeta, RojoTree,
|
InstanceSnapshot, InstanceWithMeta, RojoTree,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[profiling::function]
|
pub fn compute_patch_set(
|
||||||
pub fn compute_patch_set(snapshot: Option<InstanceSnapshot>, tree: &RojoTree, id: Ref) -> PatchSet {
|
snapshot: Option<&InstanceSnapshot>,
|
||||||
|
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,
|
||||||
mut snapshot: InstanceSnapshot,
|
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(&mut snapshot, &instance, patch_set);
|
compute_property_patches(snapshot, &instance, patch_set);
|
||||||
compute_children_patches(context, &mut snapshot, tree, id, patch_set);
|
compute_children_patches(context, snapshot, tree, id, patch_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_property_patches(
|
fn compute_property_patches(
|
||||||
snapshot: &mut InstanceSnapshot,
|
snapshot: &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(take(&mut snapshot.name).into_owned())
|
Some(snapshot.name.clone().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(take(&mut snapshot.class_name).into_owned())
|
Some(snapshot.class_name.clone().into_owned())
|
||||||
};
|
};
|
||||||
|
|
||||||
let changed_metadata = if &snapshot.metadata == instance.metadata() {
|
let changed_metadata = if &snapshot.metadata == instance.metadata() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(take(&mut snapshot.metadata))
|
Some(snapshot.metadata.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
for (name, snapshot_value) in take(&mut snapshot.properties) {
|
for (name, snapshot_value) in &snapshot.properties {
|
||||||
visited_properties.insert(name.clone());
|
visited_properties.insert(name.as_str());
|
||||||
|
|
||||||
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, Some(snapshot_value));
|
changed_properties.insert(name.clone(), Some(snapshot_value.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
changed_properties.insert(name, Some(snapshot_value));
|
changed_properties.insert(name.clone(), Some(snapshot_value.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ fn compute_property_patches(
|
|||||||
|
|
||||||
fn compute_children_patches(
|
fn compute_children_patches(
|
||||||
context: &mut ComputePatchContext,
|
context: &mut ComputePatchContext,
|
||||||
snapshot: &mut InstanceSnapshot,
|
snapshot: &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 take(&mut snapshot.children) {
|
for snapshot_child in snapshot.children.iter() {
|
||||||
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,
|
instance: snapshot_child.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,9 +87,8 @@ 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::empty()
|
let builder = InstanceBuilder::new(snapshot.class_name.to_owned())
|
||||||
.with_class(snapshot.class_name.into_owned())
|
.with_name(snapshot.name.to_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);
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ 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,7 +7,6 @@ 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,
|
||||||
@@ -22,8 +21,7 @@ pub fn snapshot_rbxm(
|
|||||||
let children = root_instance.children();
|
let children = root_instance.children();
|
||||||
|
|
||||||
if children.len() == 1 {
|
if children.len() == 1 {
|
||||||
let child = children[0];
|
let snapshot = InstanceSnapshot::from_tree(&temp_tree, children[0])
|
||||||
let snapshot = InstanceSnapshot::from_tree(temp_tree, child)
|
|
||||||
.name(name)
|
.name(name)
|
||||||
.metadata(
|
.metadata(
|
||||||
InstanceMetadata::new()
|
InstanceMetadata::new()
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ pub fn snapshot_rbxmx(
|
|||||||
let children = root_instance.children();
|
let children = root_instance.children();
|
||||||
|
|
||||||
if children.len() == 1 {
|
if children.len() == 1 {
|
||||||
let child = children[0];
|
let snapshot = InstanceSnapshot::from_tree(&temp_tree, children[0])
|
||||||
let snapshot = InstanceSnapshot::from_tree(temp_tree, child)
|
|
||||||
.name(name)
|
.name(name)
|
||||||
.metadata(
|
.metadata(
|
||||||
InstanceMetadata::new()
|
InstanceMetadata::new()
|
||||||
|
|||||||
Reference in New Issue
Block a user