diff --git a/src/main.rs b/src/main.rs index 19b1871..0dacec2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -626,7 +626,7 @@ async fn get_asset_exp_backoff( context:&CloudContext, create_asset_response:&rbx_asset::cloud::CreateAssetResponse )->Result{ - let mut backoff:u64=0; + let mut backoff:u16=0; loop{ match create_asset_response.try_get_asset(&context).await{ //try again when the operation is not done @@ -672,9 +672,12 @@ struct CreateAssetMediasConfig{ } #[derive(Debug)] +#[allow(dead_code)] enum CreateAssetMediasError{ NoFileStem(PathBuf), - UnknownFourCC(Option>), + IO(std::io::Error), + UnknownFourCC(Option<[u8;4]>), + Create(rbx_asset::cloud::CreateError), } impl std::fmt::Display for CreateAssetMediasError{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>)->std::fmt::Result{ @@ -684,7 +687,22 @@ impl std::fmt::Display for CreateAssetMediasError{ impl std::error::Error for CreateAssetMediasError{} #[derive(Debug)] +#[allow(dead_code)] +enum PollOperationError{ + CreateAssetMedias(CreateAssetMediasError), + CreateAssetResponseGetAsset(rbx_asset::cloud::CreateAssetResponseGetAssetError), +} +impl std::fmt::Display for PollOperationError{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{self:?}") + } +} +impl std::error::Error for PollOperationError{} + +#[derive(Debug)] +#[allow(dead_code)] enum DownloadDecalError{ + PollOperation(PollOperationError), ParseInt(std::num::ParseIntError), Get(rbx_asset::cookie::GetError), LoadDom(LoadDomError), @@ -703,7 +721,7 @@ async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{ let context=CloudContext::new(config.api_key); let cookie_context=CookieContext::new(config.cookie); let expected_price=Some(config.expected_price.unwrap_or(0)); - let asset_id_list=futures::stream::iter(config.input_files.into_iter() + futures::stream::iter(config.input_files.into_iter() //step 1: read file, make create request .map(|path|{ let description=&config.description; @@ -712,9 +730,9 @@ async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{ async move{ let model_name=path.file_stem() .and_then(std::ffi::OsStr::to_str) - .ok_or(CreateAssetMediasError::NoFileStem(path.clone()))? + .ok_or_else(||CreateAssetMediasError::NoFileStem(path.clone()))? .to_owned(); - let file=tokio::fs::read(path).await?; + let file=tokio::fs::read(path).await.map_err(CreateAssetMediasError::IO)?; let asset_type=match file.get(0..4){ //png Some(b"\x89PNG")=>rbx_asset::cloud::AssetType::Decal, @@ -722,9 +740,9 @@ async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{ Some(b"\xFF\xD8\xFF\xE0")=>rbx_asset::cloud::AssetType::Decal, //Some("fbx")=>rbx_asset::cloud::AssetType::Model, //Some("ogg")=>rbx_asset::cloud::AssetType::Audio, - fourcc=>Err(CreateAssetMediasError::UnknownFourCC(fourcc.map(<[u8]>::to_owned)))?, + fourcc=>Err(CreateAssetMediasError::UnknownFourCC(fourcc.map(|s|s.try_into().unwrap())))?, }; - Ok(context.create_asset(rbx_asset::cloud::CreateAssetRequest{ + context.create_asset(rbx_asset::cloud::CreateAssetRequest{ assetType:asset_type, displayName:model_name, description:description.clone(), @@ -732,59 +750,46 @@ async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{ creator:creator.clone(), expectedPrice:expected_price, } - },file).await?) + },file).await.map_err(CreateAssetMediasError::Create) } })) //parallel requests .buffer_unordered(CONCURRENT_REQUESTS) //step 2: poll operation until it completes - .filter_map(|create_result:AResult<_>|{ + .then(|create_result|{ let context=&context; async{ - match create_result{ - Ok(create_asset_response)=>match get_asset_exp_backoff(context,&create_asset_response).await{ - Ok(asset_response)=>Some(asset_response), - Err(e)=>{ - eprintln!("operation error: {}",e); - None - }, - }, - Err(e)=>{ - eprintln!("create_asset error: {}",e); - None - }, - } + let create_asset_response=create_result.map_err(PollOperationError::CreateAssetMedias)?; + get_asset_exp_backoff(context,&create_asset_response).await.map_err(PollOperationError::CreateAssetResponseGetAsset) } }) //step 3: read decal id from operation and download it, decode it as a roblox file and extract the texture content url - .filter_map(|asset_response|{ - let parse_result=asset_response.assetId.parse(); - async{ - match async{ - let file=cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{ - asset_id:parse_result.map_err(DownloadDecalError::ParseInt)?, - version:None, - }).await.map_err(DownloadDecalError::Get)?; - let dom=load_dom(std::io::Cursor::new(file)).map_err(DownloadDecalError::LoadDom)?; - let instance=dom.get_by_ref( - *dom.root().children().first().ok_or(DownloadDecalError::NoFirstInstance)? - ).ok_or(DownloadDecalError::NoFirstInstance)?; - match instance.properties.get("Texture").ok_or(DownloadDecalError::NoTextureProperty)?{ - rbx_dom_weak::types::Variant::Content(url)=>Ok(url.clone().into_string()), - _=>Err(DownloadDecalError::TexturePropertyInvalid), - } - }.await{ - Ok(asset_url)=>Some((asset_response.displayName,asset_url)), - Err(e)=>{ - eprintln!("get_asset error: {}",e); - None - }, - } + .then(|asset_response_result|{ + let cookie_context=&cookie_context; + async move{ + let asset_response=asset_response_result.map_err(DownloadDecalError::PollOperation)?; + let file=cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{ + asset_id:asset_response.assetId.parse().map_err(DownloadDecalError::ParseInt)?, + version:None, + }).await.map_err(DownloadDecalError::Get)?; + let dom=load_dom(std::io::Cursor::new(file)).map_err(DownloadDecalError::LoadDom)?; + let instance=dom.get_by_ref( + *dom.root().children().first().ok_or(DownloadDecalError::NoFirstInstance)? + ).ok_or(DownloadDecalError::NoFirstInstance)?; + let texture=instance.properties.get("Texture").ok_or(DownloadDecalError::NoTextureProperty)?; + let asset_url=match texture{ + rbx_dom_weak::types::Variant::Content(url)=>url.clone().into_string(), + _=>Err(DownloadDecalError::TexturePropertyInvalid)?, + }; + Ok::<_,DownloadDecalError>((asset_response.displayName,asset_url)) } - }).collect::>().await; - for (file_name,asset_url) in asset_id_list{ - println!("{}={}",file_name,asset_url); - } + }) + .for_each(|download_decal_result|async{ + match download_decal_result{ + Ok((file_name,asset_url))=>println!("{}={}",file_name,asset_url), + Err(e)=>eprintln!("ERROR error={e}"), + } + }).await; Ok(()) }