From 816e5f260be2aad136fbc111b85ae1d426e1165d Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Tue, 13 Apr 2021 15:57:40 -0700 Subject: [PATCH 01/12] Add writeModelAsset and writePlaceAsset --- src/remodel_api/remodel.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/remodel_api/remodel.rs b/src/remodel_api/remodel.rs index e701a0a..8c1868f 100644 --- a/src/remodel_api/remodel.rs +++ b/src/remodel_api/remodel.rs @@ -282,6 +282,14 @@ impl Remodel { Remodel::import_tree_root(context, source_tree) } + fn write_model_asset(context: Context<'_>, lua_instance: LuaInstance) -> rlua::Result<()> { + Remodel::write_existing_model_asset(context, lua_instance, 0) + } + + fn write_place_asset(context: Context<'_>, lua_instance: LuaInstance) -> rlua::Result<()> { + Remodel::write_existing_place_asset(context, lua_instance, 0) + } + fn write_existing_model_asset( context: Context<'_>, lua_instance: LuaInstance, @@ -471,6 +479,14 @@ impl UserData for Remodel { Remodel::read_place_asset(context, asset_id) }); + methods.add_function("writeModelAsset", |context, instance: LuaInstance| { + Remodel::write_model_asset(context, instance) + }); + + methods.add_function("writePlaceAsset", |context, instance: LuaInstance| { + Remodel::write_place_asset(context, instance) + }); + methods.add_function( "writeExistingModelAsset", |context, (instance, asset_id): (LuaInstance, String)| { From b3d6b2306f08dcb3947511f53dc887227a18e7fa Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Tue, 13 Apr 2021 22:19:25 -0700 Subject: [PATCH 02/12] Finish writeNewModelAsset and writeNewPlaceAsset implementation --- src/remodel_api/remodel.rs | 187 ++++++++++++++++++++++++++++++++----- 1 file changed, 166 insertions(+), 21 deletions(-) diff --git a/src/remodel_api/remodel.rs b/src/remodel_api/remodel.rs index 8c1868f..78a2aa6 100644 --- a/src/remodel_api/remodel.rs +++ b/src/remodel_api/remodel.rs @@ -11,7 +11,7 @@ use reqwest::{ header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT}, StatusCode, }; -use rlua::{Context, UserData, UserDataMethods}; +use rlua::{Context, Table, UserData, UserDataMethods, Value}; use crate::{ remodel_context::RemodelContext, @@ -28,6 +28,55 @@ fn xml_decode_options() -> rbx_xml::DecodeOptions { rbx_xml::DecodeOptions::new().property_behavior(rbx_xml::DecodePropertyBehavior::ReadUnknown) } +fn get_required_string_option(options: &Table, option: &str) -> rlua::Result { + let value = options.get(option).map_err(rlua::Error::external)?; + + match value { + Value::String(value) => Ok(value.to_str().map_err(rlua::Error::external)?.to_string()), + Value::Nil => Err(rlua::Error::external(format!( + "The option {} must be specified", + option + ))), + _ => Err(rlua::Error::external(format!( + "The option {} must be a string", + option + ))), + } +} + +fn get_string_option(options: &Table, option: &str, default: &str) -> rlua::Result { + let value = options.get(option).map_err(rlua::Error::external)?; + + match value { + Value::String(value) => Ok(value.to_str().map_err(rlua::Error::external)?.to_string()), + Value::Nil => Ok(default.to_string()), + _ => Err(rlua::Error::external(format!( + "The option {} must be a string", + option + ))), + } +} + +fn get_bool_option(options: &Table, option: &str, default: bool) -> rlua::Result { + let value = options.get(option).map_err(rlua::Error::external)?; + + match value { + Value::Boolean(value) => Ok(value), + Value::Nil => Ok(default), + _ => Err(rlua::Error::external(format!( + "The option {} must be a bool", + option + ))), + } +} + +fn bool_into_query(boolean: bool) -> String { + match boolean { + true => String::from("True"), + false => String::from("False"), + } +} + pub struct Remodel; impl Remodel { @@ -282,18 +331,11 @@ impl Remodel { Remodel::import_tree_root(context, source_tree) } - fn write_model_asset(context: Context<'_>, lua_instance: LuaInstance) -> rlua::Result<()> { - Remodel::write_existing_model_asset(context, lua_instance, 0) - } - - fn write_place_asset(context: Context<'_>, lua_instance: LuaInstance) -> rlua::Result<()> { - Remodel::write_existing_place_asset(context, lua_instance, 0) - } - - fn write_existing_model_asset( + fn write_model_asset( context: Context<'_>, lua_instance: LuaInstance, asset_id: u64, + queries: &[(&str, &str)], ) -> rlua::Result<()> { let tree = lua_instance.tree.lock().unwrap(); let instance = tree @@ -310,13 +352,14 @@ impl Remodel { rbx_binary::to_writer_default(&mut buffer, &tree, &[lua_instance.id]) .map_err(rlua::Error::external)?; - Remodel::upload_asset(context, buffer, asset_id) + Remodel::upload_asset(context, buffer, asset_id, queries) } - fn write_existing_place_asset( + fn write_place_asset( context: Context<'_>, lua_instance: LuaInstance, asset_id: u64, + queries: &[(&str, &str)], ) -> rlua::Result<()> { let tree = lua_instance.tree.lock().unwrap(); let instance = tree @@ -333,10 +376,81 @@ impl Remodel { rbx_binary::to_writer_default(&mut buffer, &tree, instance.children()) .map_err(rlua::Error::external)?; - Remodel::upload_asset(context, buffer, asset_id) + Remodel::upload_asset(context, buffer, asset_id, queries) + } + + fn write_new_model_asset( + context: Context<'_>, + lua_instance: LuaInstance, + name: String, + description: String, + is_public: bool, + allow_comments: bool, + ) -> rlua::Result<()> { + let is_public = bool_into_query(is_public); + let allow_comments = bool_into_query(allow_comments); + + Remodel::write_model_asset( + context, + lua_instance, + 0, + &[ + ("type", "Model"), + ("name", &name), + ("description", &description), + ("isPublic", &is_public), + ("allowComments", &allow_comments), + ], + ) + } + + fn write_new_place_asset( + context: Context<'_>, + lua_instance: LuaInstance, + name: String, + description: String, + is_public: bool, + allow_comments: bool, + ) -> rlua::Result<()> { + let is_public = bool_into_query(is_public); + let allow_comments = bool_into_query(allow_comments); + + Remodel::write_place_asset( + context, + lua_instance, + 0, + &[ + ("type", "Model"), + ("name", &name), + ("description", &description), + ("isPublic", &is_public), + ("allowComments", &allow_comments), + ], + ) + } + + fn write_existing_model_asset( + context: Context<'_>, + lua_instance: LuaInstance, + asset_id: u64, + ) -> rlua::Result<()> { + Remodel::write_model_asset(context, lua_instance, asset_id, &[]) + } + + fn write_existing_place_asset( + context: Context<'_>, + lua_instance: LuaInstance, + asset_id: u64, + ) -> rlua::Result<()> { + Remodel::write_model_asset(context, lua_instance, asset_id, &[]) } - fn upload_asset(context: Context<'_>, buffer: Vec, asset_id: u64) -> rlua::Result<()> { + fn upload_asset( + context: Context<'_>, + buffer: Vec, + asset_id: u64, + queries: &[(&str, &str)], + ) -> rlua::Result<()> { let re_context = RemodelContext::get(context)?; let auth_cookie = re_context.auth_cookie().ok_or_else(|| { rlua::Error::external( @@ -357,9 +471,10 @@ impl Remodel { .header(USER_AGENT, "Roblox/WinInet") .header(CONTENT_TYPE, "application/xml") .header(ACCEPT, "application/json") + .query(queries) .body(buffer.clone()) }; - + log::debug!("Uploading to Roblox..."); let mut response = build_request().send().map_err(rlua::Error::external)?; @@ -479,13 +594,43 @@ impl UserData for Remodel { Remodel::read_place_asset(context, asset_id) }); - methods.add_function("writeModelAsset", |context, instance: LuaInstance| { - Remodel::write_model_asset(context, instance) - }); + methods.add_function( + "writeNewModelAsset", + |context, (instance, options): (LuaInstance, Table)| { + let name = get_required_string_option(&options, "name")?; + let description = get_string_option(&options, "description", "")?; + let is_public = get_bool_option(&options, "isPublic", false)?; + let allow_comments = get_bool_option(&options, "allowComments", false)?; + + Remodel::write_new_model_asset( + context, + instance, + name, + description, + is_public, + allow_comments, + ) + }, + ); - methods.add_function("writePlaceAsset", |context, instance: LuaInstance| { - Remodel::write_place_asset(context, instance) - }); + methods.add_function( + "writeNewPlaceAsset", + |context, (instance, options): (LuaInstance, Table)| { + let name = get_required_string_option(&options, "name")?; + let description = get_string_option(&options, "description", "")?; + let is_public = get_bool_option(&options, "isPublic", false)?; + let allow_comments = get_bool_option(&options, "allowComments", false)?; + + Remodel::write_new_place_asset( + context, + instance, + name, + description, + is_public, + allow_comments, + ) + }, + ); methods.add_function( "writeExistingModelAsset", From 305642eedafcbc1d52caac3c8f6b0fcc66a6d38a Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Tue, 13 Apr 2021 22:24:24 -0700 Subject: [PATCH 03/12] Run cargo fmt again --- src/remodel_api/remodel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remodel_api/remodel.rs b/src/remodel_api/remodel.rs index 78a2aa6..319a5ff 100644 --- a/src/remodel_api/remodel.rs +++ b/src/remodel_api/remodel.rs @@ -474,7 +474,7 @@ impl Remodel { .query(queries) .body(buffer.clone()) }; - + log::debug!("Uploading to Roblox..."); let mut response = build_request().send().map_err(rlua::Error::external)?; From 357491a7eb23dcd006525a584ee5d13f69621481 Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Tue, 13 Apr 2021 22:55:12 -0700 Subject: [PATCH 04/12] Use if statement --- src/remodel_api/remodel.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/remodel_api/remodel.rs b/src/remodel_api/remodel.rs index 319a5ff..28a8e4a 100644 --- a/src/remodel_api/remodel.rs +++ b/src/remodel_api/remodel.rs @@ -71,9 +71,10 @@ fn get_bool_option(options: &Table, option: &str, default: bool) -> rlua::Result } fn bool_into_query(boolean: bool) -> String { - match boolean { - true => String::from("True"), - false => String::from("False"), + if boolean { + String::from("True") + } else { + String::from("False") } } From c285e80ffeee933925df8627f53c26562f5bf5de Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Fri, 16 Apr 2021 23:12:22 -0700 Subject: [PATCH 05/12] Add APIs to changelog and README --- CHANGELOG.md | 3 +++ README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f12de9..e3d03d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Remodel Changelog ## Unreleased Changes +* Added APIs for uploading new models and places on Roblox.com: + * `remodel.writeNewModelAsset` + * `remodel.writeNewPlaceAsset` ## 0.8.1 (2021-04-09) * Updated to latest rbx_xml, which should fix `OptionalCoordinateFrame`-related issues. diff --git a/README.md b/README.md index fb0c88d..68b2934 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,54 @@ If the instance is a `DataModel`, this method will throw. Places should be saved Throws on error. +### `remodel.writeNewPlaceAsset` +``` +type Options { + name: string, + description: string?, + isPublic: boolean?, + allowComments: boolean?, +} + +remodel.writeNewPlaceAsset(instance: DataModel, options: Options) +``` + +Uploads the given `DataModel` instance to Roblox.com as a new place with the corresponding options. + +The options: `description`, `isPublic`, and `allowComments` are all optional. +``description`` default to an empty string. ``isPublic`` and ``allowComments`` default to ``false``. + +``allowComments`` does not have any function for places. + +If the instance is not a `DataModel`, this method will throw. Models should be uploaded with `writeNewModelAsset` instead. + +**This method always requires web authentication! See [Authentication](#authentication) for more information.** + +Throws on error. + +### `remodel.writeNewModelAsset` +``` +type Options { + name: string, + description: string?, + isPublic: boolean?, + allowComments: boolean?, +} + +remodel.writeNewModelAsset(instance: Instance, options: Options) +``` + +Uploads the given instance to Roblox.com as a new model with the corresponding options. + +The options: `description`, `isPublic`, and `allowComments` are all optional. +``description`` default to an empty string. ``isPublic`` and ``allowComments`` default to ``false``. + +If the instance is a `DataModel`, this method will throw. Places should be uploaded with `writeNewPlaceAsset` instead. + +**This method always requires web authentication! See [Authentication](#authentication) for more information.** + +Throws on error. + ### `remodel.writeExistingPlaceAsset` (0.5.0+) ``` remodel.writeExistingPlaceAsset(instance: Instance, assetId: string) From 2ac5c253b3c5df7efcbd2569f2d2cbf051c00abd Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Sat, 17 Apr 2021 16:39:39 -0700 Subject: [PATCH 06/12] Change format --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 68b2934..5ebf118 100644 --- a/README.md +++ b/README.md @@ -149,14 +149,14 @@ Throws on error. ### `remodel.writeNewPlaceAsset` ``` -type Options { +remodel.writeNewPlaceAsset(instance: DataModel, options: Options) + +where Options: { name: string, description: string?, isPublic: boolean?, allowComments: boolean?, } - -remodel.writeNewPlaceAsset(instance: DataModel, options: Options) ``` Uploads the given `DataModel` instance to Roblox.com as a new place with the corresponding options. @@ -174,14 +174,14 @@ Throws on error. ### `remodel.writeNewModelAsset` ``` -type Options { +remodel.writeNewModelAsset(instance: Instance, options: Options) + +where Options: { name: string, description: string?, isPublic: boolean?, allowComments: boolean?, } - -remodel.writeNewModelAsset(instance: Instance, options: Options) ``` Uploads the given instance to Roblox.com as a new model with the corresponding options. From b3c23ab0bd38c31a281f358261ae0245b45a07a5 Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Sat, 17 Apr 2021 17:02:50 -0700 Subject: [PATCH 07/12] Fix typo, return asset id --- src/remodel_api/remodel.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/remodel_api/remodel.rs b/src/remodel_api/remodel.rs index 28a8e4a..bfcc63d 100644 --- a/src/remodel_api/remodel.rs +++ b/src/remodel_api/remodel.rs @@ -337,7 +337,7 @@ impl Remodel { lua_instance: LuaInstance, asset_id: u64, queries: &[(&str, &str)], - ) -> rlua::Result<()> { + ) -> rlua::Result { let tree = lua_instance.tree.lock().unwrap(); let instance = tree .get_by_ref(lua_instance.id) @@ -361,7 +361,7 @@ impl Remodel { lua_instance: LuaInstance, asset_id: u64, queries: &[(&str, &str)], - ) -> rlua::Result<()> { + ) -> rlua::Result { let tree = lua_instance.tree.lock().unwrap(); let instance = tree .get_by_ref(lua_instance.id) @@ -387,7 +387,7 @@ impl Remodel { description: String, is_public: bool, allow_comments: bool, - ) -> rlua::Result<()> { + ) -> rlua::Result { let is_public = bool_into_query(is_public); let allow_comments = bool_into_query(allow_comments); @@ -412,7 +412,7 @@ impl Remodel { description: String, is_public: bool, allow_comments: bool, - ) -> rlua::Result<()> { + ) -> rlua::Result { let is_public = bool_into_query(is_public); let allow_comments = bool_into_query(allow_comments); @@ -435,7 +435,10 @@ impl Remodel { lua_instance: LuaInstance, asset_id: u64, ) -> rlua::Result<()> { - Remodel::write_model_asset(context, lua_instance, asset_id, &[]) + match Remodel::write_model_asset(context, lua_instance, asset_id, &[]) { + Ok(_) => Ok(()), + Err(error) => Err(rlua::Error::external(error)), + } } fn write_existing_place_asset( @@ -443,7 +446,10 @@ impl Remodel { lua_instance: LuaInstance, asset_id: u64, ) -> rlua::Result<()> { - Remodel::write_model_asset(context, lua_instance, asset_id, &[]) + match Remodel::write_place_asset(context, lua_instance, asset_id, &[]) { + Ok(_) => Ok(()), + Err(error) => Err(rlua::Error::external(error)), + } } fn upload_asset( @@ -451,7 +457,7 @@ impl Remodel { buffer: Vec, asset_id: u64, queries: &[(&str, &str)], - ) -> rlua::Result<()> { + ) -> rlua::Result { let re_context = RemodelContext::get(context)?; let auth_cookie = re_context.auth_cookie().ok_or_else(|| { rlua::Error::external( @@ -493,7 +499,12 @@ impl Remodel { } if response.status().is_success() { - Ok(()) + let asset_id = response.headers().get("roblox-assetid"); + + match asset_id { + Some(asset_id) => Ok(String::from(asset_id.to_str().unwrap())), + None => Err(rlua::Error::external("Asset had no roblox-assetid")), + } } else { Err(rlua::Error::external(format!( "Roblox API returned an error, status {}.", From c936aa8816b54c1ec160453a9387dbf9c67489ea Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Sat, 17 Apr 2021 17:04:44 -0700 Subject: [PATCH 08/12] Document return type --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ebf118..688d524 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ Throws on error. ### `remodel.writeNewPlaceAsset` ``` -remodel.writeNewPlaceAsset(instance: DataModel, options: Options) +remodel.writeNewPlaceAsset(instance: DataModel, options: Options): string where Options: { name: string, @@ -166,6 +166,8 @@ The options: `description`, `isPublic`, and `allowComments` are all optional. ``allowComments`` does not have any function for places. +Returns the place's asset id. + If the instance is not a `DataModel`, this method will throw. Models should be uploaded with `writeNewModelAsset` instead. **This method always requires web authentication! See [Authentication](#authentication) for more information.** @@ -174,7 +176,7 @@ Throws on error. ### `remodel.writeNewModelAsset` ``` -remodel.writeNewModelAsset(instance: Instance, options: Options) +remodel.writeNewModelAsset(instance: Instance, options: Options): string where Options: { name: string, @@ -189,6 +191,8 @@ Uploads the given instance to Roblox.com as a new model with the corresponding o The options: `description`, `isPublic`, and `allowComments` are all optional. ``description`` default to an empty string. ``isPublic`` and ``allowComments`` default to ``false``. +Returns the model's asset id. + If the instance is a `DataModel`, this method will throw. Places should be uploaded with `writeNewPlaceAsset` instead. **This method always requires web authentication! See [Authentication](#authentication) for more information.** From a2ecaca972bf9124e6eda18a220062e4622ff56f Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Mon, 19 Apr 2021 10:45:38 -0700 Subject: [PATCH 09/12] Fix markdown --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 688d524..957991c 100644 --- a/README.md +++ b/README.md @@ -161,8 +161,7 @@ where Options: { Uploads the given `DataModel` instance to Roblox.com as a new place with the corresponding options. -The options: `description`, `isPublic`, and `allowComments` are all optional. -``description`` default to an empty string. ``isPublic`` and ``allowComments`` default to ``false``. +The options: `description`, `isPublic`, and `allowComments` are all optional.``description`` default to an empty string. ``isPublic`` and ``allowComments`` default to ``false``. ``allowComments`` does not have any function for places. @@ -188,8 +187,7 @@ where Options: { Uploads the given instance to Roblox.com as a new model with the corresponding options. -The options: `description`, `isPublic`, and `allowComments` are all optional. -``description`` default to an empty string. ``isPublic`` and ``allowComments`` default to ``false``. +The options: `description`, `isPublic`, and `allowComments` are all optional. `description` default to an empty string. `isPublic` and `allowComments` default to `false`. Returns the model's asset id. From b42dcfcab7adc767209ed0a87c4af7d403af6e9c Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Fri, 23 Apr 2021 15:44:40 -0700 Subject: [PATCH 10/12] Resolve feedback --- src/remodel_api/remodel.rs | 169 +++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 80 deletions(-) diff --git a/src/remodel_api/remodel.rs b/src/remodel_api/remodel.rs index bfcc63d..603bd4d 100644 --- a/src/remodel_api/remodel.rs +++ b/src/remodel_api/remodel.rs @@ -13,6 +13,8 @@ use reqwest::{ }; use rlua::{Context, Table, UserData, UserDataMethods, Value}; +use rlua::prelude::FromLua; + use crate::{ remodel_context::RemodelContext, roblox_api::LuaInstance, @@ -28,54 +30,43 @@ fn xml_decode_options() -> rbx_xml::DecodeOptions { rbx_xml::DecodeOptions::new().property_behavior(rbx_xml::DecodePropertyBehavior::ReadUnknown) } -fn get_required_string_option(options: &Table, option: &str) -> rlua::Result { +fn get_required_option<'lua, T: FromLua<'lua>>( + context: Context<'lua>, + options: &Table<'lua>, + option: &str, +) -> rlua::Result { let value = options.get(option).map_err(rlua::Error::external)?; - match value { - Value::String(value) => Ok(value.to_str().map_err(rlua::Error::external)?.to_string()), - Value::Nil => Err(rlua::Error::external(format!( + if let Value::Nil = value { + Err(rlua::Error::external(format!( "The option {} must be specified", option - ))), - _ => Err(rlua::Error::external(format!( - "The option {} must be a string", - option - ))), - } -} - -fn get_string_option(options: &Table, option: &str, default: &str) -> rlua::Result { - let value = options.get(option).map_err(rlua::Error::external)?; - - match value { - Value::String(value) => Ok(value.to_str().map_err(rlua::Error::external)?.to_string()), - Value::Nil => Ok(default.to_string()), - _ => Err(rlua::Error::external(format!( - "The option {} must be a string", - option - ))), + ))) + } else { + FromLua::from_lua(value, context) } } -fn get_bool_option(options: &Table, option: &str, default: bool) -> rlua::Result { +fn get_option<'lua, T: FromLua<'lua>>( + context: Context<'lua>, + options: &Table<'lua>, + option: &str, + default: T, +) -> rlua::Result { let value = options.get(option).map_err(rlua::Error::external)?; match value { - Value::Boolean(value) => Ok(value), Value::Nil => Ok(default), - _ => Err(rlua::Error::external(format!( - "The option {} must be a bool", - option - ))), + _ => FromLua::from_lua(value, context), } } -fn bool_into_query(boolean: bool) -> String { - if boolean { - String::from("True") - } else { - String::from("False") - } +struct UploadQueries { + asset_type: String, + name: String, + description: String, + is_public: bool, + allow_comments: bool, } pub struct Remodel; @@ -336,8 +327,8 @@ impl Remodel { context: Context<'_>, lua_instance: LuaInstance, asset_id: u64, - queries: &[(&str, &str)], - ) -> rlua::Result { + queries: Option, + ) -> rlua::Result { let tree = lua_instance.tree.lock().unwrap(); let instance = tree .get_by_ref(lua_instance.id) @@ -360,8 +351,8 @@ impl Remodel { context: Context<'_>, lua_instance: LuaInstance, asset_id: u64, - queries: &[(&str, &str)], - ) -> rlua::Result { + queries: Option, + ) -> rlua::Result { let tree = lua_instance.tree.lock().unwrap(); let instance = tree .get_by_ref(lua_instance.id) @@ -387,21 +378,18 @@ impl Remodel { description: String, is_public: bool, allow_comments: bool, - ) -> rlua::Result { - let is_public = bool_into_query(is_public); - let allow_comments = bool_into_query(allow_comments); - + ) -> rlua::Result { Remodel::write_model_asset( context, lua_instance, 0, - &[ - ("type", "Model"), - ("name", &name), - ("description", &description), - ("isPublic", &is_public), - ("allowComments", &allow_comments), - ], + Some(UploadQueries { + asset_type: String::from("Model"), + name: name, + description: description, + is_public: is_public, + allow_comments: allow_comments, + }), ) } @@ -412,21 +400,18 @@ impl Remodel { description: String, is_public: bool, allow_comments: bool, - ) -> rlua::Result { - let is_public = bool_into_query(is_public); - let allow_comments = bool_into_query(allow_comments); - + ) -> rlua::Result { Remodel::write_place_asset( context, lua_instance, 0, - &[ - ("type", "Model"), - ("name", &name), - ("description", &description), - ("isPublic", &is_public), - ("allowComments", &allow_comments), - ], + Some(UploadQueries { + asset_type: String::from("Place"), + name: name, + description: description, + is_public: is_public, + allow_comments: allow_comments, + }), ) } @@ -435,10 +420,9 @@ impl Remodel { lua_instance: LuaInstance, asset_id: u64, ) -> rlua::Result<()> { - match Remodel::write_model_asset(context, lua_instance, asset_id, &[]) { - Ok(_) => Ok(()), - Err(error) => Err(rlua::Error::external(error)), - } + Remodel::write_model_asset(context, lua_instance, asset_id, None)?; + + Ok(()) } fn write_existing_place_asset( @@ -446,18 +430,17 @@ impl Remodel { lua_instance: LuaInstance, asset_id: u64, ) -> rlua::Result<()> { - match Remodel::write_place_asset(context, lua_instance, asset_id, &[]) { - Ok(_) => Ok(()), - Err(error) => Err(rlua::Error::external(error)), - } + Remodel::write_place_asset(context, lua_instance, asset_id, None)?; + + Ok(()) } fn upload_asset( context: Context<'_>, buffer: Vec, asset_id: u64, - queries: &[(&str, &str)], - ) -> rlua::Result { + queries: Option, + ) -> rlua::Result { let re_context = RemodelContext::get(context)?; let auth_cookie = re_context.auth_cookie().ok_or_else(|| { rlua::Error::external( @@ -470,6 +453,28 @@ impl Remodel { asset_id ); + let mut query: Vec<(&str, String)> = Vec::new(); + + if let Some(queries) = queries { + let is_public = if queries.is_public { + String::from("True") + } else { + String::from("False") + }; + + let allow_comments = if queries.allow_comments { + String::from("True") + } else { + String::from("False") + }; + + query.push(("type", queries.asset_type)); + query.push(("name", queries.name)); + query.push(("description", queries.description)); + query.push(("isPublic", is_public)); + query.push(("allowComments", allow_comments)); + } + let client = reqwest::Client::new(); let build_request = move || { client @@ -478,7 +483,7 @@ impl Remodel { .header(USER_AGENT, "Roblox/WinInet") .header(CONTENT_TYPE, "application/xml") .header(ACCEPT, "application/json") - .query(queries) + .query(&query) .body(buffer.clone()) }; @@ -502,7 +507,11 @@ impl Remodel { let asset_id = response.headers().get("roblox-assetid"); match asset_id { - Some(asset_id) => Ok(String::from(asset_id.to_str().unwrap())), + Some(asset_id) => Ok(asset_id + .to_str() + .map_err(rlua::Error::external)? + .parse() + .map_err(rlua::Error::external)?), None => Err(rlua::Error::external("Asset had no roblox-assetid")), } } else { @@ -609,10 +618,10 @@ impl UserData for Remodel { methods.add_function( "writeNewModelAsset", |context, (instance, options): (LuaInstance, Table)| { - let name = get_required_string_option(&options, "name")?; - let description = get_string_option(&options, "description", "")?; - let is_public = get_bool_option(&options, "isPublic", false)?; - let allow_comments = get_bool_option(&options, "allowComments", false)?; + let name = get_required_option(context, &options, "name")?; + let description = get_option(context, &options, "description", String::new())?; + let is_public = get_option(context, &options, "isPublic", false)?; + let allow_comments = get_option(context, &options, "allowComments", false)?; Remodel::write_new_model_asset( context, @@ -628,10 +637,10 @@ impl UserData for Remodel { methods.add_function( "writeNewPlaceAsset", |context, (instance, options): (LuaInstance, Table)| { - let name = get_required_string_option(&options, "name")?; - let description = get_string_option(&options, "description", "")?; - let is_public = get_bool_option(&options, "isPublic", false)?; - let allow_comments = get_bool_option(&options, "allowComments", false)?; + let name = get_required_option(context, &options, "name")?; + let description = get_option(context, &options, "description", String::new())?; + let is_public = get_option(context, &options, "isPublic", false)?; + let allow_comments = get_option(context, &options, "allowComments", false)?; Remodel::write_new_place_asset( context, From a551a56fb5430684710d99cb76d3e36933e4462c Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Sat, 1 May 2021 13:56:17 -0700 Subject: [PATCH 11/12] Combine get_required_option and get_option --- src/remodel_api/remodel.rs | 90 ++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 29 deletions(-) diff --git a/src/remodel_api/remodel.rs b/src/remodel_api/remodel.rs index 603bd4d..916011a 100644 --- a/src/remodel_api/remodel.rs +++ b/src/remodel_api/remodel.rs @@ -30,34 +30,20 @@ fn xml_decode_options() -> rbx_xml::DecodeOptions { rbx_xml::DecodeOptions::new().property_behavior(rbx_xml::DecodePropertyBehavior::ReadUnknown) } -fn get_required_option<'lua, T: FromLua<'lua>>( - context: Context<'lua>, - options: &Table<'lua>, - option: &str, -) -> rlua::Result { - let value = options.get(option).map_err(rlua::Error::external)?; - - if let Value::Nil = value { - Err(rlua::Error::external(format!( - "The option {} must be specified", - option - ))) - } else { - FromLua::from_lua(value, context) - } -} - fn get_option<'lua, T: FromLua<'lua>>( context: Context<'lua>, options: &Table<'lua>, option: &str, - default: T, -) -> rlua::Result { +) -> rlua::Result> { let value = options.get(option).map_err(rlua::Error::external)?; match value { - Value::Nil => Ok(default), - _ => FromLua::from_lua(value, context), + Value::Nil => Ok(None), + _ => { + let value = FromLua::from_lua(value, context)?; + + Ok(Some(value)) + } } } @@ -618,10 +604,33 @@ impl UserData for Remodel { methods.add_function( "writeNewModelAsset", |context, (instance, options): (LuaInstance, Table)| { - let name = get_required_option(context, &options, "name")?; - let description = get_option(context, &options, "description", String::new())?; - let is_public = get_option(context, &options, "isPublic", false)?; - let allow_comments = get_option(context, &options, "allowComments", false)?; + let name: Option = get_option(context, &options, "name")?; + let name = match name { + Some(value) => value, + None => { + return Err(rlua::Error::external(format!( + "The option name must be specified", + ))) + } + }; + + let description = get_option(context, &options, "description")?; + let description = match description { + Some(value) => value, + None => String::new(), + }; + + let is_public = get_option(context, &options, "is_public")?; + let is_public = match is_public { + Some(value) => value, + None => false, + }; + + let allow_comments = get_option(context, &options, "allow_comments")?; + let allow_comments = match allow_comments { + Some(value) => value, + None => false, + }; Remodel::write_new_model_asset( context, @@ -637,10 +646,33 @@ impl UserData for Remodel { methods.add_function( "writeNewPlaceAsset", |context, (instance, options): (LuaInstance, Table)| { - let name = get_required_option(context, &options, "name")?; - let description = get_option(context, &options, "description", String::new())?; - let is_public = get_option(context, &options, "isPublic", false)?; - let allow_comments = get_option(context, &options, "allowComments", false)?; + let name: Option = get_option(context, &options, "name")?; + let name = match name { + Some(value) => value, + None => { + return Err(rlua::Error::external(format!( + "The option name must be specified", + ))) + } + }; + + let description = get_option(context, &options, "description")?; + let description = match description { + Some(value) => value, + None => String::new(), + }; + + let is_public = get_option(context, &options, "is_public")?; + let is_public = match is_public { + Some(value) => value, + None => false, + }; + + let allow_comments = get_option(context, &options, "allow_comments")?; + let allow_comments = match allow_comments { + Some(value) => value, + None => false, + }; Remodel::write_new_place_asset( context, From 28aa6b2a8b324dc2782afebcbb311115d1904db4 Mon Sep 17 00:00:00 2001 From: Micah Hinckley Date: Sat, 1 May 2021 17:04:34 -0700 Subject: [PATCH 12/12] Condense default logic --- src/remodel_api/remodel.rs | 42 ++++++++------------------------------ 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/src/remodel_api/remodel.rs b/src/remodel_api/remodel.rs index 916011a..4959bc1 100644 --- a/src/remodel_api/remodel.rs +++ b/src/remodel_api/remodel.rs @@ -614,23 +614,10 @@ impl UserData for Remodel { } }; - let description = get_option(context, &options, "description")?; - let description = match description { - Some(value) => value, - None => String::new(), - }; - - let is_public = get_option(context, &options, "is_public")?; - let is_public = match is_public { - Some(value) => value, - None => false, - }; - - let allow_comments = get_option(context, &options, "allow_comments")?; - let allow_comments = match allow_comments { - Some(value) => value, - None => false, - }; + let description = get_option(context, &options, "description")?.unwrap_or_default(); + let is_public = get_option(context, &options, "is_public")?.unwrap_or_default(); + let allow_comments = + get_option(context, &options, "allow_comments")?.unwrap_or_default(); Remodel::write_new_model_asset( context, @@ -656,23 +643,10 @@ impl UserData for Remodel { } }; - let description = get_option(context, &options, "description")?; - let description = match description { - Some(value) => value, - None => String::new(), - }; - - let is_public = get_option(context, &options, "is_public")?; - let is_public = match is_public { - Some(value) => value, - None => false, - }; - - let allow_comments = get_option(context, &options, "allow_comments")?; - let allow_comments = match allow_comments { - Some(value) => value, - None => false, - }; + let description = get_option(context, &options, "description")?.unwrap_or_default(); + let is_public = get_option(context, &options, "is_public")?.unwrap_or_default(); + let allow_comments = + get_option(context, &options, "allow_comments")?.unwrap_or_default(); Remodel::write_new_place_asset( context,