Skip to content
This repository has been archived by the owner on Jul 22, 2023. It is now read-only.

Commit

Permalink
Add DataModel:GetService(name)
Browse files Browse the repository at this point in the history
  • Loading branch information
LPGhatguy committed Sep 24, 2019
1 parent 349eed4 commit 41f299d
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased Changes
* Added `Instance:Clone()` for copying instances all over the place, as is Roblox tradition. ([#12](https://github.com/rojo-rbx/remodel/issues/12))
* Added `DataModel:GetService()` for finding services and creating them if they don't exist, like Roblox does.
* Improved error messages in preparation for [#7](https://github.com/rojo-rbx/remodel/issues/7) to be fixed upstream.

## 0.5.0 (2019-09-21)
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ Remodel supports some parts of Roblox's API in order to make code familiar to ex
* `<Instance>.ClassName` (read only)
* `<Instance>.Parent` (read + write)
* `<Instance>:Destroy()` (0.5.0+)
* `<Instance>:Clone()`, (**unreleased**)
* `<Instance>:Clone()` (**unreleased**)
* `<Instance>:GetChildren()`
* `<Instance>:FindFirstChild(name)`
* The second argument (recursive) is not supported by Remodel.
* `<DataModel>:GetService(name)` (**unreleased**)

## Remodel API
Remodel has its own API that goes beyond what can be done inside Roblox.
Expand Down
62 changes: 61 additions & 1 deletion src/remodel_api/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
sync::{Arc, Mutex},
};

use rbx_dom_weak::{RbxId, RbxTree};
use rbx_dom_weak::{RbxId, RbxInstanceProperties, RbxTree};
use rlua::{Context, FromLua, MetaMethod, ToLua, UserData, UserDataMethods};

#[derive(Clone)]
Expand Down Expand Up @@ -109,6 +109,62 @@ impl LuaInstance {
Ok(children)
}

fn get_service(&self, service_name: &str) -> rlua::Result<LuaInstance> {
let mut tree = self.tree.lock().unwrap();

let instance = tree.get_instance(self.id).ok_or_else(|| {
rlua::Error::external("Cannot call GetService() on a destroyed instance")
})?;

// It might be cleaner to avoid defining GetService() on all instances,
// but we don't have a good mechanism in Remodel to do that right now.
if instance.class_name != "DataModel" {
return Err(rlua::Error::external(
"Cannot call GetService() on an instance that is not a DataModel",
));
}

match rbx_reflection::get_class_descriptor(service_name) {
// We should only find services, even if there's a child of
// DataModel with a matching ClassName.
Some(descriptor) if descriptor.is_service() => {
let existing = instance
.get_children_ids()
.iter()
.copied()
.map(|id| tree.get_instance(id).unwrap())
.find(|instance| instance.class_name == service_name);

match existing {
Some(existing) => Ok(LuaInstance {
tree: Arc::clone(&self.tree),
id: existing.get_id(),
}),
None => {
// If we didn't find an existing service instance,
// construct a new one.

let properties = RbxInstanceProperties {
name: service_name.to_owned(),
class_name: service_name.to_owned(),
properties: Default::default(),
};

let id = tree.insert_instance(properties, self.id);
Ok(LuaInstance {
tree: Arc::clone(&self.tree),
id,
})
}
}
}
_ => Err(rlua::Error::external(format!(
"'{}' is not a valid service.",
service_name
))),
}
}

fn get_class_name<'lua>(
&self,
context: rlua::Context<'lua>,
Expand Down Expand Up @@ -269,6 +325,10 @@ impl UserData for LuaInstance {
this.get_children()
});

methods.add_method("GetService", |_context, this, name: String| {
this.get_service(&name)
});

methods.add_meta_method(MetaMethod::ToString, |context, this, _arg: ()| {
this.meta_to_string(context)
});
Expand Down
7 changes: 7 additions & 0 deletions test-scripts/get-service-existing.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
local game = Instance.new("DataModel")

local workspace = Instance.new("Workspace")
workspace.Parent = game

local found = game:GetService("Workspace")
assert(found == workspace)
8 changes: 8 additions & 0 deletions test-scripts/get-service-invalid-service.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
local game = Instance.new("DataModel")

local ok, err = pcall(function()
return game:GetService("GitService") -- this service will never exist
end)

assert(not ok)
assert(tostring(err):find("GitService") ~= nil)
5 changes: 5 additions & 0 deletions test-scripts/get-service-new.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
local game = Instance.new("DataModel")

local workspace = game:GetService("Workspace")
assert(workspace.Parent == game)
assert(workspace.ClassName == "Workspace")
8 changes: 8 additions & 0 deletions test-scripts/get-service-not-datamodel.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
local folder = Instance.new("Folder")

local ok, err = pcall(function()
return folder:GetService("Workspace")
end)

assert(not ok)
assert(tostring(err):find("DataModel") ~= nil)

0 comments on commit 41f299d

Please sign in to comment.