Refactor Validator Errors #29
validation
@ -31,6 +31,47 @@ impl Context{
|
|||||||
).await.map_err(Error::Response)?
|
).await.map_err(Error::Response)?
|
||||||
.json().await.map_err(Error::Reqwest)
|
.json().await.map_err(Error::Reqwest)
|
||||||
}
|
}
|
||||||
|
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
|
||||||
|
let url_raw=format!("{}/scripts",self.0.base_url);
|
||||||
|
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut query_pairs=url.query_pairs_mut();
|
||||||
|
query_pairs.append_pair("Page",config.Page.to_string().as_str());
|
||||||
|
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
|
||||||
|
if let Some(name)=config.Name{
|
||||||
|
query_pairs.append_pair("Name",name);
|
||||||
|
}
|
||||||
|
if let Some(hash)=config.Hash{
|
||||||
|
query_pairs.append_pair("Hash",hash);
|
||||||
|
}
|
||||||
|
if let Some(source)=config.Source{
|
||||||
|
query_pairs.append_pair("Source",source);
|
||||||
|
}
|
||||||
|
if let Some(submission_id)=config.SubmissionID{
|
||||||
|
query_pairs.append_pair("SubmissionID",submission_id.to_string().as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response_ok(
|
||||||
|
self.0.get(url).await.map_err(Error::Reqwest)?
|
||||||
|
).await.map_err(Error::Response)?
|
||||||
|
.json().await.map_err(Error::Reqwest)
|
||||||
|
}
|
||||||
|
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
|
||||||
|
let scripts=self.get_scripts(GetScriptsRequest{
|
||||||
|
Page:1,
|
||||||
|
Limit:2,
|
||||||
|
Hash:Some(config.hash),
|
||||||
|
Name:None,
|
||||||
|
Source:None,
|
||||||
|
SubmissionID:None,
|
||||||
|
}).await.map_err(SingleItemError::Other)?;
|
||||||
|
if 1<scripts.len(){
|
||||||
|
return Err(SingleItemError::DuplicateItems);
|
||||||
|
}
|
||||||
|
Ok(scripts.into_iter().next())
|
||||||
|
}
|
||||||
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
|
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
|
||||||
let url_raw=format!("{}/scripts",self.0.base_url);
|
let url_raw=format!("{}/scripts",self.0.base_url);
|
||||||
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
|
||||||
|
@ -60,9 +60,9 @@ pub async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy,PartialEq,Eq,serde::Serialize,serde::Deserialize)]
|
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde::Serialize,serde::Deserialize)]
|
||||||
pub struct ScriptID(pub(crate)i64);
|
pub struct ScriptID(pub(crate)i64);
|
||||||
#[derive(Clone,Copy,serde::Serialize,serde::Deserialize)]
|
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
|
||||||
pub struct ScriptPolicyID(pub(crate)i64);
|
pub struct ScriptPolicyID(pub(crate)i64);
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
@ -70,7 +70,7 @@ pub struct GetScriptRequest{
|
|||||||
pub ScriptID:ScriptID,
|
pub ScriptID:ScriptID,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct GetScriptsRequest<'a>{
|
pub struct GetScriptsRequest<'a>{
|
||||||
pub Page:u32,
|
pub Page:u32,
|
||||||
pub Limit:u32,
|
pub Limit:u32,
|
||||||
@ -83,11 +83,12 @@ pub struct GetScriptsRequest<'a>{
|
|||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
pub SubmissionID:Option<i64>,
|
pub SubmissionID:Option<i64>,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone,Copy,Debug)]
|
||||||
pub struct HashRequest<'a>{
|
pub struct HashRequest<'a>{
|
||||||
pub hash:&'a str,
|
pub hash:&'a str,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(Clone,Debug,serde::Deserialize)]
|
||||||
pub struct ScriptResponse{
|
pub struct ScriptResponse{
|
||||||
pub ID:ScriptID,
|
pub ID:ScriptID,
|
||||||
pub Name:String,
|
pub Name:String,
|
||||||
@ -96,7 +97,7 @@ pub struct ScriptResponse{
|
|||||||
pub SubmissionID:i64,
|
pub SubmissionID:i64,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct CreateScriptRequest<'a>{
|
pub struct CreateScriptRequest<'a>{
|
||||||
pub Name:&'a str,
|
pub Name:&'a str,
|
||||||
pub Source:&'a str,
|
pub Source:&'a str,
|
||||||
@ -104,12 +105,12 @@ pub struct CreateScriptRequest<'a>{
|
|||||||
pub SubmissionID:Option<i64>,
|
pub SubmissionID:Option<i64>,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(Clone,Debug,serde::Deserialize)]
|
||||||
pub struct ScriptIDResponse{
|
pub struct ScriptIDResponse{
|
||||||
pub ID:ScriptID,
|
pub ID:ScriptID,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
|
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum Policy{
|
pub enum Policy{
|
||||||
None=0, // not yet reviewed
|
None=0, // not yet reviewed
|
||||||
@ -120,7 +121,7 @@ pub enum Policy{
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct GetScriptPoliciesRequest<'a>{
|
pub struct GetScriptPoliciesRequest<'a>{
|
||||||
pub Page:u32,
|
pub Page:u32,
|
||||||
pub Limit:u32,
|
pub Limit:u32,
|
||||||
@ -132,7 +133,7 @@ pub struct GetScriptPoliciesRequest<'a>{
|
|||||||
pub Policy:Option<Policy>,
|
pub Policy:Option<Policy>,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(Clone,Debug,serde::Deserialize)]
|
||||||
pub struct ScriptPolicyResponse{
|
pub struct ScriptPolicyResponse{
|
||||||
pub ID:ScriptPolicyID,
|
pub ID:ScriptPolicyID,
|
||||||
pub FromScriptHash:String,
|
pub FromScriptHash:String,
|
||||||
@ -140,20 +141,20 @@ pub struct ScriptPolicyResponse{
|
|||||||
pub Policy:Policy
|
pub Policy:Policy
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct CreateScriptPolicyRequest{
|
pub struct CreateScriptPolicyRequest{
|
||||||
pub FromScriptID:ScriptID,
|
pub FromScriptID:ScriptID,
|
||||||
pub ToScriptID:ScriptID,
|
pub ToScriptID:ScriptID,
|
||||||
pub Policy:Policy,
|
pub Policy:Policy,
|
||||||
}
|
}
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(Clone,Debug,serde::Deserialize)]
|
||||||
pub struct ScriptPolicyIDResponse{
|
pub struct ScriptPolicyIDResponse{
|
||||||
pub ID:ScriptPolicyID,
|
pub ID:ScriptPolicyID,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Clone,Debug,serde::Serialize)]
|
||||||
pub struct UpdateScriptPolicyRequest{
|
pub struct UpdateScriptPolicyRequest{
|
||||||
pub ID:ScriptPolicyID,
|
pub ID:ScriptPolicyID,
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
@ -165,6 +166,7 @@ pub struct UpdateScriptPolicyRequest{
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
pub struct UpdateSubmissionModelRequest{
|
pub struct UpdateSubmissionModelRequest{
|
||||||
pub SubmissionID:i64,
|
pub SubmissionID:i64,
|
||||||
pub ModelID:u64,
|
pub ModelID:u64,
|
||||||
@ -172,15 +174,18 @@ pub struct UpdateSubmissionModelRequest{
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
pub struct ActionSubmissionUploadedRequest{
|
pub struct ActionSubmissionUploadedRequest{
|
||||||
pub SubmissionID:i64,
|
pub SubmissionID:i64,
|
||||||
pub TargetAssetID:Option<u64>,
|
pub TargetAssetID:Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
pub struct ActionSubmissionAcceptedRequest{
|
pub struct ActionSubmissionAcceptedRequest{
|
||||||
pub SubmissionID:i64,
|
pub SubmissionID:i64,
|
||||||
pub StatusMessage:String,
|
pub StatusMessage:String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Copy,Debug)]
|
||||||
pub struct SubmissionID(pub i64);
|
pub struct SubmissionID(pub i64);
|
||||||
|
@ -21,23 +21,33 @@ fn source_has_illegal_keywords(source:&str)->bool{
|
|||||||
source.find("getfenv").is_some()||source.find("require").is_some()
|
source.find("getfenv").is_some()||source.find("require").is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hash_source(source:&str)->String{
|
||||||
|
let mut hasher=siphasher::sip::SipHasher::new();
|
||||||
|
std::hash::Hasher::write(&mut hasher,source.as_bytes());
|
||||||
|
let hash=std::hash::Hasher::finish(&hasher);
|
||||||
|
format!("{:016x}",hash)
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ValidateError{
|
pub enum ValidateError{
|
||||||
Flagged,
|
ScriptFlaggedIllegalKeyword,
|
||||||
Blocked,
|
ScriptBlocked(Option<submissions_api::types::ScriptID>),
|
||||||
NotAllowed,
|
ScriptNotYetReviewed(Option<submissions_api::types::ScriptID>),
|
||||||
Get(rbx_asset::cookie::GetError),
|
ModelFileDownload(rbx_asset::cookie::GetError),
|
||||||
ReadDom(ReadDomError),
|
ModelFileDecode(ReadDomError),
|
||||||
ApiGetScriptPolicy(submissions_api::types::SingleItemError),
|
ApiGetScriptPolicyFromHash(submissions_api::types::SingleItemError),
|
||||||
ApiGetScript(submissions_api::Error),
|
ApiGetScript(submissions_api::Error),
|
||||||
ApiCreateScript(submissions_api::Error),
|
ApiCreateScript(submissions_api::Error),
|
||||||
ApiCreateScriptPolicy(submissions_api::Error),
|
ApiCreateScriptPolicy(submissions_api::Error),
|
||||||
|
ApiGetScriptFromHash(submissions_api::types::SingleItemError),
|
||||||
ApiUpdateSubmissionModel(submissions_api::Error),
|
ApiUpdateSubmissionModel(submissions_api::Error),
|
||||||
ApiActionSubmissionValidate(submissions_api::Error),
|
ApiActionSubmissionValidate(submissions_api::Error),
|
||||||
WriteDom(rbx_binary::EncodeError),
|
ModelFileRootMustHaveOneChild,
|
||||||
Upload(rbx_asset::cookie::UploadError),
|
ModelFileChildRefIsNil,
|
||||||
Create(rbx_asset::cookie::CreateError),
|
ModelFileEncode(rbx_binary::EncodeError),
|
||||||
|
AssetUpload(rbx_asset::cookie::UploadError),
|
||||||
|
AssetCreate(rbx_asset::cookie::CreateError),
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for ValidateError{
|
impl std::fmt::Display for ValidateError{
|
||||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
@ -89,10 +99,10 @@ impl Validator{
|
|||||||
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
|
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
|
||||||
asset_id:validate_info.ModelID,
|
asset_id:validate_info.ModelID,
|
||||||
version:Some(validate_info.ModelVersion),
|
version:Some(validate_info.ModelVersion),
|
||||||
}).await.map_err(ValidateError::Get)?;
|
}).await.map_err(ValidateError::ModelFileDownload)?;
|
||||||
|
|
||||||
// decode dom (slow!)
|
// decode dom (slow!)
|
||||||
let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ReadDom)?;
|
let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ModelFileDecode)?;
|
||||||
|
|
||||||
/* VALIDATE MAP */
|
/* VALIDATE MAP */
|
||||||
|
|
||||||
@ -105,12 +115,12 @@ impl Validator{
|
|||||||
// check the source for illegal keywords
|
// check the source for illegal keywords
|
||||||
if source_has_illegal_keywords(source){
|
if source_has_illegal_keywords(source){
|
||||||
// immediately abort
|
// immediately abort
|
||||||
return Err(ValidateError::Flagged);
|
return Err(ValidateError::ScriptFlaggedIllegalKeyword);
|
||||||
}
|
}
|
||||||
// associate a name and policy with the source code
|
// associate a name and policy with the source code
|
||||||
// policy will be fetched from the database to replace the default policy
|
// policy will be fetched from the database to replace the default policy
|
||||||
script_map.insert(source.clone(),NamePolicy{
|
script_map.insert(source.clone(),NamePolicy{
|
||||||
name:script.name.clone(),
|
name:get_partial_path(&dom,script),
|
||||||
policy:Policy::None,
|
policy:Policy::None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -121,14 +131,12 @@ impl Validator{
|
|||||||
futures::stream::iter(script_map.iter_mut().map(Ok))
|
futures::stream::iter(script_map.iter_mut().map(Ok))
|
||||||
.try_for_each_concurrent(Some(SCRIPT_CONCURRENCY),|(source,NamePolicy{policy,name})|async{
|
.try_for_each_concurrent(Some(SCRIPT_CONCURRENCY),|(source,NamePolicy{policy,name})|async{
|
||||||
// get the hash
|
// get the hash
|
||||||
let mut hasher=siphasher::sip::SipHasher::new();
|
let hash=hash_source(source.as_str());
|
||||||
std::hash::Hasher::write(&mut hasher,source.as_bytes());
|
|
||||||
let hash=std::hash::Hasher::finish(&hasher);
|
|
||||||
|
|
||||||
// fetch the script policy
|
// fetch the script policy
|
||||||
let script_policy=self.api.get_script_policy_from_hash(submissions_api::types::HashRequest{
|
let script_policy=self.api.get_script_policy_from_hash(submissions_api::types::HashRequest{
|
||||||
hash:format!("{:016x}",hash).as_str(),
|
hash:hash.as_str(),
|
||||||
}).await.map_err(ValidateError::ApiGetScriptPolicy)?;
|
}).await.map_err(ValidateError::ApiGetScriptPolicyFromHash)?;
|
||||||
|
|
||||||
// write the policy to the script_map, fetching the replacement code if necessary
|
// write the policy to the script_map, fetching the replacement code if necessary
|
||||||
if let Some(script_policy)=script_policy{
|
if let Some(script_policy)=script_policy{
|
||||||
@ -170,10 +178,22 @@ impl Validator{
|
|||||||
if let Some(script)=dom.get_by_ref_mut(script_ref){
|
if let Some(script)=dom.get_by_ref_mut(script_ref){
|
||||||
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut("Source"){
|
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut("Source"){
|
||||||
match script_map.get(source.as_str()).map(|p|&p.policy){
|
match script_map.get(source.as_str()).map(|p|&p.policy){
|
||||||
Some(Policy::Blocked)=>return Err(ValidateError::Blocked),
|
Some(Policy::Blocked)=>{
|
||||||
|
let hash=hash_source(source.as_str());
|
||||||
|
let script=self.api.get_script_from_hash(submissions_api::types::HashRequest{
|
||||||
|
hash:hash.as_str(),
|
||||||
|
}).await.map_err(ValidateError::ApiGetScriptFromHash)?;
|
||||||
|
return Err(ValidateError::ScriptBlocked(script.map(|s|s.ID)));
|
||||||
|
},
|
||||||
None
|
None
|
||||||
|Some(Policy::None)
|
|Some(Policy::None)
|
||||||
=>return Err(ValidateError::NotAllowed),
|
=>{
|
||||||
|
let hash=hash_source(source.as_str());
|
||||||
|
let script=self.api.get_script_from_hash(submissions_api::types::HashRequest{
|
||||||
|
hash:hash.as_str(),
|
||||||
|
}).await.map_err(ValidateError::ApiGetScriptFromHash)?;
|
||||||
|
return Err(ValidateError::ScriptNotYetReviewed(script.map(|s|s.ID)));
|
||||||
|
},
|
||||||
Some(Policy::Allowed)=>(),
|
Some(Policy::Allowed)=>(),
|
||||||
Some(Policy::Delete)=>{
|
Some(Policy::Delete)=>{
|
||||||
modified=true;
|
modified=true;
|
||||||
@ -195,7 +215,10 @@ impl Validator{
|
|||||||
if modified{
|
if modified{
|
||||||
// serialize model (slow!)
|
// serialize model (slow!)
|
||||||
let mut data=Vec::new();
|
let mut data=Vec::new();
|
||||||
rbx_binary::to_writer(&mut data,&dom,dom.root().children()).map_err(ValidateError::WriteDom)?;
|
let &[map_ref]=dom.root().children()else{
|
||||||
|
return Err(ValidateError::ModelFileRootMustHaveOneChild);
|
||||||
|
};
|
||||||
|
rbx_binary::to_writer(&mut data,&dom,&[map_ref]).map_err(ValidateError::ModelFileEncode)?;
|
||||||
|
|
||||||
// upload a model lol
|
// upload a model lol
|
||||||
let model_id=if let Some(model_id)=validate_info.ValidatedModelID{
|
let model_id=if let Some(model_id)=validate_info.ValidatedModelID{
|
||||||
@ -207,18 +230,22 @@ impl Validator{
|
|||||||
ispublic:None,
|
ispublic:None,
|
||||||
allowComments:None,
|
allowComments:None,
|
||||||
groupId:None,
|
groupId:None,
|
||||||
},data).await.map_err(ValidateError::Upload)?;
|
},data).await.map_err(ValidateError::AssetUpload)?;
|
||||||
|
|
||||||
response.AssetId
|
response.AssetId
|
||||||
}else{
|
}else{
|
||||||
|
// grab the map instance from the map re
|
||||||
|
let Some(map_instance)=dom.get_by_ref(map_ref)else{
|
||||||
|
return Err(ValidateError::ModelFileChildRefIsNil);
|
||||||
|
};
|
||||||
// create new model
|
// create new model
|
||||||
let response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
|
let response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
|
||||||
name:dom.root().name.clone(),
|
name:map_instance.name.clone(),
|
||||||
description:"".to_owned(),
|
description:"".to_owned(),
|
||||||
ispublic:true,
|
ispublic:true,
|
||||||
allowComments:true,
|
allowComments:true,
|
||||||
groupId:None,
|
groupId:None,
|
||||||
},data).await.map_err(ValidateError::Create)?;
|
},data).await.map_err(ValidateError::AssetCreate)?;
|
||||||
|
|
||||||
response.AssetId
|
response.AssetId
|
||||||
};
|
};
|
||||||
@ -291,6 +318,25 @@ fn recursive_collect_superclass(objects:&mut std::vec::Vec<rbx_dom_weak::types::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_partial_path(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->String{
|
||||||
|
struct ParentIter<'a>{
|
||||||
|
dom:&'a rbx_dom_weak::WeakDom,
|
||||||
|
instance:Option<&'a rbx_dom_weak::Instance>,
|
||||||
|
}
|
||||||
|
impl<'a> Iterator for ParentIter<'a>{
|
||||||
|
type Item=&'a rbx_dom_weak::Instance;
|
||||||
|
fn next(&mut self)->Option<Self::Item>{
|
||||||
|
let parent=self.instance.map(|i|i.parent()).and_then(|p|self.dom.get_by_ref(p));
|
||||||
|
core::mem::replace(&mut self.instance,parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tragic:Vec<_>=ParentIter{dom,instance:Some(instance)}.map(|i|i.name.as_str()).collect();
|
||||||
|
tragic.pop();
|
||||||
|
tragic.reverse();
|
||||||
|
tragic.join(".")
|
||||||
|
}
|
||||||
|
|
||||||
fn get_script_refs(dom:&rbx_dom_weak::WeakDom)->Vec<rbx_dom_weak::types::Ref>{
|
fn get_script_refs(dom:&rbx_dom_weak::WeakDom)->Vec<rbx_dom_weak::types::Ref>{
|
||||||
let mut scripts=std::vec::Vec::new();
|
let mut scripts=std::vec::Vec::new();
|
||||||
recursive_collect_superclass(&mut scripts,dom,dom.root(),"LuaSourceContainer");
|
recursive_collect_superclass(&mut scripts,dom,dom.root(),"LuaSourceContainer");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user