diff --git a/rbx_asset/src/cookie.rs b/rbx_asset/src/cookie.rs index 54df2ae..f517cdd 100644 --- a/rbx_asset/src/cookie.rs +++ b/rbx_asset/src/cookie.rs @@ -95,6 +95,56 @@ impl std::fmt::Display for GetError{ } impl std::error::Error for GetError{} +#[derive(Debug)] +pub enum GetAssetV2Error{ + ParseError(url::ParseError), + Response(ResponseError), + VersionHeaderMissing, + ToStr(reqwest::header::ToStrError), + ParseInt(std::num::ParseIntError), + Reqwest(reqwest::Error), +} +impl std::fmt::Display for GetAssetV2Error{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f,"{self:?}") + } +} +impl std::error::Error for GetAssetV2Error{} + +#[derive(serde::Deserialize)] +#[allow(nonstandard_style,dead_code)] +pub struct GetAssetV2AssetMetadata{ + pub metadataType:u32, + pub value:String, +} +#[derive(serde::Deserialize)] +#[allow(nonstandard_style,dead_code)] +pub struct GetAssetV2Location{ + pub assetFormat:String,// "source" + location:String,// this value is private so users cannot mutate it + pub assetMetadatas:Vec<GetAssetV2AssetMetadata>, +} +impl GetAssetV2Location{ + pub fn location(&self)->&str{ + &self.location + } +} +#[derive(serde::Deserialize)] +#[allow(nonstandard_style,dead_code)] +pub struct GetAssetV2Info{ + pub locations:Vec<GetAssetV2Location>, + pub requestId:String, + pub IsHashDynamic:bool, + pub IsCopyrightProtected:bool, + pub isArchived:bool, + pub assetTypeId:u32, +} + +pub struct GetAssetV2{ + pub version:u64, + pub info:GetAssetV2Info, +} + #[derive(Debug)] #[derive(serde::Deserialize,serde::Serialize)] pub enum CreatorType{ @@ -411,6 +461,50 @@ impl CookieContext{ Err(e)=>Err(e), }.map_err(GetError::IO) } + pub async fn get_asset_v2(&self,config:GetAssetRequest)->Result<GetAssetV2,GetAssetV2Error>{ + let mut url=reqwest::Url::parse("https://assetdelivery.roblox.com/v2/asset").map_err(GetAssetV2Error::ParseError)?; + //url borrow scope + { + let mut query=url.query_pairs_mut();//borrow here + query.append_pair("ID",config.asset_id.to_string().as_str()); + if let Some(version)=config.version{ + query.append_pair("version",version.to_string().as_str()); + } + } + let response=crate::response_ok( + self.get(url).await.map_err(GetAssetV2Error::Reqwest)? + ).await.map_err(GetAssetV2Error::Response)?; + + let version=response + .headers() + .get("roblox-assetversionnumber") + .ok_or(GetAssetV2Error::VersionHeaderMissing)? + .to_str() + .map_err(GetAssetV2Error::ToStr)? + .parse() + .map_err(GetAssetV2Error::ParseInt)?; + + let info=response.json().await.map_err(GetAssetV2Error::Reqwest)?; + + Ok(GetAssetV2{ + version, + info, + }) + } + pub async fn get_asset_v2_download(&self,config:&GetAssetV2Location)->Result<Vec<u8>,GetError>{ + let url=reqwest::Url::parse(config.location.as_str()).map_err(GetError::ParseError)?; + + let body=crate::response_ok( + self.get(url).await.map_err(GetError::Reqwest)? + ).await.map_err(GetError::Response)? + .bytes().await.map_err(GetError::Reqwest)?; + + match maybe_gzip_decode(std::io::Cursor::new(body)){ + Ok(ReaderType::GZip(readable))=>read_readable(readable), + Ok(ReaderType::Raw(readable))=>read_readable(readable), + Err(e)=>Err(e), + }.map_err(GetError::IO) + } pub async fn get_asset_details(&self,config:GetAssetDetailsRequest)->Result<AssetDetails,GetError>{ let url=reqwest::Url::parse(format!("https://economy.roblox.com/v2/assets/{}/details",config.asset_id).as_str()).map_err(GetError::ParseError)?; crate::response_ok(